Skip to content

Commit b4af482

Browse files
authored
Merge pull request #20 from aoao-eth/app
App
2 parents 6b4dcd5 + 723a076 commit b4af482

File tree

13 files changed

+463
-73
lines changed

13 files changed

+463
-73
lines changed

app/electron-builder.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
appId: com.html-js.wx-summarize-bot
2-
productName: 群聊总结智囊
2+
productName: wx-summarize-bot
33
copyright: Copyright © 2023 Yutou
44
directories:
55
output: dist
@@ -20,6 +20,7 @@ mac:
2020
entitlementsInherit: build/entitlements.mac.plist
2121
icon: public/logo.icns
2222
provisioningProfile: build/wxsummarizebot.provisionprofile
23+
afterSign: ./scripts/notarize.js
2324
win:
2425
target: nsis
2526
icon: public/logo.ico
@@ -34,15 +35,15 @@ nsis:
3435
installerHeaderIcon: public/logo.ico
3536
createDesktopShortcut: true
3637
createStartMenuShortcut: true
37-
shortcutName: 群聊总结智囊
38+
shortcutName: wx-summarize-bot
3839
license: LICENSE
3940
artifactName: '${productName}-setup-${version}.${ext}'
4041
linux:
4142
target: AppImage
4243
icon: public/logo.png
4344
category: Utility
44-
synopsis: 群聊总结智囊
45-
description: 群聊总结智囊
45+
synopsis: wx-summarize-bot
46+
description: wx-summarize-bot
4647
desktop: build/wx-summarize-bot.desktop
4748
publish: null
4849
artifactName: '${productName}-setup-${version}.${ext}'

app/main/background.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { createWindow } from './helpers';
44
import { summarize } from './summarize';
55
import { getAllDirs } from './helpers/getAllDirs';
66
import { getConfig, setConfig } from './config';
7-
import { botStatus, sendAudio, sendImage, sendText, startBot } from './startBot';
7+
import { botAccount, botStatus, logoutBot, sendAudio, sendImage, sendText, startBot } from './startBot';
88
import path from 'path';
9-
import { BASE_PATH, delay, PUBLIC_PATH, saveData } from './util';
9+
import { BASE_PATH, delay, getChatHistoryFromFile, PUBLIC_PATH, saveData } from './util';
1010
import fs from 'fs';
1111

1212
const isProd: boolean = process.env.NODE_ENV === 'production';
@@ -38,8 +38,25 @@ if (isProd) {
3838
ipcMain.on('get-bot-status', (event, title) => {
3939
mainWindow.webContents.send('bot-status-reply', {
4040
status: botStatus,
41+
account: botAccount,
4142
});
4243
});
44+
ipcMain.on('get-chat-content', (event, args) => {
45+
const date = args.date;
46+
const roomName = args.roomName;
47+
const filePath = path.join(BASE_PATH, date, roomName);
48+
const chats = getChatHistoryFromFile(filePath);
49+
50+
mainWindow.webContents.send('chat-content-replay', {
51+
date,
52+
roomName,
53+
chats,
54+
});
55+
56+
});
57+
ipcMain.on('logout-bot', (event, title) => {
58+
logoutBot();
59+
});
4360
ipcMain.on('summarize', (event, { dateDir, chatFileName }) => {
4461
const summarizeEvent = summarize(path.join(BASE_PATH, dateDir, chatFileName));
4562
summarizeEvent.addListener('update', (info) => {
@@ -86,41 +103,48 @@ if (isProd) {
86103
ipcMain.on('send-summarize', async (e, { dateDir, chatFileName }) => {
87104
await sendImage(
88105
chatFileName.replace('.txt', ''),
89-
path.join(BASE_PATH, dateDir, chatFileName.replace('.txt', ' 的今日群聊总结.png'))
106+
path.join(BASE_PATH, dateDir, chatFileName.replace('.txt', ' 的今日群聊总结.png')),
90107
);
91-
await delay(2000);
108+
await delay(5000);
92109
await sendAudio(
93110
chatFileName.replace('.txt', ''),
94-
path.join(BASE_PATH, dateDir, chatFileName.replace('.txt', ' 的今日群聊总结.mp3'))
111+
path.join(BASE_PATH, dateDir, chatFileName.replace('.txt', ' 的今日群聊总结.mp3')),
95112
);
96-
await delay(2000);
113+
await delay(5000);
97114
await sendText(
98115
chatFileName.replace('.txt', ''),
99116
getConfig().LAST_MESSAGE ||
100-
'主人们,智囊 AI 为您奉上今日群聊总结,祝您用餐愉快!由开源项目 https://github.com/aoao-eth/wechat-ai-summarize-bot 生成'
117+
'主人们,智囊 AI 为您奉上今日群聊总结,祝您用餐愉快!由开源项目 https://github.com/aoao-eth/wechat-ai-summarize-bot 生成',
101118
);
102119

103120
try {
104121
const file = path.join(BASE_PATH, dateDir, chatFileName.replace('.txt', ' 的今日群聊总结.txt'));
105122
const summarized = fs.readFileSync(file).toString();
106123
const 评价 = summarized.match(/.*?\n/);
124+
const 我的建议 = summarized.match(/.*?\n/);
107125
const 活跃发言者 = summarized.match(/.*?\n/);
108126
if (活跃发言者) {
109-
await delay(2000);
127+
await delay(5000);
110128
await sendText(chatFileName.replace('.txt', ''), 活跃发言者[0]);
111129
}
112130
if (评价) {
113-
await delay(2000);
114-
await sendText(chatFileName.replace('.txt', ''), 评价[0]);
131+
await delay(5000);
132+
await sendText(chatFileName.replace('.txt', ''), 评价[0] + (我的建议 ? 我的建议[0] : ''));
115133
}
116-
} catch (e) {}
134+
} catch (e) {
135+
}
117136

118137
mainWindow.webContents.send('toast', `发送成功`);
119138
saveData(dateDir, chatFileName.replace('.txt', ''), {
120139
sended: true,
121140
send_time: new Date().getTime(),
122141
});
123142
});
143+
ipcMain.on('send-chat-content', (event, arg) => {
144+
const roomName = arg.roomName;
145+
const content = arg.content;
146+
sendText(roomName, content);
147+
});
124148
})();
125149

126150
app.on('window-all-closed', () => {

app/main/startBot.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import { RoomInterface } from 'wechaty/dist/esm/src/user-modules/room';
99
let bot: WechatyInterface;
1010

1111
export let botStatus = '已停止';
12+
export let botAccount = '';
13+
14+
export async function logoutBot() {
15+
if (bot) {
16+
await bot.logout();
17+
}
18+
}
1219

1320
export async function startBot(mainWindow: Electron.BrowserWindow) {
1421
if (!checkConfigIsOk()) {
@@ -52,6 +59,7 @@ export async function startBot(mainWindow: Electron.BrowserWindow) {
5259
mainWindow.webContents.send('toast', `${user} login success`);
5360
mainWindow.webContents.send('login');
5461
botStatus = '登录成功';
62+
botAccount = user.name();
5563
})
5664
.on('logout', (user, reason) => {
5765
log.info(LOGPRE, `${user} logout, reason: ${reason}`);

app/main/util.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export function saveData(date: string, roomName: string, kvs: Record<string, any
2929
if (fs.existsSync(dataFilePath)) {
3030
try {
3131
data = JSON.parse(fs.readFileSync(dataFilePath).toString());
32-
} catch (e) {}
32+
} catch (e) {
33+
}
3334
}
3435
if (!data[roomName]) {
3536
data[roomName] = {};
@@ -44,10 +45,45 @@ export function getData(date: string, roomName: string) {
4445
if (fs.existsSync(dataFilePath)) {
4546
try {
4647
data = JSON.parse(fs.readFileSync(dataFilePath).toString());
47-
} catch (e) {}
48+
} catch (e) {
49+
}
4850
}
4951
if (!data[roomName]) {
5052
data[roomName] = {};
5153
}
5254
return data[roomName];
5355
}
56+
57+
export function getChatHistoryFromFile(filePath: string) {
58+
const fileContent = fs.readFileSync(filePath).toString();
59+
/**
60+
* 2023-09-16 19:49:47:
61+
* 甘泉:
62+
* 一个中文,一个英文
63+
*
64+
* 2023-09-16 19:56:28:
65+
* Update!9.9.9:
66+
* 嘿嘿,到手了
67+
*
68+
* 2023-09-16 20:02:43:
69+
* 芋头 🚀🌙:
70+
* 芋头 : [图片]
71+
*/
72+
// 写一段脚本,从类似的结构中抽取时间、用户名、内容
73+
74+
const pattern = /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):([\s\S]*?):([\s\S]*?)(?=(\n\n|$))/g;
75+
76+
const res = [];
77+
let result;
78+
while ((result = pattern.exec(fileContent))) {
79+
const time = result[1];
80+
const name = result[2];
81+
const content = result[3].trim();
82+
res.push({
83+
time,
84+
name,
85+
content,
86+
});
87+
}
88+
return res;
89+
}

app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"private": true,
33
"name": "wx-summarize-bot",
44
"description": "本项目是基于微信机器人的微信群聊总结助手,可以帮助群主或管理员自动收集群聊中的聊天记录,并使用 AI 进行总结,最终将其发送到指定的群聊中",
5-
"version": "1.0.2",
5+
"version": "1.1.0",
66
"author": "Yutou <xinyu198736@gmail.com>",
77
"main": "app/background.js",
88
"scripts": {
@@ -37,6 +37,7 @@
3737
"react-hot-toast": "^2.4.1",
3838
"sass": "^1.67.0",
3939
"tailwindcss": "^3.3.3",
40+
"uikit.chat": "^0.1.42",
4041
"wechaty": "^1.19.10",
4142
"wechaty-puppet": "^1.19.6",
4243
"wechaty-puppet-padlocal-plus": "^1.20.1"

app/public/template/css/githubDark.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,12 +1025,12 @@ template {
10251025

10261026
.markdown-body {
10271027
padding: 2rem 1rem;
1028-
font-family: ZhuqueFangsong-Regular !important;
1028+
font-family: Montserrat, Inter, Roboto, -apple-system, BlinkMacSystemFont, PingFang SC, Microsoft YaHei, sans-serif !important;
10291029
background-color: #1c1b1f !important;
10301030
}
10311031

10321032
.markdown-body * {
1033-
font-family: ZhuqueFangsong-Regular !important;
1033+
font-family: Montserrat, Inter, Roboto, -apple-system, BlinkMacSystemFont, PingFang SC, Microsoft YaHei, sans-serif !important;
10341034
color: #e4e2ce !important;
10351035
}
10361036

app/renderer/components/Chat.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { ChatContainer, ChatInput, ChatProvider, createClient, MessageList, MessageLoading } from 'uikit.chat';
2+
import styles from '../styles/ChatGPT.module.scss';
3+
import { useEffect } from 'react';
4+
import { ipcRenderer } from 'electron';
5+
6+
const chatuiClient = createClient();
7+
8+
export default function Chat(props: {
9+
date: string;
10+
roomName: string;
11+
}) {
12+
13+
const sendMessage = (message: string) => {
14+
ipcRenderer.send('send-chat-content', {
15+
roomName: props.roomName.replace('.txt', ''),
16+
content: message,
17+
});
18+
};
19+
useEffect(() => {
20+
21+
chatuiClient.chatboxStore.on('submit', sendMessage);
22+
chatuiClient.messageStore.clear();
23+
ipcRenderer.send('get-chat-content', {
24+
date: props.date,
25+
roomName: props.roomName,
26+
});
27+
const timer = setInterval(() => {
28+
ipcRenderer.send('get-chat-content', {
29+
date: props.date,
30+
roomName: props.roomName,
31+
});
32+
}, 1000);
33+
ipcRenderer.on('chat-content-replay', (event, args) => {
34+
console.log('chat-replay', args);
35+
if (args.date == props.date && args.roomName == props.roomName) {
36+
args.chats.forEach((chat) => {
37+
if (chatuiClient.messageStore.messages.find((m) => m.id == chat.name + chat.content + chat.time)) {
38+
return;
39+
}
40+
chatuiClient.messageStore.addMessageDirect({
41+
id: chat.name + chat.content + chat.time,
42+
role: 'assistant',
43+
content: chat.content,
44+
external: {
45+
userName: chat.name,
46+
time: chat.time,
47+
},
48+
typing: false,
49+
timestamp: (new Date(chat.time)).getTime(),
50+
});
51+
chatuiClient.messageStore.emit('change');
52+
});
53+
54+
} else {
55+
}
56+
57+
});
58+
return () => {
59+
timer && clearInterval(timer);
60+
ipcRenderer.removeAllListeners('chat-content-replay');
61+
chatuiClient.chatboxStore.removeListener('submit', sendMessage);
62+
};
63+
}, []);
64+
return <ChatProvider client={chatuiClient!}>
65+
<div className={styles['chatgpt-container']}>
66+
<div className={styles['chatgpt-container-inner']}>
67+
<ChatContainer>
68+
<div
69+
style={{
70+
display: 'flex',
71+
flexDirection: 'column',
72+
justifyContent: 'space-between',
73+
height: '100%',
74+
}}
75+
>
76+
<MessageList
77+
header={(message) => {
78+
return <div className={styles['chatgpt-ui-message-header']}>
79+
<div className={styles['chatgpt-ui-message-header-name']}>
80+
{message.external.userName}
81+
</div>
82+
83+
</div>;
84+
}}
85+
86+
content={(message) => {
87+
return <>
88+
<div
89+
className={[
90+
styles['chatgpt-ui-message'],
91+
message.typingStatus == 'typing'
92+
? styles['chatgpt-ui-message-typing']
93+
: '',
94+
].join(' ')}
95+
>
96+
97+
<div
98+
dangerouslySetInnerHTML={{
99+
__html: message.formatedContent || '',
100+
}}
101+
></div>
102+
</div>
103+
</>;
104+
}}
105+
>
106+
<MessageLoading></MessageLoading>
107+
</MessageList>
108+
<ChatInput />
109+
</div>
110+
</ChatContainer>
111+
</div>
112+
</div>
113+
</ChatProvider>;
114+
}

0 commit comments

Comments
 (0)