From b8dcf94bac0875982c262c077c618446e3197942 Mon Sep 17 00:00:00 2001 From: ZhangWei-KUMO Date: Tue, 24 Dec 2024 14:54:14 +0800 Subject: [PATCH] add push --- README.md | 21 +----- index.js | 5 +- package.json | 1 - router/public/chats.html | 4 +- router/public/docs.html | 11 +-- router/public/flashmemo.html | 17 +++-- router/public/knowledge.html | 3 +- router/public/settings.html | 6 ++ util/gemini.js | 1 - util/group.js | 7 +- util/handle.js | 92 +++++++++++++++++++----- util/init.js | 133 +++-------------------------------- 12 files changed, 116 insertions(+), 185 deletions(-) diff --git a/README.md b/README.md index 7249e97..10f85ab 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ logo -这是一个基于Gemini大语言模型开源的微信机器人AI智能体,可以识别微信小程序、转账、文本聊天、语音聊天、图片,并做出及时反应。同时,它集成了中国实时经济新闻、数字货币信息。用户只需配置自己的api key即可使用。 - -This is an open-source WeChat robot project based on WechatY, Gemini, and Microsoft Azure ASR voice services. After users fill in the Dify API information and Microsoft ASR KEY in the configuration file, they can quickly deploy their own AI robot. +作为TubeX Chat商业智能产品中的重要一环,TubeX微信机器人依托于Google Gemini大模型、线上分析处理技术、数据挖掘,通过微信渠道实时分发数据分析结果实现商业价值。用户只需扫码即可实现自用微信向BI商业智能体的转变。 |function|功能|progress| |--|--|--| @@ -30,14 +28,8 @@ This is an open-source WeChat robot project based on WechatY, Gemini, and Micros | Self-running business | 自我运维 |✅| | Crypto Coin Market Analysis | 数字货币市场分析 |✅| | Chinese Financial Market Analysis | 中国金融市场数据获取及分析 |✅| -| USNG | 天然气CFD |✅| -| UKOIL | 布伦特原油 |✅| -| USGC | 纽约黄金价格 |✅| | Inpainting | 机器人集群启动 |✅| -| Websocket监听通信 | Websocket监听通信 |✅| | SQLite | 支持SQLite数据库 |✅| -| Group Chat Export | 群聊天记录的导出 |✅| - ## Workflow @@ -92,17 +84,6 @@ pm2 logs tubex-wechatbot 由于市场分析的难度较大,建议开发者使用GPT-4o、Gemini 1.5、Gemini flash等海外大模型。 本机器人未来会接入StochRSI技术数据,以1小时线为基础向用户推送。对于普通用户来说,我们不建议您参与数字货币的投机。 -### 数字货币数据标注 - -当符合如下条件之一,该数字货币将会被标注为空 - -1. MACD快慢线数值均低于正数数据1/2之下且MACD值为负数 -2. 涨幅超过30%且当日交易额低于1亿; - -当符合如下条件之一,该数字货币将会被标注为多: - -1. MACD快慢线数值均低于正数数据1/2之上; -2. 当日交易额高于10亿; ### Cooperation diff --git a/index.js b/index.js index 882a5f9..4854e78 100644 --- a/index.js +++ b/index.js @@ -101,8 +101,6 @@ export async function prepareBot() { const selfAvatarFileBox = await selfContact.avatar(); if (selfAvatarFileBox.buffer) { const buffer = await selfAvatarFileBox.toBuffer(); - // const image = await Jimp.read(buffer); - // const compressedBuffer = await image.getBufferAsync(Jimp.MIME_JPEG); const base64 = buffer.toString('base64'); dataUrl = `data:image/jpeg;base64,${base64}`; } @@ -113,9 +111,8 @@ export async function prepareBot() { log('info', "机器人登录成功,账号名:"+user.payload.name); }) - bot.on('logout', async ()=>{ + bot.on('logout', async (user)=>{ log('info', user.payload.name+"退出登录"); - await logout() transporter.sendMail(mailOptions, (error, info) => { if (error) { return console.log(error); diff --git a/package.json b/package.json index b208102..db72f47 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "type": "module", "bin": "index.js", "scripts": { - "dev": "nodemon index.js", "start": "node index.js", "build": "pkg . --target node16-macos-arm64 --output wechaty-client" }, diff --git a/router/public/chats.html b/router/public/chats.html index 2ec7dc8..ff28ec8 100644 --- a/router/public/chats.html +++ b/router/public/chats.html @@ -189,7 +189,9 @@ }, []); const links = [ - { path: "/", text: "首页" },{ path: "/list", text: "好友列表" },{ path: "/flashmemo", text: "短期记忆" }, + { path: "/", text: "首页" }, + { path: "/list", text: "好友列表" }, + { path: "/flashmemo", text: "短期记忆" }, { path: "/chats", text: "聊天记录" }, { path: "/logs", text: "日志记录" }, { path: "/settings", text: "系统设置" }, diff --git a/router/public/docs.html b/router/public/docs.html index 492afd7..2efd7ac 100644 --- a/router/public/docs.html +++ b/router/public/docs.html @@ -145,16 +145,17 @@ }, []); const links = [ - { path: "/", text: "首页" },{ path: "/list", text: "好友列表" },{ path: "/flashmemo", text: "短期记忆" }, + { path: "/", text: "首页" },{ path: "/list", text: "好友列表" }, + { path: "/flashmemo", text: "短期记忆" }, { path: "/chats", text: "聊天记录" }, { path: "/logs", text: "日志记录" }, { path: "/settings", text: "系统设置" }, { path: "/docs", text: "开发文档" }, - { path: "/poster", text: "图片库" }, - { path: "/knowledge", text: "长期记忆" }, + { path: "/poster", text: "图片库" }, + { path: "/knowledge", text: "长期记忆" }, { path: "/forgetpassword", text: "重置密码" }, - { path: "/voice", text: "语音设置" }, - { path: "/email", text: "邮箱设置" }, + { path: "/voice", text: "语音设置" }, + { path: "/email", text: "邮箱设置" }, ]; return ( diff --git a/router/public/flashmemo.html b/router/public/flashmemo.html index cfd9176..b5181be 100644 --- a/router/public/flashmemo.html +++ b/router/public/flashmemo.html @@ -186,7 +186,8 @@ }, []); const links = [ - { path: "/", text: "首页" },{ path: "/list", text: "好友列表" }, + { path: "/", text: "首页" }, + { path: "/list", text: "好友列表" }, { path: "/flashmemo", text: "短期记忆" }, { path: "/chats", text: "聊天记录" }, { path: "/logs", text: "日志记录" }, @@ -217,11 +218,15 @@ title: '消息内容', dataIndex: 'content', width: '40%', - render:(content)=>( -
- -
- ) + render:(obj,content)=>{ + if(obj.type === 'image'){ + return
+ +
+ }else{ + return

文本内容

+ } + } }, ]; diff --git a/router/public/knowledge.html b/router/public/knowledge.html index 11c5f54..f381355 100644 --- a/router/public/knowledge.html +++ b/router/public/knowledge.html @@ -229,7 +229,8 @@ }, []); const links = [ - { path: "/", text: "首页" },{ path: "/list", text: "好友列表" },{ path: "/flashmemo", text: "短期记忆" }, + { path: "/", text: "首页" },{ path: "/list", text: "好友列表" }, + { path: "/flashmemo", text: "短期记忆" }, { path: "/chats", text: "聊天记录" }, { path: "/logs", text: "日志记录" }, { path: "/settings", text: "系统设置" }, diff --git a/router/public/settings.html b/router/public/settings.html index 0e8f91b..5db8ffa 100644 --- a/router/public/settings.html +++ b/router/public/settings.html @@ -169,6 +169,12 @@ letter-spacing: 1px; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } + .semi-input-inset-label { + color: #ddd; + } + .semi-input { + color: #fff; + } .semi-switch-wrapper{ background: rgba(255, 255, 255, 0.1); border: 1px solid #444; diff --git a/util/gemini.js b/util/gemini.js index a4bc262..e7e68e1 100644 --- a/util/gemini.js +++ b/util/gemini.js @@ -39,7 +39,6 @@ export const stockCheck = async (query) => { } export const chat = async (query) => { - console.log(global_prompt) const result = await model.generateContent(global_prompt+"。"+query); return result.response.text() } diff --git a/util/group.js b/util/group.js index 2c970e8..bd57971 100644 --- a/util/group.js +++ b/util/group.js @@ -1,13 +1,14 @@ /* eslint-disable no-undef */ // import {transporter,mailOptionsBigquant} from './mailer.js'; - +const NEWS_ENDPOINT="https://api-one-wscn.awtmt.com/apiv1/search/live?channel=global-channel&limit=40&score=2" +const NEWS_ETAG="VKikV7rHg+OhU+DW+HiofA==" export const getNews = async () => { try { - const res = await fetch(process.env.NEWS_ENDPOINT, { + const res = await fetch(NEWS_ENDPOINT, { method: 'GET', headers: { - 'If-None-Match': process.env.NEWS_ETAG + 'If-None-Match': NEWS_ETAG }, }); const wallstreetNews = await res.json(); diff --git a/util/handle.js b/util/handle.js index 4178670..0e2c2f3 100644 --- a/util/handle.js +++ b/util/handle.js @@ -9,6 +9,8 @@ import process from 'process'; import schedule from 'node-schedule'; import {getNews} from './group.js' import {saveFlashMemory} from '../db/flashmemories.js' +import {getConfig} from '../db/config.js' +let config = await getConfig(); export const singleChat = async (talkerId,listenerId,text) => { if(talkerId!==listenerId && talkerId!=='weixin' && text!==''){ @@ -236,30 +238,84 @@ export const handleArticle = async (xmlstr,talkerId) => { }); } -// 处理主动推送信息 -export const handlePush = async (rooms) => { - let id = "" - rooms.filter(room=>{ - if(process.env.MANAGE_ROOMS.includes(room.payload.topic)){ - id = room.id - } +function stringToArray(str) { + if (!str) { + return []; // 处理空字符串或 undefined 的情况 + } + + return str.split(',').map(item => item.trim()).filter(item => item !== ""); +} + +export const getBinanceRanker = async () => { + try { + const res = await fetch("https://www.binance.com/fapi/v1/ticker/24hr", { + method: 'GET', }); + const items = await res.json(); + items.sort((a, b) => { + return b.priceChangePercent - a.priceChangePercent + }) + // 遍历所有的交易对 + const newItems = []; + items.forEach(item => { + // 删除symbol中的USDT,其余字段将字符串转换成数字 + + // 过滤涨幅小于10%和交易量小于5000万的交易对 + if(parseFloat(item.priceChangePercent) < 10 || parseFloat(item.quoteVolume) < 50000000) return + // 如果交易对位AGIXUSDT、OCEANUSDT则过滤,如果包含USDC则过滤 + if(item.symbol === "AGIXUSDT" || item.symbol === "OCEANUSDT" || item.symbol.includes("USDC")) return + + // 如果交易量大于10亿则为市场热点 + if(parseFloat(item.quoteVolume) > 1000000000) item.市场热点 = true + const obj = { + "数字货币":item.symbol.replace("USDT",""), + "交易额": (parseFloat(item.quoteVolume)/100000000).toFixed(2)+"亿", + "涨幅":(parseFloat(item.priceChangePercent).toFixed(1))+"%", + "当前价格":parseFloat(item.lastPrice).toFixed(3)+"$", + } + newItems.push(obj) + }); + // 转换成字符串输出 + let str = newItems.map((item) => { + return `数字货币:${item.数字货币} | 交易额:${item.交易额} | 涨幅:${item.涨幅} | 当前价格:${item.当前价格}`; + }).join("\n"); + + return str + } catch (err) { + console.error(err); + + } +}; - if(process.env.IS_PUSH_MESSAGE){ +// 处理主动推送信息 +export const handlePush = async (rooms) => { + let {groups,isPushEnable,isEnable,pushTime} = config; + // 当AI开启情况下,推送启动及各项配置均已满足则进入推送机制; + if(groups && isPushEnable && isEnable && pushTime){ + let groupArray = stringToArray(groups); const rule = new schedule.RecurrenceRule(); - rule.hour = process.env.SCHEDULE_HOUR - rule.minute = process.env.SCHEDULE_MINUES + const times = pushTime.split(":") + rule.hour = times[0] + rule.minute = times[1] rule.tz = 'Asia/Shanghai'; - schedule.scheduleJob(rule, async()=>{ - let {data} = await getNews(); - if(data){ - let answer = await think(id,`请根据当前的经济数据,对当前市场进行分析`) - answer = answer.replace(/\*/g, ''); - await sendMessage(id, answer); + const filteredRooms = rooms.filter(room => { + if (!room || !room.payload || !room.payload.topic) { + return false; // 如果 room, room.payload 或者 room.payload.topic 不存在则跳过 } + const topic = room.payload.topic; + return groupArray.some(group => topic.trim() === group.trim()); }); - } -} + schedule.scheduleJob(rule, async()=>{ + let {data} = await getNews(); + let cryptoInfo = await getBinanceRanker() + filteredRooms.forEach(async room => { + let answer = chat("请将下列文字转换成一个严肃专业的报告:"+data+cryptoInfo); + await sendMessage(room.id, answer); + }); + }) + }; + } + // 处理图片,该函数的触发条件是指用户在当前文本信息之前发送了一张图片 export const handleImage = async (message, talkerId) => { try{ diff --git a/util/init.js b/util/init.js index ca57ed9..ad1637c 100644 --- a/util/init.js +++ b/util/init.js @@ -5,14 +5,12 @@ config(); import { PuppetWechat4u } from 'wechaty-puppet-wechat4u'; // import fs from 'fs'; import {getNews} from './group.js' -import {classfication, stockCheck,chat,recgonizeImage,chatWithFile} from './gemini.js' +import {classfication,chat,recgonizeImage,chatWithFile} from './gemini.js' import moment from 'moment'; import {deleteFlashMemory, getFlashMemory} from '../db/flashmemories.js' import 'dotenv/config' -// import Redis from 'ioredis'; -// const redis = new Redis(); -const fetchStockInfo = async (stockName) => { +export const fetchStockInfo = async (stockName) => { try{ let res = await fetch(`https://api-one-wscn.awtmt.com/apiv1/search/live?&cursor=&limit=5&query=${stockName}`); let timenews = await res.json(); @@ -33,7 +31,7 @@ const fetchStockInfo = async (stockName) => { } } -const fetchKlines = async (symbol, interval='1h', limit=100) => { +export const fetchKlines = async (symbol, interval='1h', limit=100) => { try{ let res = await fetch(`https://api.binance.com/api/v3/klines?interval=${interval}&limit=${limit}&symbol=${symbol}USDT`); /** @@ -73,7 +71,7 @@ const fetchKlines = async (symbol, interval='1h', limit=100) => { } } -const fetchBasicCryptoMarketInfo = async () => { +export const fetchBasicCryptoMarketInfo = async () => { try { let res = await fetch(process.env.BINANCE_TOPSEARCH); let topsearchList = await res.json(); @@ -111,46 +109,7 @@ const fetchBasicCryptoMarketInfo = async () => { } -const getBinanceRanker = async () => { - try { - const res = await fetch("https://www.binance.com/fapi/v1/ticker/24hr", { - method: 'GET', - }); - const items = await res.json(); - items.sort((a, b) => { - return b.priceChangePercent - a.priceChangePercent - }) - // 遍历所有的交易对 - const newItems = []; - items.forEach(item => { - // 删除symbol中的USDT,其余字段将字符串转换成数字 - - // 过滤涨幅小于10%和交易量小于5000万的交易对 - if(parseFloat(item.priceChangePercent) < 10 || parseFloat(item.quoteVolume) < 50000000) return - // 如果交易对位AGIXUSDT、OCEANUSDT则过滤,如果包含USDC则过滤 - if(item.symbol === "AGIXUSDT" || item.symbol === "OCEANUSDT" || item.symbol.includes("USDC")) return - - // 如果交易量大于10亿则为市场热点 - if(parseFloat(item.quoteVolume) > 1000000000) item.市场热点 = true - const obj = { - "数字货币":item.symbol.replace("USDT",""), - "交易额": (parseFloat(item.quoteVolume)/100000000).toFixed(2)+"亿", - "涨幅":(parseFloat(item.priceChangePercent).toFixed(1))+"%", - "当前价格":parseFloat(item.lastPrice).toFixed(3)+"$", - } - newItems.push(obj) - }); - // 转换成字符串输出 - let str = newItems.map((item) => { - return `数字货币:${item.数字货币} | 交易额:${item.交易额} | 涨幅:${item.涨幅} | 当前价格:${item.当前价格}`; - }).join("\n"); - - return str - } catch (err) { - console.error(err); - - } -}; + export const think = async (talkid,query) => { /** @@ -168,91 +127,15 @@ export const think = async (talkid,query) => { return await chatWithFile(query,flashMemory.content) } - let type = await classfication(query); + console.log("type",type) if(type.includes("股票")){ - let stock = await stockCheck(query); - if(stock=="0"){ - let news = await getNews(query); - query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${news.data},你可以参考这些信息回答问题。问题:${query}` - }else{ - let stockInfo = await fetchStockInfo(stock); - console.log(stockInfo) - if(stockInfo=='[]'){ - let news = await getNews(query); - query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${news.data},你可以参考这些信息回答问题。问题:${query}` - }else{ - query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${stockInfo},你可以参考这些信息回答问题。问题:${query}` - } - } + let news = await getNews(); + query = query +"。已知信息:"+news.data return await chat(query) }else{ return await chat(query) } - // try{ - - // let type = await classfication(query); - // type = type.trim() - // console.log(type) - // switch(type){ - - // case "数字货币": - // { let basicCryptoMarketInfo = await getBinanceRanker(); - // console.log("。当前币安涨幅榜:"+basicCryptoMarketInfo) - // query = query +"。当前币安涨幅榜:"+basicCryptoMarketInfo+"。判断标准:数字货币交易额超过10亿或者涨幅超过40%表示当前数字货币处于狂热状态,否则为正常状态,如果所有的交易对的涨幅都为负数则表示当前市场处于恐慌状态。" - // break; - // } - // case "股票": - // { - // let stock = await stockCheck(query); - // if(stock=="0"){ - // let news = await getNews(query); - // query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${news.data},你可以参考这些信息回答问题。问题:${query}` - // }else{ - // let stockInfo = await fetchStockInfo(stock); - // console.log(stockInfo) - // if(stockInfo=='[]'){ - // let news = await getNews(query); - // query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${news.data},你可以参考这些信息回答问题。问题:${query}` - // }else{ - // query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${stockInfo},你可以参考这些信息回答问题。问题:${query}` - // } - // } - - // break; - // } - // case "原油": - // query = query +"。已知信息:原油信息" - // break; - // case "外汇": - // query = query +"。已知信息:外汇信息" - // break; - // case "其他": - // { - // let stock = await stockCheck(query); - // if(stock=="0"){ - // let news = await getNews(query); - // query = query +"before you'r:"+news.data - // }else{ - // let stockInfo = await fetchStockInfo(stock); - // console.log(stockInfo) - // if(stockInfo=='[]'){ - // let news = await getNews(query); - // query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${news.data},你可以参考这些信息回答问题。问题:${query}` - // }else{ - // query = `扮演一名与客户对话的金融专家,已经获取的知识储备如下:${news.data},你可以参考这些信息回答问题。问题:${stockInfo}` - // } - // } - - // break; - // } - // default: - // break; - // } - // }catch(e){ - // query = query +"。已知信息:其他信息"+e - // } - // return await chat(query) } const puppet = new PuppetWechat4u()