2022-03-06 12:24:19 +00:00
|
|
|
|
import Telegram from '../client/Telegram';
|
2022-03-07 10:14:45 +00:00
|
|
|
|
import { getLogger, Logger } from 'log4js';
|
2022-03-06 12:24:19 +00:00
|
|
|
|
import { Api } from 'telegram';
|
2022-03-07 08:36:13 +00:00
|
|
|
|
import db from '../models/db';
|
2023-06-30 11:26:45 +00:00
|
|
|
|
import { Friend, FriendRecallEvent, Group, GroupRecallEvent } from 'icqq';
|
2022-03-07 08:36:13 +00:00
|
|
|
|
import Instance from '../models/Instance';
|
2022-03-07 10:05:14 +00:00
|
|
|
|
import { Pair } from '../models/Pair';
|
2022-03-12 14:53:53 +00:00
|
|
|
|
import { consumer } from '../utils/highLevelFunces';
|
2022-12-23 12:00:04 +00:00
|
|
|
|
import forwardHelper from '../helpers/forwardHelper';
|
2024-01-12 15:29:40 +00:00
|
|
|
|
import flags from '../constants/flags';
|
2022-03-06 12:24:19 +00:00
|
|
|
|
|
|
|
|
|
export default class DeleteMessageService {
|
2022-03-07 10:14:45 +00:00
|
|
|
|
private readonly log: Logger;
|
2024-01-13 09:28:27 +00:00
|
|
|
|
private readonly lockIds = new Set<string>();
|
2022-03-06 12:24:19 +00:00
|
|
|
|
|
2022-03-07 08:36:13 +00:00
|
|
|
|
constructor(private readonly instance: Instance,
|
2022-03-07 10:14:45 +00:00
|
|
|
|
private readonly tgBot: Telegram) {
|
|
|
|
|
this.log = getLogger(`DeleteMessageService - ${instance.id}`);
|
2022-03-06 12:24:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-13 09:28:27 +00:00
|
|
|
|
private lock(lockId: string) {
|
|
|
|
|
if (this.lockIds.has(lockId)) {
|
|
|
|
|
this.log.debug('重复执行消息撤回', lockId);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
this.lockIds.add(lockId);
|
|
|
|
|
setTimeout(() => this.lockIds.delete(lockId), 5000);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 14:53:53 +00:00
|
|
|
|
// 500ms 内只撤回一条消息,防止频繁导致一部分消息没有成功撤回。不过这样的话,会得不到返回的结果
|
2023-02-27 08:30:32 +00:00
|
|
|
|
private recallQqMessage = consumer(async (qq: Friend | Group, seq: number, rand: number, timeOrPktnum: number, pair: Pair, isOthersMsg: boolean, noSendError = false) => {
|
2022-03-16 13:49:17 +00:00
|
|
|
|
try {
|
|
|
|
|
const result = await qq.recallMsg(seq, rand, timeOrPktnum);
|
|
|
|
|
if (!result) throw new Error('撤回失败');
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('撤回失败', e);
|
2023-02-27 08:30:32 +00:00
|
|
|
|
if (noSendError) return;
|
2022-03-16 13:49:17 +00:00
|
|
|
|
const tipMsg = await pair.tg.sendMessage({
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message: '<i>撤回 QQ 中对应的消息失败' +
|
2022-03-16 13:49:17 +00:00
|
|
|
|
(this.instance.workMode === 'group' ? ',QQ Bot 需要是管理员' : '') +
|
|
|
|
|
(isOthersMsg ? ',而且无法撤回其他管理员的消息' : '') +
|
2022-03-16 15:00:21 +00:00
|
|
|
|
'</i>' +
|
2022-03-16 13:49:17 +00:00
|
|
|
|
(e.message ? '\n' + e.message : ''),
|
|
|
|
|
silent: true,
|
|
|
|
|
});
|
|
|
|
|
this.instance.workMode === 'group' && setTimeout(async () => await tipMsg.delete({ revoke: true }), 5000);
|
|
|
|
|
}
|
|
|
|
|
}, 1000);
|
2022-03-12 14:53:53 +00:00
|
|
|
|
|
2022-03-06 15:32:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* 删除 QQ 对应的消息
|
|
|
|
|
* @param messageId
|
|
|
|
|
* @param pair
|
|
|
|
|
* @param isOthersMsg
|
|
|
|
|
*/
|
2022-03-06 12:24:19 +00:00
|
|
|
|
async telegramDeleteMessage(messageId: number, pair: Pair, isOthersMsg = false) {
|
|
|
|
|
// 删除的时候会返回记录
|
2024-01-13 09:28:27 +00:00
|
|
|
|
if (this.lock(`tg-${pair.tgId}-${messageId}`)) return;
|
2022-03-06 12:24:19 +00:00
|
|
|
|
try {
|
2022-03-13 05:57:45 +00:00
|
|
|
|
const messageInfo = await db.message.findFirst({
|
2022-03-07 11:41:59 +00:00
|
|
|
|
where: {
|
2022-03-13 05:57:45 +00:00
|
|
|
|
tgChatId: pair.tgId,
|
|
|
|
|
tgMsgId: messageId,
|
|
|
|
|
instanceId: this.instance.id,
|
2022-03-07 11:41:59 +00:00
|
|
|
|
},
|
2022-03-06 12:24:19 +00:00
|
|
|
|
});
|
|
|
|
|
if (messageInfo) {
|
|
|
|
|
try {
|
2024-01-13 09:28:27 +00:00
|
|
|
|
if (this.lock(`qq-${pair.qqRoomId}-${messageInfo.seq}`)) return;
|
2023-02-27 08:30:32 +00:00
|
|
|
|
const mapQq = pair.instanceMapForTg[messageInfo.tgSenderId.toString()];
|
|
|
|
|
mapQq && this.recallQqMessage(mapQq, messageInfo.seq, Number(messageInfo.rand), messageInfo.pktnum, pair, false, true);
|
|
|
|
|
// 假如 mapQQ 是普通成员,机器人是管理员,上面撤回失败了也可以由机器人撤回
|
|
|
|
|
// 所以撤回两次
|
|
|
|
|
// 不知道哪次会成功,所以就都不发失败提示了
|
2024-01-12 15:58:50 +00:00
|
|
|
|
this.recallQqMessage(pair.qq, messageInfo.seq, Number(messageInfo.rand),
|
|
|
|
|
pair.qq instanceof Friend ? messageInfo.time : messageInfo.pktnum,
|
|
|
|
|
pair, isOthersMsg, !!mapQq);
|
2024-01-13 09:28:27 +00:00
|
|
|
|
await db.message.delete({
|
|
|
|
|
where: { id: messageInfo.id },
|
|
|
|
|
});
|
2022-03-06 12:24:19 +00:00
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
2022-03-16 13:49:17 +00:00
|
|
|
|
this.log.error(e);
|
2022-03-06 12:24:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理 TG 里面发送的 /rm
|
|
|
|
|
* @param message
|
|
|
|
|
* @param pair
|
|
|
|
|
*/
|
|
|
|
|
async handleTelegramMessageRm(message: Api.Message, pair: Pair) {
|
|
|
|
|
const replyMessage = await message.getReplyMessage();
|
|
|
|
|
if (replyMessage instanceof Api.Message) {
|
|
|
|
|
// 检查权限并撤回被回复的消息
|
2022-03-07 08:36:13 +00:00
|
|
|
|
let hasPermission = this.instance.workMode === 'personal' || replyMessage.senderId?.eq(message.senderId);
|
2022-03-06 12:24:19 +00:00
|
|
|
|
if (!hasPermission && message.chat instanceof Api.Channel) {
|
|
|
|
|
// 可能是超级群
|
|
|
|
|
try {
|
|
|
|
|
const member = (await pair.tg.getMember(message.sender)).participant;
|
|
|
|
|
hasPermission = member instanceof Api.ChannelParticipantCreator ||
|
|
|
|
|
(member instanceof Api.ChannelParticipantAdmin && member.adminRights.deleteMessages);
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
// 不管了
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!hasPermission && message.chat instanceof Api.Chat) {
|
|
|
|
|
// 不是超级群,我也不知道怎么判断,而且应该用不到
|
|
|
|
|
}
|
|
|
|
|
if (hasPermission) {
|
|
|
|
|
// 双平台撤回被回复的消息
|
|
|
|
|
// 撤回 QQ 的
|
|
|
|
|
await this.telegramDeleteMessage(message.replyToMsgId, pair, replyMessage.senderId?.eq(this.tgBot.me.id));
|
|
|
|
|
try {
|
|
|
|
|
// 撤回 TG 的
|
|
|
|
|
await pair.tg.deleteMessages(message.replyToMsgId);
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
2022-03-16 15:00:21 +00:00
|
|
|
|
await pair.tg.sendMessage(`<i>删除消息失败</i>:${e.message}`);
|
2022-03-06 12:24:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const tipMsg = await pair.tg.sendMessage({
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message: '<i>不能撤回别人的消息</i>',
|
2022-03-06 12:24:19 +00:00
|
|
|
|
silent: true,
|
|
|
|
|
});
|
|
|
|
|
setTimeout(async () => await tipMsg.delete({ revoke: true }), 5000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 撤回消息本身
|
|
|
|
|
try {
|
|
|
|
|
await message.delete({ revoke: true });
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
const tipMsg = await message.reply({
|
2022-03-16 15:00:21 +00:00
|
|
|
|
message: '<i>Bot 目前无法撤回其他用户的消息,Bot 需要「删除消息」权限</i>',
|
2022-03-06 12:24:19 +00:00
|
|
|
|
silent: true,
|
|
|
|
|
});
|
|
|
|
|
setTimeout(async () => await tipMsg.delete({ revoke: true }), 5000);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-06 13:07:08 +00:00
|
|
|
|
|
|
|
|
|
public async handleQqRecall(event: FriendRecallEvent | GroupRecallEvent, pair: Pair) {
|
2024-01-13 09:28:27 +00:00
|
|
|
|
if (this.lock(`qq-${pair.qqRoomId}-${event.seq}`)) return;
|
2022-03-06 13:07:08 +00:00
|
|
|
|
try {
|
|
|
|
|
const message = await db.message.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
seq: event.seq,
|
|
|
|
|
rand: event.rand,
|
|
|
|
|
qqRoomId: pair.qqRoomId,
|
2022-03-07 11:41:59 +00:00
|
|
|
|
instanceId: this.instance.id,
|
2022-03-06 13:07:08 +00:00
|
|
|
|
},
|
|
|
|
|
});
|
2024-01-12 15:29:40 +00:00
|
|
|
|
if (!message) return;
|
2024-01-13 09:28:27 +00:00
|
|
|
|
if (this.lock(`tg-${pair.tgId}-${message.tgMsgId}`)) return;
|
2024-01-13 08:51:44 +00:00
|
|
|
|
if ((pair.flags | this.instance.flags) & flags.NO_DELETE_MESSAGE) {
|
2024-01-12 15:29:40 +00:00
|
|
|
|
await pair.tg.editMessages({
|
|
|
|
|
message: message.tgMsgId,
|
|
|
|
|
text: `<del>${message.tgMessageText}</del>\n<i>此消息已删除</i>`,
|
|
|
|
|
parseMode: 'html',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
await pair.tg.deleteMessages(message.tgMsgId);
|
2022-03-07 07:35:15 +00:00
|
|
|
|
await db.message.delete({
|
|
|
|
|
where: { id: message.id },
|
|
|
|
|
});
|
2022-03-06 13:07:08 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
this.log.error('处理 QQ 消息撤回失败', e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-23 12:00:04 +00:00
|
|
|
|
|
|
|
|
|
public async isInvalidEdit(message: Api.Message, pair: Pair) {
|
|
|
|
|
const messageInfo = await db.message.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
tgChatId: pair.tgId,
|
|
|
|
|
tgMsgId: message.id,
|
|
|
|
|
instanceId: this.instance.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (!messageInfo) return false;
|
|
|
|
|
const isTextSame = messageInfo.tgMessageText === message.message;
|
|
|
|
|
if (forwardHelper.getMessageDocumentId(message)) {
|
|
|
|
|
return forwardHelper.getMessageDocumentId(message) === messageInfo.tgFileId && isTextSame;
|
|
|
|
|
}
|
|
|
|
|
return isTextSame;
|
|
|
|
|
}
|
2022-03-06 12:24:19 +00:00
|
|
|
|
}
|