mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-18 20:41:36 +00:00
165 lines
6.1 KiB
TypeScript
165 lines
6.1 KiB
TypeScript
// Telegram plugin module implements outbound message context behavior.
|
|
import type { Message } from "grammy/types";
|
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
|
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
import { buildTelegramSelfSenderName } from "./group-history-window.js";
|
|
import { createTelegramMessageCache, resolveTelegramMessageCacheScope } from "./message-cache.js";
|
|
import type { TelegramPromptContextProjection } from "./prompt-context-projection.js";
|
|
|
|
type TelegramOutboundPromptContextUser = {
|
|
id?: number;
|
|
is_bot?: boolean;
|
|
first_name?: string;
|
|
last_name?: string;
|
|
username?: string;
|
|
};
|
|
|
|
export type TelegramOutboundPromptContextMessage = {
|
|
message_id?: number;
|
|
chat?: { id?: string | number; type?: string; title?: string; username?: string };
|
|
date?: number;
|
|
from?: TelegramOutboundPromptContextUser;
|
|
sender_chat?: { id?: number; title?: string; username?: string };
|
|
sender_business_bot?: TelegramOutboundPromptContextUser;
|
|
openclaw_prompt_context_timestamp_ms?: number;
|
|
text?: string;
|
|
caption?: string;
|
|
message_thread_id?: number;
|
|
};
|
|
|
|
type TelegramOutboundPromptContextAccount = {
|
|
accountId: string;
|
|
name?: string;
|
|
bot?: { first_name?: string; username?: string };
|
|
};
|
|
|
|
type TelegramOutboundGroupHistoryRecord = {
|
|
chatId: string | number;
|
|
messageId: number;
|
|
text?: string;
|
|
messageThreadId?: number;
|
|
timestamp?: number;
|
|
};
|
|
|
|
type TelegramOutboundGroupHistoryRecorder = (record: TelegramOutboundGroupHistoryRecord) => void;
|
|
|
|
const outboundGroupHistoryRecorders = new Map<string, TelegramOutboundGroupHistoryRecorder>();
|
|
|
|
export function registerTelegramOutboundGroupHistoryRecorder(params: {
|
|
accountId: string;
|
|
recorder: TelegramOutboundGroupHistoryRecorder;
|
|
}): () => void {
|
|
outboundGroupHistoryRecorders.set(params.accountId, params.recorder);
|
|
return () => {
|
|
if (outboundGroupHistoryRecorders.get(params.accountId) === params.recorder) {
|
|
outboundGroupHistoryRecorders.delete(params.accountId);
|
|
}
|
|
};
|
|
}
|
|
|
|
function resolveOutboundCacheMessageTimestamp(
|
|
msg: TelegramOutboundPromptContextMessage,
|
|
): number | undefined {
|
|
if (
|
|
typeof msg.openclaw_prompt_context_timestamp_ms === "number" &&
|
|
Number.isFinite(msg.openclaw_prompt_context_timestamp_ms)
|
|
) {
|
|
return msg.openclaw_prompt_context_timestamp_ms;
|
|
}
|
|
return typeof msg.date === "number" && Number.isFinite(msg.date) ? msg.date * 1000 : undefined;
|
|
}
|
|
|
|
function inferTelegramChatType(chatId: string | number): "private" | "supergroup" {
|
|
return String(chatId).startsWith("-") ? "supergroup" : "private";
|
|
}
|
|
|
|
function buildOutboundCacheMessage(params: {
|
|
account: TelegramOutboundPromptContextAccount;
|
|
chatId: string | number;
|
|
message: TelegramOutboundPromptContextMessage;
|
|
messageId: number;
|
|
botUserId?: number;
|
|
text?: string;
|
|
messageThreadId?: number;
|
|
promptContextTimestampMs?: number;
|
|
}): TelegramOutboundPromptContextMessage {
|
|
const chat = params.message.chat ?? {};
|
|
const text = params.message.text ?? params.message.caption ?? params.text;
|
|
const rawSender = params.message.from;
|
|
const stableSender = params.message.sender_chat ? undefined : rawSender;
|
|
const selfSenderName = buildTelegramSelfSenderName(
|
|
params.account.name,
|
|
params.account.bot ?? stableSender,
|
|
);
|
|
return {
|
|
...params.message,
|
|
message_id: params.messageId,
|
|
...(params.promptContextTimestampMs !== undefined
|
|
? { openclaw_prompt_context_timestamp_ms: params.promptContextTimestampMs }
|
|
: {}),
|
|
date:
|
|
typeof params.message.date === "number" && Number.isFinite(params.message.date)
|
|
? params.message.date
|
|
: Math.floor(Date.now() / 1000),
|
|
chat: {
|
|
id: chat.id ?? params.chatId,
|
|
type: chat.type ?? inferTelegramChatType(params.chatId),
|
|
...(chat.title ? { title: chat.title } : {}),
|
|
...(chat.username ? { username: chat.username } : {}),
|
|
},
|
|
// Every message entering here came from this bot. Keep only Telegram's real
|
|
// id/username; sender_chat uses a synthetic compatibility user.
|
|
from: {
|
|
id: params.message.sender_chat ? 0 : (stableSender?.id ?? params.botUserId ?? 0),
|
|
is_bot: true,
|
|
first_name: selfSenderName,
|
|
...(stableSender?.username ? { username: stableSender.username } : {}),
|
|
},
|
|
...(text ? { text } : {}),
|
|
...(params.messageThreadId !== undefined ? { message_thread_id: params.messageThreadId } : {}),
|
|
};
|
|
}
|
|
|
|
export async function recordOutboundMessageForPromptContext(params: {
|
|
cfg: OpenClawConfig;
|
|
account: TelegramOutboundPromptContextAccount;
|
|
chatId: string | number;
|
|
message: TelegramOutboundPromptContextMessage;
|
|
messageId: number;
|
|
botUserId?: number;
|
|
text?: string;
|
|
messageThreadId?: number;
|
|
promptContextTimestampMs?: number;
|
|
promptContextProjection?: TelegramPromptContextProjection;
|
|
}): Promise<boolean> {
|
|
try {
|
|
const cacheMessage = buildOutboundCacheMessage(params);
|
|
const cache = createTelegramMessageCache({
|
|
scope: resolveTelegramMessageCacheScope(resolveStorePath(params.cfg.session?.store)),
|
|
});
|
|
await cache.record({
|
|
accountId: params.account.accountId,
|
|
chatId: params.chatId,
|
|
msg: cacheMessage as Message,
|
|
...(params.botUserId !== undefined ? { botUserId: params.botUserId } : {}),
|
|
...(params.promptContextProjection
|
|
? { promptContextProjection: params.promptContextProjection }
|
|
: {}),
|
|
...(params.messageThreadId !== undefined ? { threadId: params.messageThreadId } : {}),
|
|
});
|
|
const timestamp = resolveOutboundCacheMessageTimestamp(cacheMessage);
|
|
outboundGroupHistoryRecorders.get(params.account.accountId)?.({
|
|
chatId: params.chatId,
|
|
messageId: params.messageId,
|
|
text: params.text ?? cacheMessage.text ?? cacheMessage.caption,
|
|
...(params.messageThreadId !== undefined ? { messageThreadId: params.messageThreadId } : {}),
|
|
...(timestamp !== undefined ? { timestamp } : {}),
|
|
});
|
|
return true;
|
|
} catch (error) {
|
|
logVerbose(`telegram: failed to record outbound message context: ${String(error)}`);
|
|
return false;
|
|
}
|
|
}
|