@amykit/plugin-telegram 0.0.0-stage → 0.7.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 +74 -2
- package/dist/BotApi.d.ts +74 -0
- package/dist/BotApi.d.ts.map +1 -0
- package/dist/BotApi.js +162 -0
- package/dist/BotApi.js.map +1 -0
- package/dist/TelegramConversation.d.ts +207 -0
- package/dist/TelegramConversation.d.ts.map +1 -0
- package/dist/TelegramConversation.js +1190 -0
- package/dist/TelegramConversation.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +41 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +37 -4
package/README.md
CHANGED
|
@@ -1,3 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- Generated by `npm run docs:generate` from the package itself. Do not edit by hand. -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @amykit/plugin-telegram
|
|
4
|
+
|
|
5
|
+
A topic per piece of work in the operator's private chat with a Telegram bot, as the conversation port.
|
|
6
|
+
|
|
7
|
+
A plugin for [amy](https://github.com/nicolasmelo1/amy). It provides the `conversation` port.
|
|
8
|
+
|
|
9
|
+
Use it when you want to follow and answer amy from Telegram. It mounts the conversation port: each piece of work gets a topic in your private chat with a bot, and it can also hold amy's chat. It needs the bot token in TELEGRAM_BOT_TOKEN and your numeric Telegram user id.
|
|
10
|
+
|
|
11
|
+
## What it extends
|
|
12
|
+
|
|
13
|
+
| | |
|
|
14
|
+
| :-- | :-- |
|
|
15
|
+
| Ports | `conversation` |
|
|
16
|
+
| Actions | _none_ |
|
|
17
|
+
| Operations | _none_ |
|
|
18
|
+
| Commands | _none_ |
|
|
19
|
+
| Events | _none_ |
|
|
20
|
+
| Environment | `TELEGRAM_BOT_TOKEN` |
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
amy add @amykit/plugin-telegram
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then name it in `~/.amy/config.yaml`:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
workflows:
|
|
32
|
+
your-profile:
|
|
33
|
+
plugins:
|
|
34
|
+
- "@amykit/plugin-telegram"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
plugins:
|
|
41
|
+
"@amykit/plugin-telegram":
|
|
42
|
+
directory: "telegram"
|
|
43
|
+
operator: <string>
|
|
44
|
+
server: "https://api.telegram.org"
|
|
45
|
+
topics: true
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Setting | Type | Required | Default | What it is |
|
|
49
|
+
| :-- | :-- | :-- | :-- | :-- |
|
|
50
|
+
| `directory` | `string` | no | `telegram` | where this profile's topics are remembered, by work id, relative to the state directory; the replies read for every profile are kept once per bot |
|
|
51
|
+
| `operator` | `string` | **yes** | | your Telegram user id, a number such as 123456789: the private chat each work item's topic opens in, and the only person whose replies are answers |
|
|
52
|
+
| `server` | `string` | no | `https://api.telegram.org` | the Bot API server, for a local one instead of Telegram's |
|
|
53
|
+
| `topics` | `boolean` | no | `true` | whether each piece of work opens a topic of its own; off, works share the private chat and a reply reaches one by quoting amy's message for it or starting with its id |
|
|
54
|
+
|
|
55
|
+
Every field is checked at boot against the schema this package declares. A key that is
|
|
56
|
+
not one of the above is a refusal naming the plugin and the key, not a setting that
|
|
57
|
+
silently never applied.
|
|
58
|
+
|
|
59
|
+
## Environment
|
|
60
|
+
|
|
61
|
+
This package reads the following, which `amy doctor` checks for you:
|
|
62
|
+
|
|
63
|
+
| Variable | |
|
|
64
|
+
| :-- | :-- |
|
|
65
|
+
| `TELEGRAM_BOT_TOKEN` | required at mount |
|
|
66
|
+
|
|
67
|
+
## Where the reasoning is
|
|
68
|
+
|
|
69
|
+
- [Writing a plugin](https://github.com/nicolasmelo1/amy/tree/main/docs/guides/write-a-plugin.md)
|
|
70
|
+
- [Reference: plugins](https://github.com/nicolasmelo1/amy/tree/main/docs/reference/plugins.md)
|
|
71
|
+
- [Ports and the registry](https://github.com/nicolasmelo1/amy/tree/main/docs/concepts/ports.md)
|
|
72
|
+
|
|
73
|
+
## Licence
|
|
74
|
+
|
|
75
|
+
MIT
|
package/dist/BotApi.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** One HTTP exchange, small enough to script in a test. */
|
|
2
|
+
export interface HttpRequest {
|
|
3
|
+
url: string;
|
|
4
|
+
method: "GET" | "POST";
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
body?: string;
|
|
7
|
+
/** Refuse the answer once its body passes this many bytes, while it arrives. */
|
|
8
|
+
maxBytes?: number;
|
|
9
|
+
/** Abandon the exchange, the body's last byte included, after this long. */
|
|
10
|
+
deadlineMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface HttpResponse {
|
|
13
|
+
status: number;
|
|
14
|
+
body: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
export type HttpTransport = (request: HttpRequest) => Promise<HttpResponse>;
|
|
17
|
+
/** An answer larger than the request allowed, refused before it was held whole. */
|
|
18
|
+
export declare class TooLarge extends Error {
|
|
19
|
+
readonly limit: number;
|
|
20
|
+
constructor(limit: number);
|
|
21
|
+
}
|
|
22
|
+
export declare const fetchTransport: HttpTransport;
|
|
23
|
+
/** Telegram's own server; a local Bot API server, or a stand-in, is another. */
|
|
24
|
+
export declare const TELEGRAM_API = "https://api.telegram.org";
|
|
25
|
+
/** How many times a `429` is waited out before the call fails. */
|
|
26
|
+
export declare const DEFAULT_RATE_LIMIT_RETRIES = 5;
|
|
27
|
+
/**
|
|
28
|
+
* How long one exchange may take, body included. Updates are asked for with
|
|
29
|
+
* no long poll, so anything this slow is a server that stopped answering, and
|
|
30
|
+
* holding the bot's lock for it would stall every profile.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_DEADLINE_MS = 60000;
|
|
33
|
+
/**
|
|
34
|
+
* What the Bot API answered when it refused, with its `error_code` kept
|
|
35
|
+
* apart, so a 409 is told from a 400 without reading the words.
|
|
36
|
+
*/
|
|
37
|
+
export declare class BotApiRefusal extends Error {
|
|
38
|
+
readonly method: string;
|
|
39
|
+
readonly code: number;
|
|
40
|
+
readonly description: string;
|
|
41
|
+
constructor(method: string, code: number, description: string);
|
|
42
|
+
}
|
|
43
|
+
export interface BotApiOptions {
|
|
44
|
+
server?: string;
|
|
45
|
+
transport?: HttpTransport;
|
|
46
|
+
sleep?: (ms: number) => Promise<void>;
|
|
47
|
+
maxRateLimitRetries?: number;
|
|
48
|
+
deadlineMs?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The Bot API, called by method name with a JSON body.
|
|
52
|
+
*
|
|
53
|
+
* The token is part of every URL Telegram serves, so no URL is ever put in
|
|
54
|
+
* an error, a log or a file: what goes wrong is said by method name.
|
|
55
|
+
*/
|
|
56
|
+
export declare class BotApi {
|
|
57
|
+
private readonly token;
|
|
58
|
+
private readonly server;
|
|
59
|
+
private readonly transport;
|
|
60
|
+
private readonly sleep;
|
|
61
|
+
private readonly maxRetries;
|
|
62
|
+
private readonly deadlineMs;
|
|
63
|
+
constructor(token: string, options?: BotApiOptions);
|
|
64
|
+
/** The method's `result`, or a {@link BotApiRefusal} when `ok` is false. */
|
|
65
|
+
call<T>(method: string, args?: Record<string, unknown>): Promise<T>;
|
|
66
|
+
/** A file's bytes, by the `file_path` getFile answered, refused past `maxBytes` while they arrive. */
|
|
67
|
+
download(filePath: string, maxBytes?: number): Promise<Uint8Array>;
|
|
68
|
+
/**
|
|
69
|
+
* The transport's failure, said without the URL that carries the token. The
|
|
70
|
+
* original is not kept as the cause: its message may carry that URL.
|
|
71
|
+
*/
|
|
72
|
+
private send;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=BotApi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotApi.d.ts","sourceRoot":"","sources":["../src/BotApi.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAE5E,mFAAmF;AACnF,qBAAa,QAAS,SAAQ,KAAK;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CAGnC;AAED,eAAO,MAAM,cAAc,EAAE,aAK5B,CAAC;AAkCF,gFAAgF;AAChF,eAAO,MAAM,YAAY,6BAA6B,CAAC;AAEvD,kEAAkE;AAClE,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAEpC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM;gBAFnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM;CAI/B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD;;;;;GAKG;AACH,qBAAa,MAAM;IAQf,OAAO,CAAC,QAAQ,CAAC,KAAK;IAPxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGjB,KAAK,EAAE,MAAM,EAC9B,OAAO,GAAE,aAAkB;IAS7B,4EAA4E;IACtE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAsB7E,sGAAsG;IAChG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAcxE;;;OAGG;IACH,OAAO,CAAC,IAAI;CAMb"}
|
package/dist/BotApi.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/** An answer larger than the request allowed, refused before it was held whole. */
|
|
2
|
+
export class TooLarge extends Error {
|
|
3
|
+
limit;
|
|
4
|
+
constructor(limit) {
|
|
5
|
+
super(`the answer is larger than ${limit} bytes`);
|
|
6
|
+
this.limit = limit;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const fetchTransport = async (request) => {
|
|
10
|
+
// One signal for the request and the body, so a server that trickles bytes forever is cut off too.
|
|
11
|
+
const signal = request.deadlineMs === undefined ? undefined : AbortSignal.timeout(request.deadlineMs);
|
|
12
|
+
const response = await fetch(request.url, { method: request.method, headers: request.headers, body: request.body, ...(signal ? { signal } : {}) });
|
|
13
|
+
return { status: response.status, body: await readBody(response, request.maxBytes) };
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The body, counted as it arrives: a declared length over the limit is
|
|
17
|
+
* refused unread, and a missing or understated one is caught by the count.
|
|
18
|
+
*/
|
|
19
|
+
async function readBody(response, maxBytes) {
|
|
20
|
+
if (maxBytes === undefined || !response.body)
|
|
21
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
22
|
+
if (Number(response.headers.get("content-length") ?? 0) > maxBytes) {
|
|
23
|
+
await response.body.cancel();
|
|
24
|
+
throw new TooLarge(maxBytes);
|
|
25
|
+
}
|
|
26
|
+
const chunks = [];
|
|
27
|
+
let total = 0;
|
|
28
|
+
const reader = response.body.getReader();
|
|
29
|
+
for (;;) {
|
|
30
|
+
const { done, value } = await reader.read();
|
|
31
|
+
if (done)
|
|
32
|
+
break;
|
|
33
|
+
total += value.byteLength;
|
|
34
|
+
if (total > maxBytes) {
|
|
35
|
+
await reader.cancel();
|
|
36
|
+
throw new TooLarge(maxBytes);
|
|
37
|
+
}
|
|
38
|
+
chunks.push(value);
|
|
39
|
+
}
|
|
40
|
+
const body = new Uint8Array(total);
|
|
41
|
+
let offset = 0;
|
|
42
|
+
for (const chunk of chunks) {
|
|
43
|
+
body.set(chunk, offset);
|
|
44
|
+
offset += chunk.byteLength;
|
|
45
|
+
}
|
|
46
|
+
return body;
|
|
47
|
+
}
|
|
48
|
+
/** Telegram's own server; a local Bot API server, or a stand-in, is another. */
|
|
49
|
+
export const TELEGRAM_API = "https://api.telegram.org";
|
|
50
|
+
/** How many times a `429` is waited out before the call fails. */
|
|
51
|
+
export const DEFAULT_RATE_LIMIT_RETRIES = 5;
|
|
52
|
+
/**
|
|
53
|
+
* How long one exchange may take, body included. Updates are asked for with
|
|
54
|
+
* no long poll, so anything this slow is a server that stopped answering, and
|
|
55
|
+
* holding the bot's lock for it would stall every profile.
|
|
56
|
+
*/
|
|
57
|
+
export const DEFAULT_DEADLINE_MS = 60_000;
|
|
58
|
+
/**
|
|
59
|
+
* What the Bot API answered when it refused, with its `error_code` kept
|
|
60
|
+
* apart, so a 409 is told from a 400 without reading the words.
|
|
61
|
+
*/
|
|
62
|
+
export class BotApiRefusal extends Error {
|
|
63
|
+
method;
|
|
64
|
+
code;
|
|
65
|
+
description;
|
|
66
|
+
constructor(method, code, description) {
|
|
67
|
+
super(`telegram ${method} refused: ${code} ${description}`);
|
|
68
|
+
this.method = method;
|
|
69
|
+
this.code = code;
|
|
70
|
+
this.description = description;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The Bot API, called by method name with a JSON body.
|
|
75
|
+
*
|
|
76
|
+
* The token is part of every URL Telegram serves, so no URL is ever put in
|
|
77
|
+
* an error, a log or a file: what goes wrong is said by method name.
|
|
78
|
+
*/
|
|
79
|
+
export class BotApi {
|
|
80
|
+
token;
|
|
81
|
+
server;
|
|
82
|
+
transport;
|
|
83
|
+
sleep;
|
|
84
|
+
maxRetries;
|
|
85
|
+
deadlineMs;
|
|
86
|
+
constructor(token, options = {}) {
|
|
87
|
+
this.token = token;
|
|
88
|
+
this.server = (options.server ?? TELEGRAM_API).replace(/\/+$/, "");
|
|
89
|
+
this.transport = options.transport ?? fetchTransport;
|
|
90
|
+
this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
91
|
+
this.maxRetries = options.maxRateLimitRetries ?? DEFAULT_RATE_LIMIT_RETRIES;
|
|
92
|
+
this.deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
93
|
+
}
|
|
94
|
+
/** The method's `result`, or a {@link BotApiRefusal} when `ok` is false. */
|
|
95
|
+
async call(method, args = {}) {
|
|
96
|
+
for (let attempt = 0;; attempt++) {
|
|
97
|
+
const response = await this.send(method, {
|
|
98
|
+
url: `${this.server}/bot${this.token}/${method}`,
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
body: JSON.stringify(args),
|
|
102
|
+
deadlineMs: this.deadlineMs,
|
|
103
|
+
});
|
|
104
|
+
const answer = parse(method, response);
|
|
105
|
+
if (answer.ok === true)
|
|
106
|
+
return answer.result;
|
|
107
|
+
const code = answer.error_code ?? response.status;
|
|
108
|
+
const retryAfter = answer.parameters?.retry_after;
|
|
109
|
+
if (code === 429 && attempt < this.maxRetries) {
|
|
110
|
+
await this.sleep(Math.max(0, retryAfter ?? 1) * 1000);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
// A local server or a proxy may echo the request, and with it the URL.
|
|
114
|
+
throw new BotApiRefusal(method, code, withoutToken(answer.description ?? "no description", this.token));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** A file's bytes, by the `file_path` getFile answered, refused past `maxBytes` while they arrive. */
|
|
118
|
+
async download(filePath, maxBytes) {
|
|
119
|
+
const response = await this.send("file download", {
|
|
120
|
+
url: `${this.server}/file/bot${this.token}/${filePath}`,
|
|
121
|
+
method: "GET",
|
|
122
|
+
headers: {},
|
|
123
|
+
deadlineMs: this.deadlineMs,
|
|
124
|
+
...(maxBytes === undefined ? {} : { maxBytes }),
|
|
125
|
+
});
|
|
126
|
+
if (response.status !== 200)
|
|
127
|
+
throw new Error(`telegram file download answered HTTP ${response.status}`);
|
|
128
|
+
// Checked again here for a transport that does not honour the limit.
|
|
129
|
+
if (maxBytes !== undefined && response.body.byteLength > maxBytes)
|
|
130
|
+
throw new TooLarge(maxBytes);
|
|
131
|
+
return response.body;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The transport's failure, said without the URL that carries the token. The
|
|
135
|
+
* original is not kept as the cause: its message may carry that URL.
|
|
136
|
+
*/
|
|
137
|
+
send(what, request) {
|
|
138
|
+
return this.transport(request).catch((error) => {
|
|
139
|
+
if (error instanceof TooLarge)
|
|
140
|
+
throw error;
|
|
141
|
+
throw new Error(`telegram ${what} could not be reached: ${withoutToken(error, this.token)}`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function parse(method, response) {
|
|
146
|
+
try {
|
|
147
|
+
return JSON.parse(new TextDecoder().decode(response.body));
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
throw new Error(`telegram ${method} answered HTTP ${response.status} with a body that is not JSON`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** The message with the token taken out, as written and as a URL would encode it, in either case. */
|
|
154
|
+
function withoutToken(error, token) {
|
|
155
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
156
|
+
if (!token)
|
|
157
|
+
return text;
|
|
158
|
+
const encoded = encodeURIComponent(token);
|
|
159
|
+
const forms = [...new Set([token, encoded, encoded.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase())])];
|
|
160
|
+
return forms.reduce((said, form) => said.split(form).join("<token>"), text);
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=BotApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BotApi.js","sourceRoot":"","sources":["../src/BotApi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Ask, ChatMessage, Conversation, Reply, ThreadRef } from "@amykit/core";
|
|
2
|
+
import { BotApi } from "./BotApi.js";
|
|
3
|
+
export interface TelegramConversationOptions {
|
|
4
|
+
/** The operator's user id, which is also their private chat with the bot. */
|
|
5
|
+
operator: string;
|
|
6
|
+
/**
|
|
7
|
+
* What this profile owns: its topics by work id, the replies filed under
|
|
8
|
+
* them and the files they carried. Removing the profile removes all of it.
|
|
9
|
+
*/
|
|
10
|
+
directory: string;
|
|
11
|
+
/**
|
|
12
|
+
* What one bot shares across profiles: which profile owns each topic, the
|
|
13
|
+
* update offset and the lock. The directory itself when not given.
|
|
14
|
+
*/
|
|
15
|
+
shared?: string;
|
|
16
|
+
/** The largest file downloaded; a larger one is named in the reply instead. */
|
|
17
|
+
maxFileBytes?: number;
|
|
18
|
+
/** How long to wait for another process reading the bot. */
|
|
19
|
+
lockWaitMs?: number;
|
|
20
|
+
/** How long a hold on the bot lasts without being renewed. */
|
|
21
|
+
leaseMs?: number;
|
|
22
|
+
/** An inspection may read existing state but must not initialize it. */
|
|
23
|
+
readOnly?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether each piece of work opens a topic of its own. Off, works share the
|
|
26
|
+
* private chat, and a reply reaches one only when it quotes amy's message
|
|
27
|
+
* for it or starts with its id (S15).
|
|
28
|
+
*/
|
|
29
|
+
topics?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** What `amy doctor` prints for this plugin, one line per question. */
|
|
32
|
+
export interface TelegramCheck {
|
|
33
|
+
label: string;
|
|
34
|
+
ok: boolean;
|
|
35
|
+
detail?: string;
|
|
36
|
+
}
|
|
37
|
+
/** The most a bot may download, by the Bot API's own limit. */
|
|
38
|
+
export declare const DEFAULT_MAX_FILE_BYTES: number;
|
|
39
|
+
/** A posted structured ask and its durable selection/acceptance state. */
|
|
40
|
+
export interface FiledAsk {
|
|
41
|
+
ask: Ask;
|
|
42
|
+
messageId: number;
|
|
43
|
+
at: string;
|
|
44
|
+
selected: number[];
|
|
45
|
+
accepted?: {
|
|
46
|
+
at: string;
|
|
47
|
+
chosen: string[];
|
|
48
|
+
};
|
|
49
|
+
/** Callback ids already applied; retries must not toggle a persisted selection. */
|
|
50
|
+
callbacks?: string[];
|
|
51
|
+
acknowledged?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One topic per piece of work, in the operator's private chat with the bot.
|
|
55
|
+
*
|
|
56
|
+
* Telegram hands a bot its updates once, to whoever asks first, so the
|
|
57
|
+
* process reading them files each message, durably, under the profile that
|
|
58
|
+
* owns its topic before the offset that confirms it moves.
|
|
59
|
+
*
|
|
60
|
+
* Nothing here depends on that reader being alone. Every reply is a file of
|
|
61
|
+
* its own, created and never rewritten; every topic and owner is a file
|
|
62
|
+
* created once; the offset only moves forward. A second reader, or one that
|
|
63
|
+
* was paused past its hold, can only add what is already there, never lose
|
|
64
|
+
* it. The hold on the bot saves work (a 409 from Telegram, a topic opened
|
|
65
|
+
* twice) and nothing else.
|
|
66
|
+
*/
|
|
67
|
+
export declare class TelegramConversation implements Conversation {
|
|
68
|
+
private readonly api;
|
|
69
|
+
private readonly options;
|
|
70
|
+
readonly asks: true;
|
|
71
|
+
readonly edit: true;
|
|
72
|
+
readonly threads?: true;
|
|
73
|
+
/** The bot: its directory is named for it, so two bots never share a conversation. */
|
|
74
|
+
readonly source: string;
|
|
75
|
+
private readonly opening;
|
|
76
|
+
/** Who this profile's directory is: a directory removed and made again is somebody else. */
|
|
77
|
+
private readonly identity;
|
|
78
|
+
/** The operator's id as Telegram writes it, whatever spelling the config used. */
|
|
79
|
+
private readonly operator;
|
|
80
|
+
constructor(api: BotApi, options: TelegramConversationOptions);
|
|
81
|
+
open(workId: string, title: string): Promise<ThreadRef>;
|
|
82
|
+
/**
|
|
83
|
+
* A work in a chat without topics: nothing to ask Telegram for, only its
|
|
84
|
+
* thread to name and record, so a reply addressed to it is filed under it.
|
|
85
|
+
*/
|
|
86
|
+
private openInTheChat;
|
|
87
|
+
/**
|
|
88
|
+
* Records who owns a work's thread, the thread by work id, and the work by
|
|
89
|
+
* its id for the whole bot, so a message addressed to it from outside every
|
|
90
|
+
* thread finds it. Owner first: a reply that arrives before the work knows
|
|
91
|
+
* its thread is still filed here. The directory is kept relative to the
|
|
92
|
+
* bot's, so a state root moved or mounted elsewhere still finds it.
|
|
93
|
+
*/
|
|
94
|
+
private record;
|
|
95
|
+
post(thread: ThreadRef, message: {
|
|
96
|
+
text: string;
|
|
97
|
+
files?: string[];
|
|
98
|
+
ask?: Ask;
|
|
99
|
+
}): Promise<string>;
|
|
100
|
+
private postAsk;
|
|
101
|
+
private sendParts;
|
|
102
|
+
replies(thread: ThreadRef, since: string): Promise<Reply[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Whether this bot can hold the conversation, asked of Telegram itself:
|
|
105
|
+
* the token, the bot's topic mode, a private chat the operator started,
|
|
106
|
+
* and nobody else reading its updates.
|
|
107
|
+
*/
|
|
108
|
+
checks(): Promise<TelegramCheck[]>;
|
|
109
|
+
/**
|
|
110
|
+
* The messages in the private chat that reached no work, oldest first,
|
|
111
|
+
* until they are settled. Telegram is read for them first, so the chat is
|
|
112
|
+
* read all the time and not only when a work is looked at (S20).
|
|
113
|
+
*/
|
|
114
|
+
chat(): Promise<ChatMessage[]>;
|
|
115
|
+
settle(id: string): Promise<void>;
|
|
116
|
+
/** Says something in the private chat, outside every topic, in as many messages as it takes. */
|
|
117
|
+
say(place: string, text: string): Promise<string>;
|
|
118
|
+
/** Replaces what a message amy said reads, as much of it as one message holds. */
|
|
119
|
+
revise(place: string, id: string, text: string): Promise<void>;
|
|
120
|
+
private chatId;
|
|
121
|
+
private topics;
|
|
122
|
+
/**
|
|
123
|
+
* Where each work's thread is remembered, one place per mode: a topic is no
|
|
124
|
+
* thread in a chat without topics, nor the other way round, so turning
|
|
125
|
+
* topics on or off opens each work afresh rather than reusing an id the
|
|
126
|
+
* new mode cannot post to.
|
|
127
|
+
*/
|
|
128
|
+
private threadsDirectory;
|
|
129
|
+
/** Where a work's message goes: its topic, or the chat itself when works share it. */
|
|
130
|
+
private inThread;
|
|
131
|
+
private threadOf;
|
|
132
|
+
private workOfThread;
|
|
133
|
+
private shared;
|
|
134
|
+
private inbox;
|
|
135
|
+
/**
|
|
136
|
+
* Reads every update Telegram holds and files each of the operator's
|
|
137
|
+
* messages under the profile owning its topic. A topic no profile owns any
|
|
138
|
+
* more, its profile removed or made again, is skipped. The offset moves
|
|
139
|
+
* only once what it confirms is durably on disk.
|
|
140
|
+
*/
|
|
141
|
+
private fileUpdates;
|
|
142
|
+
/** Handles a button only after resolving its real topic owner from the callback message. */
|
|
143
|
+
private fileCallback;
|
|
144
|
+
private callbackContext;
|
|
145
|
+
private applyCallback;
|
|
146
|
+
private sendChoices;
|
|
147
|
+
private choose;
|
|
148
|
+
private acknowledgeAccepted;
|
|
149
|
+
private redrawKeyboard;
|
|
150
|
+
private closeKeyboard;
|
|
151
|
+
private askFile;
|
|
152
|
+
private askByKey;
|
|
153
|
+
private writeAsk;
|
|
154
|
+
protected askedUnder(topic: string): FiledAsk[];
|
|
155
|
+
/**
|
|
156
|
+
* Files one message in its owner's inbox for that owner's identity, as a
|
|
157
|
+
* file of its own. The inbox is named for the identity, so a write racing a
|
|
158
|
+
* profile's removal lands where no later profile at that path reads.
|
|
159
|
+
*/
|
|
160
|
+
private file;
|
|
161
|
+
/**
|
|
162
|
+
* Where a topic's replies are filed for its owner, if that owner's directory
|
|
163
|
+
* still carries the identity that opened the topic; made if needed.
|
|
164
|
+
*/
|
|
165
|
+
private inboxOf;
|
|
166
|
+
/** The id of the last message amy posted in a topic by an instant, or 0 when none is known. */
|
|
167
|
+
private postedBy;
|
|
168
|
+
/** Files what arrived in a topic before its owner was recorded, now that it is. */
|
|
169
|
+
private adopt;
|
|
170
|
+
/** Whether a topic is being opened in this chat now, by any process: an opening older than a day was abandoned. */
|
|
171
|
+
private openingIn;
|
|
172
|
+
/** Replies kept for a topic whose opening was abandoned a day ago, and the openings themselves, go. */
|
|
173
|
+
private pruneAbandoned;
|
|
174
|
+
/**
|
|
175
|
+
* The offset to confirm with, unless it has outlived Telegram's update
|
|
176
|
+
* ids: then the earliest update Telegram still holds is asked for instead,
|
|
177
|
+
* and the ones already filed are filed once.
|
|
178
|
+
*/
|
|
179
|
+
private offset;
|
|
180
|
+
/** Moves the offset forward, never back, so a late reader cannot unconfirm what another confirmed. */
|
|
181
|
+
private confirm;
|
|
182
|
+
/** The files as local paths, and a line naming each one too large to fetch. */
|
|
183
|
+
private downloadAll;
|
|
184
|
+
/**
|
|
185
|
+
* Fetches one file to its place, or answers false when it is too large: by
|
|
186
|
+
* what getFile says, by Telegram refusing it as too big when the message did
|
|
187
|
+
* not say its size, or by its bytes as they arrive. A local Bot API server
|
|
188
|
+
* answers with a path on its own disk, which is read where it lies.
|
|
189
|
+
*/
|
|
190
|
+
private download;
|
|
191
|
+
private topicOfWork;
|
|
192
|
+
/**
|
|
193
|
+
* Files a message written outside every topic: under the work it is
|
|
194
|
+
* addressed to — a quote of amy's message for it, or its id at the start —
|
|
195
|
+
* and in the chat otherwise (S42). The rule is the core's; this only says
|
|
196
|
+
* what amy filed where (S16).
|
|
197
|
+
*/
|
|
198
|
+
private fileOutside;
|
|
199
|
+
/**
|
|
200
|
+
* Runs `work` holding the bot, so one process reads its updates or opens a
|
|
201
|
+
* topic at a time: a hold not renewed within its lease is taken over. Being
|
|
202
|
+
* passed over costs a 409 or a second topic, never a reply, since nothing
|
|
203
|
+
* filed is ever rewritten.
|
|
204
|
+
*/
|
|
205
|
+
private held;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=TelegramConversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelegramConversation.d.ts","sourceRoot":"","sources":["../src/TelegramConversation.ts"],"names":[],"mappings":"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"}
|