@grom.js/effect-tg 0.3.1 → 0.5.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 (119) hide show
  1. package/dist/Bot.d.ts +13 -0
  2. package/dist/Bot.d.ts.map +1 -0
  3. package/dist/Bot.js +5 -0
  4. package/dist/Bot.js.map +1 -0
  5. package/dist/BotApi.d.ts +12 -3
  6. package/dist/BotApi.d.ts.map +1 -1
  7. package/dist/BotApi.js +7 -2
  8. package/dist/BotApi.js.map +1 -0
  9. package/dist/BotApiTransport.d.ts +11 -10
  10. package/dist/BotApiTransport.d.ts.map +1 -1
  11. package/dist/BotApiTransport.js +6 -8
  12. package/dist/BotApiTransport.js.map +1 -0
  13. package/dist/BotApiUrl.d.ts +14 -0
  14. package/dist/BotApiUrl.d.ts.map +1 -0
  15. package/dist/BotApiUrl.js +13 -0
  16. package/dist/BotApiUrl.js.map +1 -0
  17. package/dist/Content.d.ts +288 -16
  18. package/dist/Content.d.ts.map +1 -1
  19. package/dist/Content.js +189 -28
  20. package/dist/Content.js.map +1 -0
  21. package/dist/Dialog.d.ts +61 -0
  22. package/dist/Dialog.d.ts.map +1 -0
  23. package/dist/Dialog.js +35 -0
  24. package/dist/Dialog.js.map +1 -0
  25. package/dist/File.d.ts +23 -0
  26. package/dist/File.d.ts.map +1 -0
  27. package/dist/File.js +24 -0
  28. package/dist/File.js.map +1 -0
  29. package/dist/LinkPreview.d.ts +2 -2
  30. package/dist/LinkPreview.d.ts.map +1 -1
  31. package/dist/LinkPreview.js +17 -16
  32. package/dist/LinkPreview.js.map +1 -0
  33. package/dist/Markup.d.ts +41 -0
  34. package/dist/Markup.d.ts.map +1 -0
  35. package/dist/Markup.js +10 -0
  36. package/dist/Markup.js.map +1 -0
  37. package/dist/Runner.d.ts +18 -0
  38. package/dist/Runner.d.ts.map +1 -0
  39. package/dist/Runner.js +7 -0
  40. package/dist/Runner.js.map +1 -0
  41. package/dist/Send.d.ts +177 -13
  42. package/dist/Send.d.ts.map +1 -1
  43. package/dist/Send.js +187 -18
  44. package/dist/Send.js.map +1 -0
  45. package/dist/Text.d.ts +26 -10
  46. package/dist/Text.d.ts.map +1 -1
  47. package/dist/Text.js +11 -12
  48. package/dist/Text.js.map +1 -0
  49. package/dist/index.d.ts +6 -3
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +7 -3
  52. package/dist/index.js.map +1 -0
  53. package/dist/internal/botApi.d.ts +3 -4
  54. package/dist/internal/botApi.d.ts.map +1 -1
  55. package/dist/internal/botApi.js +18 -22
  56. package/dist/internal/botApi.js.map +1 -0
  57. package/dist/internal/botApiMethods.gen.d.ts +19 -19
  58. package/dist/internal/botApiMethods.gen.d.ts.map +1 -1
  59. package/dist/internal/botApiMethods.gen.js +1 -0
  60. package/dist/internal/botApiMethods.gen.js.map +1 -0
  61. package/dist/internal/botApiShape.gen.d.ts +159 -159
  62. package/dist/internal/botApiShape.gen.d.ts.map +1 -1
  63. package/dist/internal/botApiShape.gen.js +1 -0
  64. package/dist/internal/botApiShape.gen.js.map +1 -0
  65. package/dist/internal/botApiTransport.d.ts +4 -6
  66. package/dist/internal/botApiTransport.d.ts.map +1 -1
  67. package/dist/internal/botApiTransport.js +89 -17
  68. package/dist/internal/botApiTransport.js.map +1 -0
  69. package/dist/internal/botApiTypes.gen.js +1 -0
  70. package/dist/internal/botApiTypes.gen.js.map +1 -0
  71. package/dist/internal/dialog.d.ts +12 -0
  72. package/dist/internal/dialog.d.ts.map +1 -0
  73. package/dist/internal/dialog.js +19 -0
  74. package/dist/internal/dialog.js.map +1 -0
  75. package/dist/internal/runner.d.ts +8 -0
  76. package/dist/internal/runner.d.ts.map +1 -0
  77. package/dist/internal/runner.js +38 -0
  78. package/dist/internal/runner.js.map +1 -0
  79. package/dist/internal/send.d.ts +13 -0
  80. package/dist/internal/send.d.ts.map +1 -0
  81. package/dist/internal/send.js +220 -0
  82. package/dist/internal/send.js.map +1 -0
  83. package/package.json +14 -10
  84. package/src/Bot.ts +16 -0
  85. package/src/BotApi.ts +68 -0
  86. package/src/BotApiTransport.ts +55 -0
  87. package/src/BotApiUrl.ts +28 -0
  88. package/src/Content.ts +410 -0
  89. package/src/Dialog.ts +54 -0
  90. package/src/File.ts +45 -0
  91. package/src/LinkPreview.ts +26 -0
  92. package/src/Markup.ts +33 -0
  93. package/src/Runner.ts +22 -0
  94. package/src/Send.ts +330 -0
  95. package/src/Text.ts +42 -0
  96. package/src/index.ts +12 -0
  97. package/src/internal/botApi.ts +31 -0
  98. package/src/internal/botApiMethods.gen.ts +2027 -0
  99. package/src/internal/botApiShape.gen.ts +398 -0
  100. package/src/internal/botApiTransport.ts +118 -0
  101. package/src/internal/botApiTypes.gen.ts +4178 -0
  102. package/src/internal/dialog.ts +33 -0
  103. package/src/internal/runner.ts +57 -0
  104. package/src/internal/send.ts +318 -0
  105. package/dist/BotApiWebhook.d.ts +0 -41
  106. package/dist/BotApiWebhook.d.ts.map +0 -1
  107. package/dist/BotApiWebhook.js +0 -43
  108. package/dist/Chat.d.ts +0 -96
  109. package/dist/Chat.d.ts.map +0 -1
  110. package/dist/Chat.js +0 -48
  111. package/dist/InputFile.d.ts +0 -4
  112. package/dist/InputFile.d.ts.map +0 -1
  113. package/dist/InputFile.js +0 -3
  114. package/dist/internal/botApiMethod.d.ts +0 -8
  115. package/dist/internal/botApiMethod.d.ts.map +0 -1
  116. package/dist/internal/botApiMethod.js +0 -1
  117. package/dist/internal/chat.d.ts +0 -14
  118. package/dist/internal/chat.d.ts.map +0 -1
  119. package/dist/internal/chat.js +0 -20
@@ -0,0 +1,33 @@
1
+ export function PeerId<Tag extends string>({
2
+ tag,
3
+ isValid,
4
+ toDialogId,
5
+ }: {
6
+ tag: Tag
7
+ isValid: (peerId: number) => boolean
8
+ toDialogId: (peerId: number) => number
9
+ }): {
10
+ new (id: number): {
11
+ readonly _tag: Tag
12
+ readonly id: number
13
+ readonly dialogId: number
14
+ }
15
+ } {
16
+ class Base {
17
+ public readonly _tag = tag
18
+ public readonly id: number
19
+ public readonly dialogId: number
20
+
21
+ constructor(id: number) {
22
+ if (!Number.isSafeInteger(id)) {
23
+ throw new TypeError(`invalid integer: ${id}`)
24
+ }
25
+ if (!isValid(id)) {
26
+ throw new Error(`invalid peer id: ${id}`)
27
+ }
28
+ this.id = id
29
+ this.dialogId = toDialogId(id)
30
+ }
31
+ }
32
+ return Base
33
+ }
@@ -0,0 +1,57 @@
1
+ import type { BotApiError } from '../BotApi.ts'
2
+ import type { BotApiTransportError } from '../BotApiTransport.ts'
3
+ import type { Runner } from '../Runner.ts'
4
+ import * as Duration from 'effect/Duration'
5
+ import * as Effect from 'effect/Effect'
6
+ import * as Match from 'effect/Match'
7
+ import * as Schedule from 'effect/Schedule'
8
+ import { Update } from '../Bot.ts'
9
+ import { BotApi } from '../BotApi.ts'
10
+
11
+ export const makeSimple = (options?: {
12
+ allowedUpdates?: string[]
13
+ }): Runner<BotApiError | BotApiTransportError, BotApi> => ({
14
+ run: Effect.fnUntraced(
15
+ function* (bot) {
16
+ const { allowedUpdates } = options ?? {}
17
+ const api = yield* BotApi
18
+ let lastUpdateId: undefined | number
19
+ while (true) {
20
+ const [update] = yield* api
21
+ .getUpdates({
22
+ offset: lastUpdateId == null ? undefined : lastUpdateId + 1,
23
+ allowed_updates: allowedUpdates,
24
+ timeout: 30,
25
+ limit: 1,
26
+ })
27
+ .pipe(
28
+ Effect.retry({
29
+ schedule: Schedule.spaced(Duration.seconds(3)),
30
+ while: error => Match.value(error).pipe(
31
+ Match.tagsExhaustive({
32
+ '@grom.js/effect-tg/BotApi/BotApiError': error => Effect.succeed(
33
+ error.code >= 500 || (
34
+ error.code !== 401
35
+ && error.code !== 403
36
+ && error.code !== 404
37
+ ),
38
+ ),
39
+ '@grom.js/effect-tg/BotApiTransport/BotApiTransportError': () => Effect.succeed(true),
40
+ }),
41
+ ),
42
+ }),
43
+ )
44
+ if (update) {
45
+ yield* Effect
46
+ .provideService(bot, Update, update)
47
+ .pipe(
48
+ Effect.catchAll(error => (
49
+ Effect.logError('Error in bot:', error)
50
+ )),
51
+ )
52
+ lastUpdateId = update.update_id
53
+ }
54
+ }
55
+ },
56
+ ),
57
+ })
@@ -0,0 +1,318 @@
1
+ import type * as Content from '../Content.ts'
2
+ import type * as Dialog from '../Dialog.ts'
3
+ import type * as Dialog_ from '../Dialog.ts'
4
+ import type * as Markup from '../Markup.ts'
5
+ import type * as Send from '../Send.ts'
6
+ import type * as Text from '../Text.ts'
7
+ import * as Tgx from '@grom.js/tgx'
8
+ import * as Duration from 'effect/Duration'
9
+ import * as Effect from 'effect/Effect'
10
+ import * as Function from 'effect/Function'
11
+ import * as Match from 'effect/Match'
12
+ import * as Option from 'effect/Option'
13
+ import * as BotApi from '../BotApi.ts'
14
+ import * as LinkPreview from '../LinkPreview.ts'
15
+
16
+ type ParamsText = PickMethodParams<'sendMessage', 'text' | 'entities' | 'parse_mode'>
17
+ const paramsText: (text: Text.Text) => ParamsText = Function.pipe(
18
+ Match.type<Text.Text>(),
19
+ Match.withReturnType<ParamsText>(),
20
+ Match.tags({
21
+ Plain: ({ text, entities }) => ({ text, entities }),
22
+ Html: ({ html }) => ({ text: html, parse_mode: 'HTML' }),
23
+ Markdown: ({ markdown }) => ({ text: markdown, parse_mode: 'MarkdownV2' }),
24
+ Tgx: ({ tgx }) => ({ text: Tgx.html(tgx), parse_mode: 'HTML' }),
25
+ }),
26
+ Match.exhaustive,
27
+ )
28
+
29
+ type ParamsCaption = PickMethodParams<'sendPhoto', 'caption' | 'caption_entities' | 'parse_mode'>
30
+ const paramsCaption = (caption: Option.Option<Text.Text>): ParamsCaption => (
31
+ Option.match(caption, {
32
+ onNone: () => ({}),
33
+ onSome: (caption) => {
34
+ const { text, entities, parse_mode } = paramsText(caption)
35
+ return { caption: text, caption_entities: entities, parse_mode }
36
+ },
37
+ })
38
+ )
39
+
40
+ type ParamsContentText = PickMethodParams<'sendMessage', 'text' | 'entities' | 'parse_mode' | 'link_preview_options'>
41
+ type ParamsContentPhoto = PickMethodParams<'sendPhoto', 'photo' | 'caption' | 'caption_entities' | 'parse_mode' | 'show_caption_above_media' | 'has_spoiler'>
42
+ type ParamsContentAudio = PickMethodParams<'sendAudio', 'audio' | 'caption' | 'caption_entities' | 'parse_mode' | 'duration' | 'performer' | 'title' | 'thumbnail'>
43
+ type ParamsContentDocument = PickMethodParams<'sendDocument', 'document' | 'thumbnail' | 'caption' | 'parse_mode' | 'caption_entities' | 'disable_content_type_detection'>
44
+ type ParamsContentVideo = PickMethodParams<'sendVideo', 'video' | 'duration' | 'width' | 'height' | 'thumbnail' | 'cover' | 'start_timestamp' | 'show_caption_above_media' | 'has_spoiler' | 'supports_streaming'>
45
+ type ParamsContentAnimation = PickMethodParams<'sendAnimation', 'animation' | 'duration' | 'width' | 'height' | 'thumbnail' | 'has_spoiler'>
46
+ type ParamsContentVoice = PickMethodParams<'sendVoice', 'voice' | 'duration'>
47
+ type ParamsContentVideoNote = PickMethodParams<'sendVideoNote', 'video_note' | 'duration' | 'length' | 'thumbnail'>
48
+ type ParamsContentLocation = PickMethodParams<'sendLocation', 'latitude' | 'longitude' | 'horizontal_accuracy' | 'live_period' | 'heading' | 'proximity_alert_radius'>
49
+ type ParamsContentVenue = PickMethodParams<'sendVenue', 'latitude' | 'longitude' | 'title' | 'address' | 'foursquare_id' | 'foursquare_type' | 'google_place_id' | 'google_place_type'>
50
+ type ParamsContentContact = PickMethodParams<'sendContact', 'phone_number' | 'first_name' | 'last_name' | 'vcard'>
51
+ type ParamsContentDice = PickMethodParams<'sendDice', 'emoji'>
52
+ type ParamsContentSticker = PickMethodParams<'sendSticker', 'sticker' | 'emoji'>
53
+ const paramsContent: {
54
+ (content: Content.Text): ParamsContentText
55
+ (content: Content.Photo): ParamsContentPhoto
56
+ (content: Content.Audio): ParamsContentAudio
57
+ (content: Content.Document): ParamsContentDocument
58
+ (content: Content.Video): ParamsContentVideo
59
+ (content: Content.Animation): ParamsContentAnimation
60
+ (content: Content.Voice): ParamsContentVoice
61
+ (content: Content.VideoNote): ParamsContentVideoNote
62
+ (content: Content.Location): ParamsContentLocation
63
+ (content: Content.Venue): ParamsContentVenue
64
+ (content: Content.Contact): ParamsContentContact
65
+ (content: Content.Dice): ParamsContentDice
66
+ (content: Content.Sticker): ParamsContentSticker
67
+ } = Function.pipe(
68
+ Match.type<Content.Content>(),
69
+ Match.tags({
70
+ Text: ({ text, linkPreview }): ParamsContentText => ({
71
+ ...paramsText(text),
72
+ link_preview_options: Option.match(linkPreview, {
73
+ onNone: () => ({ is_disabled: true }),
74
+ onSome: linkPreview => LinkPreview.options(linkPreview),
75
+ }),
76
+ }),
77
+ Photo: ({ file, caption, layout, spoiler }): ParamsContentPhoto => ({
78
+ ...paramsCaption(caption),
79
+ photo: file instanceof URL ? file.toString() : file,
80
+ show_caption_above_media: layout === 'caption-above',
81
+ has_spoiler: spoiler,
82
+ }),
83
+ Audio: ({ file, caption, duration, performer, title, thumbnail }): ParamsContentAudio => ({
84
+ ...paramsCaption(caption),
85
+ audio: file instanceof URL ? file.toString() : file,
86
+ duration: Option.match(duration, {
87
+ onNone: () => undefined,
88
+ onSome: duration => Math.round(Duration.toSeconds(duration)),
89
+ }),
90
+ performer: Option.getOrUndefined(performer),
91
+ title: Option.getOrUndefined(title),
92
+ thumbnail: Option.getOrUndefined(thumbnail),
93
+ }),
94
+ Document: ({ file, caption, thumbnail, contentTypeDetection }): ParamsContentDocument => ({
95
+ ...paramsCaption(caption),
96
+ document: file instanceof URL ? file.toString() : file,
97
+ thumbnail: Option.getOrUndefined(thumbnail),
98
+ disable_content_type_detection: !contentTypeDetection,
99
+ }),
100
+ Video: ({ file, caption, layout, spoiler, duration, width, height, thumbnail, cover, startAt, supportsStreaming }): ParamsContentVideo => ({
101
+ ...paramsCaption(caption),
102
+ video: file instanceof URL ? file.toString() : file,
103
+ duration: Option.match(duration, {
104
+ onNone: () => undefined,
105
+ onSome: duration => Math.round(Duration.toSeconds(duration)),
106
+ }),
107
+ width: Option.getOrUndefined(width),
108
+ height: Option.getOrUndefined(height),
109
+ thumbnail: Option.getOrUndefined(thumbnail),
110
+ cover: Option.map(cover, c => c instanceof URL ? c.toString() : c).pipe(Option.getOrUndefined),
111
+ start_timestamp: Option.match(startAt, {
112
+ onNone: () => undefined,
113
+ onSome: startAt => Math.round(Duration.toSeconds(startAt)),
114
+ }),
115
+ show_caption_above_media: layout === 'caption-above',
116
+ has_spoiler: spoiler,
117
+ supports_streaming: supportsStreaming,
118
+ }),
119
+ Animation: ({ file, caption, spoiler, duration, width, height, thumbnail }): ParamsContentAnimation => ({
120
+ ...paramsCaption(caption),
121
+ animation: file instanceof URL ? file.toString() : file,
122
+ duration: Option.match(duration, {
123
+ onNone: () => undefined,
124
+ onSome: duration => Math.round(Duration.toSeconds(duration)),
125
+ }),
126
+ width: Option.getOrUndefined(width),
127
+ height: Option.getOrUndefined(height),
128
+ thumbnail: Option.getOrUndefined(thumbnail),
129
+ has_spoiler: spoiler,
130
+ }),
131
+ Voice: ({ file, caption, duration }): ParamsContentVoice => ({
132
+ ...paramsCaption(caption),
133
+ voice: file instanceof URL ? file.toString() : file,
134
+ duration: Option.match(duration, {
135
+ onNone: () => undefined,
136
+ onSome: duration => Math.round(Duration.toSeconds(duration)),
137
+ }),
138
+ }),
139
+ VideoNote: ({ file, duration, diameter, thumbnail }): ParamsContentVideoNote => ({
140
+ video_note: file,
141
+ duration: Option.match(duration, {
142
+ onNone: () => undefined,
143
+ onSome: duration => Math.round(Duration.toSeconds(duration)),
144
+ }),
145
+ length: Option.getOrUndefined(diameter),
146
+ thumbnail: Option.getOrUndefined(thumbnail),
147
+ }),
148
+ Location: ({ latitude, longitude, uncertaintyRadius, livePeriod, heading, proximityAlertRadius }): ParamsContentLocation => ({
149
+ latitude,
150
+ longitude,
151
+ horizontal_accuracy: Option.getOrUndefined(uncertaintyRadius),
152
+ live_period: Option.match(livePeriod, {
153
+ onNone: () => undefined,
154
+ onSome: livePeriod => Duration.isFinite(livePeriod)
155
+ ? Math.round(Duration.toSeconds(livePeriod))
156
+ : 0x7FFFFFFF,
157
+ }),
158
+ heading: Option.getOrUndefined(heading),
159
+ proximity_alert_radius: Option.getOrUndefined(proximityAlertRadius),
160
+ }),
161
+ Venue: ({ latitude, longitude, title, address, foursquareId, foursquareType, googlePlaceId, googlePlaceType }): ParamsContentVenue => ({
162
+ latitude,
163
+ longitude,
164
+ title,
165
+ address,
166
+ foursquare_id: Option.getOrUndefined(foursquareId),
167
+ foursquare_type: Option.getOrUndefined(foursquareType),
168
+ google_place_id: Option.getOrUndefined(googlePlaceId),
169
+ google_place_type: Option.getOrUndefined(googlePlaceType),
170
+ }),
171
+ Contact: ({ phoneNumber, firstName, lastName, vcard }): ParamsContentContact => ({
172
+ phone_number: phoneNumber,
173
+ first_name: firstName,
174
+ last_name: Option.getOrUndefined(lastName),
175
+ vcard: Option.getOrUndefined(vcard),
176
+ }),
177
+ Dice: ({ emoji }): ParamsContentDice => ({
178
+ emoji,
179
+ }),
180
+ Sticker: ({ file, emoji }): ParamsContentSticker => ({
181
+ sticker: file instanceof URL ? file.toString() : file,
182
+ emoji: Option.getOrUndefined(emoji),
183
+ }),
184
+ }),
185
+ Match.exhaustive,
186
+ ) as any
187
+
188
+ type ParamsDialog = PickMethodParams<SendMethod, 'chat_id' | 'message_thread_id' | 'direct_messages_topic_id'>
189
+ const paramsDialog: (dialog: Dialog.Dialog) => ParamsDialog = Function.pipe(
190
+ Match.type<Dialog.Dialog>(),
191
+ Match.withReturnType<ParamsDialog>(),
192
+ Match.tagsExhaustive({
193
+ UserId: ({ dialogId }) => ({ chat_id: dialogId }),
194
+ GroupId: ({ dialogId }) => ({ chat_id: dialogId }),
195
+ ChannelId: ({ dialogId }) => ({ chat_id: dialogId }),
196
+ SupergroupId: ({ dialogId }) => ({ chat_id: dialogId }),
197
+ PublicChannel: ({ username }) => ({ chat_id: username }),
198
+ PublicSupergroup: ({ username }) => ({ chat_id: username }),
199
+ ForumTopic: ({ forum, topicId }) => ({
200
+ chat_id: Match.value(forum).pipe(
201
+ Match.tagsExhaustive({
202
+ SupergroupId: ({ dialogId }) => dialogId,
203
+ PublicSupergroup: ({ username }) => username,
204
+ }),
205
+ ),
206
+ message_thread_id: topicId,
207
+ }),
208
+ ChannelDm: ({ channel, userId }) => ({
209
+ chat_id: Match.value(channel).pipe(
210
+ Match.tagsExhaustive({
211
+ ChannelId: ({ dialogId }) => dialogId,
212
+ PublicChannel: ({ username }) => username,
213
+ }),
214
+ ),
215
+ direct_messages_topic_id: userId,
216
+ }),
217
+ }),
218
+ )
219
+
220
+ type ParamsOptions = PickMethodParams<SendMethod, 'disable_notification' | 'protect_content' | 'allow_paid_broadcast'>
221
+ const paramsOptions = (options: Send.Options): ParamsOptions => {
222
+ return {
223
+ disable_notification: options.disableNotification || undefined,
224
+ protect_content: options.protectContent || undefined,
225
+ allow_paid_broadcast: options.allowPaidBroadcast || undefined,
226
+ }
227
+ }
228
+
229
+ type ParamsMarkup = PickMethodParams<SendMethod, 'reply_markup'>
230
+ const paramsMarkup: (markup: Markup.Markup) => ParamsMarkup = Function.pipe(
231
+ Match.type<Markup.Markup>(),
232
+ Match.withReturnType<ParamsMarkup>(),
233
+ Match.tagsExhaustive({
234
+ InlineKeyboard: markup => ({
235
+ reply_markup: {
236
+ inline_keyboard: markup.rows, // TODO
237
+ },
238
+ }),
239
+ ReplyKeyboard: markup => ({
240
+ reply_markup: {
241
+ keyboard: markup.rows, // TODO
242
+ is_persistent: markup.persistent,
243
+ resize_keyboard: markup.resizable,
244
+ one_time_keyboard: markup.oneTime,
245
+ input_field_placeholder: Option.getOrUndefined(markup.inputPlaceholder),
246
+ selective: markup.selective,
247
+ },
248
+ }),
249
+ ReplyKeyboardRemove: markup => ({
250
+ reply_markup: {
251
+ remove_keyboard: true,
252
+ selective: markup.selective,
253
+ },
254
+ }),
255
+ ForceReply: markup => ({
256
+ reply_markup: {
257
+ force_reply: true,
258
+ input_field_placeholder: Option.getOrUndefined(markup.inputPlaceholder),
259
+ selective: markup.selective,
260
+ },
261
+ }),
262
+ }),
263
+ )
264
+
265
+ type PickMethodParams<
266
+ TMethod extends keyof BotApi.MethodParams,
267
+ TPick extends keyof BotApi.MethodParams[TMethod],
268
+ > = Pick<BotApi.MethodParams[TMethod], TPick>
269
+
270
+ type SendMethod = Extract<
271
+ keyof BotApi.MethodParams,
272
+ | 'sendMessage'
273
+ | 'sendPhoto'
274
+ | 'sendAudio'
275
+ | 'sendDocument'
276
+ | 'sendVideo'
277
+ | 'sendAnimation'
278
+ | 'sendVoice'
279
+ | 'sendVideoNote'
280
+ | 'sendPaidMedia'
281
+ | 'sendLocation'
282
+ | 'sendVenue'
283
+ | 'sendContact'
284
+ | 'sendDice'
285
+ | 'sendSticker'
286
+ | 'sendInvoice'
287
+ >
288
+
289
+ export const sendMessage = Effect.fnUntraced(function* (params: {
290
+ content: Content.Content
291
+ dialog: Dialog_.Dialog
292
+ options?: Send.Options
293
+ markup?: Markup.Markup
294
+ }) {
295
+ const api = yield* BotApi.BotApi
296
+ const common = {
297
+ ...paramsDialog(params.dialog),
298
+ ...(params.options ? paramsOptions(params.options) : {}),
299
+ ...(params.markup ? paramsMarkup(params.markup) : {}),
300
+ }
301
+ return yield* Match.value(params.content).pipe(
302
+ Match.tagsExhaustive({
303
+ Text: content => api.sendMessage({ ...common, ...paramsContent(content) }),
304
+ Photo: content => api.sendPhoto({ ...common, ...paramsContent(content) }),
305
+ Audio: content => api.sendAudio({ ...common, ...paramsContent(content) }),
306
+ Document: content => api.sendDocument({ ...common, ...paramsContent(content) }),
307
+ Video: content => api.sendVideo({ ...common, ...paramsContent(content) }),
308
+ Animation: content => api.sendAnimation({ ...common, ...paramsContent(content) }),
309
+ Voice: content => api.sendVoice({ ...common, ...paramsContent(content) }),
310
+ VideoNote: content => api.sendVideoNote({ ...common, ...paramsContent(content) }),
311
+ Location: content => api.sendLocation({ ...common, ...paramsContent(content) }),
312
+ Venue: content => api.sendVenue({ ...common, ...paramsContent(content) }),
313
+ Contact: content => api.sendContact({ ...common, ...paramsContent(content) }),
314
+ Dice: content => api.sendDice({ ...common, ...paramsContent(content) }),
315
+ Sticker: content => api.sendSticker({ ...common, ...paramsContent(content) }),
316
+ }),
317
+ )
318
+ })
@@ -1,41 +0,0 @@
1
- /**
2
- * This module implements Telegram Bot API webhook requests verification.
3
- *
4
- * @see https://core.telegram.org/bots/api#setwebhook
5
- */
6
- import type * as HttpServerError from '@effect/platform/HttpServerError';
7
- import type * as BotApi from './BotApi.ts';
8
- import * as HttpApiMiddleware from '@effect/platform/HttpApiMiddleware';
9
- import * as $Context from 'effect/Context';
10
- import * as Effect from 'effect/Effect';
11
- import * as Layer from 'effect/Layer';
12
- import * as Redacted from 'effect/Redacted';
13
- import * as Schema from 'effect/Schema';
14
- export declare const SECRET_HEADER = "x-telegram-bot-api-secret-token";
15
- declare const VerificationFailedError_base: Schema.TaggedErrorClass<VerificationFailedError, "@grom.js/effect-tg/BotApiWebhook/VerificationFailedError", {
16
- readonly _tag: Schema.tag<"@grom.js/effect-tg/BotApiWebhook/VerificationFailedError">;
17
- }>;
18
- export declare class VerificationFailedError extends VerificationFailedError_base {
19
- }
20
- declare const Context_base: $Context.TagClass<Context, "@grom.js/effect-tg/BotApiWebhook/Context", {
21
- update: Effect.Effect<BotApi.Types.Update, HttpServerError.RequestError>;
22
- }>;
23
- export declare class Context extends Context_base {
24
- }
25
- declare const VerifyMiddleware_base: HttpApiMiddleware.TagClass.Base<VerifyMiddleware, "@grom.js/effect-tg/BotApiWebhook/VerifyMiddleware", {
26
- readonly optional: false;
27
- readonly failure: typeof VerificationFailedError;
28
- readonly provides: typeof Context;
29
- }, HttpApiMiddleware.HttpApiMiddleware<{
30
- update: Effect.Effect<BotApi.Types.Update, HttpServerError.RequestError>;
31
- }, VerificationFailedError>>;
32
- export declare class VerifyMiddleware extends VerifyMiddleware_base {
33
- static layer(options: {
34
- /**
35
- * Telegram webhook secret token.
36
- */
37
- secret: Redacted.Redacted<string>;
38
- }): Layer.Layer<VerifyMiddleware, never, never>;
39
- }
40
- export {};
41
- //# sourceMappingURL=BotApiWebhook.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BotApiWebhook.d.ts","sourceRoot":"","sources":["../src/BotApiWebhook.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,KAAK,eAAe,MAAM,kCAAkC,CAAA;AACxE,OAAO,KAAK,KAAK,MAAM,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,iBAAiB,MAAM,oCAAoC,CAAA;AAGvE,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,eAAO,MAAM,aAAa,oCAAoC,CAAA;;;;AAG9D,qBAAa,uBAAwB,SAAQ,4BAO5C;CAAG;;YAIQ,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,eAAe,CAAC,YAAY,CAAC;;AAF5E,qBAAa,OAAQ,SAAQ,YAG1B;CAAG;;;;;;YADM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,eAAe,CAAC,YAAY,CAAC;;AAG5E,qBAAa,gBAAiB,SAAQ,qBAOrC;WACe,KAAK,CAAC,OAAO,EAAE;QAC3B;;WAEG;QACH,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;KAClC;CAkBF"}
@@ -1,43 +0,0 @@
1
- /**
2
- * This module implements Telegram Bot API webhook requests verification.
3
- *
4
- * @see https://core.telegram.org/bots/api#setwebhook
5
- */
6
- import * as HttpApiMiddleware from '@effect/platform/HttpApiMiddleware';
7
- import * as HttpApiSchema from '@effect/platform/HttpApiSchema';
8
- import * as HttpServerRequest from '@effect/platform/HttpServerRequest';
9
- import * as $Context from 'effect/Context';
10
- import * as Effect from 'effect/Effect';
11
- import * as Layer from 'effect/Layer';
12
- import * as Redacted from 'effect/Redacted';
13
- import * as Schema from 'effect/Schema';
14
- export const SECRET_HEADER = 'x-telegram-bot-api-secret-token';
15
- // eslint-disable-next-line unicorn/throw-new-error
16
- export class VerificationFailedError extends Schema.TaggedError()('@grom.js/effect-tg/BotApiWebhook/VerificationFailedError', {}, HttpApiSchema.annotations({
17
- status: 401,
18
- description: 'Missing or invalid webhook secret.',
19
- })) {
20
- }
21
- export class Context extends $Context.Tag('@grom.js/effect-tg/BotApiWebhook/Context')() {
22
- }
23
- export class VerifyMiddleware extends HttpApiMiddleware.Tag()('@grom.js/effect-tg/BotApiWebhook/VerifyMiddleware', {
24
- optional: false,
25
- failure: VerificationFailedError,
26
- provides: Context,
27
- }) {
28
- static layer(options) {
29
- return Layer.succeed(VerifyMiddleware, Effect.gen(function* () {
30
- const request = yield* HttpServerRequest.HttpServerRequest;
31
- const actual = request.headers[SECRET_HEADER];
32
- if (actual == null) {
33
- return yield* new VerificationFailedError();
34
- }
35
- if (actual !== Redacted.value(options.secret)) {
36
- return yield* new VerificationFailedError();
37
- }
38
- return {
39
- update: Effect.map(request.json, update => update),
40
- };
41
- }));
42
- }
43
- }
package/dist/Chat.d.ts DELETED
@@ -1,96 +0,0 @@
1
- import * as Data from 'effect/Data';
2
- export type Chat = PeerUser | PeerGroup | PeerChannel | PeerSupergroup | PublicChannel | PublicSupergroup | ForumTopic | ChannelDm;
3
- declare const PeerUser_base: new (options: {
4
- id: number;
5
- }) => {
6
- readonly id: number;
7
- readonly dialogId: number;
8
- sendParams: () => {
9
- chat_id: number | string;
10
- message_thread_id?: number | undefined;
11
- direct_messages_topic_id?: number | undefined;
12
- };
13
- };
14
- export declare class PeerUser extends PeerUser_base {
15
- }
16
- declare const PeerGroup_base: new (options: {
17
- id: number;
18
- }) => {
19
- readonly id: number;
20
- readonly dialogId: number;
21
- sendParams: () => {
22
- chat_id: number | string;
23
- message_thread_id?: number | undefined;
24
- direct_messages_topic_id?: number | undefined;
25
- };
26
- };
27
- export declare class PeerGroup extends PeerGroup_base {
28
- }
29
- declare const PeerChannel_base: new (options: {
30
- id: number;
31
- }) => {
32
- readonly id: number;
33
- readonly dialogId: number;
34
- sendParams: () => {
35
- chat_id: number | string;
36
- message_thread_id?: number | undefined;
37
- direct_messages_topic_id?: number | undefined;
38
- };
39
- };
40
- export declare class PeerChannel extends PeerChannel_base {
41
- }
42
- declare const PeerSupergroup_base: new (options: {
43
- id: number;
44
- }) => {
45
- readonly id: number;
46
- readonly dialogId: number;
47
- sendParams: () => {
48
- chat_id: number | string;
49
- message_thread_id?: number | undefined;
50
- direct_messages_topic_id?: number | undefined;
51
- };
52
- };
53
- export declare class PeerSupergroup extends PeerSupergroup_base {
54
- }
55
- export declare class PublicChannel extends Data.Class<{
56
- username: string;
57
- }> {
58
- sendParams(): {
59
- chat_id: string;
60
- };
61
- }
62
- export declare class PublicSupergroup extends Data.Class<{
63
- username: string;
64
- }> {
65
- sendParams(): {
66
- chat_id: string;
67
- };
68
- }
69
- export declare class ForumTopic extends Data.Class<{
70
- forum: PeerSupergroup | PublicSupergroup;
71
- topicId: number;
72
- }> {
73
- sendParams(): {
74
- message_thread_id: number;
75
- chat_id: number | string;
76
- direct_messages_topic_id?: number | undefined;
77
- } | {
78
- message_thread_id: number;
79
- chat_id: string;
80
- };
81
- }
82
- export declare class ChannelDm extends Data.Class<{
83
- channel: PeerChannel | PublicChannel;
84
- userId: number;
85
- }> {
86
- sendParams(): {
87
- direct_messages_topic_id: number;
88
- chat_id: number | string;
89
- message_thread_id?: number | undefined;
90
- } | {
91
- direct_messages_topic_id: number;
92
- chat_id: string;
93
- };
94
- }
95
- export {};
96
- //# sourceMappingURL=Chat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../src/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AAGnC,MAAM,MAAM,IAAI,GACV,QAAQ,GACR,SAAS,GACT,WAAW,GACX,cAAc,GACd,aAAa,GACb,gBAAgB,GAChB,UAAU,GACV,SAAS,CAAA;;;;;;;;;;;;AAEf,qBAAa,QAAS,SAAQ,aAG5B;CAAG;;;;;;;;;;;;AAEL,qBAAa,SAAU,SAAQ,cAG7B;CAAG;;;;;;;;;;;;AAEL,qBAAa,WAAY,SAAQ,gBAG/B;CAAG;;;;;;;;;;;;AAEL,qBAAa,cAAe,SAAQ,mBAGlC;CAAG;AAEL,qBAAa,aAAc,SAAQ,IAAI,CAAC,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;IACjE,UAAU;;;CAGX;AAED,qBAAa,gBAAiB,SAAQ,IAAI,CAAC,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;IACpE,UAAU;;;CAGX;AAED,qBAAa,UAAW,SAAQ,IAAI,CAAC,KAAK,CAAC;IACzC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAAA;IACxC,OAAO,EAAE,MAAM,CAAA;CAChB,CAAC;IACA,UAAU;;;;;;;;CAMX;AAED,qBAAa,SAAU,SAAQ,IAAI,CAAC,KAAK,CAAC;IACxC,OAAO,EAAE,WAAW,GAAG,aAAa,CAAA;IACpC,MAAM,EAAE,MAAM,CAAA;CACf,CAAC;IACA,UAAU;;;;;;;;CAMX"}
package/dist/Chat.js DELETED
@@ -1,48 +0,0 @@
1
- import * as Data from 'effect/Data';
2
- import * as internal from "./internal/chat.js";
3
- export class PeerUser extends internal.Peer({
4
- validatePeerId: id => (id >= 1 && id <= 0xFFFFFFFFFF),
5
- peerIdToDialogId: id => id,
6
- }) {
7
- }
8
- export class PeerGroup extends internal.Peer({
9
- validatePeerId: id => (id >= 1 && id <= 999999999999),
10
- peerIdToDialogId: id => -id,
11
- }) {
12
- }
13
- export class PeerChannel extends internal.Peer({
14
- validatePeerId: id => (id >= 1 && id <= 997852516352),
15
- peerIdToDialogId: id => -(1000000000000 + id),
16
- }) {
17
- }
18
- export class PeerSupergroup extends internal.Peer({
19
- validatePeerId: id => (id >= 1 && id <= 997852516352),
20
- peerIdToDialogId: id => -(1000000000000 + id),
21
- }) {
22
- }
23
- export class PublicChannel extends Data.Class {
24
- sendParams() {
25
- return { chat_id: this.username };
26
- }
27
- }
28
- export class PublicSupergroup extends Data.Class {
29
- sendParams() {
30
- return { chat_id: this.username };
31
- }
32
- }
33
- export class ForumTopic extends Data.Class {
34
- sendParams() {
35
- return {
36
- ...this.forum.sendParams(),
37
- message_thread_id: this.topicId,
38
- };
39
- }
40
- }
41
- export class ChannelDm extends Data.Class {
42
- sendParams() {
43
- return {
44
- ...this.channel.sendParams(),
45
- direct_messages_topic_id: this.userId,
46
- };
47
- }
48
- }
@@ -1,4 +0,0 @@
1
- /** @todo */
2
- export declare class InputFile {
3
- }
4
- //# sourceMappingURL=InputFile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InputFile.d.ts","sourceRoot":"","sources":["../src/InputFile.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,qBAAa,SAAS;CAAG"}
package/dist/InputFile.js DELETED
@@ -1,3 +0,0 @@
1
- /** @todo */
2
- export class InputFile {
3
- }
@@ -1,8 +0,0 @@
1
- import type { Effect } from 'effect/Effect';
2
- import type { BotApiError } from '../BotApi.ts';
3
- import type { BotApiTransportError } from '../BotApiTransport.ts';
4
- import type { MethodParams, MethodResults } from './botApiMethods.gen.ts';
5
- export interface BotApiMethod<M extends keyof MethodParams> {
6
- (params: MethodParams[M]): Effect<MethodResults[M], BotApiError | BotApiTransportError>;
7
- }
8
- //# sourceMappingURL=botApiMethod.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"botApiMethod.d.ts","sourceRoot":"","sources":["../../src/internal/botApiMethod.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAEzE,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,YAAY;IACxD,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,MAAM,CAC/B,aAAa,CAAC,CAAC,CAAC,EAChB,WAAW,GAAG,oBAAoB,CACnC,CAAA;CACF"}
@@ -1 +0,0 @@
1
- export {};