@prajwalshete/telegram-plugin 0.0.0-stage → 0.1.0
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/CHANGELOG.md +87 -0
- package/LICENSE +21 -0
- package/README.md +109 -2
- package/dist/adapters/clock.d.ts +3 -0
- package/dist/adapters/clock.js +4 -0
- package/dist/adapters/console-log.d.ts +13 -0
- package/dist/adapters/console-log.js +41 -0
- package/dist/adapters/d1-db.d.ts +5 -0
- package/dist/adapters/d1-db.js +37 -0
- package/dist/adapters/ids.d.ts +5 -0
- package/dist/adapters/ids.js +25 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/keyring.d.ts +50 -0
- package/dist/adapters/keyring.js +174 -0
- package/dist/adapters/metrics.d.ts +28 -0
- package/dist/adapters/metrics.js +48 -0
- package/dist/adapters/r2-files.d.ts +20 -0
- package/dist/adapters/r2-files.js +53 -0
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +45 -0
- package/dist/client/broadcasts.d.ts +35 -0
- package/dist/client/commands.d.ts +23 -0
- package/dist/client/endpoint.d.ts +34 -0
- package/dist/client/events.d.ts +33 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.js +3 -0
- package/dist/client/schedules.d.ts +42 -0
- package/dist/client/schema.d.ts +181 -0
- package/dist/client/send.d.ts +15 -0
- package/dist/client/templates.d.ts +47 -0
- package/dist/contracts/bots.d.ts +129 -0
- package/dist/contracts/bots.js +70 -0
- package/dist/contracts/broadcasts.d.ts +116 -0
- package/dist/contracts/broadcasts.js +63 -0
- package/dist/contracts/chats.d.ts +97 -0
- package/dist/contracts/chats.js +66 -0
- package/dist/contracts/commands.d.ts +52 -0
- package/dist/contracts/commands.js +53 -0
- package/dist/contracts/common.d.ts +38 -0
- package/dist/contracts/common.js +41 -0
- package/dist/contracts/deliveries.d.ts +103 -0
- package/dist/contracts/deliveries.js +74 -0
- package/dist/contracts/destinations.d.ts +24 -0
- package/dist/contracts/destinations.js +28 -0
- package/dist/contracts/events.d.ts +165 -0
- package/dist/contracts/events.js +98 -0
- package/dist/contracts/features.d.ts +20 -0
- package/dist/contracts/features.js +21 -0
- package/dist/contracts/health.d.ts +55 -0
- package/dist/contracts/health.js +58 -0
- package/dist/contracts/index.d.ts +22 -0
- package/dist/contracts/index.js +22 -0
- package/dist/contracts/labels.d.ts +38 -0
- package/dist/contracts/labels.js +52 -0
- package/dist/contracts/links.d.ts +51 -0
- package/dist/contracts/links.js +50 -0
- package/dist/contracts/meta.d.ts +132 -0
- package/dist/contracts/meta.js +85 -0
- package/dist/contracts/permissions.d.ts +24 -0
- package/dist/contracts/permissions.js +27 -0
- package/dist/contracts/problems.d.ts +106 -0
- package/dist/contracts/problems.js +101 -0
- package/dist/contracts/recipients.d.ts +36 -0
- package/dist/contracts/recipients.js +22 -0
- package/dist/contracts/schedules.d.ts +276 -0
- package/dist/contracts/schedules.js +143 -0
- package/dist/contracts/send.d.ts +126 -0
- package/dist/contracts/send.js +69 -0
- package/dist/contracts/subscriptions.d.ts +38 -0
- package/dist/contracts/subscriptions.js +31 -0
- package/dist/contracts/templates.d.ts +358 -0
- package/dist/contracts/templates.js +174 -0
- package/dist/contracts/users.d.ts +15 -0
- package/dist/contracts/users.js +19 -0
- package/dist/core/guards.d.ts +22 -0
- package/dist/core/guards.js +47 -0
- package/dist/core/html/escape.d.ts +2 -0
- package/dist/core/html/escape.js +36 -0
- package/dist/core/html/tokenize.js +65 -0
- package/dist/core/index.d.ts +18 -0
- package/dist/core/pacing.d.ts +1 -0
- package/dist/core/pacing.js +86 -0
- package/dist/core/random.d.ts +2 -0
- package/dist/core/random.js +38 -0
- package/dist/core/recipients.d.ts +1 -0
- package/dist/core/recipients.js +86 -0
- package/dist/core/schedule/describe.d.ts +3 -0
- package/dist/core/schedule/describe.js +74 -0
- package/dist/core/schedule/index.d.ts +4 -0
- package/dist/core/schedule/policy.d.ts +1 -0
- package/dist/core/schedule/policy.js +37 -0
- package/dist/core/schedule/rule.d.ts +25 -0
- package/dist/core/schedule/rule.js +142 -0
- package/dist/core/schedule/tz.d.ts +2 -0
- package/dist/core/schedule/tz.js +59 -0
- package/dist/core/split/plain.d.ts +2 -0
- package/dist/core/split/plain.js +23 -0
- package/dist/core/split/split.d.ts +15 -0
- package/dist/core/split/split.js +158 -0
- package/dist/core/template/ast.d.ts +58 -0
- package/dist/core/template/ast.js +5 -0
- package/dist/core/template/expr.js +108 -0
- package/dist/core/template/filters.d.ts +20 -0
- package/dist/core/template/filters.js +127 -0
- package/dist/core/template/format.d.ts +17 -0
- package/dist/core/template/format.js +88 -0
- package/dist/core/template/index.d.ts +20 -0
- package/dist/core/template/index.js +29 -0
- package/dist/core/template/parse.d.ts +8 -0
- package/dist/core/template/parse.js +369 -0
- package/dist/core/template/render.d.ts +17 -0
- package/dist/core/template/render.js +118 -0
- package/dist/core/template/shape.d.ts +28 -0
- package/dist/core/template/shape.js +151 -0
- package/dist/core/template/tags.js +105 -0
- package/dist/core/template/validate.d.ts +10 -0
- package/dist/core/template/validate.js +144 -0
- package/dist/core/template/variables.d.ts +15 -0
- package/dist/core/template/variables.js +41 -0
- package/dist/do/index.d.ts +3 -0
- package/dist/do/index.js +3 -0
- package/dist/do/telegram-bot-do.d.ts +45 -0
- package/dist/do/telegram-bot-do.js +219 -0
- package/dist/engine/actions/callback.js +256 -0
- package/dist/engine/actions/tokens.d.ts +2 -0
- package/dist/engine/actions/tokens.js +51 -0
- package/dist/engine/bot-api.js +20 -0
- package/dist/engine/bot-features.js +39 -0
- package/dist/engine/chat-state.js +66 -0
- package/dist/engine/content/audience.js +54 -0
- package/dist/engine/content/buttons.d.ts +1 -0
- package/dist/engine/content/buttons.js +113 -0
- package/dist/engine/content/fanout.js +284 -0
- package/dist/engine/content/intersect.d.ts +6 -0
- package/dist/engine/content/intersect.js +26 -0
- package/dist/engine/content/providers.d.ts +6 -0
- package/dist/engine/content/providers.js +142 -0
- package/dist/engine/content/recipients.js +128 -0
- package/dist/engine/content/render.js +138 -0
- package/dist/engine/content/reply.js +156 -0
- package/dist/engine/content/report-files.js +78 -0
- package/dist/engine/content/template-rows.js +74 -0
- package/dist/engine/dialogs/dialog.d.ts +2 -0
- package/dist/engine/dialogs/dialog.js +375 -0
- package/dist/engine/dispatch/builtins.js +198 -0
- package/dist/engine/dispatch/commands.js +194 -0
- package/dist/engine/dispatch/context.d.ts +2 -0
- package/dist/engine/dispatch/context.js +194 -0
- package/dist/engine/dispatch/dispatcher.js +66 -0
- package/dist/engine/dispatch/handlers.d.ts +1 -0
- package/dist/engine/dispatch/handlers.js +103 -0
- package/dist/engine/dispatch/lifecycle.js +85 -0
- package/dist/engine/dispatch/link-chat.js +31 -0
- package/dist/engine/dispatch/linking.js +273 -0
- package/dist/engine/errors.d.ts +60 -0
- package/dist/engine/errors.js +72 -0
- package/dist/engine/events/filters.d.ts +5 -0
- package/dist/engine/events/filters.js +82 -0
- package/dist/engine/events/kick.js +30 -0
- package/dist/engine/events/payload.d.ts +3 -0
- package/dist/engine/events/payload.js +16 -0
- package/dist/engine/events/tick.js +208 -0
- package/dist/engine/i18n.d.ts +12 -0
- package/dist/engine/i18n.js +55 -0
- package/dist/engine/inbound/files.js +121 -0
- package/dist/engine/inbound/handler.js +46 -0
- package/dist/engine/jobs.d.ts +32 -0
- package/dist/engine/jobs.js +85 -0
- package/dist/engine/link-codes.js +46 -0
- package/dist/engine/metrics.js +12 -0
- package/dist/engine/options.d.ts +369 -0
- package/dist/engine/options.js +81 -0
- package/dist/engine/outbox/dead-letter.js +78 -0
- package/dist/engine/outbox/do-registry.d.ts +10 -0
- package/dist/engine/outbox/do-registry.js +10 -0
- package/dist/engine/outbox/do-transport.d.ts +33 -0
- package/dist/engine/outbox/do-transport.js +73 -0
- package/dist/engine/outbox/drain.d.ts +13 -0
- package/dist/engine/outbox/drain.js +381 -0
- package/dist/engine/outbox/enqueue.d.ts +1 -0
- package/dist/engine/outbox/enqueue.js +63 -0
- package/dist/engine/outbox/select.js +24 -0
- package/dist/engine/outbox/send.js +362 -0
- package/dist/engine/outbox/transport.d.ts +35 -0
- package/dist/engine/outbox/transport.js +175 -0
- package/dist/engine/outbox/wake.js +28 -0
- package/dist/engine/registry.d.ts +502 -0
- package/dist/engine/registry.js +258 -0
- package/dist/engine/rows.d.ts +25 -0
- package/dist/engine/rows.js +131 -0
- package/dist/engine/runtime.d.ts +95 -0
- package/dist/engine/runtime.js +149 -0
- package/dist/engine/schedules/fire.js +208 -0
- package/dist/engine/schedules/resume.js +75 -0
- package/dist/engine/schedules/rows.js +69 -0
- package/dist/engine/schedules/tick.js +30 -0
- package/dist/engine/schedules/wake.js +50 -0
- package/dist/engine/state.d.ts +11 -0
- package/dist/engine/state.js +78 -0
- package/dist/locales/en.js +339 -0
- package/dist/locales/en.json +341 -0
- package/dist/locales/mr.js +339 -0
- package/dist/locales/mr.json +341 -0
- package/dist/ports/db.d.ts +22 -0
- package/dist/ports/index.d.ts +4 -0
- package/dist/ports/ports.d.ts +257 -0
- package/dist/ports/sql.d.ts +39 -0
- package/dist/ports/sql.js +76 -0
- package/dist/renderers/csv.d.ts +14 -0
- package/dist/renderers/csv.js +51 -0
- package/dist/renderers/html.d.ts +44 -0
- package/dist/renderers/html.js +81 -0
- package/dist/renderers/index.d.ts +5 -0
- package/dist/renderers/index.js +4 -0
- package/dist/renderers/table.d.ts +30 -0
- package/dist/renderers/table.js +114 -0
- package/dist/routes/admin.js +146 -0
- package/dist/routes/app.d.ts +3111 -0
- package/dist/routes/app.js +9 -0
- package/dist/routes/broadcasts.js +37 -0
- package/dist/routes/commands.js +35 -0
- package/dist/routes/content.js +12 -0
- package/dist/routes/events.js +36 -0
- package/dist/routes/http.d.ts +30 -0
- package/dist/routes/http.js +223 -0
- package/dist/routes/me.js +59 -0
- package/dist/routes/schedules.js +62 -0
- package/dist/routes/send.js +24 -0
- package/dist/routes/templates.js +70 -0
- package/dist/routes/webhook.js +147 -0
- package/dist/schema/columns.js +307 -0
- package/dist/schema/ddl.d.ts +7 -0
- package/dist/schema/ddl.js +45 -0
- package/dist/schema/index.d.ts +4255 -0
- package/dist/schema/index.js +44 -0
- package/dist/schema/reference.sql +359 -0
- package/dist/schema/tables.d.ts +4243 -0
- package/dist/schema/tables.js +327 -0
- package/dist/schema/version.d.ts +5 -0
- package/dist/schema/version.js +7 -0
- package/dist/server/index.d.ts +55 -0
- package/dist/server/index.js +46 -0
- package/dist/server/plugin.d.ts +81 -0
- package/dist/server/plugin.js +247 -0
- package/dist/server/version.d.ts +2 -0
- package/dist/server/version.js +4 -0
- package/dist/services/ai-tool.d.ts +26 -0
- package/dist/services/ai-tool.js +148 -0
- package/dist/services/bots.js +300 -0
- package/dist/services/broadcasts.js +390 -0
- package/dist/services/chats.js +83 -0
- package/dist/services/cleanup.js +83 -0
- package/dist/services/commands.js +140 -0
- package/dist/services/deliveries.js +53 -0
- package/dist/services/destinations.js +91 -0
- package/dist/services/event-rules.js +229 -0
- package/dist/services/events.js +20 -0
- package/dist/services/idempotency.js +64 -0
- package/dist/services/links.js +200 -0
- package/dist/services/menu.js +95 -0
- package/dist/services/ops.js +90 -0
- package/dist/services/rekey.js +63 -0
- package/dist/services/reports.js +87 -0
- package/dist/services/schedules.js +214 -0
- package/dist/services/send.d.ts +14 -0
- package/dist/services/send.js +421 -0
- package/dist/services/subscriptions.js +28 -0
- package/dist/services/templates.js +259 -0
- package/dist/telegram/classify.d.ts +30 -0
- package/dist/telegram/classify.js +164 -0
- package/dist/telegram/client.d.ts +73 -0
- package/dist/telegram/client.js +135 -0
- package/dist/telegram/errors.d.ts +37 -0
- package/dist/telegram/errors.js +62 -0
- package/dist/telegram/inbound.d.ts +40 -0
- package/dist/telegram/inbound.js +72 -0
- package/dist/telegram/index.d.ts +8 -0
- package/dist/telegram/multipart.d.ts +5 -0
- package/dist/telegram/multipart.js +47 -0
- package/dist/telegram/types.d.ts +271 -0
- package/dist/telegram/update.d.ts +833 -0
- package/dist/telegram/update.js +129 -0
- package/dist/testing/fake-html.d.ts +9 -0
- package/dist/testing/fake-html.js +83 -0
- package/dist/testing/fake-telegram.d.ts +206 -0
- package/dist/testing/fake-telegram.js +856 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js +7 -0
- package/dist/testing/reference-sql.d.ts +4 -0
- package/dist/testing/reference-sql.js +8 -0
- package/dist/testing/test-plugin.d.ts +126 -0
- package/dist/testing/test-plugin.js +182 -0
- package/dist/testing/updates.d.ts +100 -0
- package/dist/testing/updates.js +212 -0
- package/package.json +113 -3
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { silentLog } from "../adapters/console-log.js";
|
|
2
|
+
import { HtmlCheck, checkTelegramHtml } from "./fake-html.js";
|
|
3
|
+
import { BotRef, FakeBotInfo, FakeBotInit, FakeCall, FakeChatInit, FakeChatStatus, FakeFailure, FakeSentFile, FakeSentMessage, FakeTelegram, FakeTelegramOptions, FakeWebhook } from "./fake-telegram.js";
|
|
4
|
+
import { REFERENCE_SQL_FILE, splitSqlStatements } from "./reference-sql.js";
|
|
5
|
+
import { CapturedLog, TestActor, TestClock, TestPlugin, TestPluginOptions, TestUser, createTestPlugin, memoryLog } from "./test-plugin.js";
|
|
6
|
+
import { FAKE_BOT_ID, FAKE_BOT_USERNAME, FAKE_GROUP_ID, UpdateBuilderOptions, UpdateBuilders, commandEntities, createUpdateBuilders, fakeBotUser, fakeUpdate, fakeUser } from "./updates.js";
|
|
7
|
+
export { BotRef, type CapturedLog, FAKE_BOT_ID, FAKE_BOT_USERNAME, FAKE_GROUP_ID, FakeBotInfo, FakeBotInit, FakeCall, FakeChatInit, FakeChatStatus, FakeFailure, FakeSentFile, FakeSentMessage, FakeTelegram, FakeTelegramOptions, FakeWebhook, type HtmlCheck, REFERENCE_SQL_FILE, type TestActor, TestClock, type TestPlugin, type TestPluginOptions, type TestUser, UpdateBuilderOptions, UpdateBuilders, checkTelegramHtml, commandEntities, createTestPlugin, createUpdateBuilders, fakeBotUser, fakeUpdate, fakeUser, memoryLog, silentLog, splitSqlStatements };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { silentLog } from "../adapters/console-log.js";
|
|
2
|
+
import { checkTelegramHtml } from "./fake-html.js";
|
|
3
|
+
import { FakeTelegram } from "./fake-telegram.js";
|
|
4
|
+
import { REFERENCE_SQL_FILE, splitSqlStatements } from "./reference-sql.js";
|
|
5
|
+
import { TestClock, createTestPlugin, memoryLog } from "./test-plugin.js";
|
|
6
|
+
import { FAKE_BOT_ID, FAKE_BOT_USERNAME, FAKE_GROUP_ID, commandEntities, createUpdateBuilders, fakeBotUser, fakeUpdate, fakeUser } from "./updates.js";
|
|
7
|
+
export { FAKE_BOT_ID, FAKE_BOT_USERNAME, FAKE_GROUP_ID, FakeTelegram, REFERENCE_SQL_FILE, TestClock, checkTelegramHtml, commandEntities, createTestPlugin, createUpdateBuilders, fakeBotUser, fakeUpdate, fakeUser, memoryLog, silentLog, splitSqlStatements };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Package-relative path of the reference schema (`@prajwalshete/telegram-plugin/schema/reference.sql`). */
|
|
2
|
+
export declare const REFERENCE_SQL_FILE = "@prajwalshete/telegram-plugin/schema/reference.sql";
|
|
3
|
+
/** Splits reference SQL into statements (for `D1Database.batch` or `applyD1Migrations`). */
|
|
4
|
+
export declare function splitSqlStatements(sqlText: string): string[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// src/testing/reference-sql.ts
|
|
2
|
+
/** Package-relative path of the reference schema (`@prajwalshete/telegram-plugin/schema/reference.sql`). */
|
|
3
|
+
const REFERENCE_SQL_FILE = "@prajwalshete/telegram-plugin/schema/reference.sql";
|
|
4
|
+
/** Splits reference SQL into statements (for `D1Database.batch` or `applyD1Migrations`). */
|
|
5
|
+
function splitSqlStatements(sqlText) {
|
|
6
|
+
return sqlText.split(/;\s*(?:\n|$)/).map((s) => s.split("\n").filter((l) => !l.trim().startsWith("--")).join("\n").trim()).filter((s) => s.length > 0);
|
|
7
|
+
}
|
|
8
|
+
export { REFERENCE_SQL_FILE, splitSqlStatements };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { LogFields, LogPort } from "../ports/ports.js";
|
|
2
|
+
import { TgUpdate } from "../telegram/update.js";
|
|
3
|
+
import { TelegramLogLevel, TelegramPluginOptions } from "../engine/options.js";
|
|
4
|
+
import { TelegramPlugin } from "../server/plugin.js";
|
|
5
|
+
import { BotRef, FakeTelegram } from "./fake-telegram.js";
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
/** The actor type of `createTestPlugin` (users from `users`, group / role audiences synthesized). */
|
|
8
|
+
export interface TestActor {
|
|
9
|
+
/** User id, or null for an audience actor. */
|
|
10
|
+
userId: string | null;
|
|
11
|
+
/** Role (`"group"` for a group audience without a role). */
|
|
12
|
+
role: string;
|
|
13
|
+
/** Display name (default the user id). */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Preferred locale. */
|
|
16
|
+
locale?: string;
|
|
17
|
+
/** Plugin / host permission names, or `"*"` for all. */
|
|
18
|
+
permissions: readonly string[] | "*";
|
|
19
|
+
}
|
|
20
|
+
/** One entry of the test user table. */
|
|
21
|
+
export interface TestUser {
|
|
22
|
+
/** Role (looked up in `roles` for permissions). */
|
|
23
|
+
role: string;
|
|
24
|
+
/** Display name. Default the user id. */
|
|
25
|
+
name?: string;
|
|
26
|
+
/** Preferred locale. */
|
|
27
|
+
locale?: string;
|
|
28
|
+
/** Overrides the role's permissions. */
|
|
29
|
+
permissions?: readonly string[] | "*";
|
|
30
|
+
/** Disabled users resolve to no actor (as a revoked host user would). */
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Epoch-ms clock that only moves when told to; `sleep` advances it (so drain pacing runs instantly). */
|
|
34
|
+
export declare class TestClock {
|
|
35
|
+
#private;
|
|
36
|
+
constructor(start?: number);
|
|
37
|
+
/** Current time (epoch ms UTC). */
|
|
38
|
+
now: () => number;
|
|
39
|
+
/** Moves the clock forward by `ms` (negative values are ignored); returns the new time. */
|
|
40
|
+
advance(ms: number): number;
|
|
41
|
+
/** Sets the clock to `ms` (may go backwards). */
|
|
42
|
+
set(ms: number): void;
|
|
43
|
+
/** Advances the clock by `ms` and resolves at once. */
|
|
44
|
+
sleep: (ms: number) => Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
/** One line kept by `memoryLog`. */
|
|
47
|
+
export interface CapturedLog {
|
|
48
|
+
/** Log level. */
|
|
49
|
+
level: "debug" | "info" | "warn" | "error";
|
|
50
|
+
/** Event name (`tg.send`, …). */
|
|
51
|
+
evt: string;
|
|
52
|
+
/** Structured fields. */
|
|
53
|
+
fields: LogFields;
|
|
54
|
+
}
|
|
55
|
+
/** A logger that keeps every line (to assert on events, or to scan for secrets). */
|
|
56
|
+
export declare function memoryLog(): LogPort & {
|
|
57
|
+
lines: CapturedLog[];
|
|
58
|
+
text(): string;
|
|
59
|
+
};
|
|
60
|
+
/** Options of `createTestPlugin`. */
|
|
61
|
+
export interface TestPluginOptions<E> {
|
|
62
|
+
/** Worker env with the D1 binding (`env.DB`), with the plugin's reference SQL applied. */
|
|
63
|
+
env: E;
|
|
64
|
+
/** Bot API fake to use. Default a new `FakeTelegram` on `clock`. */
|
|
65
|
+
fake?: FakeTelegram;
|
|
66
|
+
/** Clock to use. Default a new `TestClock` (2026-10-25 09:00 UTC). */
|
|
67
|
+
clock?: TestClock;
|
|
68
|
+
/** Role → permissions. Default: `owner` has everything, `staff` only `telegram.self.link`. */
|
|
69
|
+
roles?: Record<string, readonly string[] | "*">;
|
|
70
|
+
/** User id → user. Default: `owner-1` (owner) and `staff-1` (staff). */
|
|
71
|
+
users?: Record<string, TestUser>;
|
|
72
|
+
/** Default `https://app.example.com/api/telegram`; null = polling mode. */
|
|
73
|
+
publicBaseUrl?: string | null;
|
|
74
|
+
/** `env.appEnv`. Default `"test"` (the dev polling route is available). */
|
|
75
|
+
appEnv?: string;
|
|
76
|
+
/** Pass the queue binding to use the queue transport. */
|
|
77
|
+
queue?: Queue<unknown>;
|
|
78
|
+
/** Pass a TelegramBotDO namespace to use the DO transport. */
|
|
79
|
+
botDO?: DurableObjectNamespace;
|
|
80
|
+
/** `TELEGRAM_KEYRING` JSON. Default a fresh `generateKeyringSecret({ kid: "k1" })`. */
|
|
81
|
+
keyring?: string;
|
|
82
|
+
/** Default `silentLog` (tests stay quiet); pass `memoryLog()` to assert on events. */
|
|
83
|
+
log?: LogPort | TelegramLogLevel;
|
|
84
|
+
/** Anything else `createTelegramPlugin` accepts (features, limits, hooks, registries…). */
|
|
85
|
+
options?: Partial<Omit<TelegramPluginOptions<E, TestActor>, "env" | "ports">> & {
|
|
86
|
+
ports?: Partial<TelegramPluginOptions<E, TestActor>["ports"]>;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** What `createTestPlugin` returns. */
|
|
90
|
+
export interface TestPlugin<E> {
|
|
91
|
+
/** The real plugin, wired to the fakes. */
|
|
92
|
+
plugin: TelegramPlugin<E, TestActor>;
|
|
93
|
+
/** The Bot API fake (inspect sent messages, script errors). */
|
|
94
|
+
fake: FakeTelegram;
|
|
95
|
+
/** The controllable clock. */
|
|
96
|
+
clock: TestClock;
|
|
97
|
+
/** The keyring secret in use. */
|
|
98
|
+
keyring: string;
|
|
99
|
+
/** The user table (mutate it to disable users or change roles). */
|
|
100
|
+
users: Record<string, TestUser>;
|
|
101
|
+
/** Calls the plugin routes as a user (header `x-test-user`), with an execution context `flush()` awaits. */
|
|
102
|
+
request(path: string, init?: RequestInit & {
|
|
103
|
+
as?: string | null;
|
|
104
|
+
json?: unknown;
|
|
105
|
+
}): Promise<Response>;
|
|
106
|
+
/** Delivers an update the way Telegram would (registered webhook URL + secret header). */
|
|
107
|
+
webhook(update: TgUpdate, o?: {
|
|
108
|
+
bot?: BotRef;
|
|
109
|
+
secret?: string | null;
|
|
110
|
+
}): Promise<Response>;
|
|
111
|
+
/** The host app the plugin routes are mounted on (`/api/telegram`). */
|
|
112
|
+
host: Hono;
|
|
113
|
+
/** Waits for all `waitUntil` work (webhook processing, kicks). */
|
|
114
|
+
flush(): Promise<void>;
|
|
115
|
+
/** The execution context passed to every request (collects `waitUntil` work for `flush()`). */
|
|
116
|
+
exec: ExecutionContext;
|
|
117
|
+
/** The live actor of a user id, or null when unknown or disabled. */
|
|
118
|
+
actor(userId: string): TestActor | null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A plugin on FakeTelegram (no network), a `TestClock` and a small user/role table. Callers pick the user with
|
|
122
|
+
* `request(path, { as: userId })`; the `x-test-user` header is the test session. Test use only.
|
|
123
|
+
*/
|
|
124
|
+
export declare function createTestPlugin<E extends {
|
|
125
|
+
DB: D1Database;
|
|
126
|
+
}>(opts: TestPluginOptions<E>): TestPlugin<E>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { silentLog } from "../adapters/console-log.js";
|
|
2
|
+
import { generateKeyringSecret } from "../adapters/keyring.js";
|
|
3
|
+
import { fetchTelegram } from "../telegram/client.js";
|
|
4
|
+
import { createTelegramPlugin } from "../server/plugin.js";
|
|
5
|
+
import { FakeTelegram } from "./fake-telegram.js";
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
// src/testing/test-plugin.ts
|
|
8
|
+
const BASE = "https://app.example.com/api/telegram";
|
|
9
|
+
/** Epoch-ms clock that only moves when told to; `sleep` advances it (so drain pacing runs instantly). */
|
|
10
|
+
var TestClock = class {
|
|
11
|
+
#now;
|
|
12
|
+
constructor(start = Date.UTC(2026, 9, 25, 9, 0, 0)) {
|
|
13
|
+
this.#now = start;
|
|
14
|
+
}
|
|
15
|
+
/** Current time (epoch ms UTC). */
|
|
16
|
+
now = () => this.#now;
|
|
17
|
+
/** Moves the clock forward by `ms` (negative values are ignored); returns the new time. */
|
|
18
|
+
advance(ms) {
|
|
19
|
+
this.#now += Math.max(0, ms);
|
|
20
|
+
return this.#now;
|
|
21
|
+
}
|
|
22
|
+
/** Sets the clock to `ms` (may go backwards). */
|
|
23
|
+
set(ms) {
|
|
24
|
+
this.#now = ms;
|
|
25
|
+
}
|
|
26
|
+
/** Advances the clock by `ms` and resolves at once. */
|
|
27
|
+
sleep = async (ms) => {
|
|
28
|
+
this.advance(ms);
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/** A logger that keeps every line (to assert on events, or to scan for secrets). */
|
|
32
|
+
function memoryLog() {
|
|
33
|
+
const lines = [];
|
|
34
|
+
const add = (level) => (evt, fields = {}) => {
|
|
35
|
+
lines.push({
|
|
36
|
+
level,
|
|
37
|
+
evt,
|
|
38
|
+
fields
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
lines,
|
|
43
|
+
debug: add("debug"),
|
|
44
|
+
info: add("info"),
|
|
45
|
+
warn: add("warn"),
|
|
46
|
+
error: add("error"),
|
|
47
|
+
text: () => lines.map((l) => JSON.stringify(l)).join("\n")
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const DEFAULT_ROLES = {
|
|
51
|
+
owner: "*",
|
|
52
|
+
staff: ["telegram.self.link"]
|
|
53
|
+
};
|
|
54
|
+
const DEFAULT_USERS = {
|
|
55
|
+
"owner-1": {
|
|
56
|
+
role: "owner",
|
|
57
|
+
name: "Owner One"
|
|
58
|
+
},
|
|
59
|
+
"staff-1": {
|
|
60
|
+
role: "staff",
|
|
61
|
+
name: "Staff One"
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* A plugin on FakeTelegram (no network), a `TestClock` and a small user/role table. Callers pick the user with
|
|
66
|
+
* `request(path, { as: userId })`; the `x-test-user` header is the test session. Test use only.
|
|
67
|
+
*/
|
|
68
|
+
function createTestPlugin(opts) {
|
|
69
|
+
const clock = opts.clock ?? new TestClock();
|
|
70
|
+
const fake = opts.fake ?? new FakeTelegram({ now: () => clock.now() });
|
|
71
|
+
const roles = {
|
|
72
|
+
...DEFAULT_ROLES,
|
|
73
|
+
...opts.roles
|
|
74
|
+
};
|
|
75
|
+
const users = {
|
|
76
|
+
...DEFAULT_USERS,
|
|
77
|
+
...opts.users
|
|
78
|
+
};
|
|
79
|
+
const keyring = opts.keyring ?? generateKeyringSecret({ kid: "k1" });
|
|
80
|
+
const permsOf = (role, override) => override ?? roles[role] ?? [];
|
|
81
|
+
const actor = (userId) => {
|
|
82
|
+
const u = users[userId];
|
|
83
|
+
if (!u || u.disabled) return null;
|
|
84
|
+
return {
|
|
85
|
+
userId,
|
|
86
|
+
role: u.role,
|
|
87
|
+
name: u.name ?? userId,
|
|
88
|
+
permissions: permsOf(u.role, u.permissions),
|
|
89
|
+
...u.locale ? { locale: u.locale } : {}
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
const audienceActor = (a) => "role" in a ? {
|
|
93
|
+
userId: null,
|
|
94
|
+
role: a.role,
|
|
95
|
+
name: a.role,
|
|
96
|
+
permissions: permsOf(a.role)
|
|
97
|
+
} : {
|
|
98
|
+
userId: null,
|
|
99
|
+
role: "group",
|
|
100
|
+
name: "group",
|
|
101
|
+
permissions: []
|
|
102
|
+
};
|
|
103
|
+
const pending = [];
|
|
104
|
+
const exec = {
|
|
105
|
+
waitUntil(p) {
|
|
106
|
+
pending.push(p);
|
|
107
|
+
},
|
|
108
|
+
passThroughOnException() {},
|
|
109
|
+
props: {}
|
|
110
|
+
};
|
|
111
|
+
const extra = opts.options ?? {};
|
|
112
|
+
const plugin = createTelegramPlugin({
|
|
113
|
+
...extra,
|
|
114
|
+
env: (env) => ({
|
|
115
|
+
db: env.DB,
|
|
116
|
+
keyring,
|
|
117
|
+
queue: opts.queue,
|
|
118
|
+
botDO: opts.botDO,
|
|
119
|
+
publicBaseUrl: opts.publicBaseUrl === null ? void 0 : opts.publicBaseUrl ?? BASE,
|
|
120
|
+
appEnv: opts.appEnv ?? "test"
|
|
121
|
+
}),
|
|
122
|
+
ports: {
|
|
123
|
+
actor: {
|
|
124
|
+
fromRequest: (c) => {
|
|
125
|
+
const id = c.req.header("x-test-user");
|
|
126
|
+
return id ? actor(id) : null;
|
|
127
|
+
},
|
|
128
|
+
forUser: (id) => actor(id),
|
|
129
|
+
forAudience: (a) => audienceActor(a),
|
|
130
|
+
describe: (a) => ({
|
|
131
|
+
userId: a.userId,
|
|
132
|
+
role: a.role,
|
|
133
|
+
name: a.name,
|
|
134
|
+
locale: a.locale
|
|
135
|
+
})
|
|
136
|
+
},
|
|
137
|
+
authorize: (a, permission) => a.permissions === "*" || a.permissions.includes(permission),
|
|
138
|
+
clock: {
|
|
139
|
+
now: clock.now,
|
|
140
|
+
sleep: clock.sleep
|
|
141
|
+
},
|
|
142
|
+
log: opts.log ?? silentLog,
|
|
143
|
+
...extra.ports
|
|
144
|
+
},
|
|
145
|
+
telegram: extra.telegram ?? fetchTelegram({ fetch: fake.fetch })
|
|
146
|
+
});
|
|
147
|
+
const host = new Hono();
|
|
148
|
+
host.route("/api/telegram", plugin.routes);
|
|
149
|
+
return {
|
|
150
|
+
plugin,
|
|
151
|
+
host,
|
|
152
|
+
fake,
|
|
153
|
+
clock,
|
|
154
|
+
keyring,
|
|
155
|
+
users,
|
|
156
|
+
exec,
|
|
157
|
+
actor,
|
|
158
|
+
async request(path, init = {}) {
|
|
159
|
+
const { as, json, ...rest } = init;
|
|
160
|
+
const headers = new Headers(rest.headers);
|
|
161
|
+
if (as) headers.set("x-test-user", as);
|
|
162
|
+
let body = rest.body;
|
|
163
|
+
if (json !== void 0) {
|
|
164
|
+
headers.set("content-type", "application/json");
|
|
165
|
+
body = JSON.stringify(json);
|
|
166
|
+
}
|
|
167
|
+
const url = path.startsWith("http") ? path : `${BASE}${path}`;
|
|
168
|
+
return host.fetch(new Request(url, {
|
|
169
|
+
...rest,
|
|
170
|
+
headers,
|
|
171
|
+
...body === void 0 ? {} : { body }
|
|
172
|
+
}), opts.env, exec);
|
|
173
|
+
},
|
|
174
|
+
async webhook(update, o = {}) {
|
|
175
|
+
return host.fetch(fake.webhookRequest(update, o), opts.env, exec);
|
|
176
|
+
},
|
|
177
|
+
async flush() {
|
|
178
|
+
while (pending.length > 0) await Promise.allSettled(pending.splice(0));
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
export { TestClock, createTestPlugin, memoryLog };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { TgMessageEntity, TgUpdate, TgUser } from "../telegram/update.js";
|
|
2
|
+
/** Username of the default fake bot. */
|
|
3
|
+
export declare const FAKE_BOT_USERNAME = "FakeTestBot";
|
|
4
|
+
/** Telegram id of the default fake bot. */
|
|
5
|
+
export declare const FAKE_BOT_ID = 7000000001;
|
|
6
|
+
/** Default supergroup chat id in the builders. */
|
|
7
|
+
export declare const FAKE_GROUP_ID = -1001234567890;
|
|
8
|
+
/** A Telegram user (default: Asha, id 1001, `en`) with overrides. */
|
|
9
|
+
export declare function fakeUser(overrides?: Partial<TgUser>): TgUser;
|
|
10
|
+
/** The fake bot as a Telegram user (`is_bot: true`). */
|
|
11
|
+
export declare function fakeBotUser(overrides?: Partial<TgUser>): TgUser;
|
|
12
|
+
type ChatType = "private" | "group" | "supergroup" | "channel";
|
|
13
|
+
/** `bot_command` entity for text that starts with `/cmd` or `/cmd@bot`. */
|
|
14
|
+
export declare function commandEntities(text: string): TgMessageEntity[] | undefined;
|
|
15
|
+
/** Options for {@link createUpdateBuilders}. */
|
|
16
|
+
export interface UpdateBuilderOptions {
|
|
17
|
+
/** First update_id (default 10000). */
|
|
18
|
+
startId?: number;
|
|
19
|
+
/** Clock for `date` fields (epoch ms). */
|
|
20
|
+
now?: () => number;
|
|
21
|
+
botUsername?: string;
|
|
22
|
+
botId?: number;
|
|
23
|
+
}
|
|
24
|
+
interface MessageBase {
|
|
25
|
+
from?: TgUser;
|
|
26
|
+
chatId?: number;
|
|
27
|
+
threadId?: number;
|
|
28
|
+
}
|
|
29
|
+
/** Builders for realistic updates (commands, text, callbacks, media, membership) with increasing `update_id` / `message_id`. */
|
|
30
|
+
export declare function createUpdateBuilders(opts?: UpdateBuilderOptions): {
|
|
31
|
+
bot: {
|
|
32
|
+
[x: string]: unknown;
|
|
33
|
+
id: number;
|
|
34
|
+
is_bot: boolean;
|
|
35
|
+
first_name: string;
|
|
36
|
+
last_name?: string | undefined;
|
|
37
|
+
username?: string | undefined;
|
|
38
|
+
language_code?: string | undefined;
|
|
39
|
+
};
|
|
40
|
+
/** The id the next builder will use. */
|
|
41
|
+
peekUpdateId: () => number;
|
|
42
|
+
/** A private-chat text (commands get a `bot_command` entity automatically). */
|
|
43
|
+
privateText(text: string, o?: MessageBase): TgUpdate;
|
|
44
|
+
/** Plain text in a group (only replies to the bot reach a privacy-mode bot). */
|
|
45
|
+
groupText(text: string, o?: MessageBase & {
|
|
46
|
+
chatType?: "group" | "supergroup";
|
|
47
|
+
title?: string;
|
|
48
|
+
}): TgUpdate;
|
|
49
|
+
/** `/command@bot args` in a group. `mention: false` sends a bare `/command`. */
|
|
50
|
+
groupCommand(command: string, o?: MessageBase & {
|
|
51
|
+
args?: string;
|
|
52
|
+
mention?: boolean | string;
|
|
53
|
+
chatType?: "group" | "supergroup";
|
|
54
|
+
title?: string;
|
|
55
|
+
isForum?: boolean;
|
|
56
|
+
}): TgUpdate;
|
|
57
|
+
/** Button tap. `inaccessible` gives a message with `date: 0` (too old for the bot to read). */
|
|
58
|
+
callback(data: string, o?: MessageBase & {
|
|
59
|
+
messageId?: number;
|
|
60
|
+
chatType?: ChatType;
|
|
61
|
+
inaccessible?: boolean;
|
|
62
|
+
}): TgUpdate;
|
|
63
|
+
/** The bot's own membership changed (added, removed, blocked by a user, promoted). */
|
|
64
|
+
myChatMember(o: {
|
|
65
|
+
chatId?: number;
|
|
66
|
+
chatType?: ChatType;
|
|
67
|
+
title?: string;
|
|
68
|
+
from?: TgUser;
|
|
69
|
+
oldStatus?: string;
|
|
70
|
+
newStatus: string;
|
|
71
|
+
}): TgUpdate;
|
|
72
|
+
/**
|
|
73
|
+
* Group → supergroup migration. `side: "to"` (default) is the service message in the old group with
|
|
74
|
+
* `migrate_to_chat_id`; `side: "from"` is the one in the new supergroup with `migrate_from_chat_id`.
|
|
75
|
+
*/
|
|
76
|
+
migrate(o: {
|
|
77
|
+
fromChatId: number;
|
|
78
|
+
toChatId: number;
|
|
79
|
+
title?: string;
|
|
80
|
+
side?: "to" | "from";
|
|
81
|
+
from?: TgUser;
|
|
82
|
+
}): TgUpdate;
|
|
83
|
+
photo(o?: MessageBase & {
|
|
84
|
+
fileId?: string;
|
|
85
|
+
caption?: string;
|
|
86
|
+
chatType?: ChatType;
|
|
87
|
+
}): TgUpdate;
|
|
88
|
+
document(o?: MessageBase & {
|
|
89
|
+
fileId?: string;
|
|
90
|
+
fileName?: string;
|
|
91
|
+
mime?: string;
|
|
92
|
+
caption?: string;
|
|
93
|
+
chatType?: ChatType;
|
|
94
|
+
}): TgUpdate;
|
|
95
|
+
};
|
|
96
|
+
/** The builders returned by {@link createUpdateBuilders}. */
|
|
97
|
+
export type UpdateBuilders = ReturnType<typeof createUpdateBuilders>;
|
|
98
|
+
/** Shared default builders (ids keep increasing across a test file). */
|
|
99
|
+
export declare const fakeUpdate: UpdateBuilders;
|
|
100
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// src/testing/updates.ts
|
|
2
|
+
/** Username of the default fake bot. */
|
|
3
|
+
const FAKE_BOT_USERNAME = "FakeTestBot";
|
|
4
|
+
/** Telegram id of the default fake bot. */
|
|
5
|
+
const FAKE_BOT_ID = 7000000001;
|
|
6
|
+
/** Default supergroup chat id in the builders. */
|
|
7
|
+
const FAKE_GROUP_ID = -0xe91e3b12d2;
|
|
8
|
+
/** A Telegram user (default: Asha, id 1001, `en`) with overrides. */
|
|
9
|
+
function fakeUser(overrides = {}) {
|
|
10
|
+
return {
|
|
11
|
+
id: 1001,
|
|
12
|
+
is_bot: false,
|
|
13
|
+
first_name: "Asha",
|
|
14
|
+
username: "asha",
|
|
15
|
+
language_code: "en",
|
|
16
|
+
...overrides
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** The fake bot as a Telegram user (`is_bot: true`). */
|
|
20
|
+
function fakeBotUser(overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
id: FAKE_BOT_ID,
|
|
23
|
+
is_bot: true,
|
|
24
|
+
first_name: "Fake Test",
|
|
25
|
+
username: FAKE_BOT_USERNAME,
|
|
26
|
+
...overrides
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function chatOf(id, type, from, title, isForum) {
|
|
30
|
+
if (type === "private") return {
|
|
31
|
+
id,
|
|
32
|
+
type,
|
|
33
|
+
first_name: from.first_name,
|
|
34
|
+
...from.username === void 0 ? {} : { username: from.username }
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
id,
|
|
38
|
+
type,
|
|
39
|
+
title: title ?? "Site Team",
|
|
40
|
+
...isForum ? { is_forum: true } : {}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** `bot_command` entity for text that starts with `/cmd` or `/cmd@bot`. */
|
|
44
|
+
function commandEntities(text) {
|
|
45
|
+
const m = /^\/[A-Za-z0-9_]{1,32}(?:@[A-Za-z0-9_]{3,64})?(?=\s|$)/.exec(text);
|
|
46
|
+
return m ? [{
|
|
47
|
+
type: "bot_command",
|
|
48
|
+
offset: 0,
|
|
49
|
+
length: m[0].length
|
|
50
|
+
}] : void 0;
|
|
51
|
+
}
|
|
52
|
+
/** Builders for realistic updates (commands, text, callbacks, media, membership) with increasing `update_id` / `message_id`. */
|
|
53
|
+
function createUpdateBuilders(opts = {}) {
|
|
54
|
+
let nextUpdate = opts.startId ?? 1e4;
|
|
55
|
+
let nextMessage = 1;
|
|
56
|
+
let nextQuery = 1;
|
|
57
|
+
const now = opts.now ?? (() => Date.now());
|
|
58
|
+
const botUsername = opts.botUsername ?? "FakeTestBot";
|
|
59
|
+
const bot = fakeBotUser({
|
|
60
|
+
id: opts.botId ?? 7000000001,
|
|
61
|
+
username: botUsername
|
|
62
|
+
});
|
|
63
|
+
const date = () => Math.floor(now() / 1e3);
|
|
64
|
+
function message(chat, from, extra = {}) {
|
|
65
|
+
const { threadId, ...rest } = extra;
|
|
66
|
+
return {
|
|
67
|
+
message_id: nextMessage++,
|
|
68
|
+
date: date(),
|
|
69
|
+
chat,
|
|
70
|
+
from,
|
|
71
|
+
...threadId === void 0 ? {} : {
|
|
72
|
+
message_thread_id: threadId,
|
|
73
|
+
is_topic_message: true
|
|
74
|
+
},
|
|
75
|
+
...rest
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const update = (body) => ({
|
|
79
|
+
update_id: nextUpdate++,
|
|
80
|
+
...body
|
|
81
|
+
});
|
|
82
|
+
function withText(text) {
|
|
83
|
+
const entities = commandEntities(text);
|
|
84
|
+
return entities ? {
|
|
85
|
+
text,
|
|
86
|
+
entities
|
|
87
|
+
} : { text };
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
bot,
|
|
91
|
+
/** The id the next builder will use. */
|
|
92
|
+
peekUpdateId: () => nextUpdate,
|
|
93
|
+
/** A private-chat text (commands get a `bot_command` entity automatically). */
|
|
94
|
+
privateText(text, o = {}) {
|
|
95
|
+
const from = o.from ?? fakeUser();
|
|
96
|
+
return update({ message: message(chatOf(o.chatId ?? from.id, "private", from), from, {
|
|
97
|
+
...withText(text),
|
|
98
|
+
threadId: o.threadId
|
|
99
|
+
}) });
|
|
100
|
+
},
|
|
101
|
+
/** Plain text in a group (only replies to the bot reach a privacy-mode bot). */
|
|
102
|
+
groupText(text, o = {}) {
|
|
103
|
+
const from = o.from ?? fakeUser();
|
|
104
|
+
const chat = chatOf(o.chatId ?? -0xe91e3b12d2, o.chatType ?? "supergroup", from, o.title);
|
|
105
|
+
return update({ message: message(chat, from, {
|
|
106
|
+
...withText(text),
|
|
107
|
+
threadId: o.threadId
|
|
108
|
+
}) });
|
|
109
|
+
},
|
|
110
|
+
/** `/command@bot args` in a group. `mention: false` sends a bare `/command`. */
|
|
111
|
+
groupCommand(command, o = {}) {
|
|
112
|
+
const from = o.from ?? fakeUser();
|
|
113
|
+
const text = `/${command}${o.mention === false ? "" : `@${typeof o.mention === "string" ? o.mention : botUsername}`}${o.args ? ` ${o.args}` : ""}`;
|
|
114
|
+
const chat = chatOf(o.chatId ?? -0xe91e3b12d2, o.chatType ?? "supergroup", from, o.title, o.isForum);
|
|
115
|
+
return update({ message: message(chat, from, {
|
|
116
|
+
...withText(text),
|
|
117
|
+
threadId: o.threadId
|
|
118
|
+
}) });
|
|
119
|
+
},
|
|
120
|
+
/** Button tap. `inaccessible` gives a message with `date: 0` (too old for the bot to read). */
|
|
121
|
+
callback(data, o = {}) {
|
|
122
|
+
const from = o.from ?? fakeUser();
|
|
123
|
+
const chat = chatOf(o.chatId ?? from.id, o.chatType ?? "private", from);
|
|
124
|
+
const msg = o.inaccessible ? {
|
|
125
|
+
message_id: o.messageId ?? nextMessage++,
|
|
126
|
+
date: 0,
|
|
127
|
+
chat
|
|
128
|
+
} : {
|
|
129
|
+
...message(chat, bot, {
|
|
130
|
+
text: "…",
|
|
131
|
+
threadId: o.threadId
|
|
132
|
+
}),
|
|
133
|
+
...o.messageId === void 0 ? {} : { message_id: o.messageId }
|
|
134
|
+
};
|
|
135
|
+
return update({ callback_query: {
|
|
136
|
+
id: `cbq-${nextQuery++}`,
|
|
137
|
+
from,
|
|
138
|
+
chat_instance: `ci${chat.id}`,
|
|
139
|
+
message: msg,
|
|
140
|
+
data
|
|
141
|
+
} });
|
|
142
|
+
},
|
|
143
|
+
/** The bot's own membership changed (added, removed, blocked by a user, promoted). */
|
|
144
|
+
myChatMember(o) {
|
|
145
|
+
const from = o.from ?? fakeUser();
|
|
146
|
+
const type = o.chatType ?? "supergroup";
|
|
147
|
+
const chat = chatOf(o.chatId ?? (type === "private" ? from.id : -0xe91e3b12d2), type, from, o.title);
|
|
148
|
+
return update({ my_chat_member: {
|
|
149
|
+
chat,
|
|
150
|
+
from,
|
|
151
|
+
date: date(),
|
|
152
|
+
old_chat_member: {
|
|
153
|
+
status: o.oldStatus ?? (o.newStatus === "member" ? "left" : "member"),
|
|
154
|
+
user: bot
|
|
155
|
+
},
|
|
156
|
+
new_chat_member: {
|
|
157
|
+
status: o.newStatus,
|
|
158
|
+
user: bot
|
|
159
|
+
}
|
|
160
|
+
} });
|
|
161
|
+
},
|
|
162
|
+
/**
|
|
163
|
+
* Group → supergroup migration. `side: "to"` (default) is the service message in the old group with
|
|
164
|
+
* `migrate_to_chat_id`; `side: "from"` is the one in the new supergroup with `migrate_from_chat_id`.
|
|
165
|
+
*/
|
|
166
|
+
migrate(o) {
|
|
167
|
+
const from = o.from ?? fakeUser();
|
|
168
|
+
return o.side === "from" ? update({ message: message(chatOf(o.toChatId, "supergroup", from, o.title), from, { migrate_from_chat_id: o.fromChatId }) }) : update({ message: message(chatOf(o.fromChatId, "group", from, o.title), from, { migrate_to_chat_id: o.toChatId }) });
|
|
169
|
+
},
|
|
170
|
+
photo(o = {}) {
|
|
171
|
+
const from = o.from ?? fakeUser();
|
|
172
|
+
const fileId = o.fileId ?? `fake-photo-${nextMessage}`;
|
|
173
|
+
const chat = chatOf(o.chatId ?? from.id, o.chatType ?? "private", from);
|
|
174
|
+
return update({ message: message(chat, from, {
|
|
175
|
+
photo: [{
|
|
176
|
+
file_id: `${fileId}-s`,
|
|
177
|
+
file_unique_id: `${fileId}-us`,
|
|
178
|
+
width: 90,
|
|
179
|
+
height: 90,
|
|
180
|
+
file_size: 1200
|
|
181
|
+
}, {
|
|
182
|
+
file_id: fileId,
|
|
183
|
+
file_unique_id: `${fileId}-u`,
|
|
184
|
+
width: 1280,
|
|
185
|
+
height: 960,
|
|
186
|
+
file_size: 12e4
|
|
187
|
+
}],
|
|
188
|
+
...o.caption === void 0 ? {} : { caption: o.caption },
|
|
189
|
+
threadId: o.threadId
|
|
190
|
+
}) });
|
|
191
|
+
},
|
|
192
|
+
document(o = {}) {
|
|
193
|
+
const from = o.from ?? fakeUser();
|
|
194
|
+
const fileId = o.fileId ?? `fake-doc-${nextMessage}`;
|
|
195
|
+
const chat = chatOf(o.chatId ?? from.id, o.chatType ?? "private", from);
|
|
196
|
+
return update({ message: message(chat, from, {
|
|
197
|
+
document: {
|
|
198
|
+
file_id: fileId,
|
|
199
|
+
file_unique_id: `${fileId}-u`,
|
|
200
|
+
file_name: o.fileName ?? "site-photo.pdf",
|
|
201
|
+
mime_type: o.mime ?? "application/pdf",
|
|
202
|
+
file_size: 2048
|
|
203
|
+
},
|
|
204
|
+
...o.caption === void 0 ? {} : { caption: o.caption },
|
|
205
|
+
threadId: o.threadId
|
|
206
|
+
}) });
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** Shared default builders (ids keep increasing across a test file). */
|
|
211
|
+
const fakeUpdate = createUpdateBuilders();
|
|
212
|
+
export { FAKE_BOT_ID, FAKE_BOT_USERNAME, FAKE_GROUP_ID, commandEntities, createUpdateBuilders, fakeBotUser, fakeUpdate, fakeUser };
|