@grom.js/effect-tg 0.9.0 → 0.11.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.
Files changed (48) hide show
  1. package/README.md +387 -22
  2. package/dist/BotApi.d.ts +32 -0
  3. package/dist/BotApi.d.ts.map +1 -1
  4. package/dist/BotApi.js +16 -0
  5. package/dist/BotApi.js.map +1 -1
  6. package/dist/BotApiError.js +1 -1
  7. package/dist/BotApiError.js.map +1 -1
  8. package/dist/Dialog.d.ts +24 -23
  9. package/dist/Dialog.d.ts.map +1 -1
  10. package/dist/Dialog.js +15 -17
  11. package/dist/Dialog.js.map +1 -1
  12. package/dist/Markup.d.ts +200 -11
  13. package/dist/Markup.d.ts.map +1 -1
  14. package/dist/Markup.js +39 -0
  15. package/dist/Markup.js.map +1 -1
  16. package/dist/Reply.d.ts +22 -0
  17. package/dist/Reply.d.ts.map +1 -0
  18. package/dist/Reply.js +18 -0
  19. package/dist/Reply.js.map +1 -0
  20. package/dist/Send.d.ts +16 -1
  21. package/dist/Send.d.ts.map +1 -1
  22. package/dist/Send.js +28 -5
  23. package/dist/Send.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/internal/botApi.gen.d.ts +263 -191
  29. package/dist/internal/botApi.gen.d.ts.map +1 -1
  30. package/dist/internal/dialog.d.ts +4 -4
  31. package/dist/internal/dialog.d.ts.map +1 -1
  32. package/dist/internal/dialog.js +12 -13
  33. package/dist/internal/dialog.js.map +1 -1
  34. package/dist/internal/send.d.ts +4 -1
  35. package/dist/internal/send.d.ts.map +1 -1
  36. package/dist/internal/send.js +150 -6
  37. package/dist/internal/send.js.map +1 -1
  38. package/package.json +11 -10
  39. package/src/BotApi.ts +62 -0
  40. package/src/BotApiError.ts +1 -1
  41. package/src/Dialog.ts +31 -30
  42. package/src/Markup.ts +251 -11
  43. package/src/Reply.ts +36 -0
  44. package/src/Send.ts +51 -17
  45. package/src/index.ts +1 -0
  46. package/src/internal/botApi.gen.ts +267 -191
  47. package/src/internal/dialog.ts +16 -17
  48. package/src/internal/send.ts +170 -6
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # effect-tg
2
2
 
3
- [![Effectful](https://img.shields.io/badge/Effectful-%23000?style=flat&logo=effect&logoColor=%23fff)](https://effect.website/)
4
- [![npm](https://img.shields.io/npm/v/%40grom.js%2Feffect-tg?style=flat&logo=npm&logoColor=%23BB443E&logoSize=auto&label=%C2%A0&labelColor=%23fff&color=%23BB443E)](https://www.npmjs.com/package/@grom.js/effect-tg)
3
+ [![Effectful](https://img.shields.io/badge/Yes.-%23fff?style=flat&logo=effect&logoColor=%23000&logoSize=auto&label=Effect%3F&labelColor=%23fff&color=%23000)](https://effect.website/)
4
+ [![Bot API](https://img.shields.io/badge/v9.4-%23fff?style=flat&logo=telegram&logoColor=%2325A3E1&logoSize=auto&label=Bot%20API&labelColor=%23fff&color=%2325A3E1)](https://core.telegram.org/bots/api)
5
+ [![npm](https://img.shields.io/npm/v/%40grom.js%2Feffect-tg?style=flat&logo=npm&logoColor=%23BB443E&logoSize=auto&label=Latest&labelColor=%23fff&color=%23BB443E)](https://www.npmjs.com/package/@grom.js/effect-tg)
6
+ [![codecov](https://img.shields.io/codecov/c/github/grom-dev/effect-tg?style=flat&logo=codecov&logoColor=%23f07&label=Coverage&labelColor=%23fff&color=%23f07)](https://codecov.io/gh/grom-dev/effect-tg)
5
7
 
6
8
  Effectful library for crafting Telegram bots.
7
9
 
@@ -9,6 +11,8 @@ Effectful library for crafting Telegram bots.
9
11
 
10
12
  - Modular design to build with [Effect](https://effect.website).
11
13
  - Complete type definitions for [Bot API](https://core.telegram.org/bots/api) methods and types.
14
+ - Composable API for [sending messages](#sending-messages).
15
+ - [JSX syntax](#jsx-syntax) support for creating formatted text.
12
16
 
13
17
  ## Installation
14
18
 
@@ -18,6 +22,9 @@ npm install @grom.js/effect-tg
18
22
 
19
23
  # Install Effect dependencies
20
24
  npm install effect @effect/platform
25
+
26
+ # Install JSX runtime for formatted text
27
+ npm install @grom.js/tgx
21
28
  ```
22
29
 
23
30
  ## Working with Bot API
@@ -66,7 +73,7 @@ const program = Effect.gen(function* () {
66
73
  `BotApi` has a layered architecture:
67
74
 
68
75
  ```
69
- ┌• BotApi — typed interface that delegates calls to `BotApiTransport`.
76
+ ┌• BotApi — typed interface that delegates calls to BotApiTransport.
70
77
  └─┬• BotApiTransport — serializes parameters, sends HTTP requests, parses responses.
71
78
  ├──• BotApiUrl — constructs endpoint URLs to methods and files.
72
79
  └──• HttpClient — performs HTTP requests.
@@ -78,22 +85,25 @@ This design enables:
78
85
  - **Testability**: Mock implementation of `BotApiTransport` or `HttpClient` to test your bot.
79
86
  - **Portability:** Provide different `BotApiUrl` to run a bot on test environment or with local Bot API server.
80
87
 
81
- **Example:** Constructing `BotApi` layer.
88
+ **Example:** Constructing `BotApi` layer with method call tracing.
82
89
 
83
90
  ```ts
84
91
  import { FetchHttpClient } from '@effect/platform'
85
- import { BotApi, BotApiTransport, BotApiUrl } from '@grom.js/effect-tg'
86
- import { Effect, Layer } from 'effect'
87
-
88
- const BotApiLive = BotApi.layer.pipe(
89
- Layer.provide(BotApiTransport.layer),
90
- Layer.provide(
91
- Layer.succeed(
92
- BotApiUrl.BotApiUrl,
93
- BotApiUrl.makeProd('YOUR_BOT_TOKEN')
94
- )
95
- ),
96
- Layer.provide(FetchHttpClient.layer),
92
+ import { BotApi } from '@grom.js/effect-tg'
93
+ import { Config, Effect, Layer } from 'effect'
94
+
95
+ const BotApiLive = Layer.provide(
96
+ BotApi.layerConfig({
97
+ token: Config.redacted('BOT_TOKEN'),
98
+ environment: 'prod',
99
+ transformTransport: transport => ({
100
+ sendRequest: (method, params) =>
101
+ transport.sendRequest(method, params).pipe(
102
+ Effect.withSpan(method),
103
+ ),
104
+ }),
105
+ }),
106
+ FetchHttpClient.layer
97
107
  )
98
108
  ```
99
109
 
@@ -101,11 +111,11 @@ const BotApiLive = BotApi.layer.pipe(
101
111
 
102
112
  Failed `BotApi` method calls result in `BotApiError`, which is a union of tagged errors with additional information:
103
113
 
104
- - `TransportError` — HTTP or network failure. `cause` property contains the original error from `HttpClient`.
105
- - `RateLimited` — bot has exceeded flood limit. `retryAfter` property contains the duration to wait before retry.
106
- - `GroupUpgraded` — group has been migrated to supergroup. `supergroup` property contains an object with the new ID.
107
- - `MethodFailed` — response was unsuccessful, but the exact reason could not be determined. `possibleReason` property contains common failure reasons as string literals, determined by error code and description, which are subject to change.
108
- - `InternalServerError` — Bot API server failed with 5xx error code.
114
+ - **`TransportError`** — HTTP or network failure. The `cause` property contains the original error from `HttpClient`.
115
+ - **`RateLimited`** — bot has exceeded the flood limit. The `retryAfter` property contains the duration to wait before the next attempt.
116
+ - **`GroupUpgraded`** — group has been migrated to a supergroup. The `supergroup` property contains an object with the ID of the new supergroup.
117
+ - **`MethodFailed`** — response was unsuccessful, but the exact reason could not be determined. The `possibleReason` property contains a string literal representing one of the common failure reasons. It is determined by the error code and description of the Bot API response, which are subject to change.
118
+ - **`InternalServerError`** — Bot API server failed with a 5xx error code.
109
119
 
110
120
  All errors except `TransportError` also have `response` property that contains the original response from Bot API.
111
121
 
@@ -125,7 +135,7 @@ const program = BotApi.callMethod('doSomething').pipe(
125
135
  RateLimited: ({ retryAfter }) =>
126
136
  Effect.logError(`Try again in ${Duration.format(retryAfter)}`),
127
137
  GroupUpgraded: ({ supergroup }) =>
128
- Effect.logError(`Group now has a new ID: ${supergroup.id}`),
138
+ Effect.logError(`Group is now a supergroup with ID: ${supergroup.id}`),
129
139
  MethodFailed: ({ possibleReason, response }) =>
130
140
  Match.value(possibleReason).pipe(
131
141
  Match.when('BotBlockedByUser', () =>
@@ -164,3 +174,358 @@ type GiftsCollector = (
164
174
  BotApi.BotApi
165
175
  >
166
176
  ```
177
+
178
+ ## Sending messages
179
+
180
+ One of the most common tasks for a messenger bot is sending messages.
181
+
182
+ Bot API exposes multiple methods for sending a message, each corresponding to a different content type:
183
+
184
+ - `sendMessage` for text;
185
+ - `sendPhoto` for photos;
186
+ - `sendVideo` for videos;
187
+ - and so on.
188
+
189
+ `Send` module provides a unified, more composable way to send messages of all kinds.
190
+
191
+ ### Basic usage
192
+
193
+ To send a message, you need:
194
+
195
+ - **Content** — content of the message to be sent.
196
+ - **Dialog** — target chat and topic where the message will be sent.
197
+ - **Markup** — (optional) markup for replying to the message.
198
+ - **Reply** — (optional) information about the message being replied to.
199
+ - **Options** — (optional) additional options for sending the message.
200
+
201
+ `Send.sendMessage` function accepts mentioned parameters and returns an `Effect` that sends a message, automatically choosing the appropriate Bot API method based on the content type.
202
+
203
+ **Example:** Sending messages using `Send.sendMessage`.
204
+
205
+ ```ts
206
+ import { Content, Dialog, File, Markup, Reply, Send, Text } from '@grom.js/effect-tg'
207
+ import { Effect } from 'effect'
208
+
209
+ const program = Effect.gen(function* () {
210
+ // Plain text to a user
211
+ const greeting = yield* Send.sendMessage({
212
+ content: Content.text(Text.plain('Hey! Wanna roll a dice?')),
213
+ dialog: Dialog.user(382713),
214
+ })
215
+
216
+ // Photo with formatted caption and inline keyboard
217
+ yield* Send.sendMessage({
218
+ content: Content.photo(
219
+ File.External(new URL('https://cataas.com/cat')),
220
+ { caption: Text.html('<b>Cat of the day</b>\n<i>Rate this cat:</i>') },
221
+ ),
222
+ dialog: Dialog.user(382713),
223
+ markup: Markup.inlineKeyboard([
224
+ [Markup.InlineButton.callback('❤️', 'rate_love')],
225
+ [Markup.InlineButton.callback('👎', 'rate_nope')],
226
+ ]),
227
+ })
228
+
229
+ // Reply with a dice
230
+ const roll = yield* Send.sendMessage({
231
+ content: Content.dice('🎲'),
232
+ dialog: Dialog.user(382713),
233
+ reply: Reply.toMessage(greeting),
234
+ })
235
+
236
+ const rolled = roll.dice!.value
237
+ if (rolled === 6) {
238
+ // DM channel
239
+ yield* Send.sendMessage({
240
+ content: Content.text(Text.plain(`User 382713 rolled ${rolled}.`)),
241
+ dialog: Dialog.channel(100200).directMessages(42),
242
+ })
243
+ }
244
+ else {
245
+ // Send silently
246
+ yield* Send.sendMessage({
247
+ content: Content.text(Text.plain(`You rolled ${rolled}. Disappointing.`)),
248
+ dialog: Dialog.user(382713),
249
+ options: Send.options({ disableNotification: true })
250
+ })
251
+ }
252
+ })
253
+ ```
254
+
255
+ #### Content
256
+
257
+ `Content` module provides constructors for creating objects that represent the content of a message. `Send.sendMessage` uses the content type to choose the appropriate Bot API method automatically.
258
+
259
+ | Constructor | Bot API method | Description |
260
+ | ---------------------- | --------------- | ---------------------- |
261
+ | `Content.text` | `sendMessage` | Text |
262
+ | `Content.photo` | `sendPhoto` | Photo |
263
+ | `Content.video` | `sendVideo` | Video |
264
+ | `Content.animation` | `sendAnimation` | GIF or video w/o sound |
265
+ | `Content.audio` | `sendAudio` | Audio file |
266
+ | `Content.voice` | `sendVoice` | Voice note |
267
+ | `Content.videoNote` | `sendVideoNote` | Round video note |
268
+ | `Content.document` | `sendDocument` | File of any type |
269
+ | `Content.sticker` | `sendSticker` | Sticker |
270
+ | `Content.location` | `sendLocation` | Static location |
271
+ | `Content.liveLocation` | `sendLocation` | Live location |
272
+ | `Content.venue` | `sendVenue` | Venue with address |
273
+ | `Content.contact` | `sendContact` | Phone contact |
274
+ | `Content.dice` | `sendDice` | Random dice |
275
+
276
+ #### Dialog
277
+
278
+ `Dialog` module provides utilities for creating target chats:
279
+
280
+ - `Dialog.user(id)` — private chat with a user.
281
+ - `Dialog.group(id)` — chat of a (basic) group.
282
+ - `Dialog.supergroup(id)` — supergroup chat.
283
+ - `Dialog.channel(id)` — channel.
284
+
285
+ To target a specific topic, chain a method on the peer:
286
+
287
+ - `Dialog.user(id).topic(topicId)` — topic in a private chat.
288
+ - `Dialog.supergroup(id).topic(topicId)` — topic in a forum supergroup.
289
+ - `Dialog.channel(id).directMessages(topicId)` — channel direct messages.
290
+
291
+ `Dialog.ofMessage` helper extracts the dialog from an incoming `Message` object.
292
+
293
+ ##### Dialog and peer IDs
294
+
295
+ Bot API uses a single integer to encode peer type with its ID — [dialog ID](https://core.telegram.org/api/bots/ids).
296
+
297
+ This may not be a problem for user IDs, since user IDs map to the same dialog IDs.
298
+ However, this may cause some defects when working with other peers.
299
+ For example, to send a message to a channel with ID `3011378744`, you need to set `chat_id` parameter to `-1003011378744`.
300
+
301
+ To prevent this confusion, `Dialog` module defines **branded types** that distinguish peer IDs from dialog IDs at the type level:
302
+
303
+ - `UserId` — number representing a user ID.
304
+ - `GroupId` — number representing a group ID.
305
+ - `ChannelId` — number representing a channel ID.
306
+ - `SupergroupId` — alias to `ChannelId`, since supergroups share ID space with channels.
307
+ - `DialogId` — number encoding peer type and peer ID.
308
+
309
+ Constructors like `Dialog.user`, `Dialog.channel`, etc. validate and encode IDs internally, so you rarely need to convert manually. When you do, `Dialog` module exports conversion utilities:
310
+
311
+ - `Dialog.decodeDialogId(dialogId)` — decodes a dialog ID into peer type and peer ID.
312
+ - `Dialog.decodePeerId(peer, dialogId)` — extracts a typed peer ID from a dialog ID.
313
+ - `Dialog.encodePeerId(peer, id)` — encodes a peer type and ID into a dialog ID.
314
+
315
+ #### Markup
316
+
317
+ `Markup` module provides reply markup types and constructors:
318
+
319
+ - `Markup.inlineKeyboard(rows)` — inline keyboard attached to the message.
320
+ - `Markup.replyKeyboard(rows, options?)` — custom keyboard for quick reply or other action.
321
+ - `Markup.replyKeyboardRemove()` — hide a previously shown reply keyboard.
322
+ - `Markup.forceReply()` — forces Telegram client to reply to the message.
323
+
324
+ **Example:** Creating reply markups.
325
+
326
+ ```ts
327
+ import { Markup } from '@grom.js/effect-tg'
328
+
329
+ const inline = Markup.inlineKeyboard([
330
+ [Markup.InlineButton.callback('Like', 'liked')],
331
+ [Markup.InlineButton.url('Source code', 'https://github.com/grom-dev/effect-tg')],
332
+ ])
333
+
334
+ const reply = Markup.replyKeyboard(
335
+ [
336
+ ['Option A', 'Option B'],
337
+ [Markup.ReplyButton.requestContact('Share phone')],
338
+ ],
339
+ { oneTime: true, resizable: true }
340
+ )
341
+ ```
342
+
343
+ ### Prepared messages
344
+
345
+ `Send.message` creates a `MessageToSend` — prepared message that bundles content, markup, reply, and options.
346
+
347
+ `MessageToSend` is also an `Effect`, which means:
348
+
349
+ - It can be piped to chain modifiers that customize markup, reply, and options.
350
+ - It can be executed to send the message. To be sent, `Send.TargetDialog` service should be provided.
351
+
352
+ **Example:** Creating and sending prepared messages.
353
+
354
+ ```ts
355
+ import { Content, Dialog, Send, Text } from '@grom.js/effect-tg'
356
+ import { Effect } from 'effect'
357
+
358
+ // Reusable template
359
+ const welcomeMessage = Send.message(
360
+ Content.text(Text.html('<b>Welcome!</b> Thanks for joining.'))
361
+ ).pipe(
362
+ Send.withMarkup(
363
+ Markup.replyKeyboard([
364
+ [Markup.ReplyButton.text('Effect?')],
365
+ [Markup.ReplyButton.text('Die.')],
366
+ ]),
367
+ ),
368
+ )
369
+
370
+ // Send to different dialogs
371
+ const greet1 = Effect.gen(function* () {
372
+ yield* welcomeMessage.pipe(Send.to(Dialog.user(123)))
373
+ yield* welcomeMessage.pipe(Send.to(Dialog.channel(321)))
374
+ })
375
+
376
+ // Send to the same dialog with different options
377
+ const greet2 = Effect.gen(function* () {
378
+ yield* welcomeMessage.pipe(Send.withoutNotification)
379
+ yield* welcomeMessage.pipe(Send.withContentProtection)
380
+ }).pipe(
381
+ Send.to(Dialog.supergroup(4).topic(2)),
382
+ )
383
+ ```
384
+
385
+ ### Composing options
386
+
387
+ Chain modifiers on a `MessageToSend` to customize its behavior:
388
+
389
+ - `withMarkup`/`withoutMarkup` — set/remove reply markup.
390
+ - `withReply`/`withoutReply` — set/remove reply options.
391
+ - `withNotification`/`withoutNotification` — enable/disable notification sound.
392
+ - `withContentProtection`/`withoutContentProtection` — prevent/allow forwarding and saving.
393
+ - `withPaidBroadcast`/`withoutPaidBroadcast` — enable/disable paid broadcast.
394
+ - `withOptions` — merge with the new send options.
395
+
396
+ **Example:** Chaining modifiers on a prepared message.
397
+
398
+ ```ts
399
+ import { Content, Markup, Send, Text } from '@grom.js/effect-tg'
400
+
401
+ const secretPromo = Send.message(Content.text(Text.plain('Shh!'))).pipe(
402
+ Send.withMarkup(
403
+ Markup.inlineKeyboard([
404
+ [Markup.InlineButton.copyText('Copy promo', 'EFFECT_TG')],
405
+ ]),
406
+ ),
407
+ Send.withoutNotification,
408
+ Send.withContentProtection,
409
+ )
410
+ ```
411
+
412
+ ### Text formatting
413
+
414
+ `Text` module provides utilities for creating [formatted text](https://core.telegram.org/bots/api#formatting-options) to be used in text messages and captions.
415
+
416
+ **Example:** Formatting text with `Text` module.
417
+
418
+ ```tsx
419
+ import { Text } from '@grom.js/effect-tg'
420
+
421
+ // Plain text — sent as is
422
+ Text.plain('*Not bold*. _Not italic_.')
423
+
424
+ // HTML — sent with 'HTML' parse mode
425
+ Text.html('<b>Bold</b> and <i>italic</i>.')
426
+
427
+ // Markdown — sent with 'MarkdownV2' parse mode
428
+ Text.markdown('*Bold* and _italic_.')
429
+ ```
430
+
431
+ #### JSX syntax
432
+
433
+ `Text` module also allows to compose formatted text with JSX.
434
+
435
+ Benefits of using JSX:
436
+
437
+ - **Validation**: JSX is validated during compilation, so you can't specify invalid HTML or Markdown.
438
+ - **Composability**: JSX allows composing formatted text with custom components.
439
+ - **Auto-escaping**: JSX escapes special characters, saving you from \<s\>bAd\</s\> \_iNpUtS\_.
440
+ - **Type safety**: LSP hints and type checking for text entities and custom components.
441
+
442
+ `Text.tgx` function accepts a JSX element and returns an instance of `Text.Tgx`, which can then be used as a content of a message.
443
+
444
+ **Example:** Composing reusable messages with JSX.
445
+
446
+ ```tsx
447
+ import type { PropsWithChildren } from '@grom.js/tgx/types'
448
+ import { Content, Dialog, Send, Text } from '@grom.js/effect-tg'
449
+ import { pipe } from 'effect'
450
+
451
+ // Reusable component for a key-value field
452
+ const Field = (props: PropsWithChildren<{ label: string }>) => (
453
+ <><b>{props.label}:</b> {props.children}{'\n'}</>
454
+ )
455
+
456
+ // Simple component for convenience
457
+ const RocketEmoji = () => (
458
+ <emoji id="5445284980978621387" alt="🚀" />
459
+ )
460
+
461
+ // Component that renders a deploy summary
462
+ const DeploySummary = (props: {
463
+ service: string
464
+ version: string
465
+ env: string
466
+ author: string
467
+ url: string
468
+ }) => (
469
+ <>
470
+ <RocketEmoji /> <b>Deploy to <i>{props.env}</i></b>
471
+ {'\n\n'}
472
+ <Field label="Service"><code>{props.service}</code></Field>
473
+ <Field label="Version"><code>{props.version}</code></Field>
474
+ <Field label="Author">{props.author}</Field>
475
+ {'\n'}
476
+ <a href={props.url}>View in dashboard</a>
477
+ {'\n\n'}
478
+ <blockquote expandable>
479
+ Changelog:{'\n'}
480
+ - Fix rate limiting on /api/submit{'\n'}
481
+ - Add retry logic for webhook delivery{'\n'}
482
+ - Update dependencies
483
+ </blockquote>
484
+ </>
485
+ )
486
+
487
+ // Create summary text
488
+ const summary = Text.tgx(
489
+ <DeploySummary
490
+ service="billing-api"
491
+ version="2.14.0"
492
+ env="production"
493
+ author="Alice"
494
+ url="https://deploy.example.com/runs/4821"
495
+ />
496
+ )
497
+
498
+ // Publish a new post
499
+ const publish = Send.message(Content.text(summary)).pipe(
500
+ Send.to(Dialog.channel(3011378744)),
501
+ )
502
+ ```
503
+
504
+ <details>
505
+ <summary>How it works?</summary>
506
+
507
+ JSX is just syntactic sugar transformed by the compiler.
508
+ Result of transformation depends on the JSX runtime.
509
+ `effect-tg` relies on JSX runtime from [`@grom.js/tgx`](https://github.com/grom-dev/tgx), which transforms JSX elements to `TgxElement` instances.
510
+ When `Send.sendMessage` encounters an instance of `Text.Tgx`, it converts inner `TgxElement`s to the parameters for a `send*` method.
511
+
512
+ </details>
513
+
514
+ To enable JSX support:
515
+
516
+ 1. Install `@grom.js/tgx` package:
517
+
518
+ ```sh
519
+ npm install @grom.js/tgx
520
+ ```
521
+
522
+ 2. Update your `tsconfig.json`:
523
+
524
+ ```json
525
+ {
526
+ "compilerOptions": {
527
+ "jsx": "react-jsx",
528
+ "jsxImportSource": "@grom.js/tgx"
529
+ }
530
+ }
531
+ ```
package/dist/BotApi.d.ts CHANGED
@@ -1,8 +1,12 @@
1
+ import type * as Config from 'effect/Config';
2
+ import type * as ConfigError from 'effect/ConfigError';
1
3
  import type * as BotApiError from './BotApiError.ts';
2
4
  import type { MethodParams, MethodResults, BotApi as Service, Types } from './internal/botApi.gen.ts';
5
+ import * as HttpClient from '@effect/platform/HttpClient';
3
6
  import * as Context from 'effect/Context';
4
7
  import * as Effect from 'effect/Effect';
5
8
  import * as Layer from 'effect/Layer';
9
+ import * as Redacted from 'effect/Redacted';
6
10
  import * as BotApiTransport from './BotApiTransport.ts';
7
11
  export type { MethodParams, MethodResults, Service, Types };
8
12
  declare const BotApi_base: Context.TagClass<BotApi, "@grom.js/effect-tg/BotApi", Service>;
@@ -17,4 +21,32 @@ export declare const make: (args: {
17
21
  }) => Service;
18
22
  export declare const layer: Layer.Layer<BotApi, never, BotApiTransport.BotApiTransport>;
19
23
  type MethodArgs<TMethod extends keyof MethodParams> = void extends MethodParams[TMethod] ? [params?: MethodParams[TMethod]] : [params: MethodParams[TMethod]];
24
+ /**
25
+ * Constructs a `BotApi` layer from the given configuration.
26
+ */
27
+ export declare const layerConfig: (options: {
28
+ /**
29
+ * Bot API token from [@BotFather](https://t.me/BotFather).
30
+ */
31
+ readonly token: Config.Config<Redacted.Redacted>;
32
+ /**
33
+ * Target Bot API environment:
34
+ *
35
+ * - `prod` — production environment
36
+ * - `test` — [test environment](https://core.telegram.org/bots/features#dedicated-test-environment)
37
+ *
38
+ * @default 'prod'
39
+ */
40
+ readonly environment?: "prod" | "test";
41
+ /**
42
+ * A function to transform the underlying `BotApiTransport` before
43
+ * it's used to call Bot API methods.
44
+ *
45
+ * Useful for:
46
+ * - Adding custom middleware (logging, metrics, caching)
47
+ * - Adding custom retry logic or error handling
48
+ * - Integrating with monitoring or debugging tools
49
+ */
50
+ readonly transformTransport?: (transport: BotApiTransport.Service) => BotApiTransport.Service;
51
+ }) => Layer.Layer<BotApi, ConfigError.ConfigError, HttpClient.HttpClient>;
20
52
  //# sourceMappingURL=BotApi.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BotApi.d.ts","sourceRoot":"","sources":["../src/BotApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,WAAW,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,MAAM,IAAI,OAAO,EACjB,KAAK,EACN,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAGvD,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;;AAE3D,qBAAa,MAAO,SAAQ,WAGzB;CAAG;AAEN,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,YAAY;IAC9D,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,CAC3C,aAAa,CAAC,OAAO,CAAC,EACtB,WAAW,CAAC,WAAW,EACvB,KAAK,CACN,CAAA;CACF;AAED,eAAO,MAAM,UAAU,EAAE,CAAC,OAAO,SAAS,MAAM,YAAY,EAC1D,MAAM,EAAE,OAAO,EACf,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,KACzB,MAAM,CAAC,MAAM,CAChB,aAAa,CAAC,OAAO,CAAC,EACtB,WAAW,CAAC,WAAW,EACvB,MAAM,CAMA,CAAA;AAER,eAAO,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE;IACxB,SAAS,EAAE,eAAe,CAAC,OAAO,CAAA;CACnC,KAAK,OAAuB,CAAA;AAE7B,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAC7B,MAAM,EACN,KAAK,EACL,eAAe,CAAC,eAAe,CAOhC,CAAA;AAED,KAAK,UAAU,CAAC,OAAO,SAAS,MAAM,YAAY,IAChD,IAAI,SAAS,YAAY,CAAC,OAAO,CAAC,GAC9B,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAChC,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"BotApi.d.ts","sourceRoot":"","sources":["../src/BotApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,KAAK,WAAW,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,KAAK,WAAW,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,MAAM,IAAI,OAAO,EACjB,KAAK,EACN,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,UAAU,MAAM,6BAA6B,CAAA;AACzD,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAIvD,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;;AAE3D,qBAAa,MAAO,SAAQ,WAGzB;CAAG;AAEN,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,YAAY;IAC9D,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,CAC3C,aAAa,CAAC,OAAO,CAAC,EACtB,WAAW,CAAC,WAAW,EACvB,KAAK,CACN,CAAA;CACF;AAED,eAAO,MAAM,UAAU,EAAE,CAAC,OAAO,SAAS,MAAM,YAAY,EAC1D,MAAM,EAAE,OAAO,EACf,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,KACzB,MAAM,CAAC,MAAM,CAChB,aAAa,CAAC,OAAO,CAAC,EACtB,WAAW,CAAC,WAAW,EACvB,MAAM,CAMA,CAAA;AAER,eAAO,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE;IACxB,SAAS,EAAE,eAAe,CAAC,OAAO,CAAA;CACnC,KAAK,OAAuB,CAAA;AAE7B,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAC7B,MAAM,EACN,KAAK,EACL,eAAe,CAAC,eAAe,CAOhC,CAAA;AAED,KAAK,UAAU,CAAC,OAAO,SAAS,MAAM,YAAY,IAChD,IAAI,SAAS,YAAY,CAAC,OAAO,CAAC,GAC9B,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAChC,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;AAErC;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS;IACnC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAEhD;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAEtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,CAAC,OAAO,KAAK,eAAe,CAAC,OAAO,CAAA;CAC9F,KAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,WAAW,EAAE,UAAU,CAAC,UAAU,CAyBrE,CAAA"}
package/dist/BotApi.js CHANGED
@@ -1,11 +1,27 @@
1
+ import * as HttpClient from '@effect/platform/HttpClient';
1
2
  import * as Context from 'effect/Context';
2
3
  import * as Effect from 'effect/Effect';
4
+ import * as Function from 'effect/Function';
3
5
  import * as Layer from 'effect/Layer';
6
+ import * as Redacted from 'effect/Redacted';
4
7
  import * as BotApiTransport from "./BotApiTransport.js";
8
+ import * as BotApiUrl from "./BotApiUrl.js";
5
9
  import * as internal from "./internal/botApi.js";
6
10
  export class BotApi extends Context.Tag('@grom.js/effect-tg/BotApi')() {
7
11
  }
8
12
  export const callMethod = (method, params = undefined) => BotApi.pipe(Effect.flatMap(api => api[method](params)));
9
13
  export const make = internal.make;
10
14
  export const layer = Layer.effect(BotApi, Effect.andThen(BotApiTransport.BotApiTransport, transport => internal.make({ transport })));
15
+ /**
16
+ * Constructs a `BotApi` layer from the given configuration.
17
+ */
18
+ export const layerConfig = (options) => {
19
+ const { token, environment = 'prod', transformTransport, } = options;
20
+ return Layer.provide(layer, Layer.effect(BotApiTransport.BotApiTransport, Effect.all([
21
+ HttpClient.HttpClient,
22
+ token.pipe(Effect.map(token => (environment === 'prod'
23
+ ? BotApiUrl.makeProd(Redacted.value(token))
24
+ : BotApiUrl.makeTest(Redacted.value(token))))),
25
+ ]).pipe(Effect.map(([httpClient, botApiUrl]) => BotApiTransport.make({ httpClient, botApiUrl })), Effect.map(transformTransport ?? Function.identity))));
26
+ };
11
27
  //# sourceMappingURL=BotApi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BotApi.js","sourceRoot":"","sources":["../src/BotApi.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,QAAQ,MAAM,sBAAsB,CAAA;AAIhD,MAAM,OAAO,MAAO,SAAQ,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,EAGjE;CAAG;AAUN,MAAM,CAAC,MAAM,UAAU,GAOnB,CACF,MAAc,EACd,SAAkB,SAAS,EAC3B,EAAE,CAAC,MAAM,CAAC,IAAI,CACd,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAE,GAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAC7C,CAAA;AAER,MAAM,CAAC,MAAM,IAAI,GAED,QAAQ,CAAC,IAAI,CAAA;AAE7B,MAAM,CAAC,MAAM,KAAK,GAId,KAAK,CAAC,MAAM,CACd,MAAM,EACN,MAAM,CAAC,OAAO,CACZ,eAAe,CAAC,eAAe,EAC/B,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAC1C,CACF,CAAA"}
1
+ {"version":3,"file":"BotApi.js","sourceRoot":"","sources":["../src/BotApi.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,UAAU,MAAM,6BAA6B,CAAA;AACzD,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,QAAQ,MAAM,sBAAsB,CAAA;AAIhD,MAAM,OAAO,MAAO,SAAQ,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,EAGjE;CAAG;AAUN,MAAM,CAAC,MAAM,UAAU,GAOnB,CACF,MAAc,EACd,SAAkB,SAAS,EAC3B,EAAE,CAAC,MAAM,CAAC,IAAI,CACd,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAE,GAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAC7C,CAAA;AAER,MAAM,CAAC,MAAM,IAAI,GAED,QAAQ,CAAC,IAAI,CAAA;AAE7B,MAAM,CAAC,MAAM,KAAK,GAId,KAAK,CAAC,MAAM,CACd,MAAM,EACN,MAAM,CAAC,OAAO,CACZ,eAAe,CAAC,eAAe,EAC/B,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAC1C,CACF,CAAA;AAOD;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OA0B3B,EAAuE,EAAE;IACxE,MAAM,EACJ,KAAK,EACL,WAAW,GAAG,MAAM,EACpB,kBAAkB,GACnB,GAAG,OAAO,CAAA;IACX,OAAO,KAAK,CAAC,OAAO,CAClB,KAAK,EACL,KAAK,CAAC,MAAM,CACV,eAAe,CAAC,eAAe,EAC/B,MAAM,CAAC,GAAG,CAAC;QACT,UAAU,CAAC,UAAU;QACrB,KAAK,CAAC,IAAI,CACR,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAClB,WAAW,KAAK,MAAM;YACpB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3C,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAC9C,CAAC,CACH;KACF,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC,EACxF,MAAM,CAAC,GAAG,CAAC,kBAAkB,IAAI,QAAQ,CAAC,QAAQ,CAAC,CACpD,CACF,CACF,CAAA;AACH,CAAC,CAAA"}
@@ -54,7 +54,7 @@ export const fromResponse = (response) => {
54
54
  if (response.error_code === 400 && response.parameters?.migrate_to_chat_id != null) {
55
55
  return new GroupUpgraded({
56
56
  response,
57
- supergroup: Dialog.supergroup(Option.getOrThrow(Dialog.decodePeerId('supergroup', response.parameters.migrate_to_chat_id))),
57
+ supergroup: Dialog.supergroup(Option.getOrThrow(Dialog.decodePeerId('channel', response.parameters.migrate_to_chat_id))),
58
58
  });
59
59
  }
60
60
  if (response.error_code >= 500) {
@@ -1 +1 @@
1
- {"version":3,"file":"BotApiError.js","sourceRoot":"","sources":["../src/BotApiError.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BotApiError.js","sourceRoot":"","sources":["../src/BotApiError.ts"],"names":[],"mappings":"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"}