@krischoichoi/channel-telegram 0.6.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/README.md +129 -0
- package/lib/adapter.d.ts +94 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +417 -0
- package/lib/adapter.js.map +1 -0
- package/lib/api-error.d.ts +74 -0
- package/lib/api-error.d.ts.map +1 -0
- package/lib/api-error.js +103 -0
- package/lib/api-error.js.map +1 -0
- package/lib/config.d.ts +115 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +67 -0
- package/lib/config.js.map +1 -0
- package/lib/definition.d.ts +59 -0
- package/lib/definition.d.ts.map +1 -0
- package/lib/definition.js +158 -0
- package/lib/definition.js.map +1 -0
- package/lib/inbound.d.ts +61 -0
- package/lib/inbound.d.ts.map +1 -0
- package/lib/inbound.js +125 -0
- package/lib/inbound.js.map +1 -0
- package/lib/index.d.ts +52 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +114 -0
- package/lib/index.js.map +1 -0
- package/lib/manifest.d.ts +16 -0
- package/lib/manifest.d.ts.map +1 -0
- package/lib/manifest.js +26 -0
- package/lib/manifest.js.map +1 -0
- package/lib/mapper.d.ts +51 -0
- package/lib/mapper.d.ts.map +1 -0
- package/lib/mapper.js +314 -0
- package/lib/mapper.js.map +1 -0
- package/lib/media-hydrator.d.ts +46 -0
- package/lib/media-hydrator.d.ts.map +1 -0
- package/lib/media-hydrator.js +85 -0
- package/lib/media-hydrator.js.map +1 -0
- package/lib/outbound.d.ts +42 -0
- package/lib/outbound.d.ts.map +1 -0
- package/lib/outbound.js +215 -0
- package/lib/outbound.js.map +1 -0
- package/lib/render/html.d.ts +13 -0
- package/lib/render/html.d.ts.map +1 -0
- package/lib/render/html.js +110 -0
- package/lib/render/html.js.map +1 -0
- package/lib/render/index.d.ts +64 -0
- package/lib/render/index.d.ts.map +1 -0
- package/lib/render/index.js +76 -0
- package/lib/render/index.js.map +1 -0
- package/lib/render/markdown.d.ts +38 -0
- package/lib/render/markdown.d.ts.map +1 -0
- package/lib/render/markdown.js +187 -0
- package/lib/render/markdown.js.map +1 -0
- package/lib/render/plain.d.ts +43 -0
- package/lib/render/plain.d.ts.map +1 -0
- package/lib/render/plain.js +97 -0
- package/lib/render/plain.js.map +1 -0
- package/lib/render/segment.d.ts +39 -0
- package/lib/render/segment.d.ts.map +1 -0
- package/lib/render/segment.js +117 -0
- package/lib/render/segment.js.map +1 -0
- package/lib/rich-message.d.ts +75 -0
- package/lib/rich-message.d.ts.map +1 -0
- package/lib/rich-message.js +11 -0
- package/lib/rich-message.js.map +1 -0
- package/lib/rich-streaming-reply.d.ts +69 -0
- package/lib/rich-streaming-reply.d.ts.map +1 -0
- package/lib/rich-streaming-reply.js +207 -0
- package/lib/rich-streaming-reply.js.map +1 -0
- package/lib/streaming-reply.d.ts +98 -0
- package/lib/streaming-reply.d.ts.map +1 -0
- package/lib/streaming-reply.js +274 -0
- package/lib/streaming-reply.js.map +1 -0
- package/lib/transport.d.ts +38 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +91 -0
- package/lib/transport.js.map +1 -0
- package/lib/upstream.d.ts +174 -0
- package/lib/upstream.d.ts.map +1 -0
- package/lib/upstream.js +452 -0
- package/lib/upstream.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Telegram Bot API error (execution plan §3.1 / §5.4).
|
|
3
|
+
*
|
|
4
|
+
* Telegram's error envelope carries `error_code`/`description` and, for some
|
|
5
|
+
* failures, a `parameters` object (e.g. `retry_after`, `migrate_to_chat_id`).
|
|
6
|
+
* The previous code collapsed every non-ok `sendMessage` into a bare
|
|
7
|
+
* "invalid response" catch-all, which made it impossible to distinguish a
|
|
8
|
+
* rich-formatting parse failure (→ plain fallback) from a 401/403 (→ never
|
|
9
|
+
* fallback), a 429 (→ rate-limit retry) or a 5xx/network failure (→ retry).
|
|
10
|
+
*
|
|
11
|
+
* `TelegramApiError` preserves the original fields and classifies each error
|
|
12
|
+
* into a stable `TelegramErrorKind`. It is constructed at the envelope/post
|
|
13
|
+
* boundary in `HttpTelegramUpstream`, so downstream code (the renderer and the
|
|
14
|
+
* reply engine) can decide retry / fallback / fail without rebuilding the
|
|
15
|
+
* classification from raw numbers.
|
|
16
|
+
*/
|
|
17
|
+
import { ChannelError } from '@krischoichoi/channel-core';
|
|
18
|
+
/**
|
|
19
|
+
* Machine-readable Telegram error class used to drive fallback/retry policy.
|
|
20
|
+
*
|
|
21
|
+
* - `format` — Telegram rejected the formatted payload (bad entity, rich
|
|
22
|
+
* message parse failure). The only kind that may trigger a
|
|
23
|
+
* one-shot plain fallback.
|
|
24
|
+
* - `rate-limit`— HTTP 429; retry after `parameters.retryAfter`.
|
|
25
|
+
* - `auth` — 401 invalid token.
|
|
26
|
+
* - `permission`— 403 the bot lacks permission / was removed; never fallback.
|
|
27
|
+
* - `network` — the HTTP request never got a valid Bot API response (timeout,
|
|
28
|
+
* aborted, transport failure). Not a formatting problem.
|
|
29
|
+
* - `upstream` — 5xx / other genuine Telegram-side server errors. Retry.
|
|
30
|
+
*/
|
|
31
|
+
export type TelegramErrorKind = 'format' | 'rate-limit' | 'auth' | 'permission' | 'network' | 'upstream';
|
|
32
|
+
/** Fields Telegram may return in the error `parameters` object. */
|
|
33
|
+
export interface TelegramErrorParameters {
|
|
34
|
+
retryAfter?: number;
|
|
35
|
+
migrateToChatId?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Classify a Telegram `error_code` (plus optional retry/migration parameters)
|
|
39
|
+
* into a stable `TelegramErrorKind`. This is the single source of truth for
|
|
40
|
+
* fallback / retry policy; no caller should infer the kind from raw numbers.
|
|
41
|
+
*
|
|
42
|
+
* Logic (execution plan §20.9):
|
|
43
|
+
* - explicit retry_after -> rate-limit
|
|
44
|
+
* - 401 -> auth
|
|
45
|
+
* - 403 -> permission
|
|
46
|
+
* - 429 -> rate-limit
|
|
47
|
+
* - 5xx -> upstream
|
|
48
|
+
* - description match addenda -> format (only for 400-class non-authed)
|
|
49
|
+
* - other 400 responses -> upstream (Telegram also uses 400 for chat,
|
|
50
|
+
* message, thread, and button errors; those must never trigger fallback)
|
|
51
|
+
* - unknown / missing code -> upstream (not enough evidence to fallback)
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyTelegramError(errorCode: number | undefined, description: string | undefined, parameters?: TelegramErrorParameters): TelegramErrorKind;
|
|
54
|
+
export interface TelegramApiErrorOptions extends ErrorOptions {
|
|
55
|
+
method?: string;
|
|
56
|
+
errorCode?: number;
|
|
57
|
+
description?: string;
|
|
58
|
+
parameters?: TelegramErrorParameters;
|
|
59
|
+
kind?: TelegramErrorKind;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Structured Telegram error. Extends `ChannelError` so existing adapter-level
|
|
63
|
+
* error mapping (which asserts `ChannelError`) keeps working, while preserving
|
|
64
|
+
* the Telegram-specific original fields and the stable classification.
|
|
65
|
+
*/
|
|
66
|
+
export declare class TelegramApiError extends ChannelError {
|
|
67
|
+
readonly method: string;
|
|
68
|
+
readonly errorCode?: number;
|
|
69
|
+
readonly description?: string;
|
|
70
|
+
readonly parameters?: TelegramErrorParameters;
|
|
71
|
+
readonly kind: TelegramErrorKind;
|
|
72
|
+
constructor(options?: TelegramApiErrorOptions);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=api-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../src/api-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,SAAS,GACT,UAAU,CAAC;AAEf,mEAAmE;AACnE,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAcD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,UAAU,CAAC,EAAE,uBAAuB,GACnC,iBAAiB,CAcnB;AAcD,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;gBAErB,OAAO,GAAE,uBAA4B;CAalD"}
|
package/lib/api-error.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Telegram Bot API error (execution plan §3.1 / §5.4).
|
|
3
|
+
*
|
|
4
|
+
* Telegram's error envelope carries `error_code`/`description` and, for some
|
|
5
|
+
* failures, a `parameters` object (e.g. `retry_after`, `migrate_to_chat_id`).
|
|
6
|
+
* The previous code collapsed every non-ok `sendMessage` into a bare
|
|
7
|
+
* "invalid response" catch-all, which made it impossible to distinguish a
|
|
8
|
+
* rich-formatting parse failure (→ plain fallback) from a 401/403 (→ never
|
|
9
|
+
* fallback), a 429 (→ rate-limit retry) or a 5xx/network failure (→ retry).
|
|
10
|
+
*
|
|
11
|
+
* `TelegramApiError` preserves the original fields and classifies each error
|
|
12
|
+
* into a stable `TelegramErrorKind`. It is constructed at the envelope/post
|
|
13
|
+
* boundary in `HttpTelegramUpstream`, so downstream code (the renderer and the
|
|
14
|
+
* reply engine) can decide retry / fallback / fail without rebuilding the
|
|
15
|
+
* classification from raw numbers.
|
|
16
|
+
*/
|
|
17
|
+
import { ChannelError } from '@krischoichoi/channel-core';
|
|
18
|
+
/** Telegram errors Telegram may legitimately return (by description text). */
|
|
19
|
+
const FORMAT_TEXT_MARKERS = [
|
|
20
|
+
'can\'t parse entities',
|
|
21
|
+
'entity is too long',
|
|
22
|
+
'failed to parse',
|
|
23
|
+
'rich message',
|
|
24
|
+
'message entities',
|
|
25
|
+
'unsupported start tag',
|
|
26
|
+
'unsupported end tag',
|
|
27
|
+
'unclosed start tag',
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Classify a Telegram `error_code` (plus optional retry/migration parameters)
|
|
31
|
+
* into a stable `TelegramErrorKind`. This is the single source of truth for
|
|
32
|
+
* fallback / retry policy; no caller should infer the kind from raw numbers.
|
|
33
|
+
*
|
|
34
|
+
* Logic (execution plan §20.9):
|
|
35
|
+
* - explicit retry_after -> rate-limit
|
|
36
|
+
* - 401 -> auth
|
|
37
|
+
* - 403 -> permission
|
|
38
|
+
* - 429 -> rate-limit
|
|
39
|
+
* - 5xx -> upstream
|
|
40
|
+
* - description match addenda -> format (only for 400-class non-authed)
|
|
41
|
+
* - other 400 responses -> upstream (Telegram also uses 400 for chat,
|
|
42
|
+
* message, thread, and button errors; those must never trigger fallback)
|
|
43
|
+
* - unknown / missing code -> upstream (not enough evidence to fallback)
|
|
44
|
+
*/
|
|
45
|
+
export function classifyTelegramError(errorCode, description, parameters) {
|
|
46
|
+
if (parameters?.retryAfter !== undefined)
|
|
47
|
+
return 'rate-limit';
|
|
48
|
+
if (errorCode === 401)
|
|
49
|
+
return 'auth';
|
|
50
|
+
if (errorCode === 403)
|
|
51
|
+
return 'permission';
|
|
52
|
+
if (errorCode === 429)
|
|
53
|
+
return 'rate-limit';
|
|
54
|
+
if (errorCode !== undefined && errorCode >= 500)
|
|
55
|
+
return 'upstream';
|
|
56
|
+
if (errorCode === 400 && description) {
|
|
57
|
+
const lower = description.toLowerCase();
|
|
58
|
+
if (FORMAT_TEXT_MARKERS.some((marker) => lower.includes(marker)))
|
|
59
|
+
return 'format';
|
|
60
|
+
return 'upstream';
|
|
61
|
+
}
|
|
62
|
+
if (errorCode === 400)
|
|
63
|
+
return 'upstream';
|
|
64
|
+
// No usable code / unexpected shape — treat as an upstream-level failure.
|
|
65
|
+
return 'upstream';
|
|
66
|
+
}
|
|
67
|
+
/** Map the resolved kind to a stable, readable message fragment. */
|
|
68
|
+
function kindLabel(kind) {
|
|
69
|
+
switch (kind) {
|
|
70
|
+
case 'format': return 'format';
|
|
71
|
+
case 'rate-limit': return 'rate-limit';
|
|
72
|
+
case 'auth': return 'auth';
|
|
73
|
+
case 'permission': return 'permission';
|
|
74
|
+
case 'network': return 'network';
|
|
75
|
+
case 'upstream': return 'upstream';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Structured Telegram error. Extends `ChannelError` so existing adapter-level
|
|
80
|
+
* error mapping (which asserts `ChannelError`) keeps working, while preserving
|
|
81
|
+
* the Telegram-specific original fields and the stable classification.
|
|
82
|
+
*/
|
|
83
|
+
export class TelegramApiError extends ChannelError {
|
|
84
|
+
method;
|
|
85
|
+
errorCode;
|
|
86
|
+
description;
|
|
87
|
+
parameters;
|
|
88
|
+
kind;
|
|
89
|
+
constructor(options = {}) {
|
|
90
|
+
const kind = options.kind ?? classifyTelegramError(options.errorCode, options.description, options.parameters);
|
|
91
|
+
const method = options.method ?? 'telegram';
|
|
92
|
+
const detail = options.description ?? 'unknown error';
|
|
93
|
+
const code = kind === 'auth' ? 'CHANNEL_AUTH_FAILED' : 'CHANNEL_ERROR';
|
|
94
|
+
super(code, `telegram ${method} failed (${kindLabel(kind)}): ${detail}`, options);
|
|
95
|
+
this.name = 'TelegramApiError';
|
|
96
|
+
this.method = method;
|
|
97
|
+
this.errorCode = options.errorCode;
|
|
98
|
+
this.description = options.description;
|
|
99
|
+
this.parameters = options.parameters;
|
|
100
|
+
this.kind = kind;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=api-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-error.js","sourceRoot":"","sources":["../src/api-error.ts"],"names":[],"mappings":"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"}
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram adapter configuration (Schemastery).
|
|
3
|
+
*
|
|
4
|
+
* Every deployment-tunable parameter is configurable here — no hardcoded
|
|
5
|
+
* deployment constants. The Bot API token is a secret: config carries only its
|
|
6
|
+
* credential reference (`tokenRef`, default `TELEGRAM_BOT_TOKEN`); the real
|
|
7
|
+
* value is resolved through `ctx.credentials` at startup and never lives in
|
|
8
|
+
* profile config, logs, or fixtures.
|
|
9
|
+
*
|
|
10
|
+
* Migration note: legacy configs may still carry a plaintext `token`. The field
|
|
11
|
+
* is deprecated and hidden; the plugin's apply() migrates it into
|
|
12
|
+
* `ctx.credentials` under `tokenRef` exactly once and strips the plaintext.
|
|
13
|
+
*/
|
|
14
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
15
|
+
import type { Volatile } from '@deepseek-ai/cordis';
|
|
16
|
+
/** Default credential reference name for the Telegram Bot API token. */
|
|
17
|
+
export declare const TELEGRAM_BOT_TOKEN_REF = "TELEGRAM_BOT_TOKEN";
|
|
18
|
+
export interface TelegramReconnectConfig {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
baseDelayMs: number;
|
|
21
|
+
maxDelayMs: number;
|
|
22
|
+
maxRetries: number;
|
|
23
|
+
}
|
|
24
|
+
export interface TelegramDedupConfig {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
windowMs: number;
|
|
27
|
+
}
|
|
28
|
+
export interface TelegramStreamingConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Whether incremental outbound streaming is enabled. When false, the reply
|
|
31
|
+
* router falls back to the buffered send-once strategy.
|
|
32
|
+
*/
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
/** Text sent as the initial placeholder while the model is working. */
|
|
35
|
+
placeholder: string;
|
|
36
|
+
}
|
|
37
|
+
/** Telegram's typing action expires after a few seconds, so it is refreshed. */
|
|
38
|
+
export interface TelegramTypingConfig {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
refreshMs: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Outbound formatting policy (plan §5.1). The adapter's minimum supported
|
|
44
|
+
* upstream is Bot API 10.2, so `auto` always selects Rich Markdown.
|
|
45
|
+
*/
|
|
46
|
+
export interface TelegramFormattingConfig {
|
|
47
|
+
/**
|
|
48
|
+
* Output renderer:
|
|
49
|
+
* - `auto` -> Rich Markdown
|
|
50
|
+
* - `rich-markdown`-> sendRichMessage / sendRichMessageDraft
|
|
51
|
+
* - `html` -> sendMessage(parse_mode=HTML) via the safe HTML renderer
|
|
52
|
+
* - `markdown-v2` -> expert-compat mode (fully escaped, never raw Agent MD)
|
|
53
|
+
* - `plain` -> no formatting parsed
|
|
54
|
+
*/
|
|
55
|
+
mode: 'auto' | 'rich-markdown' | 'html' | 'markdown-v2' | 'plain';
|
|
56
|
+
/**
|
|
57
|
+
* What to fall back to when the selected mode fails with a *format* error.
|
|
58
|
+
* Only `plain` is supported today; 401/403 / 429 / network / 5xx never
|
|
59
|
+
* trigger this fallback (plan §20.9).
|
|
60
|
+
*/
|
|
61
|
+
fallback: 'plain';
|
|
62
|
+
}
|
|
63
|
+
export interface TelegramConfig {
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
/** Account id within the telegram channel (defaults to 'main'). */
|
|
66
|
+
accountId: string;
|
|
67
|
+
/** Base URL of the Telegram Bot API. */
|
|
68
|
+
baseUrl: string;
|
|
69
|
+
/**
|
|
70
|
+
* Credential reference name for the Bot API token (resolved via
|
|
71
|
+
* `ctx.credentials`). Defaults to `TELEGRAM_BOT_TOKEN_REF`. Only the
|
|
72
|
+
* reference name lives in config — never the token itself.
|
|
73
|
+
*/
|
|
74
|
+
tokenRef: string;
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated Migration-only compatibility field. Legacy plaintext configs
|
|
77
|
+
* still parse; apply() migrates the value into `ctx.credentials` under
|
|
78
|
+
* `tokenRef` and deletes this field. New configs must use `tokenRef`.
|
|
79
|
+
*/
|
|
80
|
+
token?: string;
|
|
81
|
+
/** Per-request timeout. */
|
|
82
|
+
timeoutMs: number;
|
|
83
|
+
/** Long-poll hang time for one getUpdates cycle. */
|
|
84
|
+
longPollTimeoutMs: number;
|
|
85
|
+
reconnect: TelegramReconnectConfig;
|
|
86
|
+
dedup: TelegramDedupConfig;
|
|
87
|
+
streaming: TelegramStreamingConfig;
|
|
88
|
+
typing: TelegramTypingConfig;
|
|
89
|
+
/** Outbound formatting / rich rendering policy. */
|
|
90
|
+
formatting: TelegramFormattingConfig;
|
|
91
|
+
/** Hard byte cap for one inbound media download (image / document). */
|
|
92
|
+
maxDownloadBytes: number;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Activation-time config shape (Harness 0.2.0): every volatile field resolves
|
|
96
|
+
* to a `Volatile<T>` live handle instead of a plain value. `apply()` unwraps
|
|
97
|
+
* these once via `resolveVolatileConfig` into a plain `TelegramConfig`.
|
|
98
|
+
*/
|
|
99
|
+
export interface TelegramConfigLive {
|
|
100
|
+
enabled: Volatile<boolean>;
|
|
101
|
+
accountId: Volatile<string>;
|
|
102
|
+
baseUrl: Volatile<string>;
|
|
103
|
+
tokenRef: string;
|
|
104
|
+
token?: string;
|
|
105
|
+
timeoutMs: Volatile<number>;
|
|
106
|
+
longPollTimeoutMs: Volatile<number>;
|
|
107
|
+
reconnect: Volatile<TelegramReconnectConfig>;
|
|
108
|
+
dedup: Volatile<TelegramDedupConfig>;
|
|
109
|
+
streaming: Volatile<TelegramStreamingConfig>;
|
|
110
|
+
typing: Volatile<TelegramTypingConfig>;
|
|
111
|
+
formatting: Volatile<TelegramFormattingConfig>;
|
|
112
|
+
maxDownloadBytes: Volatile<number>;
|
|
113
|
+
}
|
|
114
|
+
export declare const Config: Schema<TelegramConfigLive>;
|
|
115
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC;IAClE;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,uBAAuB,CAAC;IACnC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,mDAAmD;IACnD,UAAU,EAAE,wBAAwB,CAAC;IACrC,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,SAAS,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC7C,KAAK,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IACrC,SAAS,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC7C,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IACvC,UAAU,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;IAC/C,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACpC;AAMD,eAAO,MAAM,MAAM,EA6CF,MAAM,CAAC,kBAAkB,CAAC,CAAC"}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram adapter configuration (Schemastery).
|
|
3
|
+
*
|
|
4
|
+
* Every deployment-tunable parameter is configurable here — no hardcoded
|
|
5
|
+
* deployment constants. The Bot API token is a secret: config carries only its
|
|
6
|
+
* credential reference (`tokenRef`, default `TELEGRAM_BOT_TOKEN`); the real
|
|
7
|
+
* value is resolved through `ctx.credentials` at startup and never lives in
|
|
8
|
+
* profile config, logs, or fixtures.
|
|
9
|
+
*
|
|
10
|
+
* Migration note: legacy configs may still carry a plaintext `token`. The field
|
|
11
|
+
* is deprecated and hidden; the plugin's apply() migrates it into
|
|
12
|
+
* `ctx.credentials` under `tokenRef` exactly once and strips the plaintext.
|
|
13
|
+
*/
|
|
14
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
15
|
+
/** Default credential reference name for the Telegram Bot API token. */
|
|
16
|
+
export const TELEGRAM_BOT_TOKEN_REF = 'TELEGRAM_BOT_TOKEN';
|
|
17
|
+
// The runtime schema is genuine Schemastery (validation happens at the Loader
|
|
18
|
+
// boundary); the annotation records the activation-time output shape. The cast
|
|
19
|
+
// is required because Schemastery's inferred ObjectT models per-field optionality
|
|
20
|
+
// more precisely than the hand-written `TelegramConfigLive` alias.
|
|
21
|
+
export const Config = Schema.object({
|
|
22
|
+
// Harness 0.2.0 settings model: fields the control plane / settings page may
|
|
23
|
+
// write at runtime are declared `.volatile()` — they surface on the
|
|
24
|
+
// auto-generated settings form, `ctx.settings.update(entryId, patch)` accepts
|
|
25
|
+
// exactly these paths, and at activation they resolve to `Volatile<T>`
|
|
26
|
+
// handles (unwrapped by `resolveVolatileConfig` in definition.ts / apply()).
|
|
27
|
+
enabled: Schema.boolean().default(true).volatile(),
|
|
28
|
+
accountId: Schema.string().default('main').volatile(),
|
|
29
|
+
baseUrl: Schema.string().default('https://api.telegram.org').volatile(),
|
|
30
|
+
// Credential reference name only — never the secret value itself.
|
|
31
|
+
tokenRef: Schema.string().default(TELEGRAM_BOT_TOKEN_REF),
|
|
32
|
+
// DEPRECATED migration-only legacy plaintext field: kept so old configs still
|
|
33
|
+
// parse. apply() migrates its value to credentials once and deletes it.
|
|
34
|
+
token: Schema.string().hidden(),
|
|
35
|
+
timeoutMs: Schema.natural().default(30000).volatile(),
|
|
36
|
+
longPollTimeoutMs: Schema.natural().default(25000).volatile(),
|
|
37
|
+
reconnect: Schema.object({
|
|
38
|
+
enabled: Schema.boolean().default(true),
|
|
39
|
+
baseDelayMs: Schema.natural().default(1000),
|
|
40
|
+
maxDelayMs: Schema.natural().default(30000),
|
|
41
|
+
maxRetries: Schema.natural().default(10),
|
|
42
|
+
}).default({}).volatile(),
|
|
43
|
+
dedup: Schema.object({
|
|
44
|
+
enabled: Schema.boolean().default(true),
|
|
45
|
+
windowMs: Schema.natural().default(5000),
|
|
46
|
+
}).default({}).volatile(),
|
|
47
|
+
streaming: Schema.object({
|
|
48
|
+
enabled: Schema.boolean().default(true),
|
|
49
|
+
placeholder: Schema.string().default('…'),
|
|
50
|
+
}).default({}).volatile(),
|
|
51
|
+
typing: Schema.object({
|
|
52
|
+
enabled: Schema.boolean().default(true),
|
|
53
|
+
refreshMs: Schema.natural().min(1000).default(4000),
|
|
54
|
+
}).default({}).volatile(),
|
|
55
|
+
formatting: Schema.object({
|
|
56
|
+
mode: Schema.union([
|
|
57
|
+
Schema.const('auto'),
|
|
58
|
+
Schema.const('rich-markdown'),
|
|
59
|
+
Schema.const('html'),
|
|
60
|
+
Schema.const('markdown-v2'),
|
|
61
|
+
Schema.const('plain'),
|
|
62
|
+
]).default('auto'),
|
|
63
|
+
fallback: Schema.const('plain').default('plain'),
|
|
64
|
+
}).default({}).volatile(),
|
|
65
|
+
maxDownloadBytes: Schema.natural().default(20 * 1024 * 1024).volatile(),
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram ChannelDefinition — the description of the telegram channel exposed
|
|
3
|
+
* to the Channel Control Plane.
|
|
4
|
+
*
|
|
5
|
+
* The control plane drives setup, configured-state reporting and adapter
|
|
6
|
+
* instantiation purely through this object, never through per-channel
|
|
7
|
+
* conditionals.
|
|
8
|
+
*
|
|
9
|
+
* - `setup.fields` — a single secret field (`token`) backed by the
|
|
10
|
+
* `TELEGRAM_BOT_TOKEN_REF` credential reference. The real
|
|
11
|
+
* token is only ever written through the credentials seam.
|
|
12
|
+
* - `getConfiguredState` — configured when the token credential exists.
|
|
13
|
+
* - `saveConfig` — merges only non-secret keys (behavioural tuning) into an
|
|
14
|
+
* internal mutable snapshot used by createAdapter. Secret
|
|
15
|
+
* fields are rejected upstream by the control plane.
|
|
16
|
+
* - `createAdapter` — resolves `tokenRef` via the injected credentials seam
|
|
17
|
+
* and throws a stable error when it is missing.
|
|
18
|
+
* - `setup.setupUrl` — points at @BotFather, where operators create a bot and
|
|
19
|
+
* obtain the Bot API token.
|
|
20
|
+
*
|
|
21
|
+
* Fully offline-testable: inject a fake credentials seam and a fake transport
|
|
22
|
+
* via `deps` — no network, no host.
|
|
23
|
+
*/
|
|
24
|
+
import type { ChannelDefinition } from '@krischoichoi/channel-control';
|
|
25
|
+
import type { TelegramConfig } from './config.js';
|
|
26
|
+
import { type TelegramAdapterDeps } from './adapter.js';
|
|
27
|
+
/**
|
|
28
|
+
* Structural credential seam used by the definition. Mirrors the tiny slice of
|
|
29
|
+
* `ctx.credentials` the control plane needs; injected so the definition stays
|
|
30
|
+
* host-agnostic and offline-testable. Resolution is per call and must not be
|
|
31
|
+
* cached across calls.
|
|
32
|
+
*/
|
|
33
|
+
export interface TelegramCredentialSeam {
|
|
34
|
+
resolve(ref: string): Promise<{
|
|
35
|
+
value: string;
|
|
36
|
+
source: string;
|
|
37
|
+
} | undefined>;
|
|
38
|
+
describe(ref: string): Promise<{
|
|
39
|
+
configured: boolean;
|
|
40
|
+
source?: string;
|
|
41
|
+
writable: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
set(ref: string, value: string): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export interface CreateTelegramDefinitionOptions {
|
|
46
|
+
config: TelegramConfig;
|
|
47
|
+
deps?: TelegramAdapterDeps;
|
|
48
|
+
/** Injected credentials seam (wraps ctx.credentials in apply()). */
|
|
49
|
+
credentials: TelegramCredentialSeam;
|
|
50
|
+
/** Durable store for the enabled intent (doc §21) when the host provides one. */
|
|
51
|
+
persistEnabled?: (enabled: boolean) => Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the telegram ChannelDefinition for the control plane. Returns a fresh
|
|
55
|
+
* object each call (cheap) so a plugin can register it against any control
|
|
56
|
+
* instance.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createTelegramDefinition(options: CreateTelegramDefinitionOptions): ChannelDefinition;
|
|
59
|
+
//# sourceMappingURL=definition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5F,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,oEAAoE;IACpE,WAAW,EAAE,sBAAsB,CAAC;IACpC,iFAAiF;IACjF,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAsDD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,iBAAiB,CAyGnB"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { ControlError } from '@krischoichoi/channel-control';
|
|
2
|
+
import { resolveVolatileConfig } from '@krischoichoi/channel-core';
|
|
3
|
+
import { Config, TELEGRAM_BOT_TOKEN_REF } from './config.js';
|
|
4
|
+
import { TelegramAdapter } from './adapter.js';
|
|
5
|
+
/** Allowed non-secret nested sub-config keys merged by saveConfig. */
|
|
6
|
+
const NESTED_KEYS = ['reconnect', 'dedup', 'streaming', 'typing', 'formatting'];
|
|
7
|
+
/** Allowed non-secret top-level scalar keys merged by saveConfig. */
|
|
8
|
+
const SCALAR_KEYS = [
|
|
9
|
+
'accountId',
|
|
10
|
+
'baseUrl',
|
|
11
|
+
'timeoutMs',
|
|
12
|
+
'longPollTimeoutMs',
|
|
13
|
+
'maxDownloadBytes',
|
|
14
|
+
];
|
|
15
|
+
/** Deep-copy a TelegramConfig into an independent mutable snapshot. */
|
|
16
|
+
function snapshotOf(config) {
|
|
17
|
+
// Harness 0.2.0: volatile fields arrive as `Volatile<T>` handles; unwrap
|
|
18
|
+
// them once so the snapshot (and everything reading it) sees plain values.
|
|
19
|
+
const plain = resolveVolatileConfig(config);
|
|
20
|
+
return {
|
|
21
|
+
...plain,
|
|
22
|
+
reconnect: { ...plain.reconnect },
|
|
23
|
+
dedup: { ...plain.dedup },
|
|
24
|
+
streaming: { ...plain.streaming },
|
|
25
|
+
typing: { ...plain.typing },
|
|
26
|
+
formatting: { ...plain.formatting },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function applyScalarPatch(snapshot, key, value) {
|
|
30
|
+
switch (key) {
|
|
31
|
+
case 'accountId':
|
|
32
|
+
if (typeof value === 'string' && value)
|
|
33
|
+
snapshot.accountId = value;
|
|
34
|
+
return;
|
|
35
|
+
case 'baseUrl':
|
|
36
|
+
if (typeof value === 'string' && value)
|
|
37
|
+
snapshot.baseUrl = value;
|
|
38
|
+
return;
|
|
39
|
+
case 'timeoutMs':
|
|
40
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
41
|
+
snapshot.timeoutMs = Math.floor(value);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
case 'longPollTimeoutMs':
|
|
45
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
46
|
+
snapshot.longPollTimeoutMs = Math.floor(value);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
case 'maxDownloadBytes':
|
|
50
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
51
|
+
snapshot.maxDownloadBytes = Math.floor(value);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build the telegram ChannelDefinition for the control plane. Returns a fresh
|
|
58
|
+
* object each call (cheap) so a plugin can register it against any control
|
|
59
|
+
* instance.
|
|
60
|
+
*/
|
|
61
|
+
export function createTelegramDefinition(options) {
|
|
62
|
+
const credentials = options.credentials;
|
|
63
|
+
const deps = options.deps ?? {};
|
|
64
|
+
// Mutable snapshot: saveConfig merges non-secret patches into this; the same
|
|
65
|
+
// object feeds getConfiguredState + createAdapter so both always agree.
|
|
66
|
+
const state = snapshotOf(options.config);
|
|
67
|
+
const tokenRef = () => state.tokenRef || TELEGRAM_BOT_TOKEN_REF;
|
|
68
|
+
const setup = {
|
|
69
|
+
fields: [
|
|
70
|
+
{
|
|
71
|
+
name: 'token',
|
|
72
|
+
kind: 'secret',
|
|
73
|
+
secret: true,
|
|
74
|
+
configured: false,
|
|
75
|
+
writable: true,
|
|
76
|
+
ref: tokenRef(),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
authMethods: ['credentials'],
|
|
80
|
+
setupUrl: 'https://t.me/BotFather',
|
|
81
|
+
};
|
|
82
|
+
const configuredState = async () => {
|
|
83
|
+
const described = await credentials.describe(tokenRef());
|
|
84
|
+
return {
|
|
85
|
+
configured: described.configured,
|
|
86
|
+
fields: {
|
|
87
|
+
token: {
|
|
88
|
+
configured: described.configured,
|
|
89
|
+
writable: described.writable,
|
|
90
|
+
source: described.source,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const saveConfig = async (patch) => {
|
|
96
|
+
for (const key of NESTED_KEYS) {
|
|
97
|
+
const value = patch[key];
|
|
98
|
+
if (value && typeof value === 'object') {
|
|
99
|
+
Object.assign(state[key], value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const key of SCALAR_KEYS) {
|
|
103
|
+
if (patch[key] !== undefined) {
|
|
104
|
+
applyScalarPatch(state, key, patch[key]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (patch.enabled !== undefined)
|
|
108
|
+
state.enabled = Boolean(patch.enabled);
|
|
109
|
+
// `token` / `tokenRef` are secret fields and are rejected by the control
|
|
110
|
+
// plane before reaching this definition; nothing to persist here.
|
|
111
|
+
};
|
|
112
|
+
const restoreConfig = async (saved) => {
|
|
113
|
+
// Schemastery validates and normalizes the persisted control-plane value.
|
|
114
|
+
const restored = snapshotOf(Config(saved));
|
|
115
|
+
Object.assign(state, restored);
|
|
116
|
+
state.reconnect = restored.reconnect;
|
|
117
|
+
state.dedup = restored.dedup;
|
|
118
|
+
state.streaming = restored.streaming;
|
|
119
|
+
state.typing = restored.typing;
|
|
120
|
+
state.formatting = restored.formatting;
|
|
121
|
+
};
|
|
122
|
+
const createAdapter = async () => {
|
|
123
|
+
const resolved = await credentials.resolve(tokenRef());
|
|
124
|
+
const token = resolved?.value;
|
|
125
|
+
if (!token) {
|
|
126
|
+
throw new ControlError('CONTROL_ERROR', `telegram credential "${tokenRef()}" is not configured`);
|
|
127
|
+
}
|
|
128
|
+
return new TelegramAdapter(state, { ...deps, token });
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
id: 'telegram',
|
|
132
|
+
get enabled() {
|
|
133
|
+
return state.enabled;
|
|
134
|
+
},
|
|
135
|
+
async setEnabled(enabled) {
|
|
136
|
+
state.enabled = enabled;
|
|
137
|
+
await options.persistEnabled?.(enabled);
|
|
138
|
+
},
|
|
139
|
+
setup,
|
|
140
|
+
getConfiguredState: configuredState,
|
|
141
|
+
saveConfig,
|
|
142
|
+
snapshotConfig: () => snapshotOf(state),
|
|
143
|
+
restoreConfig,
|
|
144
|
+
createAdapter,
|
|
145
|
+
autoStart: true,
|
|
146
|
+
// Telegram entities are mapped to a reliable mentionedBot activation fact;
|
|
147
|
+
// new group rules default to requiring an explicit bot mention.
|
|
148
|
+
access: {
|
|
149
|
+
directMessages: true,
|
|
150
|
+
groups: true,
|
|
151
|
+
mentions: true,
|
|
152
|
+
ownerDiscovery: 'claim',
|
|
153
|
+
identityLabels: { user: 'Telegram User ID', group: 'Telegram Chat ID' },
|
|
154
|
+
defaults: { requireMention: true },
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=definition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"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"}
|