├── .gitignore ├── static └── imgs │ ├── 111.png │ ├── 222.png │ └── 333.png ├── src ├── dtbot.memory-card.json ├── utils │ ├── comutil.js │ ├── checker.js │ └── networkutil.js ├── test │ └── mytest.js ├── config.js └── dtbot.js ├── README.en.md ├── package.json ├── .vscode └── launch.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /static/imgs/111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDev2K/dtbot_helper/HEAD/static/imgs/111.png -------------------------------------------------------------------------------- /static/imgs/222.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDev2K/dtbot_helper/HEAD/static/imgs/222.png -------------------------------------------------------------------------------- /static/imgs/333.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeDev2K/dtbot_helper/HEAD/static/imgs/333.png -------------------------------------------------------------------------------- /src/dtbot.memory-card.json: -------------------------------------------------------------------------------- 1 | {"\rpuppet\nWECHATY_PUPPET_PADPLUS":{"qrcodeId":"","uin":"1281647324","userName":"wxid_wb4l4qwzonzq22"}} -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # DTBot 2 | 3 | #### Description 4 | {**Doutool Robot for taobao, jd**} 5 | 6 | #### Software Architecture 7 | use wechaty setting 8 | 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DTRobot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "qrcode-terminal": "^0.12.0", 14 | "wechaty": "^0.31.17", 15 | "wechaty-puppet-padplus": "^0.4.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "启动程序", 11 | "program": "${workspaceFolder}\\src\\test\\mytest.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/utils/comutil.js: -------------------------------------------------------------------------------- 1 | function getHttpString(s) { 2 | var reg = /(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-)+)/g; 3 | //var reg = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/; 4 | //var reg=/(http(s)?\:\/\/)?(www\.)?(\w+\:\d+)?(\/\w+)+\.(swf|gif|jpg|bmp|jpeg)/gi; 5 | //var reg=/(http(s)?\:\/\/)?(www\.)?(\w+\:\d+)?(\/\w+)+\.(swf|gif|jpg|bmp|jpeg)/gi; 6 | var reg = /(https?|http|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g; 7 | //var reg= /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?$/; 8 | //v = v.replace(reg, "$1$2"); //这里的reg就是上面的正则表达式 9 | //s = s.replace(reg, "$1$2"); //这里的reg就是上面的正则表达式 10 | s = s.match(reg); 11 | console.log(s) 12 | return (s) 13 | } 14 | 15 | //随机数 16 | function random(lower, upper) { 17 | return Math.floor(Math.random() * (upper - lower)) + lower; 18 | } 19 | 20 | module.exports = { 21 | getHttpString, 22 | random, 23 | } -------------------------------------------------------------------------------- /src/test/mytest.js: -------------------------------------------------------------------------------- 1 | const checker = require('../utils/checker'); 2 | const comutil = require('../utils/comutil'); 3 | const networkutil = require('../utils/networkutil'); 4 | const config = require('../config'); 5 | 6 | // const targetRooms = checker.config['TARGET_YANGMAO_ROOMS'] 7 | // for (i = 0; i < targetRooms.length; i++) { 8 | // console.log("找到目标群: " + targetRooms[i]) 9 | // } 10 | 11 | // // console.log(checker.isKeywordFromYangmao("口xx罩xxxx")) 12 | // console.log(comutil.random(5,10)) 13 | 14 | 15 | 16 | 17 | // networkutil.transJd('https://u.jd.com/jawKeJ', function (code, url) { 18 | // console.log(url) 19 | // }) 20 | 21 | // console.log('https://u.jd.com/jawKeJ'.indexOf('jd.com') != -1) 22 | 23 | 24 | // networkutil.getFullMask(async (ret, msg, data) => { 25 | // if (ret == 0) { 26 | // console.log("\n\n----------------------" + data.title) 27 | // } 28 | // }) 29 | 30 | var lastTime = new Date().getTime(); 31 | var random = comutil.random(config.intervals.GET_FULL_MASK[0], config.intervals.GET_FULL_MASK[1]); 32 | setInterval(function () { 33 | var currentTime = new Date().getTime(); 34 | var offset = currentTime - lastTime; 35 | if (offset > random) { 36 | lastTime = currentTime; 37 | console.log("offset: " + offset + " lastTime: " + lastTime + " random: " + random); 38 | random = comutil.random(1000, 10000); 39 | } 40 | }, 1000); -------------------------------------------------------------------------------- /src/utils/checker.js: -------------------------------------------------------------------------------- 1 | const config = require('../config'); 2 | 3 | //是否口罩信息群 4 | function isMaskRoom(topic) { 5 | if (topic.indexOf("口罩") != -1) { 6 | return true 7 | } 8 | return false 9 | } 10 | 11 | //检查来自口罩群信息的关键词 12 | function isKeyWordFromMask(msg) { 13 | var words = config.keyword.KOUZHAO_KEYWORD; 14 | for (i = 0; i < words.length; i++) { 15 | if (msg.indexOf(words[i])) { 16 | return true 17 | } 18 | } 19 | return false 20 | } 21 | 22 | //是否羊毛群 23 | function isYangmaoRoom(topic) { 24 | var rooms = config.groups.LISTEN_YANGMAO_ROOMS; 25 | for (i = 0; i < rooms.length; i++) { 26 | if (rooms[i] == topic) { 27 | return true 28 | } 29 | } 30 | return false 31 | } 32 | 33 | function isKeywordFromYangmao(msg) { 34 | var words = config.keyword.YANGMAO_KEYWORD; 35 | for (i = 0; i < words.length; i++) { 36 | if (msg.indexOf(words[i]) != -1) { 37 | return true 38 | } 39 | } 40 | return false 41 | } 42 | 43 | //是否合适的口罩信息 44 | function isValueMaskInfo(msg){ 45 | var words = config.keyword.KOUZHAO_REMOVE_INFO_KEYWORD; 46 | for (i = 0; i < words.length; i++) { 47 | if (msg.indexOf(words[i]) != -1) { 48 | //符合条件,不合适 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | 55 | module.exports = { 56 | isMaskRoom, 57 | isKeyWordFromMask, 58 | isKeywordFromYangmao, 59 | isYangmaoRoom, 60 | isValueMaskInfo, 61 | } -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const groups = { 2 | //要监听的羊毛群 3 | LISTEN_YANGMAO_ROOMS: [ 4 | // '白菜价游全球3群', 5 | '刚需福利好物分享发布', 6 | // '羊毛监听测试', 7 | ], 8 | //监听到羊毛消息,转发到这个群 9 | TARGET_YANGMAO_ROOMS: [ 10 | '优惠线报发布中心', 11 | '全网口罩监控分享(刚需福利群)01', 12 | // '全网口罩互助监控群01', 13 | ], 14 | //监听到口罩信息,转发到这个群 15 | TARGET_MASK_ROOMS: [ 16 | // '抖兔', 17 | // '全网口罩互助监控群01', 18 | // '全网KZ互助监控群01', 19 | // '『禁言』全平台口罩等物资放货监控', 20 | // '全网口罩监控分享(刚需福利群)01', 21 | // '[禁言]全平台口罩等物资放货监控' 22 | ], 23 | //监听退出进入的群 24 | LEAVE_JOIN_ROOMS: [ 25 | '羊毛监听测试', 26 | '全网口罩互助监控群01', 27 | '全网口罩监控分享(刚需福利群)01', 28 | '全网口罩互助监控群【停更】', 29 | ], 30 | } 31 | 32 | const keyword = { 33 | //监听羊毛群的关键词 34 | YANGMAO_KEYWORD: [ 35 | '口罩', 36 | '京东', 37 | '额温枪', 38 | '耳温枪', 39 | 'jd.com', 40 | '淘口令', 41 | ], 42 | //监听到口罩群的关键词 43 | KOUZHAO_KEYWORD: [ 44 | '口罩', 45 | '库存情况', 46 | '额温枪', 47 | ], 48 | //过滤掉的口罩信息 49 | KOUZHAO_REMOVE_INFO_KEYWORD:[ 50 | '电动', 51 | '伊藤良品', 52 | '车间', 53 | '面罩', 54 | '面具', 55 | '防毒', 56 | ] 57 | } 58 | 59 | const basic = { 60 | // HOST: 'http://192.168.246.1:8000', 61 | HOST: 'your api', 62 | TOKEN: 'your token', 63 | NAME: 'youbotname', 64 | } 65 | 66 | const intervals = { 67 | GET_FULL_MASK:[30000, 120000],// 68 | GET_SMZDM_MASK:[60000, 200000],// 69 | } 70 | 71 | module.exports = { 72 | groups, 73 | keyword, 74 | basic, 75 | intervals, 76 | } -------------------------------------------------------------------------------- /src/utils/networkutil.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const qs = require('querystring'); 3 | const config = require('../config'); 4 | 5 | function get(path, data, callback) { 6 | var content = qs.stringify(data); 7 | console.log("start http get: " + content); 8 | var url = config.basic.HOST + path; 9 | if(content != null && content != ""){ 10 | url += "?" + content; 11 | } 12 | request(url, function (error, response, ret) { 13 | console.log("response: " + response+ " ret: " + ret); 14 | console.log("error: " + error); 15 | if (response.statusCode == 200) { 16 | callback(response.statusCode, JSON.parse(ret)); 17 | } else { 18 | callback(response.statusCode, error); 19 | } 20 | }); 21 | } 22 | 23 | //京东转链 24 | function transJd(jdlink, callback) { 25 | get('/tbk/get_jd_url', { 26 | 'jdlink': jdlink 27 | }, function (statusCode, ret) { 28 | console.log(ret); 29 | if (statusCode == 200) { 30 | if (ret.code == 0) { 31 | var shortUrl = ret.data.shortURL; 32 | callback(0, shortUrl); 33 | return; 34 | } 35 | } 36 | callback(-1, null); 37 | }) 38 | } 39 | 40 | function getFullMask(callback){ 41 | get('/tbk/get_full_mask', {}, function(statusCode, ret){ 42 | if(statusCode == 200){ 43 | if(ret.code == 0){ 44 | callback(0, ret.msg, ret.data) 45 | } 46 | } 47 | callback(-1, null, null) 48 | }) 49 | } 50 | 51 | function getSmzdmMask(callback){ 52 | get('/tbk/get_smzdm_mask', {}, function(statusCode, ret){ 53 | if(statusCode == 200){ 54 | if(ret.code == 0){ 55 | callback(0, ret.msg, ret.data) 56 | } 57 | } 58 | callback(-1, null, null) 59 | }) 60 | } 61 | 62 | module.exports = { 63 | transJd, 64 | getSmzdmMask, 65 | getFullMask, 66 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![ 由Wechaty提供 ](https://img.shields.io/badge/Powered%20By-Wechaty-blue.svg)](https://github.com/chatie/wechaty) 4 | 5 | [![node version](https://img.shields.io/badge/node-%3E%3D10-blue.svg)](http://nodejs.cn/download/) 6 | [![node version](https://img.shields.io/badge/wechaty-%3E%3D0.26-blue.svg)](https://github.com/Chatie/wechaty) 7 | ![](https://img.shields.io/badge/Window-green.svg) 8 | ![](https://img.shields.io/badge/Mac-yellow.svg) 9 | ![](https://img.shields.io/badge/Centos-blue.svg) 10 | 11 | 12 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 13 | 14 | 15 | ## 淘客小助手1.0 16 | 17 | 让闲置的微信,成为你的淘客小帮手 18 | 19 | 群消息活跃,多群信息管理,自动监听同行发单,群维护,群口罩货源监控等 20 | 21 | ## 部分效果预览(功能持续完善中) 22 | 23 | ![](./static/imgs/111.png) 24 | 25 | 口罩群实时监控 26 | 27 | ![](./static/imgs/222.png) 28 | 29 | 群消息监控,转发,设置条件转发到多个群 30 | 31 | ## 安装 32 | 33 | ### 1、安装依赖 34 | ``` 35 | npm init -y 36 | npm install wechaty@next 37 | npm install wechaty-puppet-padplus@latest 38 | npm install qrcode-terminal 39 | npm install pm2 40 | ``` 41 | 42 | ### 2、配置token和要监控的群 43 | 44 | 拉取项目前,国内用户请配置好npm的淘宝源,**(很重要,防止下载chromium失败,因为下载文件在150M左右,请耐心等待)** 45 | * 在src目录下存在一个`dtbot.js`文件 46 | 47 | ``` 48 | const token = 'your wechaty token'; 49 | ``` 50 | * 在src/utils目录下存在一个`checker.js`文件 51 | 52 | ``` 53 | var config = { 54 | //要监听的羊毛群 55 | 'LISTEN_YANGMAO_ROOMS': [ 56 | '刚需福利好物分享发布', 57 | '羊毛监听测试', 58 | ], 59 | //监听到羊毛消息,转发到这个群 60 | 'TARGET_YANGMAO_ROOMS': [ 61 | '优惠线报发布中心', 62 | // '全网口罩互助监控群01', 63 | ], 64 | //监听到口罩信息,转发到这个群 65 | 'TARGET_MASK_ROOMS': [ 66 | // '抖兔', 67 | '全网口罩互助监控群01', 68 | ], 69 | //监听退出进入的群 70 | 'LEAVE_JOIN_ROOMS': [ 71 | '羊毛监听测试', 72 | '全网口罩互助监控群01', 73 | ] 74 | } 75 | ``` 76 | ### 3、服务器接口配置 77 | 关于口罩库存消息接口,需要自己完善 78 | 79 | ### 4、启动项目 80 | 81 | ``` 82 | 本地启动: node dtbot.js 83 | 服务器启动:pm2 start dtbot.js 84 | ``` 85 | 86 | 87 | ### 5. 更多问题 88 | 关于wechaty的相关接口,请 89 | [参考wechaty官网文档](https://docs.chatie.io/v/zh/),如果以上还没有解决你的问题,请先往wechaty的项目[issues](https://github.com/Chatie/wechaty/issues)中查找是否存在相同的问题,由于本项目是依赖wechaty开发,所以启动时遇到的问题大部分是wechaty的。 90 | 91 | 92 | ## 注意 93 | 94 | 本项目属于个人开发,为淘客日常活跃群气氛的小助手,你可以在此基础上新增你需要的有趣的功能 95 | 96 | ## 体验与技术交流 97 | 98 | 欢迎有兴趣的小伙伴可以微信关注“抖兔工具箱”,回复`“口罩”`进入进群 99 | 如果对技术交流有兴趣,欢迎联系我 100 | 101 | ![](./static/imgs/333.png) 102 | 103 | ### 淘客小助手功能 104 | | 功能 | 触发事件 | 例子 | 说明 | 105 | | ------------| ---------|---------- |------- | 106 | | 群欢迎 |某人加入指定群聊 |欢迎xx加入 | 完成| 107 | | 口罩货源监控 |定时消息 |自动群发到指定的群 | 完成| 108 | | 消息转发 |监控主群消息,转发到副群 |一次发布,多群转发 | 完成| 109 | | 消息转链 |监控某个群的某个人 |自动转链到自己的pid |部分完成| 110 | | 清晨问候 |每天早上定时打招呼 |清晨寄语,群人数变化等 | 待完成| 111 | | 群相册 |群里所有人的头像,生成相册 | 个性化功能|待完成| 112 | | 自动踢人 |有人发广告 |监控发广告的人,自动踢出 | 待完成| 113 | 114 | 更多功能正在规划和添加中... 115 | -------------------------------------------------------------------------------- /src/dtbot.js: -------------------------------------------------------------------------------- 1 | const { 2 | Wechaty 3 | } = require('wechaty'); 4 | const { 5 | PuppetPadplus 6 | } = require('wechaty-puppet-padplus'); 7 | const QrcodeTerminal = require('qrcode-terminal'); 8 | const comutil = require('./utils/comutil'); 9 | const checker = require('./utils/checker'); 10 | const networkutil = require('./utils/networkutil'); 11 | const config = require('./config'); 12 | 13 | const token = config.basic.TOKEN; 14 | 15 | const puppet = new PuppetPadplus({ 16 | token, 17 | }); 18 | 19 | const name = config.basic.NAME; 20 | 21 | const bot = new Wechaty({ 22 | puppet, 23 | name, // generate xxxx.memory-card.json and save login data for the next login 24 | }) 25 | 26 | bot 27 | .on('scan', (qrcode, status) => { 28 | console.log("onScan"); 29 | QrcodeTerminal.generate(qrcode, { 30 | small: true 31 | }) 32 | }) 33 | .on('message', async msg => { 34 | // console.log(`msg : ${msg}`) 35 | const contact = msg.from() 36 | const text = msg.text() 37 | const room = msg.room() 38 | if (room) { 39 | const topic = await room.topic() 40 | // console.log(`Room: ${topic} Contact: ${contact.name()} Text: ${text}`) 41 | //是否口罩的群 42 | if (checker.isMaskRoom(topic)) { 43 | //是否关键词 44 | if (checker.isKeyWordFromMask(text)) { 45 | const urls = comutil.getHttpString(text); 46 | if (urls != null && urls.length > 0) { 47 | var url = urls[0]; 48 | console.log("url: " + url); 49 | if (url.indexOf('jd.com') != -1) { 50 | console.log('开始转链'); 51 | networkutil.transJd(url, async function (code, shortUrl) { 52 | if (code == 0) { 53 | //await room.say("转换连接成功:" + shortUrl); 54 | } 55 | }); 56 | } else { 57 | console.log("连接不符合"); 58 | } 59 | } 60 | } 61 | } 62 | //是否属于监听的羊毛群,监听信息转发到自己的群 63 | else if (checker.isYangmaoRoom(topic)) { 64 | const urls = comutil.getHttpString(text); 65 | //监听到有连接或者关键词,转发 66 | if (urls != null && urls.length > 0 || text.indexOf("&&") != -1) { 67 | const targetRooms = config.groups.TARGET_YANGMAO_ROOMS; 68 | for (i = 0; i < targetRooms.length; i++) { 69 | const tRoom = await bot.Room.find({ 70 | topic: targetRooms[i] 71 | }); 72 | if (tRoom && targetRooms[i]) { 73 | //随机时间转发出去 74 | console.log(contact.name() + " 尝试转发消息目标群: " + targetRooms[i] + " contact id: " + contact.id + " 群主: " + tRoom.owner().id) 75 | if (contact.id == 'karlon2011') { 76 | setTimeout(function () { 77 | tRoom.say(text) 78 | }, comutil.random(1000, 5000)) 79 | }else{ 80 | console.log("不是+龙发短消息"); 81 | } 82 | } 83 | } 84 | } 85 | } else { 86 | console.log("NOTHING") 87 | } 88 | } else { 89 | // console.log(`Contact: ${contact.name()} Text: ${text}`) 90 | } 91 | }) 92 | .on('login', onLogin) 93 | .on('logout', onLogout) 94 | .on('error', onError) 95 | .start() 96 | .catch(console.error); 97 | 98 | async function onLogin(user) { 99 | console.log(`${user} 登录成功`); 100 | 101 | const checkRooms = config.groups.LEAVE_JOIN_ROOMS; 102 | for (i = 0; i < checkRooms.length; i++) { 103 | const room = await bot.Room.find({ 104 | topic: checkRooms[i] 105 | }) // change `event-room` to any room topic in your wechat 106 | if (room) { 107 | 108 | const contactList = await room.payload.memberIdList; 109 | for(i=0;i { 115 | const nameList = leaverList.map(c => c.name()).join(','); 116 | console.log(`Room lost member ${leaverList}`); 117 | await room.say(`${nameList}终于受不了你转身离去,但他走路的样子依然很帅`); 118 | }); 119 | room.on('join', async (inviteeList, inviter) => { 120 | const nameList = inviteeList.map(c => c.name()).join(','); 121 | console.log(`Room got new member ${nameList}, invited by ${inviter}`); 122 | await room.say(`欢迎 ${nameList}加入`); 123 | }); 124 | // console.log("length: " + contactList.length); 125 | } else { 126 | console.log("没有找到群: " + checkRooms[i]); 127 | } 128 | } 129 | 130 | var shortUrlInfo = []; //保存链接,避免重复 131 | var lastFullTime = new Date().getTime(); 132 | var lastSmzdmTime = new Date().getTime(); 133 | var fullRandom = comutil.random(config.intervals.GET_FULL_MASK[0], config.intervals.GET_FULL_MASK[1]); 134 | var smzdmRandom = comutil.random(config.intervals.GET_SMZDM_MASK[0], config.intervals.GET_SMZDM_MASK[1]); 135 | //定时 136 | setInterval(() => { 137 | var currentTime = new Date().getTime(); 138 | var fullOffset = currentTime - lastFullTime; 139 | 140 | if (fullOffset > fullRandom) { 141 | console.log(fullOffset + '----开始请求FULL MASK信息:' + fullRandom); 142 | lastFullTime = currentTime; 143 | fullRandom = comutil.random(config.intervals.GET_FULL_MASK[0], config.intervals.GET_FULL_MASK[1]);; 144 | networkutil.getFullMask(async (ret, msg, data) => { 145 | dealRet(ret, msg, data) 146 | }); 147 | }; 148 | 149 | var smzdmOffset = currentTime - lastSmzdmTime; 150 | if (smzdmOffset > smzdmRandom) { 151 | console.log(smzdmOffset + '----开始请求SMZDM MASK信息:' + smzdmRandom); 152 | lastSmzdmTime = currentTime; 153 | smzdmRandom = comutil.random(config.intervals.GET_SMZDM_MASK[0], config.intervals.GET_SMZDM_MASK[1]); 154 | networkutil.getSmzdmMask(async (ret, msg, data) => { 155 | dealRet(ret, msg, data) 156 | }); 157 | }; 158 | 159 | }, 10000); 160 | 161 | //处理返回的结果 162 | async function dealRet(ret, msg, data) { 163 | if (ret == 0) { 164 | if (!checker.isValueMaskInfo(data.title)) { 165 | console.log("此信息不推荐: " + data.title); 166 | return; 167 | } 168 | for (i = 0; i < shortUrlInfo.length; i++) { 169 | if (data.shorturl == shortUrlInfo[i]) { 170 | console.log("已经更新过: " + data.shorturl); 171 | return; 172 | } 173 | } 174 | if (shortUrlInfo.length >= 20) { 175 | console.log('超过了最大数: ' + shortUrlInfo.length); 176 | shortUrlInfo.length = 0 177 | } 178 | shortUrlInfo.push(data.shorturl); 179 | const checkRooms = config.groups.TARGET_MASK_ROOMS; 180 | 181 | for (i = 0; i < checkRooms.length; i++) { 182 | const room = await bot.Room.find({ 183 | topic: checkRooms[i] 184 | }) // change `event-room` to any room topic in your wechat 185 | if (room) { 186 | console.log('发送消息到群: ' + checkRooms[i]); 187 | setTimeout(function () { 188 | room.say(msg) 189 | }, comutil.random(1000, 6000)) 190 | } 191 | } 192 | } 193 | } 194 | } 195 | 196 | function onLogout(user) { 197 | console.log(`${user} logout`) 198 | } 199 | 200 | function onError(e) { 201 | console.error(e) 202 | } --------------------------------------------------------------------------------