From 3071300092dc1a6ffc22c4e7c4c0ede3d7721fff Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Sun, 10 Mar 2024 16:23:19 +0800 Subject: [PATCH 01/14] =?UTF-8?q?doc:=20cn=E5=90=AF=E5=8A=A8=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_cn.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 README_cn.md diff --git a/README_cn.md b/README_cn.md new file mode 100644 index 0000000..1115d06 --- /dev/null +++ b/README_cn.md @@ -0,0 +1,28 @@ +# Wechaty Getting Started + +* node 版本 + +```bash +nvm use v18.18.0 +``` + +* 设置环境变量 + +```bash +export WECHATY_LOG=verbose +export WECHATY_PUPPET=wechaty-puppet-padlocal #local pad 登录 +``` + +* 注册试用token +http://pad-local.com/ 注册个账号, 获得一个7天试用Token + +```bash +export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_e8044daf883c4523aa754d4ca9cc7997 +``` + +* 启动项目 + +```bash +npm install # 安装依赖 +npm start # 启动,出现二维码,手机扫码登录 +``` From f8c9fb1be2d46f3bf65519541f73476021671c9a Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Sun, 10 Mar 2024 18:03:46 +0800 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=E6=8D=A1=E6=BC=8F=E5=8A=A9?= =?UTF-8?q?=E6=89=8B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/api.ts | 20 +++++++++++++++ examples/ding-dong-bot.ts | 52 +++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 73 insertions(+) create mode 100644 examples/api.ts diff --git a/examples/api.ts b/examples/api.ts new file mode 100644 index 0000000..cf7aa21 --- /dev/null +++ b/examples/api.ts @@ -0,0 +1,20 @@ +import { + log, +} from 'wechaty' + +// 定义一个异步函数来调用您的 API +export async function callLocalAPI (): Promise { + try { + // 使用 fetch 发起 GET 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/pick_list') + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + // 解析 JSON 响应体 + const result = await response.json() + return result + } catch (error) { + console.error('Failed to fetch data:', error) + } +} diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index 5c17e0f..c387c78 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -14,6 +14,7 @@ import { WechatyBuilder, log, } from 'wechaty' +import { callLocalAPI } from './api.ts' import qrcodeTerminal from 'qrcode-terminal' @@ -40,8 +41,59 @@ function onLogout (user: Contact) { log.info('StarterBot', '%s logout', user) } +// 给所有联系人发送捡漏消息 +async function sendMessageToAll () { + const contactList = await bot.Contact.findAll() + + try { + const resp = await callLocalAPI() + const { success, result } = resp || {} + + if (success) { + for (const contact of contactList) { + await contact.say(result) + } + } + } catch (err) { + console.error(err) + } +} + +let interval: NodeJS.Timer + async function onMessage (msg: Message) { log.info('StarterBot', msg.toString()) + // 定时任务 5分钟触发一次 + if (!interval) { + interval = setInterval(sendMessageToAll, 5 * 60 * 1000) + } + // const contactList = await bot.Contact.findAll() + // const roomList = await bot.Room.findAll() + + // // log.info('Bot', 'on(message) skip non-text message: %s', contact) + // // await contact.say('你好,这是一条自动发送的消息!') + // await sleep(5000) + // try { + // // 获取捡漏消息 + // const resp = await callLocalAPI() + // const success = resp?.success + // const result = resp?.result + + // if (success === true) { + // for (const contact of contactList) { + // log.info('个人', contact) + // await contact.say(result) + // } + // // for (const room of roomList) { + // // log.info('群', room) + // // // await contact.say('你好,这是一条自动发送的消息!') + // // } + // // console.log(`当前用户加入的群组总数:${rooms.length}`); + // } + // } catch (error) { + // console.error('Error calling API:', error) + // return + // } if (msg.text() === 'ding') { await msg.say('dong') diff --git a/package.json b/package.json index 80623d8..87e897b 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "wechaty-cqrs": "^0.7.5", "wechaty-plugin-contrib": "^1.11.1", "wechaty-puppet-oicq": "^1.10.2", + "wechaty-puppet-padlocal": "^1.20.1", "wechaty-puppet-walnut": "^1.11.6", "wechaty-puppet-wechat": "^1.18.1", "wechaty-puppet-wechat4u": "~1.11.1", From 8ffa6c4a0c7d582355fddf37703be3894b86f8b8 Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Mon, 11 Mar 2024 10:46:19 +0800 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=E8=A7=A6=E5=8F=91=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=94=B9=E4=B8=BA1=E5=B0=8F=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/ding-dong-bot.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index c387c78..52ee324 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -63,9 +63,9 @@ let interval: NodeJS.Timer async function onMessage (msg: Message) { log.info('StarterBot', msg.toString()) - // 定时任务 5分钟触发一次 + // 定时任务 1小时触发一次 if (!interval) { - interval = setInterval(sendMessageToAll, 5 * 60 * 1000) + interval = setInterval(sendMessageToAll, 60 * 60 * 1000) } // const contactList = await bot.Contact.findAll() // const roomList = await bot.Room.findAll() From 16bab41b6372e8627a07ababeea2c29eb95a44ef Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Sat, 16 Mar 2024 14:45:29 +0800 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=E8=AE=A8?= =?UTF-8?q?=E5=8E=8C=E7=9A=84=E5=AD=97=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5885437..44435db 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,6 @@ { "typescript.tsdk": "./node_modules/typescript/lib", - "editor.fontFamily": "'Fira Code iScript', Consolas, 'Courier New', monospace", "editor.fontLigatures": true, "editor.tokenColorCustomizations": { From 372b904a1fc92b2efc9f27ad1b37d77f8bb2e621 Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Sat, 16 Mar 2024 16:09:46 +0800 Subject: [PATCH 05/14] feat: New Friend Request --- examples/ding-dong-bot.ts | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index 52ee324..6d0e76e 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -13,6 +13,7 @@ import { ScanStatus, WechatyBuilder, log, + Friendship, } from 'wechaty' import { callLocalAPI } from './api.ts' @@ -41,6 +42,43 @@ function onLogout (user: Contact) { log.info('StarterBot', '%s logout', user) } +async function onFriendship (friendship: Friendship) { + let logMsg + + try { + logMsg = 'received `friend` event from ' + friendship.contact().name() + log.info(logMsg) + + switch (friendship.type()) { + /** + * + * New Friend Request + * + * when request is set, we can get verify message from `request.hello`, + * and accept this request by `request.accept()` + */ + case bot.Friendship.Type.Receive: + logMsg = 'accepted automatically' + log.info('before accept') + await friendship.accept() + + // if want to send msg , you need to delay sometimes + await new Promise((r) => setTimeout(r, 1000)) + await friendship.contact().say(`${friendship.contact().name()} 你好, 我是剑三小舞!`) + log.info('after accept') + break + + default: + break + } + } catch (e) { + console.error(e) + logMsg = 'Friendship try catch failed' + } + + log.info(logMsg) +} + // 给所有联系人发送捡漏消息 async function sendMessageToAll () { const contactList = await bot.Contact.findAll() @@ -95,9 +133,14 @@ async function onMessage (msg: Message) { // return // } + // 测试ding~dong: if (msg.text() === 'ding') { await msg.say('dong') } + // 蹲号模板(暂未开放): + if (msg.text() === '蹲号') { + await msg.say('蹲号模版(暂未开放):\n 条件1:\n 条件2:\n 条件3:\n') + } } const bot = WechatyBuilder.build({ @@ -140,6 +183,7 @@ bot.on('scan', onScan) bot.on('login', onLogin) bot.on('logout', onLogout) bot.on('message', onMessage) +bot.on('friendship', onFriendship) bot.start() .then(() => log.info('StarterBot', 'Starter Bot Started.')) From af3d24b56906e6e550c82cef98b480730bdf7ac9 Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Sat, 16 Mar 2024 17:59:56 +0800 Subject: [PATCH 06/14] feat: support and group message --- examples/ding-dong-bot.ts | 93 ++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index 6d0e76e..d23411c 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -19,6 +19,34 @@ import { callLocalAPI } from './api.ts' import qrcodeTerminal from 'qrcode-terminal' +let interval: NodeJS.Timer| undefined + +// 给所有联系人&群发送捡漏消息 +async function sendMessageToAll () { + const contactList = await bot.Contact.findAll() + const roomList = await bot.Room.findAll() + + try { + const resp = await callLocalAPI() + const { success, result } = resp || {} + + if (success) { + // 给用户发消息 + for (const contact of contactList) { + if (contact.type() === bot.Contact.Type.Individual) { + await contact.say(result) + } + } + // 给群发消息 + for (const room of roomList) { + await room.say(result) + } + } + } catch (err) { + console.error(err) + } +} + function onScan (qrcode: string, status: ScanStatus) { if (status === ScanStatus.Waiting || status === ScanStatus.Timeout) { const qrcodeImageUrl = [ @@ -36,6 +64,20 @@ function onScan (qrcode: string, status: ScanStatus) { function onLogin (user: Contact) { log.info('StarterBot', '%s login', user) + log.info('开启定时任务!!') + // 确保定时器只被设置一次 + if (!interval) { + interval = setInterval(() => { + // 使用立即执行的异步函数来调用 sendMessageToAll + void (async () => { + try { + await sendMessageToAll() + } catch (error) { + console.error(error) + } + })() + }, 60 * 60 * 1000) // 设置为每小时执行一次 + } } function onLogout (user: Contact) { @@ -79,59 +121,8 @@ async function onFriendship (friendship: Friendship) { log.info(logMsg) } -// 给所有联系人发送捡漏消息 -async function sendMessageToAll () { - const contactList = await bot.Contact.findAll() - - try { - const resp = await callLocalAPI() - const { success, result } = resp || {} - - if (success) { - for (const contact of contactList) { - await contact.say(result) - } - } - } catch (err) { - console.error(err) - } -} - -let interval: NodeJS.Timer - async function onMessage (msg: Message) { log.info('StarterBot', msg.toString()) - // 定时任务 1小时触发一次 - if (!interval) { - interval = setInterval(sendMessageToAll, 60 * 60 * 1000) - } - // const contactList = await bot.Contact.findAll() - // const roomList = await bot.Room.findAll() - - // // log.info('Bot', 'on(message) skip non-text message: %s', contact) - // // await contact.say('你好,这是一条自动发送的消息!') - // await sleep(5000) - // try { - // // 获取捡漏消息 - // const resp = await callLocalAPI() - // const success = resp?.success - // const result = resp?.result - - // if (success === true) { - // for (const contact of contactList) { - // log.info('个人', contact) - // await contact.say(result) - // } - // // for (const room of roomList) { - // // log.info('群', room) - // // // await contact.say('你好,这是一条自动发送的消息!') - // // } - // // console.log(`当前用户加入的群组总数:${rooms.length}`); - // } - // } catch (error) { - // console.error('Error calling API:', error) - // return - // } // 测试ding~dong: if (msg.text() === 'ding') { From 1b0af9a1643c112876cdc3a29faaf5f9f55e8e12 Mon Sep 17 00:00:00 2001 From: "zhiheng.hu" Date: Sun, 17 Mar 2024 19:59:03 +0800 Subject: [PATCH 07/14] feat: add user task --- README_cn.md | 16 ++++++++- examples/api.ts | 74 ++++++++++++++++++++++++++++++++++---- examples/ding-dong-bot.ts | 75 +++++++++++++++++++++++++++++++++++---- package.json | 1 + 4 files changed, 152 insertions(+), 14 deletions(-) diff --git a/README_cn.md b/README_cn.md index 1115d06..9f09a74 100644 --- a/README_cn.md +++ b/README_cn.md @@ -17,7 +17,7 @@ export WECHATY_PUPPET=wechaty-puppet-padlocal #local pad 登录 http://pad-local.com/ 注册个账号, 获得一个7天试用Token ```bash -export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_e8044daf883c4523aa754d4ca9cc7997 +export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_f5cbb6924f3e4e958bc97fbc92a8d3c1 ``` * 启动项目 @@ -26,3 +26,17 @@ export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_e8044daf883c4523aa754d4ca9c npm install # 安装依赖 npm start # 启动,出现二维码,手机扫码登录 ``` + +* 提交代码 + +```bash +git push --no-verify origin main +``` + +`--no-verify` 忽略检查 + +* pm2 启动项目 + +```bash +pm2 start npm -- start +``` diff --git a/examples/api.ts b/examples/api.ts index cf7aa21..f20b4a1 100644 --- a/examples/api.ts +++ b/examples/api.ts @@ -1,9 +1,20 @@ -import { - log, -} from 'wechaty' +interface ApiResponse { + success: boolean; + result: string; +} + +export interface UserData { + name: string; + wechat_rename: string; +} + +export interface TaskData { + task: string; + wechat_rename: string; +} -// 定义一个异步函数来调用您的 API -export async function callLocalAPI (): Promise { +// 捡漏商品列表 +export async function pickListAPI (): Promise { try { // 使用 fetch 发起 GET 请求 const response = await fetch('http://127.0.0.1:8000/v1/api/pick_list') @@ -12,9 +23,60 @@ export async function callLocalAPI (): Promise { throw new Error(`Error! status: ${response.status}`) } // 解析 JSON 响应体 - const result = await response.json() + const result: ApiResponse = await response.json() as ApiResponse return result } catch (error) { console.error('Failed to fetch data:', error) + throw error + } +} + +export async function addUserAPI (user: UserData): Promise { + try { + // 使用 fetch 发起 POST 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/add_user', { + body: JSON.stringify(user), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) + + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + + // 解析 JSON 响应体,并使用类型断言 + const result = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to post data:', error) + throw error + } +} + +export async function addUserTaskAPI (user: TaskData): Promise { + try { + // 使用 fetch 发起 POST 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/add_user_task', { + body: JSON.stringify(user), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) + + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + + // 解析 JSON 响应体,并使用类型断言 + const result = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to post data:', error) + throw error } } diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index d23411c..e53fcde 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -15,10 +15,12 @@ import { log, Friendship, } from 'wechaty' -import { callLocalAPI } from './api.ts' +import { pickListAPI, addUserAPI, UserData, addUserTaskAPI, TaskData } from './api.ts' import qrcodeTerminal from 'qrcode-terminal' +import { nanoid } from 'nanoid' + let interval: NodeJS.Timer| undefined // 给所有联系人&群发送捡漏消息 @@ -27,8 +29,8 @@ async function sendMessageToAll () { const roomList = await bot.Room.findAll() try { - const resp = await callLocalAPI() - const { success, result } = resp || {} + const resp = await pickListAPI() + const { success, result } = resp if (success) { // 给用户发消息 @@ -121,17 +123,76 @@ async function onFriendship (friendship: Friendship) { log.info(logMsg) } +async function checkUserTask (str: string): Promise { + return str.startsWith('蹲号') && str.length > 10 +} + +async function checkUserTaskDetail (str: string): Promise { + return str.includes('规则名称:') && str.includes('规则:') && str.includes('体型:') && str.includes('门派:') && str.includes('价格:') +} + async function onMessage (msg: Message) { log.info('StarterBot', msg.toString()) + if (msg.text() === '蹲号') { + // 创建别名逻辑 + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const noid = nanoid() + const newAlias = `friend_${noid}` + let wechatRename = alias || newAlias + if (!alias) { + log.info('没别名') + await contact.alias(newAlias) + wechatRename = newAlias + } + + const newUser: UserData = { + name, + wechat_rename: wechatRename, + } + await addUserAPI(newUser) + await contact.say('回复下面内容模版(不包含本行):\n蹲号\n规则名称:一狐丝成女\n规则:金陵凤 金发·璨月蝶心 曼纱旋舞\n体型:成女\n门派:七秀 万花\n价格:5000') + return + } + + // 顿号规则任务 + const isTask = await checkUserTask(msg.text()) + if (isTask) { + + const isMap = await checkUserTaskDetail(msg.text()) + if (!isMap) { + await msg.say('蹲号规则配置错误') + return + } + + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const newTask: TaskData = { + task: msg.text(), + wechat_rename: alias || '', + } + const resp = await addUserTaskAPI(newTask) + const { success, result } = resp + + if (success) { + await contact.say('蹲号成功') + } else { + await contact.say(result) + } + return + } + // 测试ding~dong: if (msg.text() === 'ding') { await msg.say('dong') } - // 蹲号模板(暂未开放): - if (msg.text() === '蹲号') { - await msg.say('蹲号模版(暂未开放):\n 条件1:\n 条件2:\n 条件3:\n') - } } const bot = WechatyBuilder.build({ diff --git a/package.json b/package.json index 87e897b..6e325ee 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "homepage": "https://github.com/wechaty/getting-started#readme", "dependencies": { "dotenv": "^16.0.0", + "nanoid": "^5.0.6", "qrcode-terminal": "^0.12.0", "wechaty": "^1.18.1", "wechaty-cqrs": "^0.7.5", From e7ad3fc8e48a4a515f8b9a54b65812ea43818958 Mon Sep 17 00:00:00 2001 From: defnngj Date: Thu, 21 Mar 2024 21:08:22 +0800 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=B9=B2?= =?UTF-8?q?=E5=8F=B7=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/api.ts | 25 +++++++++++++++++++++++++ examples/ding-dong-bot.ts | 29 ++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/examples/api.ts b/examples/api.ts index f20b4a1..f149b37 100644 --- a/examples/api.ts +++ b/examples/api.ts @@ -3,6 +3,13 @@ interface ApiResponse { result: string; } +interface ApiTaskResponse { + success: boolean; + result: { + [key: string]: string; + }; +} + export interface UserData { name: string; wechat_rename: string; @@ -80,3 +87,21 @@ export async function addUserTaskAPI (user: TaskData): Promise { throw error } } + +// 蹲号任务列表 +export async function getDoneTaskAPI (): Promise { + try { + // 使用 fetch 发起 GET 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/get_done_task') + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + // 解析 JSON 响应体 + const result: ApiTaskResponse = await response.json() as ApiTaskResponse + return result + } catch (error) { + console.error('Failed to fetch data:', error) + throw error + } +} diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index e53fcde..943765e 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -15,7 +15,7 @@ import { log, Friendship, } from 'wechaty' -import { pickListAPI, addUserAPI, UserData, addUserTaskAPI, TaskData } from './api.ts' +import { pickListAPI, addUserAPI, UserData, addUserTaskAPI, TaskData, getDoneTaskAPI } from './api.ts' import qrcodeTerminal from 'qrcode-terminal' @@ -29,21 +29,40 @@ async function sendMessageToAll () { const roomList = await bot.Room.findAll() try { + // 捡漏任务 const resp = await pickListAPI() - const { success, result } = resp + const { success: pickListSuccess, result: pickListResult } = resp - if (success) { + if (pickListSuccess) { // 给用户发消息 for (const contact of contactList) { if (contact.type() === bot.Contact.Type.Individual) { - await contact.say(result) + await contact.say(pickListResult) } } // 给群发消息 for (const room of roomList) { - await room.say(result) + await room.say(pickListResult) } } + + // 蹲号任务 + const task = await getDoneTaskAPI() + const { success: taskSuccess, result: taskResult } = task + + if (taskSuccess) { + for (const contact of contactList) { + const alias = await contact.alias() + if (alias !== undefined) { + const content = taskResult[alias] + if (content !== undefined) { + // 发给蹲号任务 + await contact.say(content) + } + } + } + + } } catch (err) { console.error(err) } From 14e3893decbc5c791e36bad82173d5380385b7a3 Mon Sep 17 00:00:00 2001 From: defnngj Date: Sat, 23 Mar 2024 16:51:31 +0800 Subject: [PATCH 09/14] feat: user task done --- examples/api.ts | 63 +++++++++++++++++++ examples/ding-dong-bot.ts | 126 ++++++++++++++++++++++++++++++++++++-- examples/utils.ts | 16 +++++ 3 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 examples/utils.ts diff --git a/examples/api.ts b/examples/api.ts index f149b37..7c0f999 100644 --- a/examples/api.ts +++ b/examples/api.ts @@ -17,6 +17,7 @@ export interface UserData { export interface TaskData { task: string; + task_name: string; wechat_rename: string; } @@ -105,3 +106,65 @@ export async function getDoneTaskAPI (): Promise { throw error } } + +// 删除我的蹲号任务 +export async function delUserTaskAPI (task: TaskData): Promise { + try { + // 使用 fetch 发起 POST 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/delete_user_task', { + body: JSON.stringify(task), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) + + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + + // 解析 JSON 响应体,并使用类型断言 + const result = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to post data:', error) + throw error + } +} + +// 我的蹲号任务列表 +export async function getMeTaskListAPI (task: TaskData): Promise { + try { + // 使用 fetch 发起 GET 请求 + const response = await fetch(`http://127.0.0.1:8000/v1/api/get_user_task_list?wechat_rename=${task.wechat_rename}`) + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + // 解析 JSON 响应体 + const result: ApiResponse = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to fetch data:', error) + throw error + } +} + +// 我的顿号任务详情 +export async function getMeTasDetailsAPI (task: TaskData): Promise { + try { + // 使用 fetch 发起 GET 请求 + const response = await fetch(`http://127.0.0.1:8000/v1/api/get_user_task_detail?wechat_rename=${task.wechat_rename}&task_name=${task.task_name}`) + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + // 解析 JSON 响应体 + const result: ApiResponse = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to fetch data:', error) + throw error + } +} diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index 943765e..365b43a 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -15,7 +15,19 @@ import { log, Friendship, } from 'wechaty' -import { pickListAPI, addUserAPI, UserData, addUserTaskAPI, TaskData, getDoneTaskAPI } from './api.ts' +import { + pickListAPI, + addUserAPI, + UserData, + addUserTaskAPI, + TaskData, + getDoneTaskAPI, + delUserTaskAPI, + getMeTasDetailsAPI, + getMeTaskListAPI, +} from './api.ts' + +import { calculateDelayTime } from './utils.ts' import qrcodeTerminal from 'qrcode-terminal' @@ -86,10 +98,14 @@ function onScan (qrcode: string, status: ScanStatus) { function onLogin (user: Contact) { log.info('StarterBot', '%s login', user) log.info('开启定时任务!!') + // 确保定时器只被设置一次 if (!interval) { - interval = setInterval(() => { - // 使用立即执行的异步函数来调用 sendMessageToAll + const delayTime = calculateDelayTime(1) + log.info('delayTime', delayTime) + + setTimeout(() => { + // 当前小时先执行一次 void (async () => { try { await sendMessageToAll() @@ -97,7 +113,19 @@ function onLogin (user: Contact) { console.error(error) } })() - }, 60 * 60 * 1000) // 设置为每小时执行一次 + + // 随后每小时执行一次 + interval = setInterval(() => { + void (async () => { + try { + await sendMessageToAll() + } catch (error) { + console.error(error) + } + })() + }, 60 * 60 * 1000) // 设置为每小时执行一次 + }, delayTime) + } } @@ -127,7 +155,14 @@ async function onFriendship (friendship: Friendship) { // if want to send msg , you need to delay sometimes await new Promise((r) => setTimeout(r, 1000)) - await friendship.contact().say(`${friendship.contact().name()} 你好, 我是剑三小舞!`) + await friendship.contact().say(`${friendship.contact().name()} 你好, 我是剑三小舞!\n +回复: "蹲号",可配置蹲号规则。 +回复: "删除蹲号 规则名称",可删除蹲号规则。 +回复: "我的蹲号 规则名称",可显示顿号规则详情。 +回复:"我的蹲号列表",可显示配置的蹲号列表。 +回复: "意见 意见内容",可提供您宝贵意见,我们会继续改进。 +回复: "功能",可看小舞的所有功能。 +详细功能可参考最新朋友圈。`) log.info('after accept') break @@ -146,6 +181,18 @@ async function checkUserTask (str: string): Promise { return str.startsWith('蹲号') && str.length > 10 } +async function checkDelUserTask (str: string):Promise { + return str.startsWith('删除蹲号 ') && str.length > 7 +} + +async function checkMeUserTaskList (str: string):Promise { + return str.startsWith('我的蹲号') +} + +async function checkMeUserTask (str: string):Promise { + return str.startsWith('我的蹲号 ') && str.length > 7 +} + async function checkUserTaskDetail (str: string): Promise { return str.includes('规则名称:') && str.includes('规则:') && str.includes('体型:') && str.includes('门派:') && str.includes('价格:') } @@ -195,6 +242,7 @@ async function onMessage (msg: Message) { const newTask: TaskData = { task: msg.text(), + task_name: '', wechat_rename: alias || '', } const resp = await addUserTaskAPI(newTask) @@ -208,6 +256,74 @@ async function onMessage (msg: Message) { return } + const isDelTask = await checkDelUserTask(msg.text()) + if (isDelTask) { + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const delTask: TaskData = { + task: '', + task_name: msg.text(), + wechat_rename: alias || '', + } + const resp = await delUserTaskAPI(delTask) + const { success, result } = resp + + if (success) { + await contact.say('删除蹲号成功') + } else { + await contact.say(result) + } + return + } + + const isMeTask = await checkMeUserTask(msg.text()) + if (isMeTask) { + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const meTask: TaskData = { + task: '', + task_name: msg.text(), + wechat_rename: alias || '', + } + const resp = await getMeTasDetailsAPI(meTask) + const { success, result } = resp + + if (success) { + await contact.say(result) + } else { + await contact.say(result) + } + return + } + + const isMeTaskList = await checkMeUserTaskList(msg.text()) + if (isMeTaskList) { + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const meTaskList: TaskData = { + task: '', + task_name: msg.text(), + wechat_rename: alias || '', + } + const resp = await getMeTaskListAPI(meTaskList) + const { success, result } = resp + + if (success) { + await contact.say(result) + } else { + await contact.say(result) + } + return + } // 测试ding~dong: if (msg.text() === 'ding') { await msg.say('dong') diff --git a/examples/utils.ts b/examples/utils.ts new file mode 100644 index 0000000..b974cc6 --- /dev/null +++ b/examples/utils.ts @@ -0,0 +1,16 @@ +// 计算延迟时间(毫秒) +export function calculateDelayTime (minuteOfHour: number): number { + const now = new Date() + const next = new Date() + + // 如果当前时间已经过了指定分钟,设置下一个执行时间为下一个小时的指定分钟 + if (now.getMinutes() >= minuteOfHour) { + next.setHours(now.getHours() + 1) + } + + next.setMinutes(minuteOfHour, 0, 0) // 设置为下一个小时的指定分钟 + + const delay = next.getTime() - now.getTime() + + return delay +} From 253cab3e892820e73816f1835de9c01603c90e8f Mon Sep 17 00:00:00 2001 From: defnngj Date: Sat, 23 Mar 2024 16:51:53 +0800 Subject: [PATCH 10/14] feat: update token --- README_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_cn.md b/README_cn.md index 9f09a74..358e4b2 100644 --- a/README_cn.md +++ b/README_cn.md @@ -17,7 +17,7 @@ export WECHATY_PUPPET=wechaty-puppet-padlocal #local pad 登录 http://pad-local.com/ 注册个账号, 获得一个7天试用Token ```bash -export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_f5cbb6924f3e4e958bc97fbc92a8d3c1 +export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_5d75101eed104e1d971fe36a12c48fee ``` * 启动项目 From 5e44e64540de3756c1d98f6f4216056551c37c63 Mon Sep 17 00:00:00 2001 From: defnngj Date: Sat, 23 Mar 2024 17:15:29 +0800 Subject: [PATCH 11/14] =?UTF-8?q?feat=EF=BC=9Aadd=20opinion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/api.ts | 31 ++++++++++++++++++++++ examples/ding-dong-bot.ts | 56 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/examples/api.ts b/examples/api.ts index 7c0f999..db8fb85 100644 --- a/examples/api.ts +++ b/examples/api.ts @@ -21,6 +21,11 @@ export interface TaskData { wechat_rename: string; } +export interface OpinionData { + opinion: string; + wechat_rename: string; +} + // 捡漏商品列表 export async function pickListAPI (): Promise { try { @@ -168,3 +173,29 @@ export async function getMeTasDetailsAPI (task: TaskData): Promise throw error } } + +// 添加意见 +export async function addOpinionAPI (opinion: OpinionData): Promise { + try { + // 使用 fetch 发起 POST 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/add_user_opinion', { + body: JSON.stringify(opinion), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) + + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + + // 解析 JSON 响应体,并使用类型断言 + const result = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to post data:', error) + throw error + } +} diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index 365b43a..b99e4a6 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -25,6 +25,8 @@ import { delUserTaskAPI, getMeTasDetailsAPI, getMeTaskListAPI, + OpinionData, + addOpinionAPI, } from './api.ts' import { calculateDelayTime } from './utils.ts' @@ -197,6 +199,10 @@ async function checkUserTaskDetail (str: string): Promise { return str.includes('规则名称:') && str.includes('规则:') && str.includes('体型:') && str.includes('门派:') && str.includes('价格:') } +async function checkSuggest (str: string):Promise { + return str.startsWith('意见 ') && str.length > 4 +} + async function onMessage (msg: Message) { log.info('StarterBot', msg.toString()) @@ -221,11 +227,18 @@ async function onMessage (msg: Message) { wechat_rename: wechatRename, } await addUserAPI(newUser) - await contact.say('回复下面内容模版(不包含本行):\n蹲号\n规则名称:一狐丝成女\n规则:金陵凤 金发·璨月蝶心 曼纱旋舞\n体型:成女\n门派:七秀 万花\n价格:5000') + const crouchTemp = `回复下面内容模版(不包含本行): +蹲号 +规则名称:一狐丝成女 +规则:金陵凤 金发·璨月蝶心 曼纱旋舞·衣 +体型:成女 +门派:七秀 万花 +价格:5000` + await contact.say(crouchTemp) return } - // 顿号规则任务 + // 配置蹲号规则任务 const isTask = await checkUserTask(msg.text()) if (isTask) { @@ -256,6 +269,7 @@ async function onMessage (msg: Message) { return } + // 删除我的蹲号 const isDelTask = await checkDelUserTask(msg.text()) if (isDelTask) { const contact = msg.talker() @@ -279,6 +293,7 @@ async function onMessage (msg: Message) { return } + // 查看的我蹲号详情 const isMeTask = await checkMeUserTask(msg.text()) if (isMeTask) { const contact = msg.talker() @@ -302,6 +317,7 @@ async function onMessage (msg: Message) { return } + // 查看的我蹲号列表 const isMeTaskList = await checkMeUserTaskList(msg.text()) if (isMeTaskList) { const contact = msg.talker() @@ -324,6 +340,42 @@ async function onMessage (msg: Message) { } return } + + // 用户意见 + const isSuggest = await checkSuggest(msg.text()) + if (isSuggest) { + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const opinionData: OpinionData = { + opinion: msg.text(), + wechat_rename: alias || '', + } + const resp = await addOpinionAPI(opinionData) + const { success, result } = resp + + if (success) { + await contact.say(result) + } else { + await contact.say(result) + } + return + } + + // 功能 + if (msg.text() === '功能') { + const helpText = `回复: "蹲号",可配置蹲号规则。 +回复: "删除蹲号 规则名称",可删除蹲号规则。 +回复: "我的蹲号 规则名称",可显示顿号规则详情。 +回复:"我的蹲号列表",可显示配置的蹲号列表。 +回复: "意见 意见内容",可提供您宝贵意见,我们会继续改进。 +回复: "功能",可看小舞的所有功能。 +详细功能可参考最新朋友圈。` + await msg.say(helpText) + } + // 测试ding~dong: if (msg.text() === 'ding') { await msg.say('dong') From 2c4dd8cd352283f521cdaa7cb48a749eb7a7475b Mon Sep 17 00:00:00 2001 From: defnngj Date: Mon, 25 Mar 2024 16:41:33 +0800 Subject: [PATCH 12/14] feat: pick key word --- examples/ding-dong-bot.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index b99e4a6..ed6877e 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -179,6 +179,10 @@ async function onFriendship (friendship: Friendship) { log.info(logMsg) } +async function checkPicktTreasure (str: string):Promise { + return str.startsWith('捡漏') +} + async function checkUserTask (str: string): Promise { return str.startsWith('蹲号') && str.length > 10 } @@ -238,6 +242,15 @@ async function onMessage (msg: Message) { return } + const isPick = await checkPicktTreasure(msg.text()) + if (isPick) { + const helpText = `亲~! +我们会每小时自动推送捡漏信息。 +回复: "功能",可看小舞的所有功能。 +详细功能可参考最新朋友圈。` + await msg.say(helpText) + } + // 配置蹲号规则任务 const isTask = await checkUserTask(msg.text()) if (isTask) { From 4cb260b4e35c945fb5b471c8b79e4cdfb9192b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=AB=E5=B8=88?= Date: Mon, 25 Mar 2024 20:32:34 +0800 Subject: [PATCH 13/14] =?UTF-8?q?fix=EF=BC=9Amsg=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/ding-dong-bot.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index ed6877e..cd7a1a2 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -242,15 +242,6 @@ async function onMessage (msg: Message) { return } - const isPick = await checkPicktTreasure(msg.text()) - if (isPick) { - const helpText = `亲~! -我们会每小时自动推送捡漏信息。 -回复: "功能",可看小舞的所有功能。 -详细功能可参考最新朋友圈。` - await msg.say(helpText) - } - // 配置蹲号规则任务 const isTask = await checkUserTask(msg.text()) if (isTask) { @@ -377,7 +368,18 @@ async function onMessage (msg: Message) { return } - // 功能 + // 捡漏回复 + const isPick = await checkPicktTreasure(msg.text()) + if (isPick) { + const helpText = `亲~! +我们会每小时自动推送捡漏信息。 +回复: "功能",可看小舞的所有功能。 +详细功能可参考最新朋友圈。` + const contact = msg.talker() + await contact.say(helpText) + } + + // 功能回复 if (msg.text() === '功能') { const helpText = `回复: "蹲号",可配置蹲号规则。 回复: "删除蹲号 规则名称",可删除蹲号规则。 @@ -386,7 +388,8 @@ async function onMessage (msg: Message) { 回复: "意见 意见内容",可提供您宝贵意见,我们会继续改进。 回复: "功能",可看小舞的所有功能。 详细功能可参考最新朋友圈。` - await msg.say(helpText) + const contact = msg.talker() + await contact.say(helpText) } // 测试ding~dong: From 2a7bc9339452272778c7191d084ceb8102ff945a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=AB=E5=B8=88?= Date: Sun, 31 Mar 2024 13:27:49 +0800 Subject: [PATCH 14/14] feat: pick switch --- examples/api.ts | 60 ++++++++++++++++++++++++++++++ examples/ding-dong-bot.ts | 78 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/examples/api.ts b/examples/api.ts index db8fb85..bef58b2 100644 --- a/examples/api.ts +++ b/examples/api.ts @@ -10,6 +10,16 @@ interface ApiTaskResponse { }; } + +interface ApiUserResponse { + success: boolean; + result: { + [key: string]: { + is_pick_up_leak: number; + }; + }; +} + export interface UserData { name: string; wechat_rename: string; @@ -26,6 +36,12 @@ export interface OpinionData { wechat_rename: string; } +// 用户捡漏开关 +export interface PickSwitchData { + pick_up_leak: string; + wechat_rename: string; +} + // 捡漏商品列表 export async function pickListAPI (): Promise { try { @@ -199,3 +215,47 @@ export async function addOpinionAPI (opinion: OpinionData): Promise throw error } } + +// 更新用户捡漏开关 +export async function updateUserPickAPI (pick: PickSwitchData): Promise { + try { + // 使用 fetch 发起 POST 请求 + const response = await fetch('http://127.0.0.1:8000/v1/api/update_pick_up_leak', { + body: JSON.stringify(pick), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) + + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + + // 解析 JSON 响应体,并使用类型断言 + const result = await response.json() as ApiResponse + return result + } catch (error) { + console.error('Failed to post data:', error) + throw error + } +} + +// 获取用户开关列表 +export async function getUserSwitchListAPI (): Promise { + try { + // 使用 fetch 发起 GET 请求 + const response = await fetch(`http://127.0.0.1:8000/v1/api/get_user_switch`) + // 确保响应是 OK 的 + if (!response.ok) { + throw new Error(`Error! status: ${response.status}`) + } + // 解析 JSON 响应体 + const result: ApiUserResponse = await response.json() as ApiUserResponse + return result + } catch (error) { + console.error('Failed to fetch data:', error) + throw error + } +} \ No newline at end of file diff --git a/examples/ding-dong-bot.ts b/examples/ding-dong-bot.ts index cd7a1a2..5f552a2 100755 --- a/examples/ding-dong-bot.ts +++ b/examples/ding-dong-bot.ts @@ -27,6 +27,9 @@ import { getMeTaskListAPI, OpinionData, addOpinionAPI, + PickSwitchData, + updateUserPickAPI, + getUserSwitchListAPI, } from './api.ts' import { calculateDelayTime } from './utils.ts' @@ -47,9 +50,22 @@ async function sendMessageToAll () { const resp = await pickListAPI() const { success: pickListSuccess, result: pickListResult } = resp - if (pickListSuccess) { + const user = await getUserSwitchListAPI() + const { success: switchListSuccess, result: switchListResult } = user + + if (pickListSuccess === true && switchListSuccess === true) { // 给用户发消息 for (const contact of contactList) { + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const userSwitch = switchListResult[alias] + + if (!(userSwitch === undefined || userSwitch.is_pick_up_leak === 1)) { + continue + } + if (contact.type() === bot.Contact.Type.Individual) { await contact.say(pickListResult) } @@ -162,6 +178,8 @@ async function onFriendship (friendship: Friendship) { 回复: "删除蹲号 规则名称",可删除蹲号规则。 回复: "我的蹲号 规则名称",可显示顿号规则详情。 回复:"我的蹲号列表",可显示配置的蹲号列表。 +回复: "关闭捡漏",可关闭捡漏消息提醒。 +回复: "开启捡漏",可开启捡漏消息提醒。 回复: "意见 意见内容",可提供您宝贵意见,我们会继续改进。 回复: "功能",可看小舞的所有功能。 详细功能可参考最新朋友圈。`) @@ -345,6 +363,43 @@ async function onMessage (msg: Message) { return } + // 查看的我蹲号列表 + if (msg.text() === "关闭捡漏" || msg.text() == "开启捡漏") { + const contact = msg.talker() + const name = contact.name() + const alias = await contact.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const noid = nanoid() + const newAlias = `friend_${noid}` + let wechatRename = alias || newAlias + if (!alias) { + await contact.alias(newAlias) + wechatRename = newAlias + } + + const newUser: UserData = { + name, + wechat_rename: wechatRename, + } + await addUserAPI(newUser) + + const userPickSwitch: PickSwitchData = { + pick_up_leak: msg.text(), + wechat_rename: alias || '', + } + const resp = await updateUserPickAPI(userPickSwitch) + const { success, result } = resp + + if (success) { + await contact.say(result) + } else { + await contact.say(result) + } + return + } + + // 用户意见 const isSuggest = await checkSuggest(msg.text()) if (isSuggest) { @@ -381,10 +436,31 @@ async function onMessage (msg: Message) { // 功能回复 if (msg.text() === '功能') { + const contact1 = msg.talker() + const name = contact1.name() + const alias = await contact1.alias() + log.info(`联系人昵称: ${name} , 微信别名: ${alias}`) + + const noid = nanoid() + const newAlias = `friend_${noid}` + let wechatRename = alias || newAlias + if (!alias) { + await contact1.alias(newAlias) + wechatRename = newAlias + } + + const newUser: UserData = { + name, + wechat_rename: wechatRename, + } + await addUserAPI(newUser) + const helpText = `回复: "蹲号",可配置蹲号规则。 回复: "删除蹲号 规则名称",可删除蹲号规则。 回复: "我的蹲号 规则名称",可显示顿号规则详情。 回复:"我的蹲号列表",可显示配置的蹲号列表。 +回复: "关闭捡漏",可关闭捡漏消息提醒。 +回复: "开启捡漏",可开启捡漏消息提醒。 回复: "意见 意见内容",可提供您宝贵意见,我们会继续改进。 回复: "功能",可看小舞的所有功能。 详细功能可参考最新朋友圈。`