2019 前端自嗨 coolq 篇 -- 技术文章推送

1,576 阅读5分钟

背景 (Background)

在我看来,再牛逼的技术不落到实处永远是纸上谈兵,秉承着 学以致用 的校训。一个字 “干” 就完事了 💪

2019 年了,如何将前端知识学以致用呢 🤔️,前段时间 QQ 群里大佬们突然玩起了 coolq 机器人,用机器人进行 在线陪聊、在线点歌 在线搜图的功能 乃至。。。(你能想到的一切 😏)

突然脑子里闪过一道光 ⚡️,卧槽牛逼呀,我也要玩

coolq what fuck

说实话,在 coolq 的官网看了半天,愣是不知道它是个什么玩意!!!难道你都不介绍一下自己是干哈的嘛,差评

以下是 百度百科coolq 的定义

酷 Q,是一款基于 Smart 协议功能强大的机器人软件,它可以通过安装插件实现自动审核他人申请入群、自动踢人、自动管理群等自动化操作,还能实现自动群聊、自动聊天,起到活跃群组气氛的重要作用,节省您的宝贵时间。

突然发现 coolq 好牛逼,讲道理 我只是使用了 cooql 消息推送的功能 😳

技术文章来源

如果让你来搞,你会怎么办呢?你可能会想到 爬虫,可以爬页面,也可以爬接口。。。我这里选用的是 rss。你在说什么? rss 什么鬼,我怎么没听说过,难道是一门新的技术?NO RSS 简介

问题来了,不是所有的网站都提供 rss 服务,比如说 掘金 ,那么问题来了,那我怎么使用呢?在这里给大家安利一波 RSSHub,它为我这种懒人提供了福利,如果里面没有找到你想要的内容,那么很抱歉只能自己手撕代码了

在继续阅读之前,你最好能够 了解一点 Mongodb 的相关知识,了解一点 Docker 的相关知识。如果你和和我一样是个菜鸡,问题也不是很大

实操 (Practical operation)

1. 安装 coolq

这里有一点不得不提,由于 coolq 官网 只提供 windows 版本的,因此 如果想要在 Linux / MacOS 系统上安装 官方 推荐的是通过 Docker 安装对应的镜像文件。(2019 年了,如果你还不会 Docker 你就 out 了,有些知识不深可以,但是广一点是没有任何毛病的 🙃)

如果默认的功能不能满足你的需求,想要自己开发一些好玩新奇的功能,则需要安装 开发版

  1. 获取镜像
docker pull coolq/wine-coolq
  1. 创建文件夹,用于存放 coolq 持久化数据
# 任意路径均可
mkdir /root/coolq-data
  1. 运行镜像
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. 启动 coolq

打开浏览器 输入 localhost:9000

点击 connect 输入 默认密码 MAX8char

输入 QQ 账号 及 密码 (推荐注册小号,以防风险) 登录 coolq

  1. 获取镜像
docker pull richardchien/cqhttp:latest
  1. 创建文件夹,用于存放 coolq 持久化数据

  2. 运行镜像

docker run -ti --rm --name cqhttp -p 9000:9000 -p 5700:5700 -v /root/coolq-data:/home/user/coolq richardchien/cqhttp
  1. 启动 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

相关文档 (Related documents)