diff --git a/package.json b/package.json
index d1b32ab..28437d9 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
"@types/dockerode": "^3.3.23",
"@types/fluent-ffmpeg": "^2.1.24",
"@types/lodash": "^4.14.202",
- "@types/node": "^20.11.0",
+ "@types/node": "^20.11.5",
"@types/prompts": "^2.4.9",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
@@ -31,7 +31,7 @@
"cli-progress": "^3.11.2",
"date-and-time": "^3.1.1",
"dockerode": "^4.0.2",
- "dotenv": "^16.0.1",
+ "dotenv": "^16.4.0",
"eviltransform": "^0.2.2",
"file-type": "^19.0.0",
"fluent-ffmpeg": "^2.1.2",
@@ -47,7 +47,7 @@
"silk-sdk": "^0.2.2",
"telegram": "^2.19.10",
"tmp-promise": "^3.0.3",
- "undici": "^6.3.0",
+ "undici": "^6.4.0",
"zincsearch-node": "^2.1.0",
"zod": "^3.22.4"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 46a4b2f..45cb77d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -27,8 +27,8 @@ dependencies:
specifier: ^4.0.2
version: 4.0.2
dotenv:
- specifier: ^16.0.1
- version: 16.3.1
+ specifier: ^16.4.0
+ version: 16.4.0
eviltransform:
specifier: ^0.2.2
version: 0.2.2
@@ -75,11 +75,11 @@ dependencies:
specifier: ^3.0.3
version: 3.0.3
undici:
- specifier: ^6.3.0
- version: 6.3.0
+ specifier: ^6.4.0
+ version: 6.4.0
zincsearch-node:
specifier: ^2.1.0
- version: 2.1.1(undici@6.3.0)
+ version: 2.1.1(undici@6.4.0)
zod:
specifier: ^3.22.4
version: 3.22.4
@@ -101,8 +101,8 @@ devDependencies:
specifier: ^4.14.202
version: 4.14.202
'@types/node':
- specifier: ^20.11.0
- version: 20.11.0
+ specifier: ^20.11.5
+ version: 20.11.5
'@types/prompts':
specifier: ^2.4.9
version: 2.4.9
@@ -952,7 +952,7 @@ packages:
/@types/cli-progress@3.11.5:
resolution: {integrity: sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g==}
dependencies:
- '@types/node': 20.11.0
+ '@types/node': 20.11.5
dev: true
/@types/date-and-time@3.0.3:
@@ -965,7 +965,7 @@ packages:
/@types/docker-modem@3.0.6:
resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==}
dependencies:
- '@types/node': 20.11.0
+ '@types/node': 20.11.5
'@types/ssh2': 1.11.18
dev: true
@@ -973,13 +973,13 @@ packages:
resolution: {integrity: sha512-Lz5J+NFgZS4cEVhquwjIGH4oQwlVn2h7LXD3boitujBnzOE5o7s9H8hchEjoDK2SlRsJTogdKnQeiJgPPKLIEw==}
dependencies:
'@types/docker-modem': 3.0.6
- '@types/node': 20.11.0
+ '@types/node': 20.11.5
dev: true
/@types/fluent-ffmpeg@2.1.24:
resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
dependencies:
- '@types/node': 20.11.0
+ '@types/node': 20.11.5
dev: true
/@types/lodash@4.14.202:
@@ -996,8 +996,8 @@ packages:
undici-types: 5.26.5
dev: true
- /@types/node@20.11.0:
- resolution: {integrity: sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==}
+ /@types/node@20.11.5:
+ resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
dependencies:
undici-types: 5.26.5
dev: true
@@ -1005,7 +1005,7 @@ packages:
/@types/prompts@2.4.9:
resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==}
dependencies:
- '@types/node': 20.11.0
+ '@types/node': 20.11.5
kleur: 3.0.3
dev: true
@@ -1652,8 +1652,8 @@ packages:
domhandler: 4.3.1
dev: false
- /dotenv@16.3.1:
- resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
+ /dotenv@16.4.0:
+ resolution: {integrity: sha512-WvImr5kpN5NGNn7KaDjJnLTh5rDVLZiDf/YLA8T1ZEZEBZNEDOE+mnkS0PVjPax8ZxBP5zC5SLMB3/9VV5de9g==}
engines: {node: '>=12'}
dev: false
@@ -3637,8 +3637,8 @@ packages:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
- /undici@6.3.0:
- resolution: {integrity: sha512-zkSMOXs2topAR1LF0PxAaNNvhdX4LYEcmRMJLMh3mjgfZpBtc/souXOp4aYiR5Q46HrBPA2/8DkEZhD3eNFE1Q==}
+ /undici@6.4.0:
+ resolution: {integrity: sha512-wYaKgftNqf6Je7JQ51YzkEkEevzOgM7at5JytKO7BjaURQpERW8edQSMrr2xb+Yv4U8Yg47J24+lc9+NbeXMFA==}
engines: {node: '>=18.0'}
dependencies:
'@fastify/busboy': 2.0.0
@@ -3882,13 +3882,13 @@ packages:
engines: {node: '>= 4.0.0'}
dev: false
- /zincsearch-node@2.1.1(undici@6.3.0):
+ /zincsearch-node@2.1.1(undici@6.4.0):
resolution: {integrity: sha512-erRhljqQVK96HE7KWfMFQaFsiere3+DrInZY2g5pHpz5ea/78G1z4Exky2vsLvLZ+D320D2JINcc2BVedY+6aQ==}
peerDependencies:
undici: '>=5.10.0'
dependencies:
typescript: 4.9.5
- undici: 6.3.0
+ undici: 6.4.0
dev: false
/zod@3.22.4:
diff --git a/src/client/OicqClient.ts b/src/client/OicqClient.ts
index 7435dfc..578076f 100644
--- a/src/client/OicqClient.ts
+++ b/src/client/OicqClient.ts
@@ -217,15 +217,15 @@ export default class OicqClient extends Client {
await contact.uploadImages(imgs);
const compressed = await gzip(pb.encode({
1: nodes,
- 2: {
+ 2: [{
1: 'MultiMsg',
2: {
1: nodes,
},
- },
+ }],
}));
const _uploadMultiMsg = Reflect.get(contact, '_uploadMultiMsg') as Function;
- const resid = await _uploadMultiMsg.apply(contact, compressed);
+ const resid = await _uploadMultiMsg.apply(contact, [compressed]);
return {
tSum: nodes.length,
resid,
diff --git a/src/constants/emoji.ts b/src/constants/emoji.ts
index 4169ed2..d3820ed 100644
--- a/src/constants/emoji.ts
+++ b/src/constants/emoji.ts
@@ -2,4 +2,9 @@ import random from '../utils/random';
export default {
picture: () => random.pick('🎆', '🌃', '🌇', '🎇', '🌌', '🌠', '🌅', '🌉', '🏞', '🌆', '🌄', '🖼', '🗾', '🎑', '🏙', '🌁'),
+ color(index: number) {
+ const arr = [...new Intl.Segmenter().segment('🔴🟠🟡🟢🔵🟣⚫️⚪️🟤')].map(x => x.segment);
+ index = index % arr.length;
+ return arr[index];
+ },
};
diff --git a/src/constants/flags.ts b/src/constants/flags.ts
index 2b86bc1..28676dd 100644
--- a/src/constants/flags.ts
+++ b/src/constants/flags.ts
@@ -5,6 +5,7 @@ enum flags {
DISABLE_POKE = 1 << 3,
NO_DELETE_MESSAGE = 1 << 4,
NO_AUTO_CREATE_PM = 1 << 5,
+ COLOR_EMOJI_PREFIX = 1 << 6,
}
export default flags;
diff --git a/src/controllers/AliveCheckController.ts b/src/controllers/AliveCheckController.ts
new file mode 100644
index 0000000..2f3f3b6
--- /dev/null
+++ b/src/controllers/AliveCheckController.ts
@@ -0,0 +1,54 @@
+import Instance from '../models/Instance';
+import Telegram from '../client/Telegram';
+import OicqClient from '../client/OicqClient';
+import { Api } from 'telegram';
+
+export default class AliveCheckController {
+ constructor(private readonly instance: Instance,
+ private readonly tgBot: Telegram,
+ private readonly tgUser: Telegram,
+ private readonly oicq: OicqClient) {
+ tgBot.addNewMessageEventHandler(this.handleMessage);
+ }
+
+ private handleMessage = async (message: Api.Message) => {
+ if (!message.sender.id.eq(this.instance.owner) || !message.isPrivate) {
+ return false;
+ }
+ if (!['似了吗', '/alive'].includes(message.message)) {
+ return false;
+ }
+
+ await message.reply({
+ message: this.genMessage(this.instance.id === 0 ? Instance.instances : [this.instance]),
+ });
+ };
+
+ private genMessage(instances: Instance[]): string {
+ const boolToStr = (value: boolean) => {
+ return value ? '好' : '坏';
+ };
+ const messageParts: string[] = [];
+
+ for (const instance of instances) {
+ const oicq = instance.oicq;
+ const tgBot = instance.tgBot;
+ const tgUser = instance.tgUser;
+
+ const tgUserName = (tgUser.me.username || tgUser.me.usernames.length) ?
+ '@' + (tgUser.me.username || tgUser.me.usernames[0].username) : tgUser.me.firstName;
+ messageParts.push([
+ `Instance #${instance.id}`,
+
+ `QQ ${instance.qqUin}
\t` +
+ `${boolToStr(oicq.isOnline())}\t${oicq.stat.msg_cnt_per_min} msg/min`,
+
+ `TG @${tgBot.me.username}\t${boolToStr(tgBot.isOnline)}`,
+
+ `TG User ${tgUserName}\t${boolToStr(tgBot.isOnline)}`,
+ ].join('\n'));
+ }
+
+ return messageParts.join('\n\n');
+ };
+}
diff --git a/src/helpers/forwardHelper.ts b/src/helpers/forwardHelper.ts
index 45b7fa0..34f35eb 100644
--- a/src/helpers/forwardHelper.ts
+++ b/src/helpers/forwardHelper.ts
@@ -28,7 +28,6 @@ export default {
const aspectRatio = dimensions.width / dimensions.height;
if (aspectRatio > 20 || aspectRatio < 1 / 20
|| dimensions.width + dimensions.height > 10000
- || file.length > 1024 * 1024 * 10
) {
// 让 Telegram 服务器下载
return url
diff --git a/src/index.ts b/src/index.ts
index 8f873e9..c1a3c5d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,18 +12,22 @@ import db from './models/db';
},
});
const log = getLogger('Main');
+
+ if (!process.versions.node.startsWith('18.')) {
+ log.warn('当前正在使用的 Node.JS 版本为', process.versions.node, ',未经测试');
+ }
+
process.on('unhandledRejection', error => {
log.error('UnhandledException: ', error);
});
const instanceEntries = await db.instance.findMany();
- const instances = [] as Instance[];
if (!instanceEntries.length) {
- instances.push(await Instance.start(0));
+ await Instance.start(0);
}
else {
for (const instanceEntry of instanceEntries) {
- instances.push(await Instance.start(instanceEntry.id));
+ await Instance.start(instanceEntry.id);
}
}
diff --git a/src/models/Instance.ts b/src/models/Instance.ts
index b5e6358..9863a92 100644
--- a/src/models/Instance.ts
+++ b/src/models/Instance.ts
@@ -25,8 +25,11 @@ import HugController from '../controllers/HugController';
import QuotLyController from '../controllers/QuotLyController';
import MiraiSkipFilterController from '../controllers/MiraiSkipFilterController';
import env from './env';
+import AliveCheckController from '../controllers/AliveCheckController';
export default class Instance {
+ public static readonly instances: Instance[] = [];
+
private _owner = 0;
private _isSetup = false;
private _workMode = '';
@@ -57,6 +60,7 @@ export default class Instance {
private hugController: HugController;
private quotLyController: QuotLyController;
private miraiSkipFilterController: MiraiSkipFilterController;
+ private aliveCheckController: AliveCheckController;
private constructor(public readonly id: number) {
this.log = getLogger(`Instance - ${this.id}`);
@@ -150,6 +154,7 @@ export default class Instance {
this.oicqErrorNotifyController = new OicqErrorNotifyController(this, this.oicq);
this.requestController = new RequestController(this, this.tgBot, this.oicq);
this.configController = new ConfigController(this, this.tgBot, this.oicq);
+ this.aliveCheckController = new AliveCheckController(this, this.tgBot, this.oicq);
this.deleteMessageController = new DeleteMessageController(this, this.tgBot, this.oicq);
this.miraiSkipFilterController = new MiraiSkipFilterController(this, this.tgBot, this.oicq);
this.inChatCommandsController = new InChatCommandsController(this, this.tgBot, this.oicq);
@@ -170,6 +175,7 @@ export default class Instance {
public static async start(instanceId: number, botToken?: string) {
const instance = new this(instanceId);
+ Instance.instances.push(instance);
await instance.login(botToken);
return instance;
}
diff --git a/src/services/ForwardService.ts b/src/services/ForwardService.ts
index 2ef7677..97d7460 100644
--- a/src/services/ForwardService.ts
+++ b/src/services/ForwardService.ts
@@ -42,6 +42,8 @@ import { escapeXml } from 'icqq/lib/common';
import Docker from 'dockerode';
import ReplyKeyboardHide = Api.ReplyKeyboardHide;
import env from '../models/env';
+import { CustomFile } from 'telegram/client/uploads';
+import flags from '../constants/flags';
const NOT_CHAINABLE_ELEMENTS = ['flash', 'record', 'video', 'location', 'share', 'json', 'xml', 'poke'];
@@ -93,7 +95,11 @@ export default class ForwardService {
public async forwardFromQq(event: PrivateMessageEvent | GroupMessageEvent, pair: Pair) {
try {
const tempFiles: FileResult[] = [];
- let message = '', files: FileLike[] = [], buttons: ButtonLike[] = [], replyTo = 0;
+ let message = '',
+ files: FileLike[] = [],
+ buttons: ButtonLike[] = [],
+ replyTo = 0,
+ forceDocument = false;
let messageHeader = '', sender = '';
if (event.message_type === 'group') {
// 产生头部,这和工作模式没有关系
@@ -101,7 +107,10 @@ export default class ForwardService {
if (event.anonymous) {
sender = `[${sender}]${event.anonymous.name}`;
}
- messageHeader = `${helper.htmlEscape(sender)}: `;
+ if ((pair.flags | this.instance.flags) & flags.COLOR_EMOJI_PREFIX) {
+ messageHeader += emoji.color(event.sender.user_id);
+ }
+ messageHeader += `${helper.htmlEscape(sender)}: `;
}
const useSticker = (file: FileLike) => {
files.push(file);
@@ -111,7 +120,7 @@ export default class ForwardService {
}
};
const useForward = async (resId: string) => {
- if(env.CRV_API) {
+ if (env.CRV_API) {
try {
const messages = await pair.qq.getForwardMsg(resId);
message = helper.generateForwardBrief(messages);
@@ -186,7 +195,12 @@ export default class ForwardService {
useSticker(await convert.webp(elem.file as string, () => fetchFile(elem.url)));
}
else {
- files.push(await helper.downloadToCustomFile(url, !(message || messageHeader)));
+ const file = await helper.downloadToCustomFile(url, !(message || messageHeader));
+ files.push(file);
+ if (file instanceof CustomFile && elem.type === 'image' && file.size > 10 * 1024 * 1024) {
+ this.log.info('强制使用文件发送');
+ forceDocument = true;
+ }
buttons.push(Button.url(`${emoji.picture()} 查看原图`, url));
}
}
@@ -216,7 +230,12 @@ export default class ForwardService {
}
this.log.info('正在发送媒体,长度', helper.hSize(elem.size));
try {
- files.push(await helper.downloadToCustomFile(url, !(message || messageHeader), elem.name));
+ const file = await helper.downloadToCustomFile(url, !(message || messageHeader), elem.name);
+ if (file instanceof CustomFile && file.size > 10 * 1024 * 1024) {
+ this.log.info('强制使用文件发送');
+ forceDocument = true;
+ }
+ files.push(file);
}
catch (e) {
this.log.error('下载媒体失败', e);
@@ -353,7 +372,9 @@ export default class ForwardService {
}
// 发送消息
- const messageToSend: SendMessageParams = {};
+ const messageToSend: SendMessageParams = {
+ forceDocument: forceDocument as any, // 恼
+ };
message && (messageToSend.message = message);
if (files.length === 1) {
messageToSend.file = files[0];
@@ -398,6 +419,9 @@ export default class ForwardService {
helper.getUserDisplayName(await message.forward.getChat() || await message.forward.getSender())) :
'') +
': \n';
+ if ((pair.flags | this.instance.flags) & flags.COLOR_EMOJI_PREFIX) {
+ messageHeader = emoji.color(message.senderId.toJSNumber()) + messageHeader;
+ }
if (message.photo instanceof Api.Photo ||
// stickers 和以文件发送的图片都是这个
message.document?.mimeType?.startsWith('image/')) {