@intentface/latch-channels 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/ask.d.ts +69 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +94 -0
- package/dist/ask.js.map +1 -0
- package/dist/commands.d.ts +41 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +54 -0
- package/dist/commands.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +207 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +883 -0
- package/dist/kernel.js.map +1 -0
- package/dist/render.d.ts +43 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +192 -0
- package/dist/render.js.map +1 -0
- package/dist/slack/api.d.ts +207 -0
- package/dist/slack/api.d.ts.map +1 -0
- package/dist/slack/api.js +249 -0
- package/dist/slack/api.js.map +1 -0
- package/dist/slack/blocks.d.ts +36 -0
- package/dist/slack/blocks.d.ts.map +1 -0
- package/dist/slack/blocks.js +161 -0
- package/dist/slack/blocks.js.map +1 -0
- package/dist/slack/ids.d.ts +40 -0
- package/dist/slack/ids.d.ts.map +1 -0
- package/dist/slack/ids.js +47 -0
- package/dist/slack/ids.js.map +1 -0
- package/dist/slack/index.d.ts +24 -0
- package/dist/slack/index.d.ts.map +1 -0
- package/dist/slack/index.js +18 -0
- package/dist/slack/index.js.map +1 -0
- package/dist/slack/manifest.d.ts +23 -0
- package/dist/slack/manifest.d.ts.map +1 -0
- package/dist/slack/manifest.js +102 -0
- package/dist/slack/manifest.js.map +1 -0
- package/dist/slack/normalize.d.ts +35 -0
- package/dist/slack/normalize.d.ts.map +1 -0
- package/dist/slack/normalize.js +63 -0
- package/dist/slack/normalize.js.map +1 -0
- package/dist/slack/payloads.d.ts +36 -0
- package/dist/slack/payloads.d.ts.map +1 -0
- package/dist/slack/payloads.js +80 -0
- package/dist/slack/payloads.js.map +1 -0
- package/dist/slack/slackChannel.d.ts +152 -0
- package/dist/slack/slackChannel.d.ts.map +1 -0
- package/dist/slack/slackChannel.js +650 -0
- package/dist/slack/slackChannel.js.map +1 -0
- package/dist/slack/stream.d.ts +88 -0
- package/dist/slack/stream.d.ts.map +1 -0
- package/dist/slack/stream.js +418 -0
- package/dist/slack/stream.js.map +1 -0
- package/dist/slack/verify.d.ts +20 -0
- package/dist/slack/verify.d.ts.map +1 -0
- package/dist/slack/verify.js +32 -0
- package/dist/slack/verify.js.map +1 -0
- package/dist/telegram/api.d.ts +110 -0
- package/dist/telegram/api.d.ts.map +1 -0
- package/dist/telegram/api.js +170 -0
- package/dist/telegram/api.js.map +1 -0
- package/dist/telegram/format.d.ts +26 -0
- package/dist/telegram/format.d.ts.map +1 -0
- package/dist/telegram/format.js +56 -0
- package/dist/telegram/format.js.map +1 -0
- package/dist/telegram/html.d.ts +11 -0
- package/dist/telegram/html.d.ts.map +1 -0
- package/dist/telegram/html.js +35 -0
- package/dist/telegram/html.js.map +1 -0
- package/dist/telegram/index.d.ts +15 -0
- package/dist/telegram/index.d.ts.map +1 -0
- package/dist/telegram/index.js +15 -0
- package/dist/telegram/index.js.map +1 -0
- package/dist/telegram/stream.d.ts +59 -0
- package/dist/telegram/stream.d.ts.map +1 -0
- package/dist/telegram/stream.js +185 -0
- package/dist/telegram/stream.js.map +1 -0
- package/dist/telegram/telegramChannel.d.ts +136 -0
- package/dist/telegram/telegramChannel.d.ts.map +1 -0
- package/dist/telegram/telegramChannel.js +277 -0
- package/dist/telegram/telegramChannel.js.map +1 -0
- package/dist/telegram/verify.d.ts +7 -0
- package/dist/telegram/verify.d.ts.map +1 -0
- package/dist/telegram/verify.js +14 -0
- package/dist/telegram/verify.js.map +1 -0
- package/dist/types.d.ts +240 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/whatsapp/api.d.ts +59 -0
- package/dist/whatsapp/api.d.ts.map +1 -0
- package/dist/whatsapp/api.js +154 -0
- package/dist/whatsapp/api.js.map +1 -0
- package/dist/whatsapp/index.d.ts +13 -0
- package/dist/whatsapp/index.d.ts.map +1 -0
- package/dist/whatsapp/index.js +13 -0
- package/dist/whatsapp/index.js.map +1 -0
- package/dist/whatsapp/verify.d.ts +17 -0
- package/dist/whatsapp/verify.d.ts.map +1 -0
- package/dist/whatsapp/verify.js +32 -0
- package/dist/whatsapp/verify.js.map +1 -0
- package/dist/whatsapp/whatsappChannel.d.ts +104 -0
- package/dist/whatsapp/whatsappChannel.d.ts.map +1 -0
- package/dist/whatsapp/whatsappChannel.js +217 -0
- package/dist/whatsapp/whatsappChannel.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Telegram Bot API client — just the methods a channel (and its host's
|
|
3
|
+
* admin surface) needs. JSON POSTs to `api.telegram.org/bot<token>/<method>`,
|
|
4
|
+
* `{ok, result, description}` unwrap, 10s timeout per call. No SDK.
|
|
5
|
+
*/
|
|
6
|
+
export type TelegramToken = string | (() => string | Promise<string>);
|
|
7
|
+
/** A failed Bot API call. `retryAfter` (seconds) is set on 429s. */
|
|
8
|
+
export declare class TelegramApiError extends Error {
|
|
9
|
+
readonly status: number;
|
|
10
|
+
readonly retryAfter?: number;
|
|
11
|
+
constructor(method: string, status: number, description?: string, retryAfter?: number);
|
|
12
|
+
}
|
|
13
|
+
export interface TelegramApiOptions {
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
fetchImpl?: typeof fetch;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface TelegramBotInfo {
|
|
19
|
+
id: number;
|
|
20
|
+
username: string;
|
|
21
|
+
firstName: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TelegramWebhookInfo {
|
|
24
|
+
url: string;
|
|
25
|
+
pendingUpdateCount: number;
|
|
26
|
+
lastErrorMessage?: string;
|
|
27
|
+
lastErrorDate?: number;
|
|
28
|
+
}
|
|
29
|
+
/** An inline-keyboard button: either a callback button or a URL link button. */
|
|
30
|
+
export type InlineKeyboardButton = {
|
|
31
|
+
text: string;
|
|
32
|
+
callback_data: string;
|
|
33
|
+
} | {
|
|
34
|
+
text: string;
|
|
35
|
+
url: string;
|
|
36
|
+
};
|
|
37
|
+
export interface SendMessageInput {
|
|
38
|
+
chat_id: number | string;
|
|
39
|
+
text: string;
|
|
40
|
+
parse_mode?: "HTML";
|
|
41
|
+
reply_markup?: {
|
|
42
|
+
inline_keyboard: InlineKeyboardButton[][];
|
|
43
|
+
};
|
|
44
|
+
link_preview_options?: {
|
|
45
|
+
is_disabled: boolean;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Telegram's hard cap on `callback_data`. Over it, sendMessage 400s. */
|
|
49
|
+
export declare const MAX_CALLBACK_DATA_BYTES = 64;
|
|
50
|
+
/**
|
|
51
|
+
* Guard a `callback_data` payload against Telegram's 64-byte cap. Encoders call
|
|
52
|
+
* this so an over-long payload fails loudly at the call site instead of as an
|
|
53
|
+
* opaque 400 from an unrelated `sendMessage`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function assertCallbackData(data: string): string;
|
|
56
|
+
/** A bot-scoped client bound to one (possibly lazily resolved) token. */
|
|
57
|
+
export declare function telegramApi(token: TelegramToken, opts?: TelegramApiOptions): {
|
|
58
|
+
/** Validate the token and return the bot's identity. */
|
|
59
|
+
getMe(): Promise<TelegramBotInfo>;
|
|
60
|
+
/** Point Telegram's webhook at `url`, guarded by `secretToken`. */
|
|
61
|
+
setWebhook(input: {
|
|
62
|
+
url: string;
|
|
63
|
+
secretToken: string;
|
|
64
|
+
dropPendingUpdates?: boolean;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
deleteWebhook(): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Publish the bot's command list — what Telegram shows in the "/" menu and
|
|
69
|
+
* autocompletes. Cosmetic: the commands work either way, but nothing else
|
|
70
|
+
* makes them discoverable. Call it whenever the webhook is (re-)registered.
|
|
71
|
+
*/
|
|
72
|
+
setMyCommands(commands: {
|
|
73
|
+
command: string;
|
|
74
|
+
description: string;
|
|
75
|
+
}[]): Promise<void>;
|
|
76
|
+
getWebhookInfo(): Promise<TelegramWebhookInfo>;
|
|
77
|
+
/** Send a message; returns the new message id (cards are edited later by it). */
|
|
78
|
+
sendMessage(input: SendMessageInput): Promise<{
|
|
79
|
+
messageId: number;
|
|
80
|
+
}>;
|
|
81
|
+
/** Edit a previously sent message in place (the streaming primitive). */
|
|
82
|
+
editMessageText(input: SendMessageInput & {
|
|
83
|
+
message_id: number;
|
|
84
|
+
}): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Swap (or remove) just a message's inline keyboard. Used to re-tick
|
|
87
|
+
* multi-select options and to retire stale buttons — cheaper than
|
|
88
|
+
* `editMessageText` and it can't trip the "message is not modified" 400.
|
|
89
|
+
*/
|
|
90
|
+
editMessageReplyMarkup(input: {
|
|
91
|
+
chat_id: number | string;
|
|
92
|
+
message_id: number;
|
|
93
|
+
reply_markup?: {
|
|
94
|
+
inline_keyboard: InlineKeyboardButton[][];
|
|
95
|
+
};
|
|
96
|
+
}): Promise<void>;
|
|
97
|
+
/** Show "typing…" in the chat (auto-expires after ~5s or the next message). */
|
|
98
|
+
sendChatAction(chatId: number | string, action?: string): Promise<void>;
|
|
99
|
+
/** Ack a callback query (stops the client-side spinner on the button). */
|
|
100
|
+
answerCallbackQuery(callbackQueryId: string, text?: string): Promise<void>;
|
|
101
|
+
/** Resolve a file_id to its download path + size. */
|
|
102
|
+
getFile(fileId: string): Promise<{
|
|
103
|
+
filePath?: string;
|
|
104
|
+
fileSize?: number;
|
|
105
|
+
}>;
|
|
106
|
+
/** Download a file previously resolved via `getFile`. */
|
|
107
|
+
downloadFile(filePath: string): Promise<Uint8Array>;
|
|
108
|
+
};
|
|
109
|
+
export type TelegramClient = ReturnType<typeof telegramApi>;
|
|
110
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/telegram/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Telegram Bot API client — just the methods a channel (and its host's
|
|
3
|
+
* admin surface) needs. JSON POSTs to `api.telegram.org/bot<token>/<method>`,
|
|
4
|
+
* `{ok, result, description}` unwrap, 10s timeout per call. No SDK.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_BASE_URL = "https://api.telegram.org";
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
8
|
+
/** A failed Bot API call. `retryAfter` (seconds) is set on 429s. */
|
|
9
|
+
export class TelegramApiError extends Error {
|
|
10
|
+
status;
|
|
11
|
+
retryAfter;
|
|
12
|
+
constructor(method, status, description, retryAfter) {
|
|
13
|
+
super(`telegram:${method} failed: ${description ?? `HTTP ${status}`}`);
|
|
14
|
+
this.name = "TelegramApiError";
|
|
15
|
+
this.status = status;
|
|
16
|
+
if (retryAfter !== undefined)
|
|
17
|
+
this.retryAfter = retryAfter;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Telegram's hard cap on `callback_data`. Over it, sendMessage 400s. */
|
|
21
|
+
export const MAX_CALLBACK_DATA_BYTES = 64;
|
|
22
|
+
/**
|
|
23
|
+
* Guard a `callback_data` payload against Telegram's 64-byte cap. Encoders call
|
|
24
|
+
* this so an over-long payload fails loudly at the call site instead of as an
|
|
25
|
+
* opaque 400 from an unrelated `sendMessage`.
|
|
26
|
+
*/
|
|
27
|
+
export function assertCallbackData(data) {
|
|
28
|
+
const bytes = new TextEncoder().encode(data).length;
|
|
29
|
+
if (bytes > MAX_CALLBACK_DATA_BYTES) {
|
|
30
|
+
throw new Error(`callback_data too long (${bytes} > ${MAX_CALLBACK_DATA_BYTES}): ${data}`);
|
|
31
|
+
}
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
function asObject(value) {
|
|
35
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
36
|
+
? value
|
|
37
|
+
: {};
|
|
38
|
+
}
|
|
39
|
+
/** A bot-scoped client bound to one (possibly lazily resolved) token. */
|
|
40
|
+
export function telegramApi(token, opts = {}) {
|
|
41
|
+
const base = opts.baseUrl ?? DEFAULT_BASE_URL;
|
|
42
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
43
|
+
async function resolveToken() {
|
|
44
|
+
return typeof token === "function" ? token() : token;
|
|
45
|
+
}
|
|
46
|
+
async function call(method, body) {
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetchImpl(`${base}/bot${await resolveToken()}/${method}`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: { "content-type": "application/json" },
|
|
53
|
+
body: JSON.stringify(body),
|
|
54
|
+
signal: controller.signal,
|
|
55
|
+
});
|
|
56
|
+
const payload = (await res.json().catch(() => null));
|
|
57
|
+
if (!payload?.ok) {
|
|
58
|
+
throw new TelegramApiError(method, res.status, payload?.description, payload?.parameters?.retry_after);
|
|
59
|
+
}
|
|
60
|
+
return payload.result;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
/** Validate the token and return the bot's identity. */
|
|
68
|
+
async getMe() {
|
|
69
|
+
const r = asObject(await call("getMe", {}));
|
|
70
|
+
if (typeof r.id !== "number" || typeof r.username !== "string") {
|
|
71
|
+
throw new Error("Telegram getMe returned an unexpected result.");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
id: r.id,
|
|
75
|
+
username: r.username,
|
|
76
|
+
firstName: typeof r.first_name === "string" ? r.first_name : "",
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
/** Point Telegram's webhook at `url`, guarded by `secretToken`. */
|
|
80
|
+
async setWebhook(input) {
|
|
81
|
+
const r = await call("setWebhook", {
|
|
82
|
+
url: input.url,
|
|
83
|
+
secret_token: input.secretToken,
|
|
84
|
+
allowed_updates: ["message", "callback_query"],
|
|
85
|
+
...(input.dropPendingUpdates ? { drop_pending_updates: true } : {}),
|
|
86
|
+
});
|
|
87
|
+
if (r !== true)
|
|
88
|
+
throw new Error("Telegram setWebhook returned an unexpected result.");
|
|
89
|
+
},
|
|
90
|
+
async deleteWebhook() {
|
|
91
|
+
await call("deleteWebhook", {});
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* Publish the bot's command list — what Telegram shows in the "/" menu and
|
|
95
|
+
* autocompletes. Cosmetic: the commands work either way, but nothing else
|
|
96
|
+
* makes them discoverable. Call it whenever the webhook is (re-)registered.
|
|
97
|
+
*/
|
|
98
|
+
async setMyCommands(commands) {
|
|
99
|
+
await call("setMyCommands", { commands });
|
|
100
|
+
},
|
|
101
|
+
async getWebhookInfo() {
|
|
102
|
+
const r = asObject(await call("getWebhookInfo", {}));
|
|
103
|
+
return {
|
|
104
|
+
url: typeof r.url === "string" ? r.url : "",
|
|
105
|
+
pendingUpdateCount: typeof r.pending_update_count === "number" ? r.pending_update_count : 0,
|
|
106
|
+
...(typeof r.last_error_message === "string"
|
|
107
|
+
? { lastErrorMessage: r.last_error_message }
|
|
108
|
+
: {}),
|
|
109
|
+
...(typeof r.last_error_date === "number" ? { lastErrorDate: r.last_error_date } : {}),
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
/** Send a message; returns the new message id (cards are edited later by it). */
|
|
113
|
+
async sendMessage(input) {
|
|
114
|
+
const r = asObject(await call("sendMessage", { ...input }));
|
|
115
|
+
return { messageId: typeof r.message_id === "number" ? r.message_id : 0 };
|
|
116
|
+
},
|
|
117
|
+
/** Edit a previously sent message in place (the streaming primitive). */
|
|
118
|
+
async editMessageText(input) {
|
|
119
|
+
await call("editMessageText", { ...input });
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Swap (or remove) just a message's inline keyboard. Used to re-tick
|
|
123
|
+
* multi-select options and to retire stale buttons — cheaper than
|
|
124
|
+
* `editMessageText` and it can't trip the "message is not modified" 400.
|
|
125
|
+
*/
|
|
126
|
+
async editMessageReplyMarkup(input) {
|
|
127
|
+
await call("editMessageReplyMarkup", { ...input });
|
|
128
|
+
},
|
|
129
|
+
/** Show "typing…" in the chat (auto-expires after ~5s or the next message). */
|
|
130
|
+
async sendChatAction(chatId, action = "typing") {
|
|
131
|
+
await call("sendChatAction", { chat_id: chatId, action }).catch(() => {
|
|
132
|
+
/* cosmetic — never fail a turn over it */
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
/** Ack a callback query (stops the client-side spinner on the button). */
|
|
136
|
+
async answerCallbackQuery(callbackQueryId, text) {
|
|
137
|
+
await call("answerCallbackQuery", {
|
|
138
|
+
callback_query_id: callbackQueryId,
|
|
139
|
+
...(text ? { text } : {}),
|
|
140
|
+
}).catch(() => {
|
|
141
|
+
/* best-effort — the approval still resolves */
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
/** Resolve a file_id to its download path + size. */
|
|
145
|
+
async getFile(fileId) {
|
|
146
|
+
const r = asObject(await call("getFile", { file_id: fileId }));
|
|
147
|
+
return {
|
|
148
|
+
...(typeof r.file_path === "string" ? { filePath: r.file_path } : {}),
|
|
149
|
+
...(typeof r.file_size === "number" ? { fileSize: r.file_size } : {}),
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
/** Download a file previously resolved via `getFile`. */
|
|
153
|
+
async downloadFile(filePath) {
|
|
154
|
+
const controller = new AbortController();
|
|
155
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
156
|
+
try {
|
|
157
|
+
const res = await fetchImpl(`${base}/file/bot${await resolveToken()}/${filePath}`, {
|
|
158
|
+
signal: controller.signal,
|
|
159
|
+
});
|
|
160
|
+
if (!res.ok)
|
|
161
|
+
throw new Error(`telegram file download failed: HTTP ${res.status}`);
|
|
162
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/telegram/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram HTML for the cards the kernel asks the surface to post — approval
|
|
3
|
+
* prompts, questionnaire questions and their answered receipts.
|
|
4
|
+
*/
|
|
5
|
+
/** Compose the approval-prompt text (HTML) for a tool awaiting approval. */
|
|
6
|
+
export declare function approvalPromptHtml(toolName: string, input: unknown): string;
|
|
7
|
+
/** Button caption for an option — ticked marker for multi-select, length-capped. */
|
|
8
|
+
export declare function optionButtonText(label: string, ticked?: boolean): string;
|
|
9
|
+
/**
|
|
10
|
+
* Compose one question of a questionnaire (HTML). Option *labels* go on the
|
|
11
|
+
* buttons; the descriptions only fit in the body, numbered so a typed "2" is an
|
|
12
|
+
* unambiguous answer.
|
|
13
|
+
*/
|
|
14
|
+
export declare function questionPromptHtml(question: {
|
|
15
|
+
question: string;
|
|
16
|
+
header?: string;
|
|
17
|
+
options: {
|
|
18
|
+
label: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}[];
|
|
21
|
+
}, index: number, total: number): string;
|
|
22
|
+
/** Collapse an answered question card to a one-line receipt. */
|
|
23
|
+
export declare function answeredQuestionHtml(question: string, answer: string): string;
|
|
24
|
+
/** Plain-text twin of a Telegram HTML card, for the entity-parse fallback. */
|
|
25
|
+
export declare function htmlToPlain(html: string): string;
|
|
26
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/telegram/format.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAS3E;AAKD,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAIxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,EAClG,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,MAAM,CAYR;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { escapeHtml } from "./html.js";
|
|
2
|
+
/**
|
|
3
|
+
* Telegram HTML for the cards the kernel asks the surface to post — approval
|
|
4
|
+
* prompts, questionnaire questions and their answered receipts.
|
|
5
|
+
*/
|
|
6
|
+
/** Compose the approval-prompt text (HTML) for a tool awaiting approval. */
|
|
7
|
+
export function approvalPromptHtml(toolName, input) {
|
|
8
|
+
let json;
|
|
9
|
+
try {
|
|
10
|
+
json = JSON.stringify(input, null, 2) ?? String(input);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
json = String(input);
|
|
14
|
+
}
|
|
15
|
+
if (json.length > 2500)
|
|
16
|
+
json = `${json.slice(0, 2500)}\n… (truncated)`;
|
|
17
|
+
return `🔒 Approve <b>${escapeHtml(toolName)}</b>?\n\n<pre>${escapeHtml(json)}</pre>`;
|
|
18
|
+
}
|
|
19
|
+
/** Longest option label Telegram renders comfortably on a button. */
|
|
20
|
+
const MAX_BUTTON_LABEL = 40;
|
|
21
|
+
/** Button caption for an option — ticked marker for multi-select, length-capped. */
|
|
22
|
+
export function optionButtonText(label, ticked) {
|
|
23
|
+
const short = label.length > MAX_BUTTON_LABEL ? `${label.slice(0, MAX_BUTTON_LABEL - 1)}…` : label;
|
|
24
|
+
return ticked === undefined ? short : `${ticked ? "✓ " : " "}${short}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Compose one question of a questionnaire (HTML). Option *labels* go on the
|
|
28
|
+
* buttons; the descriptions only fit in the body, numbered so a typed "2" is an
|
|
29
|
+
* unambiguous answer.
|
|
30
|
+
*/
|
|
31
|
+
export function questionPromptHtml(question, index, total) {
|
|
32
|
+
const head = total > 1 ? `<b>Question ${index + 1} of ${total}</b>\n` : "";
|
|
33
|
+
const title = question.header
|
|
34
|
+
? `<i>${escapeHtml(question.header)}</i>\n${escapeHtml(question.question)}`
|
|
35
|
+
: escapeHtml(question.question);
|
|
36
|
+
const opts = question.options
|
|
37
|
+
.map((o, i) => {
|
|
38
|
+
const desc = o.description ? ` — ${escapeHtml(o.description)}` : "";
|
|
39
|
+
return `${i + 1}. <b>${escapeHtml(o.label)}</b>${desc}`;
|
|
40
|
+
})
|
|
41
|
+
.join("\n");
|
|
42
|
+
return `${head}${title}\n\n${opts}`;
|
|
43
|
+
}
|
|
44
|
+
/** Collapse an answered question card to a one-line receipt. */
|
|
45
|
+
export function answeredQuestionHtml(question, answer) {
|
|
46
|
+
return `✓ ${escapeHtml(question)}\n<b>${escapeHtml(answer)}</b>`;
|
|
47
|
+
}
|
|
48
|
+
/** Plain-text twin of a Telegram HTML card, for the entity-parse fallback. */
|
|
49
|
+
export function htmlToPlain(html) {
|
|
50
|
+
return html
|
|
51
|
+
.replace(/<[^>]+>/g, "")
|
|
52
|
+
.replace(/</g, "<")
|
|
53
|
+
.replace(/>/g, ">")
|
|
54
|
+
.replace(/&/g, "&");
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/telegram/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort GFM → Telegram HTML (parse_mode: "HTML"), same modest scope as
|
|
3
|
+
* the Slack adapter's `toMrkdwn`: bold, italic, strike, inline code, code
|
|
4
|
+
* fences, links. HTML is deliberately chosen over MarkdownV2 — MarkdownV2
|
|
5
|
+
* requires escaping 18 characters and partially streamed markdown breaks its
|
|
6
|
+
* parser, while unknown tags in HTML just error one edit (we then retry the
|
|
7
|
+
* edit as plain text).
|
|
8
|
+
*/
|
|
9
|
+
export declare function escapeHtml(text: string): string;
|
|
10
|
+
export declare function gfmToTelegramHtml(gfm: string): string;
|
|
11
|
+
//# sourceMappingURL=html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/telegram/html.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAiBD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYrD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort GFM → Telegram HTML (parse_mode: "HTML"), same modest scope as
|
|
3
|
+
* the Slack adapter's `toMrkdwn`: bold, italic, strike, inline code, code
|
|
4
|
+
* fences, links. HTML is deliberately chosen over MarkdownV2 — MarkdownV2
|
|
5
|
+
* requires escaping 18 characters and partially streamed markdown breaks its
|
|
6
|
+
* parser, while unknown tags in HTML just error one edit (we then retry the
|
|
7
|
+
* edit as plain text).
|
|
8
|
+
*/
|
|
9
|
+
export function escapeHtml(text) {
|
|
10
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
11
|
+
}
|
|
12
|
+
/** Inline formatting on an already-escaped, fence-free segment. */
|
|
13
|
+
function inline(escaped) {
|
|
14
|
+
return (escaped
|
|
15
|
+
// inline code first so its contents are not re-formatted
|
|
16
|
+
.replace(/`([^`\n]+)`/g, "<code>$1</code>")
|
|
17
|
+
.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>")
|
|
18
|
+
.replace(/__(.+?)__/g, "<b>$1</b>")
|
|
19
|
+
.replace(/~~(.+?)~~/g, "<s>$1</s>")
|
|
20
|
+
// _italic_ only at word boundaries, so snake_case survives
|
|
21
|
+
.replace(/(^|[\s(])_([^_\n]+)_(?=$|[\s).,!?:;])/gm, "$1<i>$2</i>")
|
|
22
|
+
.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '<a href="$2">$1</a>'));
|
|
23
|
+
}
|
|
24
|
+
export function gfmToTelegramHtml(gfm) {
|
|
25
|
+
// Code fences become <pre>; everything else gets inline formatting. The
|
|
26
|
+
// language tag on the opening fence line is stripped inside the code segment
|
|
27
|
+
// (splitting on the bare fence keeps the closing fence's newline in prose).
|
|
28
|
+
const parts = gfm.split("```");
|
|
29
|
+
return parts
|
|
30
|
+
.map((part, i) => i % 2 === 1
|
|
31
|
+
? `<pre>${escapeHtml(part.replace(/^[\w+-]*\n/, "").replace(/\n$/, ""))}</pre>`
|
|
32
|
+
: inline(escapeHtml(part)))
|
|
33
|
+
.join("");
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/telegram/html.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACjF,CAAC;AAED,mEAAmE;AACnE,SAAS,MAAM,CAAC,OAAe;IAC7B,OAAO,CACL,OAAO;QACL,yDAAyD;SACxD,OAAO,CAAC,cAAc,EAAE,iBAAiB,CAAC;SAC1C,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC;SACtC,OAAO,CAAC,YAAY,EAAE,WAAW,CAAC;SAClC,OAAO,CAAC,YAAY,EAAE,WAAW,CAAC;QACnC,2DAA2D;SAC1D,OAAO,CAAC,yCAAyC,EAAE,aAAa,CAAC;SACjE,OAAO,CAAC,uCAAuC,EAAE,qBAAqB,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,wEAAwE;IACxE,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CACf,CAAC,GAAG,CAAC,KAAK,CAAC;QACT,CAAC,CAAC,QAAQ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ;QAC/E,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAC7B;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @intentface/latch-channels/telegram
|
|
3
|
+
*
|
|
4
|
+
* Bring a Latch agent to Telegram DMs: per-bot webhook handling (secret-token
|
|
5
|
+
* verified), the answer posted once at finish as HTML, inline-keyboard
|
|
6
|
+
* approvals and questionnaires, and media → multimodal file parts. A Surface
|
|
7
|
+
* over the turn kernel — identity, credentials and dedupe are injected by the
|
|
8
|
+
* host.
|
|
9
|
+
*/
|
|
10
|
+
export { telegramApi, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramClient, type TelegramToken, type TelegramWebhookInfo, type InlineKeyboardButton, } from "./api.js";
|
|
11
|
+
export { verifyTelegramSecretToken } from "./verify.js";
|
|
12
|
+
export { escapeHtml, gfmToTelegramHtml } from "./html.js";
|
|
13
|
+
export { approvalPromptHtml, questionPromptHtml, answeredQuestionHtml } from "./format.js";
|
|
14
|
+
export { telegramChannel, telegramSurface, telegramChatId, parseTelegramChatId, encodeApprovalCallback, decodeApprovalCallback, type TelegramChannelConfig, type TelegramUser, type TgFileRef, } from "./telegramChannel.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telegram/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @intentface/latch-channels/telegram
|
|
3
|
+
*
|
|
4
|
+
* Bring a Latch agent to Telegram DMs: per-bot webhook handling (secret-token
|
|
5
|
+
* verified), the answer posted once at finish as HTML, inline-keyboard
|
|
6
|
+
* approvals and questionnaires, and media → multimodal file parts. A Surface
|
|
7
|
+
* over the turn kernel — identity, credentials and dedupe are injected by the
|
|
8
|
+
* host.
|
|
9
|
+
*/
|
|
10
|
+
export { telegramApi, TelegramApiError, } from "./api.js";
|
|
11
|
+
export { verifyTelegramSecretToken } from "./verify.js";
|
|
12
|
+
export { escapeHtml, gfmToTelegramHtml } from "./html.js";
|
|
13
|
+
export { approvalPromptHtml, questionPromptHtml, answeredQuestionHtml } from "./format.js";
|
|
14
|
+
export { telegramChannel, telegramSurface, telegramChatId, parseTelegramChatId, encodeApprovalCallback, decodeApprovalCallback, } from "./telegramChannel.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telegram/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,WAAW,EACX,gBAAgB,GAOjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GAIvB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { RenderEvent } from "../types.js";
|
|
2
|
+
import { type TelegramClient } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* The streaming-edit engine: Telegram (unlike Slack) can edit a sent message,
|
|
5
|
+
* so the answer streams as progressive `editMessageText` calls on one message.
|
|
6
|
+
*
|
|
7
|
+
* sendChatAction("typing") on start.
|
|
8
|
+
* First text-delta → sendMessage; keep the message_id.
|
|
9
|
+
* Buffer deltas; flush when ≥`minEditIntervalMs` since the last edit OR
|
|
10
|
+
* ≥`minEditChars` new chars. On 429, honor `retry_after` and resume.
|
|
11
|
+
* tool-start folds a status line into the draft; tool-end clears it.
|
|
12
|
+
* At `maxChars` stop growing, append the truncation notice (one message,
|
|
13
|
+
* never a spill-over second one), and keep draining events without editing.
|
|
14
|
+
* finish/error → final flush (status line removed, error appended).
|
|
15
|
+
*
|
|
16
|
+
* Renders HTML (`parse_mode: "HTML"`); if Telegram rejects the entity parse
|
|
17
|
+
* (400), the same content is retried as plain text and the message stays
|
|
18
|
+
* plain for the rest of the turn.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MAX_TELEGRAM_TEXT_CHARS = 4000;
|
|
21
|
+
export declare const TELEGRAM_TEXT_TRUNCATION_NOTICE = "\n\n_Response truncated for Telegram. The full turn is available in Latch._";
|
|
22
|
+
export interface TelegramStreamOptions {
|
|
23
|
+
api: Pick<TelegramClient, "sendMessage" | "editMessageText" | "sendChatAction">;
|
|
24
|
+
chatId: number | string;
|
|
25
|
+
maxChars?: number;
|
|
26
|
+
truncationNotice?: string;
|
|
27
|
+
minEditIntervalMs?: number;
|
|
28
|
+
minEditChars?: number;
|
|
29
|
+
/** Injectable clock/sleep for tests. */
|
|
30
|
+
now?: () => number;
|
|
31
|
+
sleep?: (ms: number) => Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface TelegramStreamResult {
|
|
34
|
+
/** The full accumulated answer text (pre-truncation). */
|
|
35
|
+
text: string;
|
|
36
|
+
errorMessage?: string;
|
|
37
|
+
messageId?: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function renderTelegramStream(events: AsyncIterable<RenderEvent>, opts: TelegramStreamOptions): Promise<TelegramStreamResult>;
|
|
40
|
+
/** Compose the approval-prompt text (HTML) for a tool awaiting approval. */
|
|
41
|
+
export declare function approvalPromptHtml(toolName: string, input: unknown): string;
|
|
42
|
+
/** Button caption for an option — ticked marker for multi-select, length-capped. */
|
|
43
|
+
export declare function optionButtonText(label: string, ticked?: boolean): string;
|
|
44
|
+
/**
|
|
45
|
+
* Compose one question of a questionnaire (HTML). Option *labels* go on the
|
|
46
|
+
* buttons; the descriptions only fit in the body, numbered so a typed "2" is an
|
|
47
|
+
* unambiguous answer.
|
|
48
|
+
*/
|
|
49
|
+
export declare function questionPromptHtml(question: {
|
|
50
|
+
question: string;
|
|
51
|
+
header?: string;
|
|
52
|
+
options: {
|
|
53
|
+
label: string;
|
|
54
|
+
description: string;
|
|
55
|
+
}[];
|
|
56
|
+
}, index: number, total: number): string;
|
|
57
|
+
/** Collapse an answered question card to a one-line receipt. */
|
|
58
|
+
export declare function answeredQuestionHtml(question: string, answer: string): string;
|
|
59
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/telegram/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGjE;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,+BAA+B,gFACmC,CAAC;AAEhF,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,GAAG,iBAAiB,GAAG,gBAAgB,CAAC,CAAC;IAChF,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,EAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CAiH/B;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAS3E;AAKD,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAIxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,EAClG,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,MAAM,CAYR;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7E"}
|