├── .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 | [](https://github.com/chatie/wechaty)
4 |
5 | [](http://nodejs.cn/download/)
6 | [](https://github.com/Chatie/wechaty)
7 | 
8 | 
9 | 
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 | 
24 |
25 | 口罩群实时监控
26 |
27 | 
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 | 
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 | }
--------------------------------------------------------------------------------