feat: 新的好友发送消息时自动创建新的 tg 群组

This commit is contained in:
Clansty 2022-03-01 21:11:14 +08:00
parent 334aa46375
commit ffdfc90b96
No known key found for this signature in database
GPG Key ID: 05F8479BA63A8E92
4 changed files with 68 additions and 25 deletions

View File

@ -1,5 +1,5 @@
import { Client, LogLevel, Platform } from 'oicq'; import { Client, DiscussMessageEvent, GroupMessageEvent, LogLevel, Platform, PrivateMessageEvent } from 'oicq';
import * as Buffer from 'buffer'; import Buffer from 'buffer';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import random from '../utils/random'; import random from '../utils/random';
import fs from 'fs'; import fs from 'fs';
@ -9,6 +9,8 @@ import { Config } from 'oicq/lib/client';
const LOG_LEVEL: LogLevel = 'warn'; const LOG_LEVEL: LogLevel = 'warn';
type MessageHandler = (event: PrivateMessageEvent | GroupMessageEvent) => Promise<boolean | void>
interface CreateOicqParams { interface CreateOicqParams {
uin: number; uin: number;
password: string; password: string;
@ -23,6 +25,8 @@ interface CreateOicqParams {
// OicqExtended?? // OicqExtended??
export default class OicqClient extends Client { export default class OicqClient extends Client {
private readonly onMessageHandlers: Array<MessageHandler> = [];
private constructor(uin: number, conf?: Config) { private constructor(uin: number, conf?: Config) {
super(uin, conf); super(uin, conf);
} }
@ -59,7 +63,8 @@ export default class OicqClient extends Client {
.off('system.login.slider', loginSliderHandler) .off('system.login.slider', loginSliderHandler)
.off('system.login.qrcode', loginQrCodeHandler) .off('system.login.qrcode', loginQrCodeHandler)
.off('system.login.error', loginErrorHandler) .off('system.login.error', loginErrorHandler)
.off('system.online', successLoginHandler); .off('system.online', successLoginHandler)
.on('message', client.onMessage);
resolve(client); resolve(client);
} }
@ -73,8 +78,8 @@ export default class OicqClient extends Client {
board: 'raincandy', board: 'raincandy',
brand: random.pick('GOOGLE', 'XIAOMI', 'HUAWEI', 'SAMSUNG', 'SONY'), brand: random.pick('GOOGLE', 'XIAOMI', 'HUAWEI', 'SAMSUNG', 'SONY'),
model: 'raincandy', model: 'raincandy',
wifi_ssid: random.pick('OpenWrt', `Redmi-${random.hex(4)}`, wifi_ssid: random.pick('OpenWrt', `Redmi-${random.hex(4).toUpperCase()}`,
`MiWifi-${random.hex(4)}`, `TP-LINK-${random.hex(6)}`), `MiWifi-${random.hex(4).toUpperCase()}`, `TP-LINK-${random.hex(6).toUpperCase()}`),
bootloader: random.pick('U-Boot', 'GRUB', 'gummiboot'), bootloader: random.pick('U-Boot', 'GRUB', 'gummiboot'),
android_id: random.hex(16), android_id: random.hex(16),
proc_version: `${execSync('uname -s').toString().replace('\n', '')} version ${execSync('uname -r').toString().replace('\n', '')}`, proc_version: `${execSync('uname -s').toString().replace('\n', '')} version ${execSync('uname -r').toString().replace('\n', '')}`,
@ -90,7 +95,7 @@ export default class OicqClient extends Client {
const client = new this(params.uin, { const client = new this(params.uin, {
platform: params.platform, platform: params.platform,
data_dir: path.resolve('./data'), data_dir: path.resolve('./data'),
log_level: 'warn', log_level: LOG_LEVEL,
}) })
.on('system.login.device', loginDeviceHandler) .on('system.login.device', loginDeviceHandler)
.on('system.login.slider', loginSliderHandler) .on('system.login.slider', loginSliderHandler)
@ -101,6 +106,23 @@ export default class OicqClient extends Client {
}); });
} }
private onMessage = async (event: PrivateMessageEvent | GroupMessageEvent | DiscussMessageEvent) => {
if (event.message_type === 'discuss') return;
for (const handler of this.onMessageHandlers) {
const res = await handler(event);
if (res) return;
}
};
public addNewMessageEventHandler(handler: MessageHandler) {
this.onMessageHandlers.push(handler);
}
public removeNewMessageEventHandler(handler: MessageHandler) {
this.onMessageHandlers.includes(handler) &&
this.onMessageHandlers.splice(this.onMessageHandlers.indexOf(handler), 1);
}
public getChat(roomId: number) { public getChat(roomId: number) {
if (roomId > 0) { if (roomId > 0) {
return this.pickFriend(roomId); return this.pickFriend(roomId);

View File

@ -5,9 +5,11 @@ import ConfigService from '../services/ConfigService';
import { config } from '../providers/userConfig'; import { config } from '../providers/userConfig';
import regExps from '../constants/regExps'; import regExps from '../constants/regExps';
import forwardPairs from '../providers/forwardPairs'; import forwardPairs from '../providers/forwardPairs';
import { GroupMessageEvent, PrivateMessageEvent } from 'oicq';
export default class ConfigController { export default class ConfigController {
private readonly configService: ConfigService; private readonly configService: ConfigService;
private readonly createPrivateMessageGroupBlockList = new Map<number, Promise<void>>();
constructor(private readonly tgBot: Telegram, constructor(private readonly tgBot: Telegram,
private readonly tgUser: Telegram, private readonly tgUser: Telegram,
@ -15,6 +17,7 @@ export default class ConfigController {
this.configService = new ConfigService(tgBot, tgUser, oicq); this.configService = new ConfigService(tgBot, tgUser, oicq);
tgBot.addNewMessageEventHandler(this.handleMessage); tgBot.addNewMessageEventHandler(this.handleMessage);
tgBot.addNewServiceMessageEventHandler(this.handleServiceMessage); tgBot.addNewServiceMessageEventHandler(this.handleServiceMessage);
oicq.addNewMessageEventHandler(this.handleQqMessage);
this.configService.configCommands(); this.configService.configCommands();
config.workMode === 'personal' && this.configService.setupFilter(); config.workMode === 'personal' && this.configService.setupFilter();
} }
@ -69,4 +72,21 @@ export default class ConfigController {
pair.tg = await this.tgBot.getChat(message.action.channelId); pair.tg = await this.tgBot.getChat(message.action.channelId);
} }
}; };
private handleQqMessage = async (message: GroupMessageEvent | PrivateMessageEvent) => {
if (message.message_type !== 'private') return false;
const pair = forwardPairs.find(message.friend);
if (pair) return false;
// 如果正在创建中,应该阻塞
let promise = this.createPrivateMessageGroupBlockList.get(message.from_id);
if (promise) {
await promise;
return false;
}
// 有未创建转发群的新私聊消息时自动创建
promise = this.configService.createGroupAndLink(message.from_id, message.friend.remark || message.friend.nickname, true);
this.createPrivateMessageGroupBlockList.set(message.from_id, promise);
await promise;
return false;
};
} }

View File

@ -2,7 +2,7 @@ import Telegram from '../client/Telegram';
import OicqClient from '../client/OicqClient'; import OicqClient from '../client/OicqClient';
import ForwardService from '../services/ForwardService'; import ForwardService from '../services/ForwardService';
import forwardPairs from '../providers/forwardPairs'; import forwardPairs from '../providers/forwardPairs';
import { DiscussMessageEvent, Friend, Group, GroupMessageEvent, PrivateMessageEvent } from 'oicq'; import { Friend, Group, GroupMessageEvent, PrivateMessageEvent } from 'oicq';
import db from '../providers/db'; import db from '../providers/db';
import helper from '../helpers/forwardHelper'; import helper from '../helpers/forwardHelper';
import { Api } from 'telegram'; import { Api } from 'telegram';
@ -15,19 +15,18 @@ export default class ForwardController {
private readonly oicq: OicqClient) { private readonly oicq: OicqClient) {
this.forwardService = new ForwardService(tgBot, oicq); this.forwardService = new ForwardService(tgBot, oicq);
forwardPairs.init(oicq, tgBot) forwardPairs.init(oicq, tgBot)
.then(() => oicq.on('message', this.onQqMessage)) .then(() => oicq.addNewMessageEventHandler(this.onQqMessage))
.then(() => tgBot.addNewMessageEventHandler(this.onTelegramMessage)); .then(() => tgBot.addNewMessageEventHandler(this.onTelegramMessage));
} }
private onQqMessage = async (event: PrivateMessageEvent | GroupMessageEvent | DiscussMessageEvent) => { private onQqMessage = async (event: PrivateMessageEvent | GroupMessageEvent) => {
let target: Friend | Group; let target: Friend | Group;
if (event.message_type === 'private') { if (event.message_type === 'private') {
target = event.friend; target = event.friend;
} }
else if (event.message_type === 'group') { else {
target = event.group; target = event.group;
} }
else return;
const pair = forwardPairs.find(target); const pair = forwardPairs.find(target);
if (!pair) return; if (!pair) return;
const tgMessage = await this.forwardService.forwardFromQq(event, pair); const tgMessage = await this.forwardService.forwardFromQq(event, pair);

View File

@ -109,7 +109,7 @@ export default class ConfigService {
// endregion // endregion
private async createGroupAndLink(roomId: number, title?: string) { public async createGroupAndLink(roomId: number, title?: string, silent = false) {
this.log.info(`创建群组并关联:${roomId}`); this.log.info(`创建群组并关联:${roomId}`);
const qEntity = this.oicq.getChat(roomId); const qEntity = this.oicq.getChat(roomId);
if (!title) { if (!title) {
@ -124,7 +124,7 @@ export default class ConfigService {
let isFinish = false; let isFinish = false;
try { try {
// 状态信息 // 状态信息
const status = await (await this.owner).sendMessage('正在创建 Telegram 群…'); const status = !silent && await (await this.owner).sendMessage('正在创建 Telegram 群…');
// 创建群聊,拿到的是 user 的 chat // 创建群聊,拿到的是 user 的 chat
const chat = await this.tgUser.createChat({ const chat = await this.tgUser.createChat({
@ -133,12 +133,12 @@ export default class ConfigService {
}); });
// 设置管理员 // 设置管理员
await status.edit({ text: '正在设置管理员…' }); status && await status.edit({ text: '正在设置管理员…' });
await chat.editAdmin(this.tgBot.me.username, true); await chat.editAdmin(this.tgBot.me.username, true);
const chatForBot = await this.tgBot.getChat(chat.id); const chatForBot = await this.tgBot.getChat(chat.id);
// 添加到 Filter // 添加到 Filter
await status.edit({ text: '正在将群添加到文件夹…' }); status && await status.edit({ text: '正在将群添加到文件夹…' });
this.filter.includePeers.push(utils.getInputPeer(chat)); this.filter.includePeers.push(utils.getInputPeer(chat));
await this.tgUser.updateDialogFilter({ await this.tgUser.updateDialogFilter({
id: this.filter.id, id: this.filter.id,
@ -146,16 +146,16 @@ export default class ConfigService {
}); });
// 关闭【添加成员】快捷条 // 关闭【添加成员】快捷条
await status.edit({ text: '正在关闭【添加成员】快捷条…' }); status && await status.edit({ text: '正在关闭【添加成员】快捷条…' });
await chat.hidePeerSettingsBar(); await chat.hidePeerSettingsBar();
// 关联写入数据库 // 关联写入数据库
await status.edit({ text: '正在写数据库…' }); status && await status.edit({ text: '正在写数据库…' });
const dbPair = await forwardPairs.add(qEntity, chatForBot); const dbPair = await forwardPairs.add(qEntity, chatForBot);
isFinish = true; isFinish = true;
// 更新头像 // 更新头像
await status.edit({ text: '正在更新头像…' }); status && await status.edit({ text: '正在更新头像…' });
const avatar = await getAvatar(roomId); const avatar = await getAvatar(roomId);
const avatarHash = md5B64(avatar); const avatarHash = md5B64(avatar);
await chatForBot.setProfilePhoto(avatar); await chatForBot.setProfilePhoto(avatar);
@ -164,16 +164,18 @@ export default class ConfigService {
}); });
// 更新关于文本 // 更新关于文本
await status.edit({ text: '正在更新关于文本…' }); status && await status.edit({ text: '正在更新关于文本…' });
await chatForBot.editAbout(await this.getAboutText(qEntity)); await chatForBot.editAbout(await this.getAboutText(qEntity));
// 完成 // 完成
await status.edit({ text: '正在获取链接…' }); if (status) {
const { link } = await chat.getInviteLink(); await status.edit({ text: '正在获取链接…' });
await status.edit({ const { link } = await chat.getInviteLink();
text: '创建完成!', await status.edit({
buttons: Button.url('打开', link), text: '创建完成!',
}); buttons: Button.url('打开', link),
});
}
} }
catch (e) { catch (e) {
this.log.error('创建群组并关联失败', e); this.log.error('创建群组并关联失败', e);