mirror of
https://github.com/wassname/pi-telegram.git
synced 2026-06-27 15:16:19 +08:00
security: require pre-configured allowedUserId, remove auto-pair
The first-DM auto-pair behavior combined with ! shell passthrough meant
the first account to DM the bot gained arbitrary shell access. This
removes that footgun entirely.
- allowedUserId must be set before polling starts; missing config blocks
polling with a TUI warning rather than silently accepting any sender
- TELEGRAM_ALLOWED_USER_ID env var is read on session start and
overwrites the saved config value, so rotating the allowed user is a
restart away
- /telegram-setup now prompts for a numeric user ID after the bot token
if one is not already configured
- Denied senders receive an auth error reply; their numeric ID is also
logged to the pi TUI as a warning so operators can identify themselves
on a fresh install without needing @userinfobot
- Dropped the {kind: "pair"} authorization state entirely; undefined
allowedUserId now produces deny, not pair
- Removed pairTelegramUserIfNeeded, shouldPair, shouldNotifyPaired
Existing installs with allowedUserId already in telegram.json are
unaffected. Fresh installs require explicit configuration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
## 5. Architectural Decisions
|
||||
|
||||
- `index.ts` stays the single extension entrypoint, while reusable runtime logic should be split into flat domain files under `/lib`; prefer domain-oriented grouping over atomizing every helper into its own file, and use `shared` sparingly for genuinely cross-domain types or constants
|
||||
- The bridge is session-local and intentionally pairs with a single allowed Telegram user per config
|
||||
- The bridge is session-local and restricted to a single pre-configured allowed Telegram user; `allowedUserId` must be set before polling starts, either via `TELEGRAM_ALLOWED_USER_ID` env var or the `/telegram-setup` prompt; there is no auto-pair-on-first-DM behavior
|
||||
- Telegram queue state is tracked locally and must stay aligned with pi agent lifecycle hooks; queued items now have explicit kinds and lanes so prompt turns and synthetic control actions can share one ordering model, while dispatch still respects active turns, pending dispatch, compaction, and pi pending-message state
|
||||
- Prompt items should remain in the queue until `agent_start` consumes the dispatched turn; removing them earlier breaks active-turn binding, preview delivery, and end-of-turn follow-up behavior
|
||||
- In-flight `/model` switching is supported only for Telegram-owned active turns and is implemented as set-model plus synthetic continuation turn plus abort; if a tool call is active, the abort is delayed until that tool finishes instead of interrupting the tool mid-flight
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## Current
|
||||
|
||||
- `[Security]` Removed auto-pair-on-first-DM behavior. The bot now requires `allowedUserId` to be set before polling starts. Configure it via `TELEGRAM_ALLOWED_USER_ID` env var or the updated `/telegram-setup` prompt (which now asks for a numeric user ID after the bot token). The env var takes precedence over the saved config on every session start. Denied senders get an auth error reply; their numeric ID is also logged to the pi TUI as a warning. Breaking change: fresh installs require explicit configuration; existing installs with `allowedUserId` already in `telegram.json` continue to work unchanged.
|
||||
|
||||
|
||||
|
||||
- `[Trace Visibility]` Added `/trace` command and status-menu inline button to toggle visibility of thinking and tool-call blocks in Telegram replies. When enabled (default), streaming previews show compact one-line trace summaries and final replies include quoted trace detail. Impact: operators can see what the model is thinking and which tools it calls without leaving Telegram.
|
||||
- `[Queue UI]` Marked liked high-priority queued Telegram turns with `⬆` in the pi status-bar queue preview. Impact: operators can now distinguish reaction-promoted turns from normal queued prompts at a glance.
|
||||
- `[Docs]` Added short responsibility header comments to every project `.ts` file. Impact: file boundaries are easier to understand while navigating the growing `/lib` split.
|
||||
|
||||
@@ -71,6 +71,16 @@ Start pi, then run:
|
||||
Paste the bot token when prompted.
|
||||
If a bot token is already saved in `~/.pi/agent/telegram.json`, `/telegram-setup` shows that stored value by default. Otherwise it pre-fills from the first configured environment variable in `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, or `TELEGRAM_KEY`.
|
||||
|
||||
After the bot token, `/telegram-setup` asks for your numeric Telegram user ID if one is not already configured. To find your ID: DM [@userinfobot](https://t.me/userinfobot) on Telegram — it replies with your numeric ID. This is your permanent account ID, not your `@username` (mutable) or phone number (never visible to bots).
|
||||
|
||||
You can also pre-configure the allowed user ID via the environment variable `TELEGRAM_ALLOWED_USER_ID`:
|
||||
|
||||
```bash
|
||||
export TELEGRAM_ALLOWED_USER_ID=123456789
|
||||
```
|
||||
|
||||
The env var takes precedence over the saved config file on every session start. Only one user ID is supported.
|
||||
|
||||
The extension stores config in:
|
||||
|
||||
```text
|
||||
@@ -97,14 +107,11 @@ Check status:
|
||||
/telegram-status
|
||||
```
|
||||
|
||||
## Pair your Telegram account
|
||||
## Allowed Telegram user
|
||||
|
||||
After token setup and `/telegram-connect`:
|
||||
The bot only accepts messages from the pre-configured allowed user. Polling will not start until an allowed user ID is configured.
|
||||
|
||||
1. Open the DM with your bot in Telegram
|
||||
2. Send `/start`
|
||||
|
||||
The first DM user becomes the allowed Telegram user for the bridge. The extension only accepts messages from that user.
|
||||
If any other Telegram account messages the bot, the bot replies with an authorization error and logs the sender's numeric user ID to the pi TUI (as a warning) so you can identify it if needed.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ import {
|
||||
import {
|
||||
getTelegramBotTokenInputDefault,
|
||||
getTelegramBotTokenPromptSpec,
|
||||
readAllowedUserIdFromEnv,
|
||||
} from "./lib/setup.ts";
|
||||
import { buildStatusHtml, extractTurnCost, formatTurnCostLine } from "./lib/status.ts";
|
||||
import {
|
||||
@@ -109,7 +110,6 @@ import {
|
||||
import {
|
||||
collectTelegramReactionEmojis,
|
||||
executeTelegramUpdate,
|
||||
getTelegramAuthorizationState,
|
||||
} from "./lib/updates.ts";
|
||||
|
||||
// --- Telegram API Types ---
|
||||
@@ -369,6 +369,7 @@ export const __telegramTestUtils = {
|
||||
canDispatchTelegramTurnState,
|
||||
getTelegramBotTokenInputDefault,
|
||||
getTelegramBotTokenPromptSpec,
|
||||
readAllowedUserIdFromEnv,
|
||||
canRestartTelegramTurnForModelSwitch,
|
||||
restartTelegramModelSwitchContinuation,
|
||||
shouldTriggerPendingTelegramModelSwitchAbort,
|
||||
@@ -502,7 +503,7 @@ export default function (pi: ExtensionAPI) {
|
||||
if (!config.allowedUserId) {
|
||||
ctx.ui.setStatus(
|
||||
"telegram",
|
||||
`${label} ${theme.fg("warning", "awaiting pairing")}`,
|
||||
`${label} ${theme.fg("warning", "awaiting config")}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -969,10 +970,33 @@ export default function (pi: ExtensionAPI) {
|
||||
`Telegram bot connected: @${config.botUsername ?? "unknown"}`,
|
||||
"info",
|
||||
);
|
||||
|
||||
if (!config.allowedUserId) {
|
||||
ctx.ui.notify(
|
||||
"Send /start to your bot in Telegram to pair this extension with your account.",
|
||||
"Enter your numeric Telegram user ID. To find it: DM @userinfobot on Telegram — it replies with your ID. This is NOT your @username or phone number.",
|
||||
"info",
|
||||
);
|
||||
const rawUserId = await ctx.ui.input(
|
||||
"Allowed Telegram user ID (numeric, e.g. 123456789)",
|
||||
"",
|
||||
);
|
||||
if (!rawUserId) return;
|
||||
const parsedUserId = Number(rawUserId.trim());
|
||||
if (!Number.isInteger(parsedUserId) || parsedUserId <= 0) {
|
||||
ctx.ui.notify(
|
||||
`"${rawUserId}" is not a valid Telegram user ID. Must be a positive integer.`,
|
||||
"error",
|
||||
);
|
||||
return;
|
||||
}
|
||||
config.allowedUserId = parsedUserId;
|
||||
await writeTelegramConfig(AGENT_DIR, CONFIG_PATH, config);
|
||||
ctx.ui.notify(
|
||||
`Allowed user ID set to ${config.allowedUserId}. Send a message from that account to confirm.`,
|
||||
"info",
|
||||
);
|
||||
}
|
||||
|
||||
await startPolling(ctx);
|
||||
updateStatus(ctx);
|
||||
} finally {
|
||||
@@ -1726,11 +1750,6 @@ export default function (pi: ExtensionAPI) {
|
||||
}
|
||||
}
|
||||
await sendTextReply(message.chat.id, message.message_id, helpText);
|
||||
if (config.allowedUserId === undefined && message.from) {
|
||||
config.allowedUserId = message.from.id;
|
||||
await writeTelegramConfig(AGENT_DIR, CONFIG_PATH, config);
|
||||
updateStatus(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTelegramCommand(
|
||||
@@ -1818,21 +1837,6 @@ export default function (pi: ExtensionAPI) {
|
||||
await dispatchAuthorizedTelegramMessages([message], ctx);
|
||||
}
|
||||
|
||||
async function pairTelegramUserIfNeeded(
|
||||
userId: number,
|
||||
ctx: ExtensionContext,
|
||||
): Promise<boolean> {
|
||||
const authorization = getTelegramAuthorizationState(
|
||||
userId,
|
||||
config.allowedUserId,
|
||||
);
|
||||
if (authorization.kind !== "pair") return false;
|
||||
config.allowedUserId = authorization.userId;
|
||||
await writeTelegramConfig(AGENT_DIR, CONFIG_PATH, config);
|
||||
updateStatus(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleUpdate(
|
||||
update: TelegramUpdate,
|
||||
ctx: ExtensionContext,
|
||||
@@ -1850,7 +1854,12 @@ export default function (pi: ExtensionAPI) {
|
||||
nextCtx,
|
||||
);
|
||||
},
|
||||
pairTelegramUserIfNeeded,
|
||||
onDeniedUserId: (userId) => {
|
||||
ctx.ui.notify(
|
||||
`Telegram: rejected message from user ID ${userId} (not the configured allowed user). To allow this user, set TELEGRAM_ALLOWED_USER_ID=${userId}.`,
|
||||
"warning",
|
||||
);
|
||||
},
|
||||
answerCallbackQuery,
|
||||
handleAuthorizedTelegramCallbackQuery: async (query, nextCtx) => {
|
||||
await handleAuthorizedTelegramCallbackQuery(
|
||||
@@ -1923,6 +1932,13 @@ export default function (pi: ExtensionAPI) {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!config.allowedUserId) {
|
||||
ctx.ui.notify(
|
||||
"Telegram polling blocked: allowedUserId is not set. Set TELEGRAM_ALLOWED_USER_ID or run /telegram-setup to configure it.",
|
||||
"warning",
|
||||
);
|
||||
return;
|
||||
}
|
||||
pollingController = new AbortController();
|
||||
pollingPromise = pollLoop(ctx, pollingController.signal).finally(() => {
|
||||
pollingPromise = undefined;
|
||||
@@ -1945,7 +1961,7 @@ export default function (pi: ExtensionAPI) {
|
||||
getStatusLines: () => {
|
||||
return [
|
||||
`bot: ${config.botUsername ? `@${config.botUsername}` : "not configured"}`,
|
||||
`allowed user: ${config.allowedUserId ?? "not paired"}`,
|
||||
`allowed user: ${config.allowedUserId ?? "not configured"}`,
|
||||
`polling: ${pollingPromise ? "running" : "stopped"}`,
|
||||
`active telegram turn: ${activeTelegramTurn ? "yes" : "no"}`,
|
||||
`queued telegram turns: ${queuedTelegramItems.length}`,
|
||||
@@ -1965,6 +1981,11 @@ export default function (pi: ExtensionAPI) {
|
||||
registerTelegramLifecycleHooks(pi, {
|
||||
onSessionStart: async (_event, ctx) => {
|
||||
config = await readTelegramConfig(CONFIG_PATH);
|
||||
const envAllowedUserId = readAllowedUserIdFromEnv(process.env);
|
||||
if (envAllowedUserId !== undefined && envAllowedUserId !== config.allowedUserId) {
|
||||
config.allowedUserId = envAllowedUserId;
|
||||
await writeTelegramConfig(AGENT_DIR, CONFIG_PATH, config);
|
||||
}
|
||||
const sessionStartState = buildTelegramSessionStartState(ctx.model);
|
||||
currentTelegramModel = sessionStartState.currentTelegramModel;
|
||||
activeTelegramToolExecutions =
|
||||
|
||||
@@ -16,6 +16,22 @@ export const TELEGRAM_BOT_TOKEN_ENV_VARS = [
|
||||
"TELEGRAM_KEY",
|
||||
] as const;
|
||||
|
||||
export const TELEGRAM_ALLOWED_USER_ID_ENV_VAR = "TELEGRAM_ALLOWED_USER_ID";
|
||||
|
||||
export function readAllowedUserIdFromEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): number | undefined {
|
||||
const raw = env[TELEGRAM_ALLOWED_USER_ID_ENV_VAR]?.trim();
|
||||
if (!raw) return undefined;
|
||||
const parsed = Number(raw);
|
||||
if (!Number.isInteger(parsed) || parsed <= 0) {
|
||||
throw new Error(
|
||||
`${TELEGRAM_ALLOWED_USER_ID_ENV_VAR}="${raw}" is not a valid Telegram user ID (must be a positive integer)`,
|
||||
);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function getTelegramBotTokenInputDefault(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
configToken?: string,
|
||||
|
||||
+3
-27
@@ -95,7 +95,6 @@ export interface TelegramUpdateRoutingLike {
|
||||
}
|
||||
|
||||
export type TelegramAuthorizationState =
|
||||
| { kind: "pair"; userId: number }
|
||||
| { kind: "allow" }
|
||||
| { kind: "deny" };
|
||||
|
||||
@@ -103,9 +102,6 @@ export function getTelegramAuthorizationState(
|
||||
userId: number,
|
||||
allowedUserId?: number,
|
||||
): TelegramAuthorizationState {
|
||||
if (allowedUserId === undefined) {
|
||||
return { kind: "pair", userId };
|
||||
}
|
||||
if (userId === allowedUserId) {
|
||||
return { kind: "allow" };
|
||||
}
|
||||
@@ -214,14 +210,11 @@ export type TelegramUpdateExecutionPlan =
|
||||
| {
|
||||
kind: "callback";
|
||||
query: TelegramCallbackQueryLike;
|
||||
shouldPair: boolean;
|
||||
shouldDeny: boolean;
|
||||
}
|
||||
| {
|
||||
kind: "message";
|
||||
message: TelegramMessageLike & { from: TelegramUserLike };
|
||||
shouldPair: boolean;
|
||||
shouldNotifyPaired: boolean;
|
||||
shouldDeny: boolean;
|
||||
};
|
||||
|
||||
@@ -239,15 +232,12 @@ export function buildTelegramUpdateExecutionPlan(
|
||||
return {
|
||||
kind: "callback",
|
||||
query: action.query,
|
||||
shouldPair: action.authorization.kind === "pair",
|
||||
shouldDeny: action.authorization.kind === "deny",
|
||||
};
|
||||
case "message":
|
||||
return {
|
||||
kind: "message",
|
||||
message: action.message,
|
||||
shouldPair: action.authorization.kind === "pair",
|
||||
shouldNotifyPaired: action.authorization.kind === "pair",
|
||||
shouldDeny: action.authorization.kind === "deny",
|
||||
};
|
||||
}
|
||||
@@ -280,10 +270,7 @@ export interface TelegramUpdateRuntimeDeps {
|
||||
>,
|
||||
ctx: ExtensionContext,
|
||||
) => Promise<void>;
|
||||
pairTelegramUserIfNeeded: (
|
||||
userId: number,
|
||||
ctx: ExtensionContext,
|
||||
) => Promise<boolean>;
|
||||
onDeniedUserId?: (userId: number) => void;
|
||||
answerCallbackQuery: (
|
||||
callbackQueryId: string,
|
||||
text?: string,
|
||||
@@ -356,10 +343,8 @@ export async function executeTelegramUpdatePlan(
|
||||
return;
|
||||
}
|
||||
if (plan.kind === "callback") {
|
||||
if (plan.shouldPair) {
|
||||
await deps.pairTelegramUserIfNeeded(plan.query.from.id, deps.ctx);
|
||||
}
|
||||
if (plan.shouldDeny) {
|
||||
deps.onDeniedUserId?.(plan.query.from.id);
|
||||
const callbackQueryId = getTelegramCallbackQueryId(plan.query);
|
||||
if (callbackQueryId) {
|
||||
await deps.answerCallbackQuery(
|
||||
@@ -372,18 +357,9 @@ export async function executeTelegramUpdatePlan(
|
||||
await deps.handleAuthorizedTelegramCallbackQuery(plan.query, deps.ctx);
|
||||
return;
|
||||
}
|
||||
const pairedNow = plan.shouldPair
|
||||
? await deps.pairTelegramUserIfNeeded(plan.message.from.id, deps.ctx)
|
||||
: false;
|
||||
const replyTarget = getTelegramMessageReplyTarget(plan.message);
|
||||
if (pairedNow && plan.shouldNotifyPaired && replyTarget) {
|
||||
await deps.sendTextReply(
|
||||
replyTarget.chatId,
|
||||
replyTarget.messageId,
|
||||
"Telegram bridge paired with this account.",
|
||||
);
|
||||
}
|
||||
if (plan.shouldDeny) {
|
||||
deps.onDeniedUserId?.(plan.message.from.id);
|
||||
if (replyTarget) {
|
||||
await deps.sendTextReply(
|
||||
replyTarget.chatId,
|
||||
|
||||
@@ -78,3 +78,28 @@ test("Bot token prompt uses placeholder input when no prefill exists", () => {
|
||||
value: "123456:ABCDEF...",
|
||||
});
|
||||
});
|
||||
|
||||
test("readAllowedUserIdFromEnv returns undefined when env var is not set", () => {
|
||||
assert.equal(__telegramTestUtils.readAllowedUserIdFromEnv({}), undefined);
|
||||
assert.equal(__telegramTestUtils.readAllowedUserIdFromEnv({ TELEGRAM_ALLOWED_USER_ID: " " }), undefined);
|
||||
});
|
||||
|
||||
test("readAllowedUserIdFromEnv parses a valid positive integer", () => {
|
||||
assert.equal(__telegramTestUtils.readAllowedUserIdFromEnv({ TELEGRAM_ALLOWED_USER_ID: "123456789" }), 123456789);
|
||||
assert.equal(__telegramTestUtils.readAllowedUserIdFromEnv({ TELEGRAM_ALLOWED_USER_ID: " 42 " }), 42);
|
||||
});
|
||||
|
||||
test("readAllowedUserIdFromEnv throws on non-integer or non-positive value", () => {
|
||||
assert.throws(
|
||||
() => __telegramTestUtils.readAllowedUserIdFromEnv({ TELEGRAM_ALLOWED_USER_ID: "notanumber" }),
|
||||
/not a valid Telegram user ID/,
|
||||
);
|
||||
assert.throws(
|
||||
() => __telegramTestUtils.readAllowedUserIdFromEnv({ TELEGRAM_ALLOWED_USER_ID: "0" }),
|
||||
/not a valid Telegram user ID/,
|
||||
);
|
||||
assert.throws(
|
||||
() => __telegramTestUtils.readAllowedUserIdFromEnv({ TELEGRAM_ALLOWED_USER_ID: "-5" }),
|
||||
/not a valid Telegram user ID/,
|
||||
);
|
||||
});
|
||||
|
||||
+12
-26
@@ -54,11 +54,8 @@ test("Update helpers extract deleted message ids from Telegram update variants",
|
||||
);
|
||||
});
|
||||
|
||||
test("Update routing classifies authorization state for pair, allow, and deny", () => {
|
||||
assert.deepEqual(getTelegramAuthorizationState(10), {
|
||||
kind: "pair",
|
||||
userId: 10,
|
||||
});
|
||||
test("Update routing classifies authorization state for allow and deny", () => {
|
||||
assert.deepEqual(getTelegramAuthorizationState(10, undefined), { kind: "deny" });
|
||||
assert.deepEqual(getTelegramAuthorizationState(10, 10), { kind: "allow" });
|
||||
assert.deepEqual(getTelegramAuthorizationState(10, 11), { kind: "deny" });
|
||||
});
|
||||
@@ -142,7 +139,7 @@ test("Update flow returns authorized callback and message actions", () => {
|
||||
assert.equal(messageAction.kind, "message");
|
||||
assert.deepEqual(
|
||||
messageAction.kind === "message" ? messageAction.authorization : undefined,
|
||||
{ kind: "pair", userId: 9 },
|
||||
{ kind: "deny" },
|
||||
);
|
||||
});
|
||||
|
||||
@@ -180,7 +177,6 @@ test("Update execution plan maps callback and message authorization to side-effe
|
||||
from: { id: 1, is_bot: false },
|
||||
message: { chat: { type: "private" } },
|
||||
},
|
||||
shouldPair: false,
|
||||
shouldDeny: true,
|
||||
});
|
||||
const messagePlan = buildTelegramUpdateExecutionPlan({
|
||||
@@ -189,11 +185,9 @@ test("Update execution plan maps callback and message authorization to side-effe
|
||||
chat: { type: "private" },
|
||||
from: { id: 2, is_bot: false },
|
||||
},
|
||||
authorization: { kind: "pair", userId: 2 },
|
||||
authorization: { kind: "allow" },
|
||||
});
|
||||
assert.equal(messagePlan.kind, "message");
|
||||
assert.equal(messagePlan.shouldPair, true);
|
||||
assert.equal(messagePlan.shouldNotifyPaired, true);
|
||||
assert.equal(messagePlan.shouldDeny, false);
|
||||
});
|
||||
|
||||
@@ -246,7 +240,6 @@ test("Update runtime executes delete and reaction plans through the right side e
|
||||
handleAuthorizedTelegramReactionUpdate: async () => {
|
||||
events.push("reaction");
|
||||
},
|
||||
pairTelegramUserIfNeeded: async () => false,
|
||||
answerCallbackQuery: async () => {},
|
||||
handleAuthorizedTelegramCallbackQuery: async () => {},
|
||||
sendTextReply: async () => undefined,
|
||||
@@ -256,7 +249,7 @@ test("Update runtime executes delete and reaction plans through the right side e
|
||||
assert.deepEqual(events, ["media:1,2", "queue:1,2"]);
|
||||
});
|
||||
|
||||
test("Update runtime can execute directly from raw updates", async () => {
|
||||
test("Update runtime denies messages when allowedUserId is undefined", async () => {
|
||||
const events: string[] = [];
|
||||
await executeTelegramUpdate(
|
||||
{
|
||||
@@ -273,9 +266,8 @@ test("Update runtime can execute directly from raw updates", async () => {
|
||||
removePendingMediaGroupMessages: () => {},
|
||||
removeQueuedTelegramTurnsByMessageIds: () => 0,
|
||||
handleAuthorizedTelegramReactionUpdate: async () => {},
|
||||
pairTelegramUserIfNeeded: async () => {
|
||||
events.push("pair");
|
||||
return true;
|
||||
onDeniedUserId: (userId) => {
|
||||
events.push(`denied:${userId}`);
|
||||
},
|
||||
answerCallbackQuery: async () => {},
|
||||
handleAuthorizedTelegramCallbackQuery: async () => {},
|
||||
@@ -288,10 +280,10 @@ test("Update runtime can execute directly from raw updates", async () => {
|
||||
},
|
||||
},
|
||||
);
|
||||
assert.deepEqual(events, ["pair", "reply:Telegram bridge paired with this account.", "message"]);
|
||||
assert.deepEqual(events, ["denied:7", "reply:This bot is not authorized for your account."]);
|
||||
});
|
||||
|
||||
test("Update runtime handles callback deny and message pair flows", async () => {
|
||||
test("Update runtime handles callback deny and message deny flows", async () => {
|
||||
const events: string[] = [];
|
||||
await executeTelegramUpdatePlan(
|
||||
{
|
||||
@@ -301,7 +293,6 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
||||
from: { id: 1, is_bot: false },
|
||||
message: { chat: { type: "private" } },
|
||||
},
|
||||
shouldPair: true,
|
||||
shouldDeny: true,
|
||||
},
|
||||
{
|
||||
@@ -309,9 +300,8 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
||||
removePendingMediaGroupMessages: () => {},
|
||||
removeQueuedTelegramTurnsByMessageIds: () => 0,
|
||||
handleAuthorizedTelegramReactionUpdate: async () => {},
|
||||
pairTelegramUserIfNeeded: async (userId) => {
|
||||
events.push(`pair:${userId}`);
|
||||
return true;
|
||||
onDeniedUserId: (userId) => {
|
||||
events.push(`denied:${userId}`);
|
||||
},
|
||||
answerCallbackQuery: async (id, text) => {
|
||||
events.push(`answer:${id}:${text}`);
|
||||
@@ -336,8 +326,6 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
||||
from: { id: 2, is_bot: false },
|
||||
message_id: 9,
|
||||
},
|
||||
shouldPair: true,
|
||||
shouldNotifyPaired: true,
|
||||
shouldDeny: false,
|
||||
},
|
||||
{
|
||||
@@ -345,7 +333,6 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
||||
removePendingMediaGroupMessages: () => {},
|
||||
removeQueuedTelegramTurnsByMessageIds: () => 0,
|
||||
handleAuthorizedTelegramReactionUpdate: async () => {},
|
||||
pairTelegramUserIfNeeded: async () => true,
|
||||
answerCallbackQuery: async () => {},
|
||||
handleAuthorizedTelegramCallbackQuery: async () => {},
|
||||
sendTextReply: async (chatId, replyToMessageId, text) => {
|
||||
@@ -358,9 +345,8 @@ test("Update runtime handles callback deny and message pair flows", async () =>
|
||||
},
|
||||
);
|
||||
assert.deepEqual(events, [
|
||||
"pair:1",
|
||||
"denied:1",
|
||||
"answer:cb:This bot is not authorized for your account.",
|
||||
"reply:7:9:Telegram bridge paired with this account.",
|
||||
"message",
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user