@grom.js/effect-tg 0.12.0 → 0.14.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 (88) hide show
  1. package/README.md +13 -14
  2. package/dist/Bot.d.ts +2 -3
  3. package/dist/Bot.d.ts.map +1 -1
  4. package/dist/Bot.js +1 -2
  5. package/dist/Bot.js.map +1 -1
  6. package/dist/BotApi.d.ts +6 -6
  7. package/dist/BotApi.d.ts.map +1 -1
  8. package/dist/BotApi.js +1 -2
  9. package/dist/BotApi.js.map +1 -1
  10. package/dist/BotApiError.d.ts +15 -19
  11. package/dist/BotApiError.d.ts.map +1 -1
  12. package/dist/BotApiError.js +10 -12
  13. package/dist/BotApiError.js.map +1 -1
  14. package/dist/BotApiTransport.d.ts +5 -8
  15. package/dist/BotApiTransport.d.ts.map +1 -1
  16. package/dist/BotApiTransport.js +1 -2
  17. package/dist/BotApiTransport.js.map +1 -1
  18. package/dist/BotApiUrl.d.ts +6 -9
  19. package/dist/BotApiUrl.d.ts.map +1 -1
  20. package/dist/BotApiUrl.js +1 -2
  21. package/dist/BotApiUrl.js.map +1 -1
  22. package/dist/Content.d.ts +89 -155
  23. package/dist/Content.d.ts.map +1 -1
  24. package/dist/Content.js +26 -119
  25. package/dist/Content.js.map +1 -1
  26. package/dist/Dialog.d.ts +28 -58
  27. package/dist/Dialog.d.ts.map +1 -1
  28. package/dist/Dialog.js +29 -48
  29. package/dist/Dialog.js.map +1 -1
  30. package/dist/File.d.ts +11 -6
  31. package/dist/File.d.ts.map +1 -1
  32. package/dist/File.js +19 -3
  33. package/dist/File.js.map +1 -1
  34. package/dist/Markup.d.ts +8 -21
  35. package/dist/Markup.d.ts.map +1 -1
  36. package/dist/Markup.js +12 -13
  37. package/dist/Markup.js.map +1 -1
  38. package/dist/Runner.d.ts +3 -1
  39. package/dist/Runner.d.ts.map +1 -1
  40. package/dist/Runner.js.map +1 -1
  41. package/dist/Send.d.ts +14 -14
  42. package/dist/Send.d.ts.map +1 -1
  43. package/dist/Send.js +6 -13
  44. package/dist/Send.js.map +1 -1
  45. package/dist/Text.d.ts +13 -26
  46. package/dist/Text.d.ts.map +1 -1
  47. package/dist/Text.js +4 -13
  48. package/dist/Text.js.map +1 -1
  49. package/dist/internal/botApi.d.ts +3 -3
  50. package/dist/internal/botApi.d.ts.map +1 -1
  51. package/dist/internal/botApi.gen.d.ts +39 -8
  52. package/dist/internal/botApi.gen.d.ts.map +1 -1
  53. package/dist/internal/botApi.js +2 -1
  54. package/dist/internal/botApi.js.map +1 -1
  55. package/dist/internal/botApiTransport.d.ts +2 -2
  56. package/dist/internal/botApiTransport.d.ts.map +1 -1
  57. package/dist/internal/botApiTransport.js +3 -4
  58. package/dist/internal/botApiTransport.js.map +1 -1
  59. package/dist/internal/dialog.d.ts.map +1 -1
  60. package/dist/internal/dialog.js +8 -16
  61. package/dist/internal/dialog.js.map +1 -1
  62. package/dist/internal/file.d.ts +5 -2
  63. package/dist/internal/file.d.ts.map +1 -1
  64. package/dist/internal/file.js +3 -0
  65. package/dist/internal/file.js.map +1 -1
  66. package/dist/internal/send.d.ts +6 -4
  67. package/dist/internal/send.d.ts.map +1 -1
  68. package/dist/internal/send.js +18 -17
  69. package/dist/internal/send.js.map +1 -1
  70. package/package.json +11 -11
  71. package/src/Bot.ts +3 -4
  72. package/src/BotApi.ts +7 -8
  73. package/src/BotApiError.ts +19 -25
  74. package/src/BotApiTransport.ts +6 -9
  75. package/src/BotApiUrl.ts +7 -10
  76. package/src/Content.ts +128 -130
  77. package/src/Dialog.ts +79 -73
  78. package/src/File.ts +36 -5
  79. package/src/Markup.ts +37 -27
  80. package/src/Runner.ts +5 -1
  81. package/src/Send.ts +27 -26
  82. package/src/Text.ts +21 -18
  83. package/src/internal/botApi.gen.ts +39 -8
  84. package/src/internal/botApi.ts +5 -6
  85. package/src/internal/botApiTransport.ts +5 -6
  86. package/src/internal/dialog.ts +8 -16
  87. package/src/internal/file.ts +5 -2
  88. package/src/internal/send.ts +23 -23
package/src/File.ts CHANGED
@@ -7,20 +7,51 @@ import type * as BotApi from './BotApi.ts'
7
7
  import type * as BotApiError from './BotApiError.ts'
8
8
  import type * as BotApiUrl from './BotApiUrl.ts'
9
9
  import * as Brand from 'effect/Brand'
10
- import * as Data from 'effect/Data'
10
+ import * as Predicate from 'effect/Predicate'
11
11
  import * as internal from './internal/file.ts'
12
12
 
13
13
  export type FileId = string & Brand.Brand<'FileId'>
14
- export const FileId = Brand.nominal<FileId>()
14
+ export const FileId: Brand.Brand.Constructor<FileId> = Brand.nominal<FileId>()
15
15
 
16
16
  export type External = URL & Brand.Brand<'External'>
17
- export const External = Brand.nominal<External>()
17
+ export const External: Brand.Brand.Constructor<External> = Brand.nominal<External>()
18
18
 
19
- export class InputFile extends Data.TaggedClass('InputFile')<{
19
+ // =============================================================================
20
+ // InputFile
21
+ // =============================================================================
22
+
23
+ const InputFileTypeId: unique symbol = Symbol.for('effect-tg/InputFile')
24
+
25
+ export type InputFileTypeId = typeof InputFileTypeId
26
+
27
+ export interface InputFile {
28
+ readonly [InputFileTypeId]: InputFileTypeId
29
+ readonly stream: Stream.Stream<Uint8Array>
30
+ readonly filename: string
31
+ readonly mimeType?: string
32
+ }
33
+
34
+ const InputFileProto = {
35
+ [InputFileTypeId]: InputFileTypeId,
36
+ }
37
+
38
+ export const make: (args: {
20
39
  stream: Stream.Stream<Uint8Array>
21
40
  filename: string
22
41
  mimeType?: string
23
- }> {}
42
+ }) => InputFile = ({ stream, filename, mimeType }) => {
43
+ const file = Object.create(InputFileProto)
44
+ file.stream = stream
45
+ file.filename = filename
46
+ file.mimeType = mimeType
47
+ return file
48
+ }
49
+
50
+ export const isInputFile = (u: unknown): u is InputFile => Predicate.hasProperty(u, InputFileTypeId)
51
+
52
+ // =============================================================================
53
+ // Utilities
54
+ // =============================================================================
24
55
 
25
56
  /**
26
57
  * Downloads a file from the Bot API server.
package/src/Markup.ts CHANGED
@@ -1,4 +1,3 @@
1
- import * as Data from 'effect/Data'
2
1
  import * as Option from 'effect/Option'
3
2
 
4
3
  // =============================================================================
@@ -14,27 +13,31 @@ export type Markup =
14
13
  | ReplyKeyboardRemove
15
14
  | ForceReply
16
15
 
17
- export class InlineKeyboard extends Data.TaggedClass('InlineKeyboard')<{
16
+ export interface InlineKeyboard {
17
+ readonly _tag: 'InlineKeyboard'
18
18
  readonly rows: ReadonlyArray<ReadonlyArray<InlineButton>>
19
- }> {}
19
+ }
20
20
 
21
- export class ReplyKeyboard extends Data.TaggedClass('ReplyKeyboard')<{
21
+ export interface ReplyKeyboard {
22
+ readonly _tag: 'ReplyKeyboard'
22
23
  readonly rows: ReadonlyArray<ReadonlyArray<ReplyButton>>
23
24
  readonly persistent: boolean
24
25
  readonly resizable: boolean
25
26
  readonly oneTime: boolean
26
27
  readonly selective: boolean
27
28
  readonly inputPlaceholder: Option.Option<string>
28
- }> {}
29
+ }
29
30
 
30
- export class ReplyKeyboardRemove extends Data.TaggedClass('ReplyKeyboardRemove')<{
31
+ export interface ReplyKeyboardRemove {
32
+ readonly _tag: 'ReplyKeyboardRemove'
31
33
  readonly selective: boolean
32
- }> {}
34
+ }
33
35
 
34
- export class ForceReply extends Data.TaggedClass('ForceReply')<{
36
+ export interface ForceReply {
37
+ readonly _tag: 'ForceReply'
35
38
  readonly selective: boolean
36
39
  readonly inputPlaceholder: Option.Option<string>
37
- }> {}
40
+ }
38
41
 
39
42
  // =============================================================================
40
43
  // Constructors
@@ -42,7 +45,10 @@ export class ForceReply extends Data.TaggedClass('ForceReply')<{
42
45
 
43
46
  export const inlineKeyboard = (
44
47
  rows: ReadonlyArray<ReadonlyArray<InlineButton>>,
45
- ): InlineKeyboard => new InlineKeyboard({ rows })
48
+ ): InlineKeyboard => ({
49
+ _tag: 'InlineKeyboard',
50
+ rows,
51
+ })
46
52
 
47
53
  export const replyKeyboard = (
48
54
  rows: ReadonlyArray<ReadonlyArray<ReplyButton>>,
@@ -53,27 +59,31 @@ export const replyKeyboard = (
53
59
  readonly selective?: boolean
54
60
  readonly inputPlaceholder?: string
55
61
  },
56
- ): ReplyKeyboard =>
57
- new ReplyKeyboard({
58
- rows,
59
- persistent: options?.persistent ?? false,
60
- resizable: options?.resizable ?? false,
61
- oneTime: options?.oneTime ?? false,
62
- selective: options?.selective ?? false,
63
- inputPlaceholder: Option.fromNullable(options?.inputPlaceholder),
64
- })
65
-
66
- export const replyKeyboardRemove = (options?: { readonly selective?: boolean }): ReplyKeyboardRemove =>
67
- new ReplyKeyboardRemove({ selective: options?.selective ?? false })
62
+ ): ReplyKeyboard => ({
63
+ _tag: 'ReplyKeyboard',
64
+ rows,
65
+ persistent: options?.persistent ?? false,
66
+ resizable: options?.resizable ?? false,
67
+ oneTime: options?.oneTime ?? false,
68
+ selective: options?.selective ?? false,
69
+ inputPlaceholder: Option.fromNullable(options?.inputPlaceholder),
70
+ })
71
+
72
+ export const replyKeyboardRemove = (options?: {
73
+ readonly selective?: boolean
74
+ }): ReplyKeyboardRemove => ({
75
+ _tag: 'ReplyKeyboardRemove',
76
+ selective: options?.selective ?? false,
77
+ })
68
78
 
69
79
  export const forceReply = (options?: {
70
80
  readonly selective?: boolean
71
81
  readonly inputPlaceholder?: string
72
- }): ForceReply =>
73
- new ForceReply({
74
- selective: options?.selective ?? false,
75
- inputPlaceholder: Option.fromNullable(options?.inputPlaceholder),
76
- })
82
+ }): ForceReply => ({
83
+ _tag: 'ForceReply',
84
+ selective: options?.selective ?? false,
85
+ inputPlaceholder: Option.fromNullable(options?.inputPlaceholder),
86
+ })
77
87
 
78
88
  // =============================================================================
79
89
  // Inline button
package/src/Runner.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type * as Effect from 'effect/Effect'
2
2
  import type * as Bot from './Bot.ts'
3
+ import type * as BotApi from './BotApi.ts'
4
+ import type * as BotApiError from './BotApiError.ts'
3
5
  import * as internal from './internal/runner.ts'
4
6
 
5
7
  /**
@@ -19,4 +21,6 @@ export interface Runner<E = never, R = never> {
19
21
  * Creates a simple runner that fetches updates by calling `BotApi.getUpdates`
20
22
  * method and handles them one by one.
21
23
  */
22
- export const makeSimple = internal.makeSimple
24
+ export const makeSimple: (options?: {
25
+ allowedUpdates?: string[]
26
+ }) => Runner<BotApiError.BotApiError, BotApi.BotApi> = internal.makeSimple
package/src/Send.ts CHANGED
@@ -36,6 +36,32 @@ export const sendMessage: (params: {
36
36
  BotApi.BotApi
37
37
  > = internal.sendMessage
38
38
 
39
+ // =============================================================================
40
+ // TargetDialog
41
+ // =============================================================================
42
+
43
+ /**
44
+ * Target dialog for sending messages.
45
+ */
46
+ export interface TargetDialog {
47
+ readonly dialog: Dialog.Dialog | Dialog.DialogId
48
+ }
49
+
50
+ export const TargetDialog: Context.Tag<TargetDialog, TargetDialog> = Context.GenericTag<TargetDialog>('@grom.js/effect-tg/Send/TargetDialog')
51
+
52
+ /**
53
+ * Provides the target dialog for sending messages.
54
+ */
55
+ export const to: {
56
+ (dialog: Dialog.Dialog | Dialog.DialogId): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, TargetDialog>>
57
+ <A, E, R>(effect: Effect.Effect<A, E, R>, dialog: Dialog.Dialog | Dialog.DialogId): Effect.Effect<A, E, Exclude<R, TargetDialog>>
58
+ } = Function.dual(2, <A, E, R>(
59
+ effect: Effect.Effect<A, E, R>,
60
+ dialog: Dialog.Dialog | Dialog.DialogId,
61
+ ): Effect.Effect<A, E, Exclude<R, TargetDialog>> => (
62
+ Effect.provideService(effect, TargetDialog, { dialog })
63
+ ))
64
+
39
65
  // =============================================================================
40
66
  // MessageToSend
41
67
  // =============================================================================
@@ -71,7 +97,7 @@ const MessageToSendProto = {
71
97
  commit(this: MessageToSend) {
72
98
  return Effect.flatMap(
73
99
  TargetDialog,
74
- dialog => sendMessage({
100
+ ({ dialog }) => sendMessage({
75
101
  dialog,
76
102
  content: this.content,
77
103
  markup: this.markup,
@@ -109,31 +135,6 @@ export const message = (content: Content.Content, params?: {
109
135
  return self
110
136
  }
111
137
 
112
- // =============================================================================
113
- // TargetDialog
114
- // =============================================================================
115
-
116
- /**
117
- * Target dialog for sending messages.
118
- */
119
- export class TargetDialog extends Context.Tag('@grom.js/effect-tg/Send/TargetDialog')<
120
- TargetDialog,
121
- Dialog.Dialog | Dialog.DialogId
122
- >() {}
123
-
124
- /**
125
- * Provides the target dialog for sending messages.
126
- */
127
- export const to: {
128
- (dialog: Dialog.Dialog): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, TargetDialog>>
129
- <A, E, R>(effect: Effect.Effect<A, E, R>, dialog: Dialog.Dialog): Effect.Effect<A, E, Exclude<R, TargetDialog>>
130
- } = Function.dual(2, <A, E, R>(
131
- effect: Effect.Effect<A, E, R>,
132
- dialog: Dialog.Dialog,
133
- ): Effect.Effect<A, E, Exclude<R, TargetDialog>> => (
134
- Effect.provideService(effect, TargetDialog, dialog)
135
- ))
136
-
137
138
  // =============================================================================
138
139
  // Reply Markup
139
140
  // =============================================================================
package/src/Text.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { TgxElement } from '@grom.js/tgx'
2
2
  import type { Types } from './BotApi.ts'
3
- import * as Data from 'effect/Data'
4
3
 
5
4
  /**
6
5
  * Formatted text.
@@ -11,32 +10,36 @@ export type Text =
11
10
  | Markdown
12
11
  | Tgx
13
12
 
14
- export class Plain extends Data.TaggedClass('Plain')<{
15
- text: string
16
- entities?: Array<Types.MessageEntity>
17
- }> {}
13
+ export interface Plain {
14
+ readonly _tag: 'Plain'
15
+ readonly text: string
16
+ readonly entities?: Array<Types.MessageEntity>
17
+ }
18
18
 
19
- export class Html extends Data.TaggedClass('Html')<{
20
- html: string
21
- }> {}
19
+ export interface Html {
20
+ readonly _tag: 'Html'
21
+ readonly html: string
22
+ }
22
23
 
23
- export class Markdown extends Data.TaggedClass('Markdown')<{
24
- markdown: string
25
- }> {}
24
+ export interface Markdown {
25
+ readonly _tag: 'Markdown'
26
+ readonly markdown: string
27
+ }
26
28
 
27
- export class Tgx extends Data.TaggedClass('Tgx')<{
28
- tgx: TgxElement
29
- }> {}
29
+ export interface Tgx {
30
+ readonly _tag: 'Tgx'
31
+ readonly tgx: TgxElement
32
+ }
30
33
 
31
34
  // ———— Constructors ———————————————————————————————————————————————————————————
32
35
 
33
36
  export const plain = (
34
37
  text: string,
35
38
  entities?: Array<Types.MessageEntity>,
36
- ): Plain => new Plain({ text, entities })
39
+ ): Plain => ({ _tag: 'Plain', text, entities })
37
40
 
38
- export const html = (html: string): Html => new Html({ html })
41
+ export const html = (html: string): Html => ({ _tag: 'Html', html })
39
42
 
40
- export const markdown = (markdown: string): Markdown => new Markdown({ markdown })
43
+ export const markdown = (markdown: string): Markdown => ({ _tag: 'Markdown', markdown })
41
44
 
42
- export const tgx = (tgx: TgxElement): Tgx => new Tgx({ tgx })
45
+ export const tgx = (tgx: TgxElement): Tgx => ({ _tag: 'Tgx', tgx })
@@ -82,7 +82,7 @@ export interface BotApi {
82
82
  sendChecklist: BotApiMethod<'sendChecklist'>
83
83
  /** Use this method to send an animated emoji that will display a random value. On success, the sent [Message](https://core.telegram.org/bots/api#message) is returned. */
84
84
  sendDice: BotApiMethod<'sendDice'>
85
- /** Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns _True_ on success. */
85
+ /** Use this method to stream a partial message to a user while the message is being generated. Returns _True_ on success. */
86
86
  sendMessageDraft: BotApiMethod<'sendMessageDraft'>
87
87
  /**
88
88
  * Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). Returns _True_ on success.
@@ -116,6 +116,8 @@ export interface BotApi {
116
116
  promoteChatMember: BotApiMethod<'promoteChatMember'>
117
117
  /** Use this method to set a custom title for an administrator in a supergroup promoted by the bot. Returns _True_ on success. */
118
118
  setChatAdministratorCustomTitle: BotApiMethod<'setChatAdministratorCustomTitle'>
119
+ /** Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the _can\_manage\_tags_ administrator right. Returns _True_ on success. */
120
+ setChatMemberTag: BotApiMethod<'setChatMemberTag'>
119
121
  /** Use this method to ban a channel chat in a supergroup or a channel. Until the chat is [unbanned](https://core.telegram.org/bots/api#unbanchatsenderchat), the owner of the banned chat won't be able to send messages on behalf of **any of their channels**. The bot must be an administrator in the supergroup or channel for this to work and must have the appropriate administrator rights. Returns _True_ on success. */
120
122
  banChatSenderChat: BotApiMethod<'banChatSenderChat'>
121
123
  /** Use this method to unban a previously banned channel chat in a supergroup or channel. The bot must be an administrator for this to work and must have the appropriate administrator rights. Returns _True_ on success. */
@@ -667,6 +669,8 @@ export declare namespace Types {
667
669
  sender_boost_count?: number
668
670
  /** The bot that actually sent the message on behalf of the business account. Available only for outgoing messages sent on behalf of the connected business account. */
669
671
  sender_business_bot?: Types.User
672
+ /** Tag or custom title of the sender of the message; for supergroups only */
673
+ sender_tag?: string
670
674
  /** Date the message was sent in Unix time. It is always a positive number, representing a valid date. */
671
675
  date: number
672
676
  /** Unique identifier of the business connection from which the message was received. If non-empty, the message belongs to a chat of the corresponding business account that is independent from any potential bot chat which might share the same identifier. */
@@ -699,7 +703,7 @@ export declare namespace Types {
699
703
  is_from_offline?: true
700
704
  /** _True_, if the message is a paid post. Note that such posts must not be deleted for 24 hours to receive the payment and can't be edited. */
701
705
  is_paid_post?: true
702
- /** The unique identifier of a media message group this message belongs to */
706
+ /** The unique identifier inside this chat of a media message group this message belongs to */
703
707
  media_group_id?: string
704
708
  /** Signature of the post author for messages in channels, or the custom title of an anonymous group administrator */
705
709
  author_signature?: string
@@ -861,7 +865,7 @@ export declare namespace Types {
861
865
  video_chat_participants_invited?: Types.VideoChatParticipantsInvited
862
866
  /** Service message: data sent by a Web App */
863
867
  web_app_data?: Types.WebAppData
864
- /** Inline keyboard attached to the message. `login_url` buttons are represented as ordinary `url` buttons. */
868
+ /** [Inline keyboard](https://core.telegram.org/bots/features#inline-keyboards) attached to the message. `login_url` buttons are represented as ordinary `url` buttons. */
865
869
  reply_markup?: Types.InlineKeyboardMarkup
866
870
  }
867
871
 
@@ -891,8 +895,8 @@ export declare namespace Types {
891
895
 
892
896
  /** This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. */
893
897
  export interface MessageEntity {
894
- /** Type of the entity. Currently, can be “mention” (`@username`), “hashtag” (`#hashtag` or `#hashtag@chatusername`), “cashtag” (`$USD` or `$USD@chatusername`), “bot\_command” (`/start@jobs_bot`), “url” (`https://telegram.org`), “email” (`do-not-reply@telegram.org`), “phone\_number” (`+1-212-555-0123`), “bold” (**bold text**), “italic” (_italic text_), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable\_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text\_link” (for clickable text URLs), “text\_mention” (for users [without usernames](https://telegram.org/blog/edit#new-mentions)), “custom\_emoji” (for inline custom emoji stickers) */
895
- type: 'mention' | 'hashtag' | 'cashtag' | 'bot_command' | 'url' | 'email' | 'phone_number' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'spoiler' | 'blockquote' | 'expandable_blockquote' | 'code' | 'pre' | 'text_link' | 'text_mention' | 'custom_emoji'
898
+ /** Type of the entity. Currently, can be “mention” (`@username`), “hashtag” (`#hashtag` or `#hashtag@chatusername`), “cashtag” (`$USD` or `$USD@chatusername`), “bot\_command” (`/start@jobs_bot`), “url” (`https://telegram.org`), “email” (`do-not-reply@telegram.org`), “phone\_number” (`+1-212-555-0123`), “bold” (**bold text**), “italic” (_italic text_), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable\_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text\_link” (for clickable text URLs), “text\_mention” (for users [without usernames](https://telegram.org/blog/edit#new-mentions)), “custom\_emoji” (for inline custom emoji stickers), or “date\_time” (for formatted date and time) */
899
+ type: 'mention' | 'hashtag' | 'cashtag' | 'bot_command' | 'url' | 'email' | 'phone_number' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'spoiler' | 'blockquote' | 'expandable_blockquote' | 'code' | 'pre' | 'text_link' | 'text_mention' | 'custom_emoji' | 'date_time'
896
900
  /** Offset in [UTF-16 code units](https://core.telegram.org/api/entities#entity-length) to the start of the entity */
897
901
  offset: number
898
902
  /** Length of the entity in [UTF-16 code units](https://core.telegram.org/api/entities#entity-length) */
@@ -905,6 +909,10 @@ export declare namespace Types {
905
909
  language?: string
906
910
  /** For “custom\_emoji” only, unique identifier of the custom emoji. Use [getCustomEmojiStickers](https://core.telegram.org/bots/api#getcustomemojistickers) to get full information about the sticker */
907
911
  custom_emoji_id?: string
912
+ /** For “date\_time” only, the Unix time associated with the entity */
913
+ unix_time?: number
914
+ /** For “date\_time” only, the string that defines the formatting of the date and time. See [date-time entity formatting](https://core.telegram.org/bots/api#date-time-entity-formatting) for more details. */
915
+ date_time_format?: string
908
916
  }
909
917
 
910
918
  /** This object contains information about the quoted part of a message that is replied to by the given message. */
@@ -2204,6 +2212,8 @@ export declare namespace Types {
2204
2212
  can_manage_topics?: boolean
2205
2213
  /** _True_, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only */
2206
2214
  can_manage_direct_messages?: boolean
2215
+ /** _True_, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can\_pin\_messages. */
2216
+ can_manage_tags?: boolean
2207
2217
  }
2208
2218
 
2209
2219
  /** This object represents changes in the status of a chat member. */
@@ -2290,6 +2300,8 @@ export declare namespace Types {
2290
2300
  can_manage_topics?: boolean
2291
2301
  /** _True_, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only */
2292
2302
  can_manage_direct_messages?: boolean
2303
+ /** _True_, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can\_pin\_messages. */
2304
+ can_manage_tags?: boolean
2293
2305
  /** Custom title for this user */
2294
2306
  custom_title?: string
2295
2307
  }
@@ -2298,6 +2310,8 @@ export declare namespace Types {
2298
2310
  export interface ChatMemberMember {
2299
2311
  /** The member's status in the chat, always “member” */
2300
2312
  status: 'member'
2313
+ /** Tag of the member */
2314
+ tag?: string
2301
2315
  /** Information about the user */
2302
2316
  user: Types.User
2303
2317
  /** Date when the user's subscription will expire; Unix time */
@@ -2308,6 +2322,8 @@ export declare namespace Types {
2308
2322
  export interface ChatMemberRestricted {
2309
2323
  /** The member's status in the chat, always “restricted” */
2310
2324
  status: 'restricted'
2325
+ /** Tag of the member */
2326
+ tag?: string
2311
2327
  /** Information about the user */
2312
2328
  user: Types.User
2313
2329
  /** _True_, if the user is a member of the chat at the moment of the request */
@@ -2332,6 +2348,8 @@ export declare namespace Types {
2332
2348
  can_send_other_messages: boolean
2333
2349
  /** _True_, if the user is allowed to add web page previews to their messages */
2334
2350
  can_add_web_page_previews: boolean
2351
+ /** _True_, if the user is allowed to edit their own tag */
2352
+ can_edit_tag: boolean
2335
2353
  /** _True_, if the user is allowed to change the chat title, photo and other settings */
2336
2354
  can_change_info: boolean
2337
2355
  /** _True_, if the user is allowed to invite new users to the chat */
@@ -2400,6 +2418,8 @@ export declare namespace Types {
2400
2418
  can_send_other_messages?: boolean
2401
2419
  /** _True_, if the user is allowed to add web page previews to their messages */
2402
2420
  can_add_web_page_previews?: boolean
2421
+ /** _True_, if the user is allowed to edit their own tag */
2422
+ can_edit_tag?: boolean
2403
2423
  /** _True_, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups */
2404
2424
  can_change_info?: boolean
2405
2425
  /** _True_, if the user is allowed to invite new users to the chat */
@@ -3792,7 +3812,7 @@ export declare namespace Types {
3792
3812
  mime_type: 'application/pdf' | 'application/zip'
3793
3813
  /** Short description of the result */
3794
3814
  description?: string
3795
- /** Inline keyboard attached to the message */
3815
+ /** [Inline keyboard](https://core.telegram.org/bots/features#inline-keyboards) attached to the message */
3796
3816
  reply_markup?: Types.InlineKeyboardMarkup
3797
3817
  /** Content of the message to be sent instead of the file */
3798
3818
  input_message_content?: Types.InputMessageContent
@@ -5401,7 +5421,7 @@ export interface MethodParams {
5401
5421
  message_effect_id?: string
5402
5422
  /** An object for description of the message to reply to */
5403
5423
  reply_parameters?: Types.ReplyParameters
5404
- /** An object for an inline keyboard */
5424
+ /** An object for an [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards) */
5405
5425
  reply_markup?: Types.InlineKeyboardMarkup
5406
5426
  }
5407
5427
  sendDice: {
@@ -5559,6 +5579,8 @@ export interface MethodParams {
5559
5579
  can_manage_topics?: boolean
5560
5580
  /** Pass _True_ if the administrator can manage direct messages within the channel and decline suggested posts; for channels only */
5561
5581
  can_manage_direct_messages?: boolean
5582
+ /** Pass _True_ if the administrator can edit the tags of regular members; for groups and supergroups only */
5583
+ can_manage_tags?: boolean
5562
5584
  }
5563
5585
  setChatAdministratorCustomTitle: {
5564
5586
  /** Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) */
@@ -5568,6 +5590,14 @@ export interface MethodParams {
5568
5590
  /** New custom title for the administrator; 0-16 characters, emoji are not allowed */
5569
5591
  custom_title: string
5570
5592
  }
5593
+ setChatMemberTag: {
5594
+ /** Unique identifier for the target chat or username of the target supergroup (in the format `@supergroupusername`) */
5595
+ chat_id: number | string
5596
+ /** Unique identifier of the target user */
5597
+ user_id: number
5598
+ /** New tag for the member; 0-16 characters, emoji are not allowed */
5599
+ tag?: string
5600
+ }
5571
5601
  banChatSenderChat: {
5572
5602
  /** Unique identifier for the target chat or username of the target channel (in the format `@channelusername`) */
5573
5603
  chat_id: number | string
@@ -6268,7 +6298,7 @@ export interface MethodParams {
6268
6298
  message_id: number
6269
6299
  /** An object for the new checklist */
6270
6300
  checklist: Types.InputChecklist
6271
- /** An object for the new inline keyboard for the message */
6301
+ /** An object for the new [inline keyboard](https://core.telegram.org/bots/features#inline-keyboards) for the message */
6272
6302
  reply_markup?: Types.InlineKeyboardMarkup
6273
6303
  }
6274
6304
  editMessageReplyMarkup: void | {
@@ -6730,6 +6760,7 @@ export interface MethodResults {
6730
6760
  restrictChatMember: true
6731
6761
  promoteChatMember: true
6732
6762
  setChatAdministratorCustomTitle: true
6763
+ setChatMemberTag: true
6733
6764
  banChatSenderChat: true
6734
6765
  unbanChatSenderChat: true
6735
6766
  setChatPermissions: true
@@ -3,17 +3,16 @@ import type * as BotApiTransport from '../BotApiTransport.ts'
3
3
  import * as Effect from 'effect/Effect'
4
4
  import * as BotApiError from '../BotApiError.ts'
5
5
 
6
- export const make = ({
7
- transport,
8
- }: {
9
- transport: BotApiTransport.Service
10
- }): BotApi.Service => (
6
+ export const make = ({ transport }: {
7
+ transport: BotApiTransport.BotApiTransport
8
+ }): BotApi.BotApi => (
11
9
  new Proxy({}, {
12
10
  get: (_target, prop) => {
13
11
  if (typeof prop !== 'string') {
14
12
  return
15
13
  }
16
14
  const method = prop
15
+ // TODO: Shouldn't we cache effects not to create them on each call?
17
16
  return Effect.fnUntraced(
18
17
  function* (params: void | Record<string, unknown> = {}) {
19
18
  const response = yield* transport.sendRequest(method, params)
@@ -24,5 +23,5 @@ export const make = ({
24
23
  },
25
24
  )
26
25
  },
27
- }) as BotApi.Service
26
+ }) as BotApi.BotApi
28
27
  )
@@ -18,7 +18,7 @@ interface ExtractedFile {
18
18
  * {@linkcode File.InputFile InputFile} instances.
19
19
  */
20
20
  const hasInputFile = (value: unknown): boolean => {
21
- if (value instanceof File.InputFile) {
21
+ if (File.isInputFile(value)) {
22
22
  return true
23
23
  }
24
24
  if (Array.isArray(value)) {
@@ -34,7 +34,7 @@ const cloneAndExtract = (
34
34
  value: unknown,
35
35
  files: ExtractedFile[],
36
36
  ): unknown => {
37
- if (value instanceof File.InputFile) {
37
+ if (File.isInputFile(value)) {
38
38
  const attachId = String(files.length + 1)
39
39
  files.push({ attachId, file: value })
40
40
  return `attach://${attachId}`
@@ -108,15 +108,14 @@ export const make = ({
108
108
  botApiUrl,
109
109
  }: {
110
110
  httpClient: HttpClient.HttpClient
111
- botApiUrl: BotApiUrl.Service
112
- }): BotApiTransport.Service => ({
111
+ botApiUrl: BotApiUrl.BotApiUrl
112
+ }): BotApiTransport.BotApiTransport => ({
113
113
  sendRequest: (method, params) => (
114
114
  Effect.gen(function* () {
115
115
  const body = yield* makeHttpBody(params)
116
116
  const response = yield* httpClient.post(botApiUrl.toMethod(method), { body })
117
- const responseJson = yield* response.json
118
117
  // We trust Bot API and don't want to introduce overhead with validation.
119
- return responseJson as BotApiTransport.BotApiResponse
118
+ return (yield* response.json) as BotApiTransport.BotApiResponse
120
119
  }).pipe(
121
120
  Effect.catchAll(cause => (
122
121
  Effect.fail(new BotApiError.TransportError({ cause }))
@@ -15,7 +15,7 @@ export const decodeDialogId = (dialogId: number): Option.Option<
15
15
  > => {
16
16
  if (Number.isSafeInteger(dialogId)) {
17
17
  if (1 <= dialogId && dialogId <= 0xFFFFFFFFFF) {
18
- return Option.some({ peer: 'user', id: +dialogId as Dialog.UserId })
18
+ return Option.some({ peer: 'user', id: dialogId as Dialog.UserId })
19
19
  }
20
20
  if (-999999999999 <= dialogId && dialogId <= -1) {
21
21
  return Option.some({ peer: 'group', id: -dialogId as Dialog.GroupId })
@@ -80,34 +80,26 @@ export const ofMessage: (
80
80
  ) => Dialog.Dialog = (m) => {
81
81
  switch (m.chat.type) {
82
82
  case 'private': {
83
- const user = new Dialog.User({
84
- id: Option.getOrThrow(decodePeerId('user', m.chat.id)),
85
- })
83
+ const user = Dialog.user(Option.getOrThrow(decodePeerId('user', m.chat.id)))
86
84
  if (m.message_thread_id != null) {
87
- return user.topic(m.message_thread_id)
85
+ return Dialog.privateTopic(user, m.message_thread_id)
88
86
  }
89
87
  return user
90
88
  }
91
89
  case 'group': {
92
- return new Dialog.Group({
93
- id: Option.getOrThrow(decodePeerId('group', m.chat.id)),
94
- })
90
+ return Dialog.group(Option.getOrThrow(decodePeerId('group', m.chat.id)))
95
91
  }
96
92
  case 'channel': {
97
- const channel = new Dialog.Channel({
98
- id: Option.getOrThrow(decodePeerId('channel', m.chat.id)),
99
- })
93
+ const channel = Dialog.channel(Option.getOrThrow(decodePeerId('channel', m.chat.id)))
100
94
  if (m.direct_messages_topic != null) {
101
- return channel.directMessages(m.direct_messages_topic.topic_id)
95
+ return Dialog.channelDm(channel, m.direct_messages_topic.topic_id)
102
96
  }
103
97
  return channel
104
98
  }
105
99
  case 'supergroup': {
106
- const supergroup = new Dialog.Supergroup({
107
- id: Option.getOrThrow(decodePeerId('channel', m.chat.id)),
108
- })
100
+ const supergroup = Dialog.supergroup(Option.getOrThrow(decodePeerId('channel', m.chat.id)))
109
101
  if (m.message_thread_id != null) {
110
- return supergroup.topic(m.message_thread_id)
102
+ return Dialog.forumTopic(supergroup, m.message_thread_id)
111
103
  }
112
104
  return supergroup
113
105
  }
@@ -1,11 +1,14 @@
1
- import type { FileId } from '../File.ts'
1
+ import type * as File from '../File.ts'
2
2
  import * as HttpClient from '@effect/platform/HttpClient'
3
3
  import * as Effect from 'effect/Effect'
4
4
  import * as BotApi from '../BotApi.ts'
5
5
  import * as BotApiUrl from '../BotApiUrl.ts'
6
6
 
7
+ /**
8
+ * @internal
9
+ */
7
10
  export const download = Effect.fnUntraced(
8
- function* (fileId: FileId) {
11
+ function* (fileId: File.FileId) {
9
12
  const file = yield* BotApi.callMethod('getFile', { file_id: fileId })
10
13
  if (file.file_path == null) {
11
14
  return yield* Effect.die(new Error(`Bot API returned no file path for file "${fileId}".`))