diff --git a/src/client/OicqClient.ts b/src/client/OicqClient.ts index a79bacd..8081f9d 100644 --- a/src/client/OicqClient.ts +++ b/src/client/OicqClient.ts @@ -1,5 +1,5 @@ -import { Client, LogLevel, Platform } from 'oicq'; -import * as Buffer from 'buffer'; +import { Client, DiscussMessageEvent, GroupMessageEvent, LogLevel, Platform, PrivateMessageEvent } from 'oicq'; +import Buffer from 'buffer'; import { execSync } from 'child_process'; import random from '../utils/random'; import fs from 'fs'; @@ -9,6 +9,8 @@ import { Config } from 'oicq/lib/client'; const LOG_LEVEL: LogLevel = 'warn'; +type MessageHandler = (event: PrivateMessageEvent | GroupMessageEvent) => Promise + interface CreateOicqParams { uin: number; password: string; @@ -23,6 +25,8 @@ interface CreateOicqParams { // OicqExtended?? export default class OicqClient extends Client { + private readonly onMessageHandlers: Array = []; + private constructor(uin: number, conf?: Config) { super(uin, conf); } @@ -59,7 +63,8 @@ export default class OicqClient extends Client { .off('system.login.slider', loginSliderHandler) .off('system.login.qrcode', loginQrCodeHandler) .off('system.login.error', loginErrorHandler) - .off('system.online', successLoginHandler); + .off('system.online', successLoginHandler) + .on('message', client.onMessage); resolve(client); } @@ -73,8 +78,8 @@ export default class OicqClient extends Client { board: 'raincandy', brand: random.pick('GOOGLE', 'XIAOMI', 'HUAWEI', 'SAMSUNG', 'SONY'), model: 'raincandy', - wifi_ssid: random.pick('OpenWrt', `Redmi-${random.hex(4)}`, - `MiWifi-${random.hex(4)}`, `TP-LINK-${random.hex(6)}`), + wifi_ssid: random.pick('OpenWrt', `Redmi-${random.hex(4).toUpperCase()}`, + `MiWifi-${random.hex(4).toUpperCase()}`, `TP-LINK-${random.hex(6).toUpperCase()}`), bootloader: random.pick('U-Boot', 'GRUB', 'gummiboot'), android_id: random.hex(16), 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, { platform: params.platform, data_dir: path.resolve('./data'), - log_level: 'warn', + log_level: LOG_LEVEL, }) .on('system.login.device', loginDeviceHandler) .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) { if (roomId > 0) { return this.pickFriend(roomId); diff --git a/src/controllers/ConfigController.ts b/src/controllers/ConfigController.ts index d72a8ca..657c46b 100644 --- a/src/controllers/ConfigController.ts +++ b/src/controllers/ConfigController.ts @@ -5,9 +5,11 @@ import ConfigService from '../services/ConfigService'; import { config } from '../providers/userConfig'; import regExps from '../constants/regExps'; import forwardPairs from '../providers/forwardPairs'; +import { GroupMessageEvent, PrivateMessageEvent } from 'oicq'; export default class ConfigController { private readonly configService: ConfigService; + private readonly createPrivateMessageGroupBlockList = new Map>(); constructor(private readonly tgBot: Telegram, private readonly tgUser: Telegram, @@ -15,6 +17,7 @@ export default class ConfigController { this.configService = new ConfigService(tgBot, tgUser, oicq); tgBot.addNewMessageEventHandler(this.handleMessage); tgBot.addNewServiceMessageEventHandler(this.handleServiceMessage); + oicq.addNewMessageEventHandler(this.handleQqMessage); this.configService.configCommands(); config.workMode === 'personal' && this.configService.setupFilter(); } @@ -69,4 +72,21 @@ export default class ConfigController { 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; + }; } diff --git a/src/controllers/ForwardController.ts b/src/controllers/ForwardController.ts index c9dd68d..7ff4964 100644 --- a/src/controllers/ForwardController.ts +++ b/src/controllers/ForwardController.ts @@ -2,7 +2,7 @@ import Telegram from '../client/Telegram'; import OicqClient from '../client/OicqClient'; import ForwardService from '../services/ForwardService'; 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 helper from '../helpers/forwardHelper'; import { Api } from 'telegram'; @@ -15,19 +15,18 @@ export default class ForwardController { private readonly oicq: OicqClient) { this.forwardService = new ForwardService(tgBot, oicq); forwardPairs.init(oicq, tgBot) - .then(() => oicq.on('message', this.onQqMessage)) + .then(() => oicq.addNewMessageEventHandler(this.onQqMessage)) .then(() => tgBot.addNewMessageEventHandler(this.onTelegramMessage)); } - private onQqMessage = async (event: PrivateMessageEvent | GroupMessageEvent | DiscussMessageEvent) => { + private onQqMessage = async (event: PrivateMessageEvent | GroupMessageEvent) => { let target: Friend | Group; if (event.message_type === 'private') { target = event.friend; } - else if (event.message_type === 'group') { + else { target = event.group; } - else return; const pair = forwardPairs.find(target); if (!pair) return; const tgMessage = await this.forwardService.forwardFromQq(event, pair); diff --git a/src/services/ConfigService.ts b/src/services/ConfigService.ts index 36f63f6..9d790c8 100644 --- a/src/services/ConfigService.ts +++ b/src/services/ConfigService.ts @@ -109,7 +109,7 @@ export default class ConfigService { // endregion - private async createGroupAndLink(roomId: number, title?: string) { + public async createGroupAndLink(roomId: number, title?: string, silent = false) { this.log.info(`创建群组并关联:${roomId}`); const qEntity = this.oicq.getChat(roomId); if (!title) { @@ -124,7 +124,7 @@ export default class ConfigService { let isFinish = false; try { // 状态信息 - const status = await (await this.owner).sendMessage('正在创建 Telegram 群…'); + const status = !silent && await (await this.owner).sendMessage('正在创建 Telegram 群…'); // 创建群聊,拿到的是 user 的 chat 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); const chatForBot = await this.tgBot.getChat(chat.id); // 添加到 Filter - await status.edit({ text: '正在将群添加到文件夹…' }); + status && await status.edit({ text: '正在将群添加到文件夹…' }); this.filter.includePeers.push(utils.getInputPeer(chat)); await this.tgUser.updateDialogFilter({ 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 status.edit({ text: '正在写数据库…' }); + status && await status.edit({ text: '正在写数据库…' }); const dbPair = await forwardPairs.add(qEntity, chatForBot); isFinish = true; // 更新头像 - await status.edit({ text: '正在更新头像…' }); + status && await status.edit({ text: '正在更新头像…' }); const avatar = await getAvatar(roomId); const avatarHash = md5B64(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 status.edit({ text: '正在获取链接…' }); - const { link } = await chat.getInviteLink(); - await status.edit({ - text: '创建完成!', - buttons: Button.url('打开', link), - }); + if (status) { + await status.edit({ text: '正在获取链接…' }); + const { link } = await chat.getInviteLink(); + await status.edit({ + text: '创建完成!', + buttons: Button.url('打开', link), + }); + } } catch (e) { this.log.error('创建群组并关联失败', e);