Skip to content

Commit

Permalink
add push
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhangWei-KUMO committed Dec 24, 2024
1 parent e9524a2 commit b8dcf94
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 185 deletions.
21 changes: 1 addition & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
<img src="public/bot.jpg" alt="logo" width="300"/>
</div>

这是一个基于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|
|--|--|--|
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
5 changes: 1 addition & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
Expand All @@ -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);
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
4 changes: 3 additions & 1 deletion router/public/chats.html
Original file line number Diff line number Diff line change
Expand Up @@ -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: "系统设置" },
Expand Down
11 changes: 6 additions & 5 deletions router/public/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
17 changes: 11 additions & 6 deletions router/public/flashmemo.html
Original file line number Diff line number Diff line change
Expand Up @@ -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: "日志记录" },
Expand Down Expand Up @@ -217,11 +218,15 @@
title: '消息内容',
dataIndex: 'content',
width: '40%',
render:(content)=>(
<div style={{wordWrap:'break-word',wordBreak:'break-all'}}>
<img src={`data:image/jpeg;base64,${content}`} width={100}/>
</div>
)
render:(obj,content)=>{
if(obj.type === 'image'){
return <div style={{wordWrap:'break-word',wordBreak:'break-all'}}>
<img src={`data:image/jpeg;base64,${content}`} width={100}/>
</div>
}else{
return <p>文本内容</p>
}
}

},
];
Expand Down
3 changes: 2 additions & 1 deletion router/public/knowledge.html
Original file line number Diff line number Diff line change
Expand Up @@ -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: "系统设置" },
Expand Down
6 changes: 6 additions & 0 deletions router/public/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion util/gemini.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
7 changes: 4 additions & 3 deletions util/group.js
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
92 changes: 74 additions & 18 deletions util/handle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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!==''){
Expand Down Expand Up @@ -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{
Expand Down
Loading

0 comments on commit b8dcf94

Please sign in to comment.