@effect-ak/tg-bot-client 1.3.3 → 1.4.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 +14 -0
- package/dist/index.cjs +59 -99
- package/dist/index.d.cts +26 -46
- package/dist/index.d.ts +26 -46
- package/dist/index.js +58 -91
- package/package.json +5 -7
- package/readme.md +11 -262
- package/src/client.ts +56 -16
- package/src/execute.ts +93 -20
- package/src/index.ts +24 -7
- package/test/execute.test.ts +31 -19
- package/test/file.test.ts +6 -4
- package/src/client-file.ts +0 -57
- package/src/const.ts +0 -20
- package/src/errors.ts +0 -18
- package/src/guards.ts +0 -27
- package/src/utils.ts +0 -3
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@effect-ak/tg-bot-client",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "Type-safe HTTP client for Telegram Bot API",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.4.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Aleksandr Kondaurov",
|
|
@@ -10,12 +10,9 @@
|
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/kondaurovDev/tg-bot-
|
|
13
|
+
"url": "https://github.com/kondaurovDev/tg-bot-sdk",
|
|
14
14
|
"directory": "packages/client"
|
|
15
15
|
},
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/effect-ak/tg-bot-client/issues"
|
|
18
|
-
},
|
|
19
16
|
"publishConfig": {
|
|
20
17
|
"access": "public"
|
|
21
18
|
},
|
|
@@ -27,10 +24,11 @@
|
|
|
27
24
|
"types": "./dist/index.d.ts",
|
|
28
25
|
"import": "./dist/index.js",
|
|
29
26
|
"require": "./dist/index.cjs"
|
|
30
|
-
}
|
|
27
|
+
},
|
|
28
|
+
"./dist/*": "./dist/*"
|
|
31
29
|
},
|
|
32
30
|
"dependencies": {
|
|
33
|
-
"@effect-ak/tg-bot-api": "^1.
|
|
31
|
+
"@effect-ak/tg-bot-api": "^1.3.0"
|
|
34
32
|
},
|
|
35
33
|
"scripts": {
|
|
36
34
|
"build": "tsup",
|
package/readme.md
CHANGED
|
@@ -1,294 +1,43 @@
|
|
|
1
1
|
# @effect-ak/tg-bot-client
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@effect-ak/tg-bot-client)
|
|
4
|
-

|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
4
|
+

|
|
7
5
|
|
|
8
6
|
Type-safe HTTP client for Telegram Bot API with complete TypeScript support.
|
|
9
7
|
|
|
10
|
-
## Table of Contents
|
|
11
|
-
|
|
12
|
-
- [Motivation](#motivation)
|
|
13
|
-
- [Features](#features)
|
|
14
|
-
- [Installation](#installation)
|
|
15
|
-
- [Quick Start](#quick-start)
|
|
16
|
-
- [Usage Examples](#usage-examples)
|
|
17
|
-
- [Sending Messages](#sending-messages)
|
|
18
|
-
- [Sending Files](#sending-files)
|
|
19
|
-
- [Downloading Files](#downloading-files)
|
|
20
|
-
- [Using Message Effects](#using-message-effects)
|
|
21
|
-
- [Error Handling](#error-handling)
|
|
22
|
-
- [API Reference](#api-reference)
|
|
23
|
-
- [Configuration](#configuration)
|
|
24
|
-
- [Related Packages](#related-packages)
|
|
25
|
-
- [Contributing](#contributing)
|
|
26
|
-
- [License](#license)
|
|
27
|
-
|
|
28
|
-
## Motivation
|
|
29
|
-
|
|
30
|
-
**Telegram** does not offer an official TypeScript **SDK** for their **API** but they provide documentation in HTML format.
|
|
31
|
-
|
|
32
|
-
This package provides a lightweight, type-safe HTTP client that uses types generated from the [official Telegram Bot API documentation](https://core.telegram.org/bots/api), ensuring it stays in sync with the latest API updates.
|
|
33
|
-
|
|
34
|
-
## Features
|
|
35
|
-
|
|
36
|
-
- **Type-Safe**: Full TypeScript support with types generated from official documentation
|
|
37
|
-
- **Lightweight**: Minimal dependencies
|
|
38
|
-
- **Complete API Coverage**: All Bot API methods are supported
|
|
39
|
-
- **Smart Type Conversion**: Automatic mapping of Telegram types to TypeScript:
|
|
40
|
-
- `Integer` → `number`
|
|
41
|
-
- `True` → `boolean`
|
|
42
|
-
- `String or Number` → `string | number`
|
|
43
|
-
- Enumerated types → Union of literal types (e.g., `"private" | "group" | "supergroup" | "channel"`)
|
|
44
|
-
- **File Handling**: Simplified file upload and download
|
|
45
|
-
- **Message Effects**: Built-in constants for message effects
|
|
46
|
-
- **Custom Base URL**: Support for custom Telegram Bot API servers
|
|
47
|
-
|
|
48
8
|
## Installation
|
|
49
9
|
|
|
50
10
|
```bash
|
|
51
11
|
npm install @effect-ak/tg-bot-client
|
|
52
12
|
```
|
|
53
13
|
|
|
54
|
-
```bash
|
|
55
|
-
pnpm add @effect-ak/tg-bot-client
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
yarn add @effect-ak/tg-bot-client
|
|
60
|
-
```
|
|
61
|
-
|
|
62
14
|
## Quick Start
|
|
63
15
|
|
|
64
16
|
```typescript
|
|
65
17
|
import { makeTgBotClient } from "@effect-ak/tg-bot-client"
|
|
66
18
|
|
|
67
19
|
const client = makeTgBotClient({
|
|
68
|
-
bot_token: "YOUR_BOT_TOKEN"
|
|
20
|
+
bot_token: "YOUR_BOT_TOKEN"
|
|
69
21
|
})
|
|
70
22
|
|
|
71
|
-
// Send a message
|
|
72
23
|
await client.execute("send_message", {
|
|
73
24
|
chat_id: "123456789",
|
|
74
25
|
text: "Hello, World!"
|
|
75
26
|
})
|
|
76
27
|
```
|
|
77
28
|
|
|
78
|
-
##
|
|
79
|
-
|
|
80
|
-
### Sending Messages
|
|
81
|
-
|
|
82
|
-
#### Basic Text Message
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
await client.execute("send_message", {
|
|
86
|
-
chat_id: "123456789",
|
|
87
|
-
text: "Hello from TypeScript!"
|
|
88
|
-
})
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
#### Message with Formatting
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
await client.execute("send_message", {
|
|
95
|
-
chat_id: "123456789",
|
|
96
|
-
text: "*Bold* _italic_ `code`",
|
|
97
|
-
parse_mode: "Markdown"
|
|
98
|
-
})
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
#### Message with Inline Keyboard
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
await client.execute("send_message", {
|
|
105
|
-
chat_id: "123456789",
|
|
106
|
-
text: "Choose an option:",
|
|
107
|
-
reply_markup: {
|
|
108
|
-
inline_keyboard: [
|
|
109
|
-
[
|
|
110
|
-
{ text: "Option 1", callback_data: "opt_1" },
|
|
111
|
-
{ text: "Option 2", callback_data: "opt_2" }
|
|
112
|
-
]
|
|
113
|
-
]
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Sending Files
|
|
119
|
-
|
|
120
|
-
#### Sending a Dice
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
await client.execute("send_dice", {
|
|
124
|
-
chat_id: "123456789",
|
|
125
|
-
emoji: "🎲"
|
|
126
|
-
})
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
#### Sending a Document
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
await client.execute("send_document", {
|
|
133
|
-
chat_id: "123456789",
|
|
134
|
-
document: {
|
|
135
|
-
file_content: new TextEncoder().encode("Hello from file!"),
|
|
136
|
-
file_name: "hello.txt"
|
|
137
|
-
},
|
|
138
|
-
caption: "Simple text file"
|
|
139
|
-
})
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
#### Sending a Photo
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
await client.execute("send_photo", {
|
|
146
|
-
chat_id: "123456789",
|
|
147
|
-
photo: {
|
|
148
|
-
file_content: photoBuffer,
|
|
149
|
-
file_name: "image.jpg"
|
|
150
|
-
},
|
|
151
|
-
caption: "Check out this photo!"
|
|
152
|
-
})
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Downloading Files
|
|
156
|
-
|
|
157
|
-
To download a file from Telegram servers, use the `getFile` method. It handles both the API call and file download in one step:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
// Get file by file_id (from a message, for example)
|
|
161
|
-
const file = await client.getFile({
|
|
162
|
-
file_id: "AgACAgIAAxkBAAI..."
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
// file is a standard File object
|
|
166
|
-
console.log(file.name) // filename.jpg
|
|
167
|
-
console.log(file.size) // file size in bytes
|
|
168
|
-
|
|
169
|
-
// Read file contents
|
|
170
|
-
const arrayBuffer = await file.arrayBuffer()
|
|
171
|
-
const text = await file.text()
|
|
172
|
-
const blob = await file.blob()
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Using Message Effects
|
|
176
|
-
|
|
177
|
-
The library includes built-in constants for message effects:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
import { MESSAGE_EFFECTS } from "@effect-ak/tg-bot-client"
|
|
181
|
-
|
|
182
|
-
await client.execute("send_message", {
|
|
183
|
-
chat_id: "123456789",
|
|
184
|
-
text: "Message with fire effect!",
|
|
185
|
-
message_effect_id: MESSAGE_EFFECTS["🔥"]
|
|
186
|
-
})
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Available effects:
|
|
190
|
-
- `MESSAGE_EFFECTS["🔥"]` - Fire
|
|
191
|
-
- `MESSAGE_EFFECTS["👍"]` - Thumbs up
|
|
192
|
-
- `MESSAGE_EFFECTS["👎"]` - Thumbs down
|
|
193
|
-
- `MESSAGE_EFFECTS["❤️"]` - Heart
|
|
194
|
-
- `MESSAGE_EFFECTS["🎉"]` - Party
|
|
195
|
-
- `MESSAGE_EFFECTS["💩"]` - Poop
|
|
196
|
-
|
|
197
|
-
## Error Handling
|
|
198
|
-
|
|
199
|
-
The client throws `TgBotClientError` for all errors:
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
import { TgBotClientError } from "@effect-ak/tg-bot-client"
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
await client.execute("send_message", {
|
|
206
|
-
chat_id: "invalid",
|
|
207
|
-
text: "Test"
|
|
208
|
-
})
|
|
209
|
-
} catch (error) {
|
|
210
|
-
if (error instanceof TgBotClientError) {
|
|
211
|
-
console.error("Error type:", error.cause._tag)
|
|
212
|
-
|
|
213
|
-
switch (error.cause._tag) {
|
|
214
|
-
case "NotOkResponse":
|
|
215
|
-
console.error("API error:", error.cause.errorCode, error.cause.details)
|
|
216
|
-
break
|
|
217
|
-
case "UnexpectedResponse":
|
|
218
|
-
console.error("Unexpected response:", error.cause.response)
|
|
219
|
-
break
|
|
220
|
-
case "ClientInternalError":
|
|
221
|
-
console.error("Internal error:", error.cause.cause)
|
|
222
|
-
break
|
|
223
|
-
case "UnableToGetFile":
|
|
224
|
-
console.error("File download error:", error.cause.cause)
|
|
225
|
-
break
|
|
226
|
-
case "NotJsonResponse":
|
|
227
|
-
console.error("Invalid JSON response:", error.cause.response)
|
|
228
|
-
break
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## API Reference
|
|
235
|
-
|
|
236
|
-
### `makeTgBotClient(config)`
|
|
237
|
-
|
|
238
|
-
Creates a new Telegram Bot API client.
|
|
239
|
-
|
|
240
|
-
**Parameters:**
|
|
241
|
-
- `config.bot_token` (string, required): Your bot token from @BotFather
|
|
242
|
-
- `config.base_url` (string, optional): Custom API base URL (default: `https://api.telegram.org`)
|
|
243
|
-
|
|
244
|
-
**Returns:** `TgBotClient`
|
|
245
|
-
|
|
246
|
-
### `client.execute(method, input)`
|
|
247
|
-
|
|
248
|
-
Executes a Telegram Bot API method.
|
|
249
|
-
|
|
250
|
-
**Parameters:**
|
|
251
|
-
- `method` (string): API method name in snake_case (e.g., `"send_message"`, `"get_updates"`)
|
|
252
|
-
- `input` (object): Method parameters as defined in Telegram Bot API
|
|
253
|
-
|
|
254
|
-
**Returns:** `Promise<ApiResponse>` - Typed response based on the method
|
|
255
|
-
|
|
256
|
-
**Note:** Method names use snake_case (e.g., `send_message`) instead of camelCase for better readability and consistency with the Telegram API documentation.
|
|
257
|
-
|
|
258
|
-
### `client.getFile(input)`
|
|
259
|
-
|
|
260
|
-
Downloads a file from Telegram servers.
|
|
261
|
-
|
|
262
|
-
**Parameters:**
|
|
263
|
-
- `input.file_id` (string): File identifier from a message
|
|
264
|
-
|
|
265
|
-
**Returns:** `Promise<File>` - Standard [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object
|
|
266
|
-
|
|
267
|
-
## Configuration
|
|
268
|
-
|
|
269
|
-
### Custom Base URL
|
|
270
|
-
|
|
271
|
-
If you're running your own Telegram Bot API server:
|
|
272
|
-
|
|
273
|
-
```typescript
|
|
274
|
-
const client = makeTgBotClient({
|
|
275
|
-
bot_token: "YOUR_BOT_TOKEN",
|
|
276
|
-
base_url: "https://your-custom-server.com"
|
|
277
|
-
})
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Related Packages
|
|
281
|
-
|
|
282
|
-
This package is part of the `tg-bot-client` monorepo:
|
|
29
|
+
## Features
|
|
283
30
|
|
|
284
|
-
- **
|
|
285
|
-
- **
|
|
286
|
-
- **
|
|
31
|
+
- **Type-Safe** — full TypeScript support with types from official documentation
|
|
32
|
+
- **Lightweight** — minimal dependencies
|
|
33
|
+
- **Complete API Coverage** — all Bot API methods supported
|
|
34
|
+
- **File Handling** — simplified upload and download
|
|
35
|
+
- **Message Effects** — built-in constants
|
|
287
36
|
|
|
288
|
-
##
|
|
37
|
+
## Documentation
|
|
289
38
|
|
|
290
|
-
|
|
39
|
+
Full documentation, usage examples, and error handling: **[tg-bot-sdk docs](https://github.com/kondaurovDev/tg-bot-sdk)**
|
|
291
40
|
|
|
292
41
|
## License
|
|
293
42
|
|
|
294
|
-
MIT
|
|
43
|
+
MIT
|
package/src/client.ts
CHANGED
|
@@ -1,35 +1,75 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { executeTgBotMethod, type ExecuteMethod } from "./execute"
|
|
3
|
-
import { getFile as getFileImpl, type TgFile } from "./client-file"
|
|
4
|
-
import { TG_BOT_API_URL } from "./const"
|
|
1
|
+
import { executeTgBotMethod, type ExecuteMethod, type ClientResult } from "./execute"
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
readonly config: Required<TgClientConfig>
|
|
8
|
-
readonly execute: ExecuteMethod
|
|
9
|
-
readonly getFile: (input: { fileId: string; type?: string }) => Promise<TgFile>
|
|
10
|
-
}
|
|
3
|
+
const TG_BOT_API_URL = "https://api.telegram.org"
|
|
11
4
|
|
|
12
5
|
export interface TgClientConfig {
|
|
13
6
|
bot_token: string
|
|
14
7
|
base_url?: string
|
|
15
8
|
}
|
|
16
9
|
|
|
10
|
+
export interface TgFile {
|
|
11
|
+
readonly content: ArrayBuffer
|
|
12
|
+
readonly file_name: string
|
|
13
|
+
readonly base64String: () => string
|
|
14
|
+
readonly file: () => File
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TgBotClient {
|
|
18
|
+
readonly config: Required<TgClientConfig>
|
|
19
|
+
readonly execute: ExecuteMethod
|
|
20
|
+
readonly getFile: (input: { fileId: string; type?: string }) => Promise<ClientResult<TgFile>>
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
export function makeTgBotClient(config: TgClientConfig): TgBotClient {
|
|
18
24
|
const tgConfig = {
|
|
19
25
|
bot_token: config.bot_token,
|
|
20
26
|
base_url: config.base_url ?? TG_BOT_API_URL
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
const execute =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
})
|
|
29
|
+
const execute: ExecuteMethod = (method, input) =>
|
|
30
|
+
executeTgBotMethod({
|
|
31
|
+
config: tgConfig, method, input: input as any
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const getFile = async (params: { fileId: string; type?: string }): Promise<ClientResult<TgFile>> => {
|
|
35
|
+
const response = await execute("get_file", { file_id: params.fileId })
|
|
36
|
+
|
|
37
|
+
if (!response.ok) return response
|
|
38
|
+
|
|
39
|
+
const file_path = response.data.file_path
|
|
40
|
+
|
|
41
|
+
if (!file_path || file_path.length === 0) {
|
|
42
|
+
return {
|
|
43
|
+
ok: false,
|
|
44
|
+
error: { _tag: "UnableToGetFile", cause: "File path not defined" }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const file_name = file_path.replaceAll("/", "-")
|
|
49
|
+
const url = `${tgConfig.base_url}/file/bot${tgConfig.bot_token}/${file_path}`
|
|
50
|
+
|
|
51
|
+
let content: ArrayBuffer
|
|
52
|
+
try {
|
|
53
|
+
content = await fetch(url).then((_) => _.arrayBuffer())
|
|
54
|
+
} catch (cause) {
|
|
55
|
+
return { ok: false, error: { _tag: "UnableToGetFile", cause } }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const base64String = () => Buffer.from(content).toString("base64")
|
|
59
|
+
const file = () =>
|
|
60
|
+
new File([content], file_name, {
|
|
61
|
+
...(params.type ? { type: params.type } : {})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
data: { content, file_name, base64String, file }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
29
69
|
|
|
30
70
|
return {
|
|
31
71
|
config: tgConfig,
|
|
32
72
|
execute,
|
|
33
|
-
getFile
|
|
73
|
+
getFile
|
|
34
74
|
}
|
|
35
75
|
}
|
package/src/execute.ts
CHANGED
|
@@ -1,14 +1,87 @@
|
|
|
1
1
|
import type { Api } from "@effect-ak/tg-bot-api"
|
|
2
2
|
|
|
3
|
-
import { TgBotClientError } from "./errors"
|
|
4
|
-
import { isFileContent, isTgBotApiResponse } from "./guards"
|
|
5
|
-
import { snakeToCamel } from "./utils"
|
|
6
3
|
import type { TgClientConfig } from "./client"
|
|
7
4
|
|
|
5
|
+
// --- result ---
|
|
6
|
+
|
|
7
|
+
export type ClientErrorReason =
|
|
8
|
+
| { _tag: "NotOkResponse"; errorCode?: number; details?: string }
|
|
9
|
+
| { _tag: "UnexpectedResponse"; response: unknown }
|
|
10
|
+
| { _tag: "ClientInternalError"; cause: unknown }
|
|
11
|
+
| { _tag: "UnableToGetFile"; cause: unknown }
|
|
12
|
+
| { _tag: "BotHandlerError"; cause: unknown }
|
|
13
|
+
| { _tag: "NotJsonResponse"; response: unknown }
|
|
14
|
+
|
|
15
|
+
export type ClientResult<T> =
|
|
16
|
+
| { readonly ok: true; readonly data: T }
|
|
17
|
+
| { readonly ok: false; readonly error: ClientErrorReason }
|
|
18
|
+
|
|
19
|
+
// --- guards ---
|
|
20
|
+
|
|
21
|
+
export interface FileContent {
|
|
22
|
+
file_content: Uint8Array<ArrayBuffer>
|
|
23
|
+
file_name: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isFileContent = (input: unknown): input is FileContent =>
|
|
27
|
+
typeof input == "object" &&
|
|
28
|
+
input != null &&
|
|
29
|
+
"file_content" in input &&
|
|
30
|
+
input.file_content instanceof Uint8Array &&
|
|
31
|
+
"file_name" in input &&
|
|
32
|
+
typeof input.file_name == "string"
|
|
33
|
+
|
|
34
|
+
interface TgBotApiResponseSchema {
|
|
35
|
+
ok: boolean
|
|
36
|
+
error_code?: number
|
|
37
|
+
description?: string
|
|
38
|
+
result?: unknown
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const isTgBotApiResponse = (
|
|
42
|
+
input: unknown
|
|
43
|
+
): input is TgBotApiResponseSchema =>
|
|
44
|
+
typeof input == "object" &&
|
|
45
|
+
input != null &&
|
|
46
|
+
"ok" in input &&
|
|
47
|
+
typeof input.ok == "boolean"
|
|
48
|
+
|
|
49
|
+
// --- message effects ---
|
|
50
|
+
|
|
51
|
+
export const MESSAGE_EFFECTS = {
|
|
52
|
+
"🔥": "5104841245755180586",
|
|
53
|
+
"👍": "5107584321108051014",
|
|
54
|
+
"👎": "5104858069142078462",
|
|
55
|
+
"❤️": "5159385139981059251",
|
|
56
|
+
"🎉": "5046509860389126442",
|
|
57
|
+
"💩": "5046589136895476101"
|
|
58
|
+
} as const
|
|
59
|
+
|
|
60
|
+
export type MessageEffect = keyof typeof MESSAGE_EFFECTS
|
|
61
|
+
|
|
62
|
+
export const messageEffectIdCodes = Object.keys(
|
|
63
|
+
MESSAGE_EFFECTS
|
|
64
|
+
) as MessageEffect[]
|
|
65
|
+
|
|
66
|
+
const isMessageEffect = (input: unknown): input is MessageEffect => {
|
|
67
|
+
return typeof input === "string" && input in MESSAGE_EFFECTS
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// --- execute ---
|
|
71
|
+
|
|
72
|
+
const snakeToCamel = (str: string): string => {
|
|
73
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type WithMessageEffect<T> =
|
|
77
|
+
T extends { message_effect_id?: string }
|
|
78
|
+
? Omit<T, "message_effect_id"> & { message_effect_id?: MessageEffect | (string & {}) }
|
|
79
|
+
: T
|
|
80
|
+
|
|
8
81
|
export type ExecuteMethod = <M extends keyof Api>(
|
|
9
82
|
method: M,
|
|
10
|
-
input: Parameters<Api[M]>[0]
|
|
11
|
-
) => Promise<ReturnType<Api[M]
|
|
83
|
+
input: WithMessageEffect<Parameters<Api[M]>[0]>
|
|
84
|
+
) => Promise<ClientResult<ReturnType<Api[M]>>>
|
|
12
85
|
|
|
13
86
|
export async function executeTgBotMethod<M extends keyof Api>(
|
|
14
87
|
params: {
|
|
@@ -16,8 +89,13 @@ export async function executeTgBotMethod<M extends keyof Api>(
|
|
|
16
89
|
method: M
|
|
17
90
|
input: Parameters<Api[M]>[0]
|
|
18
91
|
}
|
|
19
|
-
): Promise<ReturnType<Api[M]
|
|
20
|
-
const { config, method
|
|
92
|
+
): Promise<ClientResult<ReturnType<Api[M]>>> {
|
|
93
|
+
const { config, method } = params
|
|
94
|
+
let { input } = params
|
|
95
|
+
|
|
96
|
+
if ("message_effect_id" in input && isMessageEffect(input.message_effect_id)) {
|
|
97
|
+
input = { ...input, message_effect_id: MESSAGE_EFFECTS[input.message_effect_id] }
|
|
98
|
+
}
|
|
21
99
|
|
|
22
100
|
let httpResponse: Response
|
|
23
101
|
try {
|
|
@@ -29,37 +107,32 @@ export async function executeTgBotMethod<M extends keyof Api>(
|
|
|
29
107
|
}
|
|
30
108
|
)
|
|
31
109
|
} catch (cause) {
|
|
32
|
-
|
|
33
|
-
cause: { _tag: "ClientInternalError", cause }
|
|
34
|
-
})
|
|
110
|
+
return { ok: false, error: { _tag: "ClientInternalError", cause } }
|
|
35
111
|
}
|
|
36
112
|
|
|
37
113
|
let response: unknown
|
|
38
114
|
try {
|
|
39
115
|
response = await httpResponse.json()
|
|
40
116
|
} catch {
|
|
41
|
-
|
|
42
|
-
cause: { _tag: "NotJsonResponse", response: httpResponse }
|
|
43
|
-
})
|
|
117
|
+
return { ok: false, error: { _tag: "NotJsonResponse", response: httpResponse } }
|
|
44
118
|
}
|
|
45
119
|
|
|
46
120
|
if (!isTgBotApiResponse(response)) {
|
|
47
|
-
|
|
48
|
-
cause: { _tag: "UnexpectedResponse", response }
|
|
49
|
-
})
|
|
121
|
+
return { ok: false, error: { _tag: "UnexpectedResponse", response } }
|
|
50
122
|
}
|
|
51
123
|
|
|
52
124
|
if (!httpResponse.ok) {
|
|
53
|
-
|
|
54
|
-
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
error: {
|
|
55
128
|
_tag: "NotOkResponse",
|
|
56
129
|
...(response.error_code ? { errorCode: response.error_code } : {}),
|
|
57
130
|
...(response.description ? { details: response.description } : {})
|
|
58
131
|
}
|
|
59
|
-
}
|
|
132
|
+
}
|
|
60
133
|
}
|
|
61
134
|
|
|
62
|
-
return response.result as ReturnType<Api[M]>
|
|
135
|
+
return { ok: true, data: response.result as ReturnType<Api[M]> }
|
|
63
136
|
}
|
|
64
137
|
|
|
65
138
|
export const makePayload = (body: object): FormData | undefined => {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
export {
|
|
2
|
+
MESSAGE_EFFECTS,
|
|
3
|
+
messageEffectIdCodes,
|
|
4
|
+
makePayload,
|
|
5
|
+
executeTgBotMethod
|
|
6
|
+
} from "./execute"
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
ClientResult,
|
|
10
|
+
ClientErrorReason,
|
|
11
|
+
FileContent,
|
|
12
|
+
MessageEffect,
|
|
13
|
+
ExecuteMethod
|
|
14
|
+
} from "./execute"
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
makeTgBotClient
|
|
18
|
+
} from "./client"
|
|
19
|
+
|
|
20
|
+
export type {
|
|
21
|
+
TgBotClient,
|
|
22
|
+
TgClientConfig,
|
|
23
|
+
TgFile
|
|
24
|
+
} from "./client"
|