}
*/
async function commandSystem(message, command, subcommand, context) {
- let chatAgent = loadChatLLM(context)?.name;
- let imageAgent = loadImageGen(context)?.name;
+ const chatAgent = loadChatLLM(context)?.name;
+ const imageAgent = loadImageGen(context)?.name;
const agent = {
AI_PROVIDER: chatAgent,
AI_IMAGE_PROVIDER: imageAgent,
@@ -389,7 +394,7 @@ async function commandSystem(message, command, subcommand, context) {
}
let msg = `AGENT: ${JSON.stringify(agent, null, 2)}\n`;
if (ENV.DEV_MODE) {
- const shareCtx = {...context.SHARE_CONTEXT};
+ const shareCtx = { ...context.SHARE_CONTEXT };
shareCtx.currentBotToken = '******';
context.USER_CONFIG.OPENAI_API_KEY = ['******'];
context.USER_CONFIG.AZURE_API_KEY = '******';
@@ -402,7 +407,7 @@ async function commandSystem(message, command, subcommand, context) {
context.USER_CONFIG.COHERE_API_KEY = '******';
context.USER_CONFIG.ANTHROPIC_API_KEY = '******';
const config = trimUserConfig(context.USER_CONFIG);
- msg = '\n' + msg;
+ msg = `\n${msg}`;
msg += `USER_CONFIG: ${JSON.stringify(config, null, 2)}\n`;
msg += `CHAT_CONTEXT: ${JSON.stringify(context.CURRENT_CHAT_CONTEXT, null, 2)}\n`;
msg += `SHARE_CONTEXT: ${JSON.stringify(shareCtx, null, 2)}\n`;
@@ -441,9 +446,9 @@ async function commandRegenerate(message, command, subcommand, context) {
if (subcommand) {
nextText = subcommand;
}
- return {history: historyCopy, message: nextText};
+ return { history: historyCopy, message: nextText };
};
- return chatWithLLM({message: null}, context, mf);
+ return chatWithLLM({ message: null }, context, mf);
}
/**
@@ -456,7 +461,7 @@ async function commandRegenerate(message, command, subcommand, context) {
*/
async function commandEcho(message, command, subcommand, context) {
let msg = '';
- msg += JSON.stringify({message}, null, 2);
+ msg += JSON.stringify({ message }, null, 2);
msg += '
';
context.CURRENT_CHAT_CONTEXT.parse_mode = 'HTML';
return sendMessageToTelegramWithContext(context)(msg);
@@ -482,7 +487,7 @@ export async function handleCommandMessage(message, context) {
message.text = CUSTOM_COMMAND[message.text];
}
for (const key in commandHandlers) {
- if (message.text === key || message.text.startsWith(key + ' ')) {
+ if (message.text === key || message.text.startsWith(`${key} `)) {
const command = commandHandlers[key];
try {
// 如果存在权限条件
@@ -490,7 +495,7 @@ export async function handleCommandMessage(message, context) {
const roleList = command.needAuth(context.SHARE_CONTEXT.chatType);
if (roleList) {
// 获取身份并判断
- const chatRole = await getChatRoleWithContext(context)(context.SHARE_CONTEXT.speakerId);
+ const chatRole = await getChatRoleWithContext(context);
if (chatRole === null) {
return sendMessageToTelegramWithContext(context)('ERROR: Get chat role failed');
}
@@ -548,7 +553,7 @@ export async function bindCommandForTelegram(token) {
'Content-Type': 'application/json',
},
body: JSON.stringify({
- commands: scopeCommandMap[scope].map((command) => ({
+ commands: scopeCommandMap[scope].map(command => ({
command,
description: ENV.I18N.command.help[command.substring(1)] || '',
})),
@@ -557,9 +562,9 @@ export async function bindCommandForTelegram(token) {
},
}),
},
- ).then((res) => res.json());
+ ).then(res => res.json());
}
- return {ok: true, result};
+ return { ok: true, result };
}
/**
diff --git a/src/telegram/message.js b/src/telegram/message.js
index 95097d77..ebcca80a 100644
--- a/src/telegram/message.js
+++ b/src/telegram/message.js
@@ -1,13 +1,13 @@
-import {CONST, DATABASE, ENV} from '../config/env.js';
-import {Context} from '../config/context.js';
-import {getBot, getFileLink, sendMessageToTelegramWithContext} from './telegram.js';
-import {handleCommandMessage} from './command.js';
-import {errorToString} from '../utils/utils.js';
-import {chatWithLLM} from '../agent/llm.js';
+import { CONST, DATABASE, ENV } from '../config/env.js';
+import { Context } from '../config/context.js';
+import { uploadImageToTelegraph } from '../utils/image.js';
+import { errorToString } from '../utils/utils.js';
+import { getBotName, getFileLink, sendMessageToTelegramWithContext } from './telegram.js';
+import { handleCommandMessage } from './command.js';
import '../types/telegram.js';
-import {uploadImageToTelegraph} from '../utils/image.js';
-
+import { checkMention, findPhotoFileID } from './utils.js';
+import { chatWithLLM } from './agent.js';
/**
* 初始化聊天上下文
@@ -20,7 +20,6 @@ async function msgInitChatContext(message, context) {
return null;
}
-
/**
* 保存最后一条消息
* @param {TelegramMessage} message
@@ -30,7 +29,7 @@ async function msgInitChatContext(message, context) {
async function msgSaveLastMessage(message, context) {
if (ENV.DEBUG_MODE) {
const lastMessageKey = `last_message:${context.SHARE_CONTEXT.chatHistoryKey}`;
- await DATABASE.put(lastMessageKey, JSON.stringify(message), {expirationTtl: 3600});
+ await DATABASE.put(lastMessageKey, JSON.stringify(message), { expirationTtl: 3600 });
}
return null;
}
@@ -116,14 +115,13 @@ async function msgFilterWhiteList(message, context) {
);
}
-
/**
* 过滤不支持的消息
* @param {TelegramMessage} message
* @param {ContextType} context
* @returns {Promise}
*/
-// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line unused-imports/no-unused-vars
async function msgFilterUnsupportedMessage(message, context) {
if (message.text) {
return null;// 纯文本消息
@@ -149,8 +147,7 @@ async function msgHandleGroupMessage(message, context) {
return null;
}
- // 处理群组消息,过滤掉AT部分
- let botName = context.SHARE_CONTEXT.currentBotName;
+ // 处理回复消息, 如果回复的是当前机器人的消息交给下一个中间件处理
if (message.reply_to_message) {
if (`${message.reply_to_message.from.id}` === context.SHARE_CONTEXT.currentBotId) {
return null;
@@ -158,73 +155,35 @@ async function msgHandleGroupMessage(message, context) {
context.SHARE_CONTEXT.extraMessageContext = message.reply_to_message;
}
}
+
+ // 处理群组消息,过滤掉AT部分
+ let botName = context.SHARE_CONTEXT.currentBotName;
if (!botName) {
- const res = await getBot(context.SHARE_CONTEXT.currentBotToken);
- context.SHARE_CONTEXT.currentBotName = res.info.bot_name;
- botName = res.info.bot_name;
+ botName = await getBotName(context.SHARE_CONTEXT.currentBotToken);
+ context.SHARE_CONTEXT.currentBotName = botName;
}
if (!botName) {
throw new Error('Not set bot name');
}
- if (!message.entities) {
- throw new Error('No entities');
+ let isMention = false;
+ // 检查text中是否有机器人的提及
+ if (message.text && message.entities) {
+ const res = checkMention(message.text, message.entities, botName, context.SHARE_CONTEXT.currentBotId);
+ isMention = res.isMention;
+ message.text = res.content.trim();
}
-
- const {text, caption} = message;
- let originContent = text || caption || '';
- if (!originContent) {
- throw new Error('Empty message');
- }
-
- let content = '';
- let offset = 0;
- let mentioned = false;
-
- for (const entity of message.entities) {
- switch (entity.type) {
- case 'bot_command':
- if (!mentioned) {
- const mention = originContent.substring(
- entity.offset,
- entity.offset + entity.length,
- );
- if (mention.endsWith(botName)) {
- mentioned = true;
- }
- const cmd = mention
- .replaceAll('@' + botName, '')
- .replaceAll(botName, '')
- .trim();
- content += cmd;
- offset = entity.offset + entity.length;
- }
- break;
- case 'mention':
- case 'text_mention':
- if (!mentioned) {
- const mention = originContent.substring(
- entity.offset,
- entity.offset + entity.length,
- );
- if (mention === botName || mention === '@' + botName) {
- mentioned = true;
- }
- }
- content += originContent.substring(offset, entity.offset);
- offset = entity.offset + entity.length;
- break;
- }
+ // 检查caption中是否有机器人的提及
+ if (message.caption && message.caption_entities) {
+ const res = checkMention(message.caption, message.caption_entities, botName, context.SHARE_CONTEXT.currentBotId);
+ isMention = res.isMention || isMention;
+ message.caption = res.content.trim();
}
- content += originContent.substring(offset, originContent.length);
- message.text = content.trim();
- // 未AT机器人的消息不作处理
- if (!mentioned) {
- throw new Error('No mentioned');
+ if (!isMention) {
+ throw new Error('Not mention');
}
return null;
}
-
/**
* 响应命令消息
* @param {TelegramMessage} message
@@ -246,25 +205,22 @@ async function msgHandleCommand(message, context) {
* @returns {Promise}
*/
async function msgChatWithLLM(message, context) {
- const {text, caption} = message;
- let content = text || caption;
- if (ENV.EXTRA_MESSAGE_CONTEXT && context.SHARE_CONTEXT.extraMessageContext && context.SHARE_CONTEXT.extraMessageContext.text) {
- content = context.SHARE_CONTEXT.extraMessageContext.text + '\n' + text;
- }
/**
* @type {LlmRequestParams}
*/
- const params = {message: content};
- if (message.photo && message.photo.length > 0) {
- let sizeIndex = 0;
- if (ENV.TELEGRAM_PHOTO_SIZE_OFFSET >= 0) {
- sizeIndex = ENV.TELEGRAM_PHOTO_SIZE_OFFSET;
- } else if (ENV.TELEGRAM_PHOTO_SIZE_OFFSET < 0) {
- sizeIndex = message.photo.length + ENV.TELEGRAM_PHOTO_SIZE_OFFSET;
+ const params = {
+ message: message.text || message.caption || '',
+ };
+ if (ENV.EXTRA_MESSAGE_CONTEXT && context.SHARE_CONTEXT.extraMessageContext) {
+ const extra = context.SHARE_CONTEXT.extraMessageContext.text || context.SHARE_CONTEXT.extraMessageContext.caption || '';
+ if (extra) {
+ params.message = `${extra}\n${params.message}`;
}
- sizeIndex = Math.max(0, Math.min(sizeIndex, message.photo.length - 1));
- const fileId = message.photo[sizeIndex].file_id;
- let url = await getFileLink(fileId, context.SHARE_CONTEXT.currentBotToken);
+ }
+
+ if (message.photo && message.photo.length > 0) {
+ const id = findPhotoFileID(message.photo, ENV.TELEGRAM_PHOTO_SIZE_OFFSET);
+ let url = await getFileLink(id, context.SHARE_CONTEXT.currentBotToken);
if (ENV.TELEGRAPH_ENABLE) {
url = await uploadImageToTelegraph(url);
}
@@ -273,7 +229,6 @@ async function msgChatWithLLM(message, context) {
return chatWithLLM(params, context, null);
}
-
/**
* 加载真实TG消息
* @param {TelegramWebhookRequest} body
@@ -336,7 +291,7 @@ export async function handleMessage(token, body) {
}
} catch (e) {
console.error(e);
- return new Response(errorToString(e), {status: 500});
+ return new Response(errorToString(e), { status: 500 });
}
}
return null;
diff --git a/src/telegram/telegram.js b/src/telegram/telegram.js
index 24c48e7a..a6a868f8 100644
--- a/src/telegram/telegram.js
+++ b/src/telegram/telegram.js
@@ -1,8 +1,35 @@
-import {DATABASE, ENV} from '../config/env.js';
-import {escape} from '../utils/md2tgmd.js';
+import { ENV } from '../config/env.js';
+import { escape } from '../utils/md2tgmd.js';
import '../types/context.js';
import '../types/telegram.js';
+// Telegram函数
+// 1. 需要判断请求状态的返回Promise
+// 2. 无需判断请求结果的返回Promise
+// 3. 有具体数据处理需求的返回具体数据类型的Promise
+// 4. 默认返回Promise
+
+/**
+ * @param {string} method
+ * @param {string} token
+ * @param {object} body
+ * @returns {Promise}
+ */
+async function sendTelegramRequest(method, token, body = null) {
+ const headers = {};
+ if (!(body instanceof FormData)) {
+ headers['Content-Type'] = 'application/json';
+ }
+ return fetch(
+ `${ENV.TELEGRAM_API_DOMAIN}/bot${token}/${method}`,
+ {
+ method: 'POST',
+ headers,
+ body: body && ((body instanceof FormData) ? body : JSON.stringify(body)),
+ },
+ );
+}
+
/**
* @param {string} message
* @param {string} token
@@ -22,19 +49,9 @@ async function sendMessage(message, token, context) {
if (context?.message_id) {
method = 'editMessageText';
}
- return await fetch(
- `${ENV.TELEGRAM_API_DOMAIN}/bot${token}/${method}`,
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- },
- );
+ return sendTelegramRequest(method, token, body);
}
-
/**
* @param {string} message
* @param {string} token
@@ -71,43 +88,13 @@ export async function sendMessageToTelegram(message, token, context) {
chatContext.message_id = null;
}
lastMessageResponse = await sendMessage(msg, token, chatContext);
+ if (lastMessageResponse.status !== 200) {
+ break;
+ }
}
return lastMessageResponse;
}
-/**
- * @param {ContextType} context
- * @returns {function(string): Promise}
- */
-export function sendMessageToTelegramWithContext(context) {
- return async (message) => {
- return sendMessageToTelegram(message, context.SHARE_CONTEXT.currentBotToken, context.CURRENT_CHAT_CONTEXT);
- };
-}
-
-/**
- * @param {ContextType} context
- * @returns {function(string): Promise}
- */
-export function deleteMessageFromTelegramWithContext(context) {
- return async (messageId) => {
- return await fetch(
- `${ENV.TELEGRAM_API_DOMAIN}/bot${context.SHARE_CONTEXT.currentBotToken}/deleteMessage`,
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- chat_id: context.CURRENT_CHAT_CONTEXT.chat_id,
- message_id: messageId,
- }),
- },
- );
- };
-}
-
-
/**
* 发送图片消息到Telegram
* @param {string | Blob} photo
@@ -116,11 +103,8 @@ export function deleteMessageFromTelegramWithContext(context) {
* @returns {Promise}
*/
export async function sendPhotoToTelegram(photo, token, context) {
- const url = `${ENV.TELEGRAM_API_DOMAIN}/bot${token}/sendPhoto`;
- let body;
- const headers = {};
if (typeof photo === 'string') {
- body = {
+ const body = {
photo,
};
for (const key of Object.keys(context)) {
@@ -128,262 +112,128 @@ export async function sendPhotoToTelegram(photo, token, context) {
body[key] = context[key];
}
}
- body = JSON.stringify(body);
- headers['Content-Type'] = 'application/json';
+ return sendTelegramRequest('sendPhoto', token, body);
} else {
- body = new FormData();
+ const body = new FormData();
body.append('photo', photo, 'photo.png');
for (const key of Object.keys(context)) {
if (context[key] !== undefined && context[key] !== null) {
body.append(key, `${context[key]}`);
}
}
+ return sendTelegramRequest('sendPhoto', token, body);
}
- return await fetch(url, {
- method: 'POST',
- headers,
- body,
- },
- );
-}
-
-
-/**
- * @param {ContextType} context
- * @returns {function(string): Promise