chore: fork @llblab/[email protected].0 as @evandrodevbr/oh-my-pi-telegram
Fork of upstream llblab/[email protected].0 (SHA afe09c5) repackaged as a drop-in extension for can1357/oh-my-pi (omp >= 16.5.2). Changes vs upstream: - Rename package: @llblab/pi-telegram -> @evandrodevbr/oh-my-pi-telegram - Bump version: 0.22.0 -> 0.22.0-evandro.1 - Update repository/homepage/bugs URLs to evandrodevbr - Add omp field in package.json (displayName + compatibleWith) - README: prepend 'Oh My Pi (omp) Compatibility' section with install commands, verified-components table, upstream lineage, and rationale - CHANGELOG: add '0.22.0-evandro.1' entry under Unreleased - docs/: rename @llblab/pi-telegram imports to @evandrodevbr/oh-my-pi-telegram - tests/public-api.test.ts: rename package import paths Gates re-run on the fork: - npm run typecheck -> tsc --noEmit (success) - npm test -> 1229 pass / 0 fail / 1 platform-only skip - npm run audit -> found 0 vulnerabilities - npm run pack:check -> evandrodevbr-oh-my-pi-telegram-0.22.0-evandro.1.tgz Verified: - omp install --link ./pi-telegram -> Linked OK - omp plugin list -> @evandrodevbr/[email protected].0-evandro.1 Upstream lineage: badlogic/pi-telegram -> llblab/pi-telegram (0.22.0) -> evandrodevbr/oh-my-pi-telegram (this fork)
This commit is contained in:
commit
d1d04001d0
155 files changed
+107848
No files matched your search
+327
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* Telegram bus-aware API runtime
|
||||
* Zones: multi-instance bus, telegram api transport, live instance routing
|
||||
* Wraps the direct Telegram Bot API runtime so follower instances can route outbound calls through the bus leader
|
||||
*/
|
||||
|
||||
import { stripTelegramBusApiMetadata } from "./bus.ts";
|
||||
import type {
|
||||
TelegramAnswerGuestQueryOptions,
|
||||
TelegramApiCallOptions,
|
||||
TelegramBridgeApiRuntime,
|
||||
TelegramEditMessageTextBody,
|
||||
TelegramSendMessageBody,
|
||||
TelegramSendMessageDraftBody,
|
||||
TelegramSendRichMessageBody,
|
||||
TelegramSendRichMessageDraftBody,
|
||||
TelegramSentMessage,
|
||||
TelegramUpdate,
|
||||
} from "./telegram-api.ts";
|
||||
|
||||
export type TelegramBusApiCall = (
|
||||
method: string,
|
||||
args: unknown[],
|
||||
) => Promise<unknown>;
|
||||
|
||||
export interface TelegramBusAwareApiRuntimeDeps {
|
||||
directRuntime: TelegramBridgeApiRuntime;
|
||||
ownsDirect: () => boolean;
|
||||
callFollowerApi: TelegramBusApiCall;
|
||||
getDefaultTarget?: () => { chatId: number; threadId?: number } | undefined;
|
||||
}
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
|
||||
function asBoolean(value: unknown): boolean {
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
function asSentMessage(value: unknown): TelegramSentMessage {
|
||||
return asRecord(value) as unknown as TelegramSentMessage;
|
||||
}
|
||||
|
||||
function withDefaultThreadTarget(
|
||||
body: Record<string, unknown>,
|
||||
target: { chatId: number; threadId?: number } | undefined,
|
||||
): Record<string, unknown> {
|
||||
if (target?.threadId === undefined) return body;
|
||||
if (body.message_thread_id !== undefined) return body;
|
||||
return body.chat_id === target.chatId
|
||||
? { ...body, message_thread_id: target.threadId }
|
||||
: body;
|
||||
}
|
||||
|
||||
function rejectTelegramDirectOwnership(method: string): Promise<never> {
|
||||
return Promise.reject(
|
||||
new Error(`Telegram ${method} requires direct transport ownership.`),
|
||||
);
|
||||
}
|
||||
|
||||
export function createTelegramAggregateTypingActionSender(
|
||||
runtime: Pick<TelegramBridgeApiRuntime, "call">,
|
||||
): (chatId: number) => Promise<unknown> {
|
||||
return (chatId) =>
|
||||
runtime.call("sendChatAction", { chat_id: chatId, action: "typing" });
|
||||
}
|
||||
|
||||
export function createTelegramBusAwareApiRuntime(
|
||||
deps: TelegramBusAwareApiRuntimeDeps,
|
||||
): TelegramBridgeApiRuntime {
|
||||
return {
|
||||
call<TResponse>(
|
||||
method: string,
|
||||
body: Record<string, unknown>,
|
||||
options?: TelegramApiCallOptions,
|
||||
): Promise<TResponse> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.call<TResponse>(method, body, options)
|
||||
: (deps.callFollowerApi("call", [
|
||||
method,
|
||||
body,
|
||||
options,
|
||||
]) as Promise<TResponse>);
|
||||
},
|
||||
callMultipart<TResponse>(
|
||||
method: string,
|
||||
fields: Record<string, string>,
|
||||
fileField: string,
|
||||
filePath: string,
|
||||
fileName: string,
|
||||
options?: TelegramApiCallOptions,
|
||||
): Promise<TResponse> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.callMultipart<TResponse>(
|
||||
method,
|
||||
fields,
|
||||
fileField,
|
||||
filePath,
|
||||
fileName,
|
||||
options,
|
||||
)
|
||||
: (deps.callFollowerApi("callMultipart", [
|
||||
method,
|
||||
fields,
|
||||
fileField,
|
||||
filePath,
|
||||
fileName,
|
||||
options,
|
||||
]) as Promise<TResponse>);
|
||||
},
|
||||
downloadFile(fileId: string, suggestedName: string): Promise<string> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.downloadFile(fileId, suggestedName)
|
||||
: (deps.callFollowerApi("downloadFile", [
|
||||
fileId,
|
||||
suggestedName,
|
||||
]) as Promise<string>);
|
||||
},
|
||||
deleteWebhook(signal?: AbortSignal): Promise<boolean> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.deleteWebhook(signal)
|
||||
: rejectTelegramDirectOwnership("deleteWebhook");
|
||||
},
|
||||
getUpdates(
|
||||
body: Record<string, unknown>,
|
||||
signal?: AbortSignal,
|
||||
): Promise<TelegramUpdate[]> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.getUpdates(body, signal)
|
||||
: rejectTelegramDirectOwnership("getUpdates");
|
||||
},
|
||||
setMyCommands(commands): Promise<boolean> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.setMyCommands(commands)
|
||||
: deps
|
||||
.callFollowerApi("call", ["setMyCommands", { commands }])
|
||||
.then(asBoolean);
|
||||
},
|
||||
sendChatAction(
|
||||
chatId: number,
|
||||
action: string,
|
||||
options?: { message_thread_id?: number },
|
||||
): Promise<boolean> {
|
||||
const body = withDefaultThreadTarget(
|
||||
{
|
||||
chat_id: chatId,
|
||||
action,
|
||||
...(options?.message_thread_id !== undefined
|
||||
? { message_thread_id: options.message_thread_id }
|
||||
: {}),
|
||||
},
|
||||
deps.getDefaultTarget?.(),
|
||||
);
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendChatAction(chatId, action, options)
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendChatAction", body])
|
||||
.then(asBoolean);
|
||||
},
|
||||
sendTypingAction(
|
||||
chatId: number,
|
||||
options?: { message_thread_id?: number },
|
||||
): Promise<unknown> {
|
||||
const body = withDefaultThreadTarget(
|
||||
{
|
||||
chat_id: chatId,
|
||||
action: "typing",
|
||||
...(options?.message_thread_id !== undefined
|
||||
? { message_thread_id: options.message_thread_id }
|
||||
: {}),
|
||||
},
|
||||
deps.getDefaultTarget?.(),
|
||||
);
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendTypingAction(chatId, options)
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendChatAction", body])
|
||||
.then(asBoolean);
|
||||
},
|
||||
sendRecordVoiceAction(
|
||||
chatId: number,
|
||||
options?: { message_thread_id?: number },
|
||||
): Promise<unknown> {
|
||||
const body = withDefaultThreadTarget(
|
||||
{
|
||||
chat_id: chatId,
|
||||
action: "record_voice",
|
||||
...(options?.message_thread_id !== undefined
|
||||
? { message_thread_id: options.message_thread_id }
|
||||
: {}),
|
||||
},
|
||||
deps.getDefaultTarget?.(),
|
||||
);
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendRecordVoiceAction(chatId, options)
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendChatAction", body])
|
||||
.then(asBoolean);
|
||||
},
|
||||
sendMessageDraft(
|
||||
chatId: number,
|
||||
draftId: number,
|
||||
text?: string,
|
||||
options?: TelegramSendMessageDraftBody extends Record<string, unknown>
|
||||
? {
|
||||
parse_mode?: string;
|
||||
entities?: unknown[];
|
||||
message_thread_id?: number;
|
||||
}
|
||||
: never,
|
||||
): Promise<boolean> {
|
||||
const body: Record<string, unknown> = {
|
||||
chat_id: chatId,
|
||||
draft_id: draftId,
|
||||
};
|
||||
if (text !== undefined) body.text = text;
|
||||
if (options?.parse_mode !== undefined)
|
||||
body.parse_mode = options.parse_mode;
|
||||
if (options?.entities !== undefined) body.entities = options.entities;
|
||||
if (options?.message_thread_id !== undefined) {
|
||||
body.message_thread_id = options.message_thread_id;
|
||||
}
|
||||
const scopedBody = withDefaultThreadTarget(
|
||||
body,
|
||||
deps.getDefaultTarget?.(),
|
||||
);
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendMessageDraft(chatId, draftId, text, options)
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendMessageDraft", scopedBody])
|
||||
.then(asBoolean);
|
||||
},
|
||||
sendMessage(body: TelegramSendMessageBody): Promise<TelegramSentMessage> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendMessage(stripTelegramBusApiMetadata(body))
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendMessage", body])
|
||||
.then(asSentMessage);
|
||||
},
|
||||
sendRichMessage(
|
||||
body: TelegramSendRichMessageBody,
|
||||
): Promise<TelegramSentMessage> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendRichMessage(body)
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendRichMessage", body])
|
||||
.then(asSentMessage);
|
||||
},
|
||||
sendRichMessageDraft(
|
||||
body: TelegramSendRichMessageDraftBody,
|
||||
): Promise<boolean> {
|
||||
return deps.ownsDirect()
|
||||
? deps.directRuntime.sendRichMessageDraft(body)
|
||||
: deps
|
||||
.callFollowerApi("call", ["sendRichMessageDraft", body])
|
||||
.then(asBoolean);
|
||||
},
|
||||
async editMessageText(
|
||||
body: TelegramEditMessageTextBody,
|
||||
): Promise<"edited" | "unchanged"> {
|
||||
if (deps.ownsDirect()) return deps.directRuntime.editMessageText(body);
|
||||
await deps.callFollowerApi("call", ["editMessageText", body]);
|
||||
return "edited";
|
||||
},
|
||||
async answerCallbackQuery(
|
||||
callbackQueryId: string,
|
||||
text?: string,
|
||||
): Promise<void> {
|
||||
if (deps.ownsDirect()) {
|
||||
await deps.directRuntime.answerCallbackQuery(callbackQueryId, text);
|
||||
return;
|
||||
}
|
||||
await deps.callFollowerApi("call", [
|
||||
"answerCallbackQuery",
|
||||
{
|
||||
callback_query_id: callbackQueryId,
|
||||
...(text !== undefined ? { text } : {}),
|
||||
},
|
||||
]);
|
||||
},
|
||||
async answerGuestQuery(
|
||||
guestQueryId: string,
|
||||
text?: string,
|
||||
options?: TelegramAnswerGuestQueryOptions,
|
||||
): Promise<void> {
|
||||
if (deps.ownsDirect()) {
|
||||
await deps.directRuntime.answerGuestQuery(guestQueryId, text, options);
|
||||
return;
|
||||
}
|
||||
const body: Record<string, unknown> = { guest_query_id: guestQueryId };
|
||||
if (options?.result) {
|
||||
body.result = options.result;
|
||||
} else if (text !== undefined || options?.richMessage) {
|
||||
const inputContent: Record<string, unknown> = options?.richMessage
|
||||
? { rich_message: options.richMessage }
|
||||
: { message_text: text };
|
||||
if (!options?.richMessage && options?.parseMode) {
|
||||
inputContent.parse_mode = options.parseMode;
|
||||
}
|
||||
body.result = {
|
||||
type: "article",
|
||||
id: "1",
|
||||
title: "Response",
|
||||
input_message_content: inputContent,
|
||||
};
|
||||
}
|
||||
await deps.callFollowerApi("call", ["answerGuestQuery", body]);
|
||||
},
|
||||
async deleteMessage(chatId: number, messageId: number): Promise<void> {
|
||||
if (deps.ownsDirect())
|
||||
return deps.directRuntime.deleteMessage(chatId, messageId);
|
||||
await deps.callFollowerApi("call", [
|
||||
"deleteMessage",
|
||||
{
|
||||
chat_id: chatId,
|
||||
message_id: messageId,
|
||||
},
|
||||
]);
|
||||
},
|
||||
prepareTempDir(): Promise<number> {
|
||||
return deps.directRuntime.prepareTempDir();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user