當前端玩起 CoolQ:作個技術文章推送機器人

編者按:很久不見,今天的文章來自創宇前端讀者投稿。創宇前端接受讀者首發或受權轉載的原創技術分享,咱們會充分尊重讀者的全部權益,並對選用文章做者贈送任選技術書籍聊表心意 :)javascript

背景

在我看來,再牛逼的技術不落到實處永遠是紙上談兵,秉承着 學以至用 的校訓。一個字 「幹」 就完事了💪。html

2019 年了,如何將前端知識學以至用呢🤔️?前段時間 QQ 羣裏大佬們忽然玩起了 CoolQ 機器人,用機器人進行在線陪聊、在線點歌、在線搜圖乃至……(你能想到的一切😏)前端

忽然腦子裏閃過一道光 ⚡️,臥槽牛嗶呀,我也要玩!做爲一個天天早上醒來都有新輪子的前端,不如搞個技術文章推送吧!java

啥是 CoolQ

說實話,在 CoolQ 的官網看了半天,愣是不知道它是個什麼玩意!難道你都不介紹一下本身是幹哈的嘛,差評。git

(編者按:這個有必定的歷史緣由……)github

下面的介紹來自百度百科:算法

酷Q,是一款基於 Smart 協議功能強大的機器人軟件,它能夠經過安裝插件實現自動審覈他人申請入羣、自動踢人、自動管理羣等自動化操做,還能實現自動羣聊、自動聊天,起到活躍羣組氣氛的重要做用,節省您的寶貴時間。mongodb

好牛嗶,接下來我只是使用了 CoolQ 消息推送的功能😳。docker

技術文章來源

若是讓你來搞,你會怎麼辦呢?你可能會想到爬蟲。能夠爬頁面,也能夠爬接口……這裏我用 RSS 來實現。shell

一部分年輕的朋友可能已經沒有據說過 RSS 了,時代的眼淚……你能夠在這裏讀到相關介紹::www.runoob.com/rss/rss-int…

然而,並非全部的網站都提供 RSS 服務,好比說 掘金。在這裏安利 RSSHub,它替咱們獲取頁面內容並生成 RSS 源,爲咱們懶人提供了福利。若是裏面沒有找到你想要的內容,那麼很抱歉只能本身手撕代碼了

實操

1. 安裝 CoolQ

CoolQ 官網只提供 Windows 版本,所以,若是想要裝在 Linux 或 macOS 上,官方推薦經過 Docker 安裝對應的鏡像文件。(9102 年了,若是你還不會 Docker 你就 out 了,有些知識不深能夠,可是廣一點是沒有任何毛病的 🙃)

通常咱們安裝普通版

若是默認的功能不能知足你的需求,想要本身開發一些好玩新奇的功能,則能夠安裝開發版

接下來咱們分別介紹這兩個版本的安裝方式。

普通版安裝

1.1. 獲取鏡像

docker pull coolq/wine-coolq
複製代碼

1.2. 建立文件夾,用於存放 CoolQ 持久化數據

# 任意路徑都可
mkdir /root/coolq-data
複製代碼

1.3. 運行鏡像

docker run --name=coolq --rm -p 9000:9000 -v /root/coolq-data:/home/user/coolq coolq/wine-coolq
複製代碼
  • --name 建立一個容器
  • --rm 這個參數是說容器退出以後隨之將其刪除。默認狀況下,爲了排障需求,退出的容器不會當即刪除,除非手動 docker rm
  • -p <宿主端口>:<容器端口> -p,是用來映射宿主端口和容器端口,換句話說,就是將容器的對應端口服務公開給外界訪問
  • -v指定掛載一個本地主機的目錄到容器中去

1.4. 啓動 CoolQ

打開瀏覽器 輸入 localhost:9000

點擊 connect 輸入 默認密碼 MAX8char

輸入QQ號及密碼(推薦註冊小號,以防風險)登陸 CoolQ

開發版安裝

1.1. 獲取鏡像

docker pull richardchien/cqhttp:latest
複製代碼

1.2. 建立文件夾,用於存放 CoolQ 持久化數據

1.3. 運行鏡像

docker run -ti --rm --name cqhttp -p 9000:9000 -p 5700:5700 -v /root/coolq-data:/home/user/coolq richardchien/cqhttp
複製代碼

1.4. 啓動 CoolQ

2. 安裝MongoDB

略(相信這必定不會難道小天才的你 😉)

3. 擼代碼

爬文章

async function crawl(url) {
 try {
   const feed = await parser.parseURL(url);
   const items = feed.items.map(({ title, link, guid = link }) => {
     title = title.trim();
     link = link.trim();
     guid = guid.trim();

     console.log(title, link);
     return { title, link, guid };
   });

   return items;
 } catch (err) {
   console.log(err);
 }
}
複製代碼

數據庫插入數據

async function insert(db, { title, link, guid }) {
 const collection = db.collection(collectionName);
 // Insert some documents
 try {
   await collection.updateOne(
     {
       guid
     },
     {
       $set: { title, link, guid },
       $setOnInsert: { status: 0 }
     },
     {
       upsert: true
     }
   );
 } catch (err) {
   console.log(err);
 }
}
複製代碼

數據庫查找數據

status 0: 未推送 1: 已推送

async function find(db) {
 const collection = db.collection(collectionName);
 // Find some documents
 try {
   return await collection
     .find({
       status: 0
     })
     .toArray();
 } catch (err) {
   console.log(err);
 }
}
複製代碼

推送消息

group_id 羣號

const request = require('superagent');

async function send(message) {
 return await request
   .post('http://0.0.0.0:5700/send_group_msg')
   .send({ group_id: XXX, message })
   .set('Accept', 'application/json');
}
複製代碼

數據爬取及存儲代碼整合

const MongoClient = require('mongodb').MongoClient;
const Parser = require('rss-parser');
const parser = new Parser();
const url = 'mongodb://localhost:27017';
const dbName = 'robot'; // 數據庫名
const collectionName = 'juejin'; // 集合名(表名)
const pullList = ['https://rsshub.app/juejin/category/frontend'];

// 插入
async function insert(db, { title, link, guid }) {
 const collection = db.collection(collectionName);
 // Insert some documents
 try {
   await collection.updateOne(
     {
       guid
     },
     {
       $set: { title, link, guid },
       $setOnInsert: { status: 0 }
     },
     {
       upsert: true
     }
   );
 } catch (err) {
   console.log(err);
 }
}

// 爬蟲
async function crawl(url) {
 try {
   const feed = await parser.parseURL(url);
   const items = feed.items.map(({ title, link, guid = link }) => {
     title = title.trim();
     link = link.trim();
     guid = guid.trim();
     console.log(title, link);
     return { title, link, guid };
   });

   return items;
 } catch (err) {
   console.log(err);
 }
}

(async () => {
 // Create a new MongoClient
 const client = new MongoClient(url);

 try {
   // Use connect method to connect to the Server
   await client.connect();

   console.log('Connected successfully to server');

   const db = client.db(dbName);

   const promises = pullList.map((value) => {
     return (async () => {
       const items = await crawl(value);
       const insertPromises = items.map((item) => {
         return insert(db, item);
       });

       await Promise.all(insertPromises);
     })();
   });

   await Promise.all(promises).then(() => {
     client.close();
   });
 } catch (err) {
   console.log(err.stack);
 }
})();
複製代碼

數據推送及查找代碼整合

爲了保障代碼的運行記得修的修改成本身的 QQ 羣號(如下僅以發送羣組消息爲例,具體的也能夠是發送私信,討論組消息)

const MongoClient = require('mongodb').MongoClient;
const request = require('superagent');
const url = 'mongodb://localhost:27017';
const dbName = 'robot'; // 數據庫名
const collectionName = 'juejin'; // 集合名(表名)

// 查找
async function find(db) {
 const collection = db.collection(collectionName);
 // Find some documents
 try {
   return await collection
     .find({
       status: 0
     })
     .toArray();
 } catch (err) {
   console.log(err);
 }
}

// 更新
async function update(db, { guid }) {
 const collection = db.collection(collectionName);
 // Update some documents
 try {
   await collection.updateOne(
     {
       guid
     },
     {
       $set: { status: 1 }
     }
   );
 } catch (err) {
   console.log(err);
 }
}

// 推送 羣組
async function send(message) {
 return await request
   .post('http://0.0.0.0:5700/send_group_msg')
   .send({ group_id: XXX, message }) // 記得修改喲😊
   .set('Accept', 'application/json');
}

(async () => {
 // Create a new MongoClient
 const client = new MongoClient(url);

 try {
   // Use connect method to connect to the Server
   await client.connect();

   console.log('Connected successfully to server');

   const db = client.db(dbName);

   const docs = await find(db);

   console.log(docs);

   let message = '';
   message = docs.reduce((acu, { title, link }, index) => {
     return `${acu}${title} ${link}${index === docs.length - 1 ? '' : '\n'}`;
   }, message);

   const { text } = await send(message);
   const { status, retcode } = JSON.parse(text);
   if (status === 'ok' && retcode === 0) {
     const promises = docs.map((value) => {
       return update(db, value);
     });

     await Promise.all(promises);
   } else {
     console.log(status, retcode);
   }

   client.close();
 } catch (err) {
   console.log(err.stack);
 }
})();
複製代碼

展望

以上只是對於 CoolQ 的簡單應用,最近還有一個 餓了麼外賣推送 的想法,就是根據商家的滿減優惠計算出 最優套餐,可是礙於算法的問題,暫時卡在了這一塊。若是你還有什麼其餘想法歡迎一塊兒交流~

最後,送諸位一句 大膽假設,當心求證,人人都是科學家😂,歡迎曬出你的 idea!


文 / lastSeries

做者也在掘金哦,快關注他吧!

編 / 熒聲

本文由創宇前端做者受權發佈,版權屬於做者,創宇前端出品。 歡迎註明出處轉載本文。文章連接:原文連接

想要訂閱更多來自知道創宇開發一線的分享,請搜索關注咱們的微信公衆號:創宇前端(KnownsecFED)。歡迎留言討論,咱們會盡量回復。

感謝您的閱讀。

相關文章
相關標籤/搜索