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

創宇前端發表於2019-05-06

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

背景

在我看來,再牛逼的技術不落到實處永遠是紙上談兵,秉承著 學以致用 的校訓。一個字 “幹” 就完事了?。

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

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

啥是 CoolQ

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

(編者按:這個有一定的歷史原因……)

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

酷Q,是一款基於 Smart 協議功能強大的機器人軟體,它可以通過安裝外掛實現自動稽核他人申請入群、自動踢人、自動管理群等自動化操作,還能實現自動群聊、自動聊天,起到活躍群組氣氛的重要作用,節省您的寶貴時間。

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

技術文章來源

如果讓你來搞,你會怎麼辦呢?你可能會想到爬蟲。可以爬頁面,也可以爬介面……這裡我用 RSS 來實現。

一部分年輕的朋友可能已經沒有聽說過 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)。歡迎留言討論,我們會盡可能回覆。

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

感謝您的閱讀。

相關文章