@sugansociety/gallagherrich 4.16.8

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 (180) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1024 -0
  3. package/filters.d.ts +1 -0
  4. package/filters.js +1 -0
  5. package/format.d.ts +1 -0
  6. package/format.js +1 -0
  7. package/future.d.ts +1 -0
  8. package/future.js +1 -0
  9. package/lib/button.js +102 -0
  10. package/lib/cli.mjs +105 -0
  11. package/lib/composer.js +583 -0
  12. package/lib/context.js +1280 -0
  13. package/lib/core/helpers/args.js +59 -0
  14. package/lib/core/helpers/check.js +57 -0
  15. package/lib/core/helpers/compact.js +18 -0
  16. package/lib/core/helpers/deunionize.js +14 -0
  17. package/lib/core/helpers/formatting.js +92 -0
  18. package/lib/core/helpers/util.js +51 -0
  19. package/lib/core/network/client.js +321 -0
  20. package/lib/core/network/error.js +22 -0
  21. package/lib/core/network/multipart-stream.js +62 -0
  22. package/lib/core/network/polling.js +88 -0
  23. package/lib/core/network/webhook.js +55 -0
  24. package/lib/core/types/rich-message.js +288 -0
  25. package/lib/core/types/typegram.js +29 -0
  26. package/lib/filters.js +70 -0
  27. package/lib/filters.js.map +1 -0
  28. package/lib/format.js +39 -0
  29. package/lib/format.js.map +1 -0
  30. package/lib/future.js +167 -0
  31. package/lib/future.js.map +1 -0
  32. package/lib/index.js +50 -0
  33. package/lib/input.js +62 -0
  34. package/lib/markup.js +112 -0
  35. package/lib/markup.js.map +1 -0
  36. package/lib/middleware.js +3 -0
  37. package/lib/reactions.js +85 -0
  38. package/lib/router.js +47 -0
  39. package/lib/scenes/base.js +40 -0
  40. package/lib/scenes/context.js +105 -0
  41. package/lib/scenes/index.js +22 -0
  42. package/lib/scenes/stage.js +50 -0
  43. package/lib/scenes/wizard/context.js +32 -0
  44. package/lib/scenes/wizard/index.js +46 -0
  45. package/lib/scenes.js +18 -0
  46. package/lib/scenes.js.map +1 -0
  47. package/lib/session.js +167 -0
  48. package/lib/session.js.map +1 -0
  49. package/lib/telegraf.js +273 -0
  50. package/lib/telegram-types.js +7 -0
  51. package/lib/telegram.js +1270 -0
  52. package/lib/types.js +3 -0
  53. package/lib/types.js.map +1 -0
  54. package/lib/utils.js +6 -0
  55. package/lib/utils.js.map +1 -0
  56. package/markup.d.ts +1 -0
  57. package/markup.js +1 -0
  58. package/package.json +136 -0
  59. package/scenes.d.ts +1 -0
  60. package/scenes.js +1 -0
  61. package/session.d.ts +1 -0
  62. package/session.js +1 -0
  63. package/src/button.ts +182 -0
  64. package/src/composer.ts +1008 -0
  65. package/src/context.ts +1747 -0
  66. package/src/core/helpers/args.ts +63 -0
  67. package/src/core/helpers/check.ts +71 -0
  68. package/src/core/helpers/compact.ts +18 -0
  69. package/src/core/helpers/deunionize.ts +26 -0
  70. package/src/core/helpers/formatting.ts +119 -0
  71. package/src/core/helpers/util.ts +96 -0
  72. package/src/core/network/client.ts +396 -0
  73. package/src/core/network/error.ts +29 -0
  74. package/src/core/network/multipart-stream.ts +45 -0
  75. package/src/core/network/polling.ts +94 -0
  76. package/src/core/network/webhook.ts +58 -0
  77. package/src/core/types/rich-message.ts +375 -0
  78. package/src/core/types/typegram.ts +55 -0
  79. package/src/filters.ts +109 -0
  80. package/src/format.ts +110 -0
  81. package/src/future.ts +231 -0
  82. package/src/index.ts +18 -0
  83. package/src/input.ts +59 -0
  84. package/src/markup.ts +142 -0
  85. package/src/middleware.ts +24 -0
  86. package/src/reactions.ts +118 -0
  87. package/src/router.ts +55 -0
  88. package/src/scenes/base.ts +52 -0
  89. package/src/scenes/context.ts +136 -0
  90. package/src/scenes/index.ts +21 -0
  91. package/src/scenes/stage.ts +71 -0
  92. package/src/scenes/wizard/context.ts +58 -0
  93. package/src/scenes/wizard/index.ts +63 -0
  94. package/src/scenes.ts +1 -0
  95. package/src/session.ts +204 -0
  96. package/src/telegraf.ts +380 -0
  97. package/src/telegram-types.ts +256 -0
  98. package/src/telegram.ts +1676 -0
  99. package/src/types.ts +2 -0
  100. package/src/utils.ts +1 -0
  101. package/types.d.ts +1 -0
  102. package/types.js +1 -0
  103. package/typings/button.d.ts +36 -0
  104. package/typings/button.d.ts.map +1 -0
  105. package/typings/composer.d.ts +227 -0
  106. package/typings/composer.d.ts.map +1 -0
  107. package/typings/context.d.ts +699 -0
  108. package/typings/context.d.ts.map +1 -0
  109. package/typings/core/helpers/args.d.ts +11 -0
  110. package/typings/core/helpers/args.d.ts.map +1 -0
  111. package/typings/core/helpers/check.d.ts +56 -0
  112. package/typings/core/helpers/check.d.ts.map +1 -0
  113. package/typings/core/helpers/compact.d.ts +4 -0
  114. package/typings/core/helpers/compact.d.ts.map +1 -0
  115. package/typings/core/helpers/deunionize.d.ts +18 -0
  116. package/typings/core/helpers/deunionize.d.ts.map +1 -0
  117. package/typings/core/helpers/formatting.d.ts +30 -0
  118. package/typings/core/helpers/formatting.d.ts.map +1 -0
  119. package/typings/core/helpers/util.d.ts +27 -0
  120. package/typings/core/helpers/util.d.ts.map +1 -0
  121. package/typings/core/network/client.d.ts +55 -0
  122. package/typings/core/network/client.d.ts.map +1 -0
  123. package/typings/core/network/error.d.ts +16 -0
  124. package/typings/core/network/error.d.ts.map +1 -0
  125. package/typings/core/network/multipart-stream.d.ts +20 -0
  126. package/typings/core/network/multipart-stream.d.ts.map +1 -0
  127. package/typings/core/network/polling.d.ts +16 -0
  128. package/typings/core/network/polling.d.ts.map +1 -0
  129. package/typings/core/network/webhook.d.ts +7 -0
  130. package/typings/core/network/webhook.d.ts.map +1 -0
  131. package/typings/core/types/rich-message.d.ts +144 -0
  132. package/typings/core/types/rich-message.d.ts.map +1 -0
  133. package/typings/core/types/typegram.d.ts +46 -0
  134. package/typings/core/types/typegram.d.ts.map +1 -0
  135. package/typings/filters.d.ts +18 -0
  136. package/typings/filters.d.ts.map +1 -0
  137. package/typings/format.d.ts +22 -0
  138. package/typings/format.d.ts.map +1 -0
  139. package/typings/future.d.ts +12 -0
  140. package/typings/future.d.ts.map +1 -0
  141. package/typings/index.d.ts +16 -0
  142. package/typings/index.d.ts.map +1 -0
  143. package/typings/input.d.ts +59 -0
  144. package/typings/input.d.ts.map +1 -0
  145. package/typings/markup.d.ts +27 -0
  146. package/typings/markup.d.ts.map +1 -0
  147. package/typings/middleware.d.ts +8 -0
  148. package/typings/middleware.d.ts.map +1 -0
  149. package/typings/reactions.d.ts +32 -0
  150. package/typings/reactions.d.ts.map +1 -0
  151. package/typings/router.d.ts +21 -0
  152. package/typings/router.d.ts.map +1 -0
  153. package/typings/scenes/base.d.ts +22 -0
  154. package/typings/scenes/base.d.ts.map +1 -0
  155. package/typings/scenes/context.d.ts +36 -0
  156. package/typings/scenes/context.d.ts.map +1 -0
  157. package/typings/scenes/index.d.ts +11 -0
  158. package/typings/scenes/index.d.ts.map +1 -0
  159. package/typings/scenes/stage.d.ts +24 -0
  160. package/typings/scenes/stage.d.ts.map +1 -0
  161. package/typings/scenes/wizard/context.d.ts +29 -0
  162. package/typings/scenes/wizard/context.d.ts.map +1 -0
  163. package/typings/scenes/wizard/index.d.ts +16 -0
  164. package/typings/scenes/wizard/index.d.ts.map +1 -0
  165. package/typings/scenes.d.ts +2 -0
  166. package/typings/scenes.d.ts.map +1 -0
  167. package/typings/session.d.ts +55 -0
  168. package/typings/session.d.ts.map +1 -0
  169. package/typings/telegraf.d.ts +119 -0
  170. package/typings/telegraf.d.ts.map +1 -0
  171. package/typings/telegram-types.d.ts +141 -0
  172. package/typings/telegram-types.d.ts.map +1 -0
  173. package/typings/telegram.d.ts +694 -0
  174. package/typings/telegram.d.ts.map +1 -0
  175. package/typings/types.d.ts +3 -0
  176. package/typings/types.d.ts.map +1 -0
  177. package/typings/utils.d.ts +2 -0
  178. package/typings/utils.d.ts.map +1 -0
  179. package/utils.d.ts +1 -0
  180. package/utils.js +1 -0
package/src/future.ts ADDED
@@ -0,0 +1,231 @@
1
+ import { ReplyParameters } from '@telegraf/types'
2
+ import Context from './context'
3
+ import { Middleware } from './middleware'
4
+ import type { InputRichMessage } from './core/types/rich-message'
5
+ import type { ExtraSendRichMessage } from './telegram-types'
6
+
7
+ type ReplyContext = { [key in keyof Context & `reply${string}`]: Context[key] }
8
+
9
+ function makeReply<
10
+ C extends Context,
11
+ E extends { reply_parameters?: ReplyParameters },
12
+ >(ctx: C, extra?: E) {
13
+ if (ctx.msgId)
14
+ return {
15
+ // overrides in this order so user can override all properties
16
+ reply_parameters: {
17
+ message_id: ctx.msgId,
18
+ ...extra?.reply_parameters,
19
+ },
20
+ ...extra,
21
+ }
22
+ else return extra
23
+ }
24
+
25
+ const replyContext: ReplyContext = {
26
+ replyWithChatAction: function () {
27
+ throw new TypeError(
28
+ 'ctx.replyWithChatAction has been removed, use ctx.sendChatAction instead'
29
+ )
30
+ },
31
+ reply(this: Context, text, extra) {
32
+ this.assert(this.chat, 'reply')
33
+ return this.telegram.sendMessage(this.chat.id, text, makeReply(this, extra))
34
+ },
35
+ replyWithAnimation(this: Context, animation, extra) {
36
+ this.assert(this.chat, 'replyWithAnimation')
37
+ return this.telegram.sendAnimation(
38
+ this.chat.id,
39
+ animation,
40
+ makeReply(this, extra)
41
+ )
42
+ },
43
+ replyWithAudio(this: Context, audio, extra) {
44
+ this.assert(this.chat, 'replyWithAudio')
45
+ return this.telegram.sendAudio(this.chat.id, audio, makeReply(this, extra))
46
+ },
47
+ replyWithContact(this: Context, phoneNumber, firstName, extra) {
48
+ this.assert(this.chat, 'replyWithContact')
49
+ return this.telegram.sendContact(
50
+ this.chat.id,
51
+ phoneNumber,
52
+ firstName,
53
+ makeReply(this, extra)
54
+ )
55
+ },
56
+ replyWithDice(this: Context, extra) {
57
+ this.assert(this.chat, 'replyWithDice')
58
+ return this.telegram.sendDice(this.chat.id, makeReply(this, extra))
59
+ },
60
+ replyWithDocument(this: Context, document, extra) {
61
+ this.assert(this.chat, 'replyWithDocument')
62
+ return this.telegram.sendDocument(
63
+ this.chat.id,
64
+ document,
65
+ makeReply(this, extra)
66
+ )
67
+ },
68
+ replyWithGame(this: Context, gameName, extra) {
69
+ this.assert(this.chat, 'replyWithGame')
70
+ return this.telegram.sendGame(
71
+ this.chat.id,
72
+ gameName,
73
+ makeReply(this, extra)
74
+ )
75
+ },
76
+ replyWithHTML(this: Context, html, extra) {
77
+ this.assert(this.chat, 'replyWithHTML')
78
+ return this.telegram.sendMessage(this.chat.id, html, {
79
+ parse_mode: 'HTML',
80
+ ...makeReply(this, extra),
81
+ })
82
+ },
83
+ replyWithInvoice(this: Context, invoice, extra) {
84
+ this.assert(this.chat, 'replyWithInvoice')
85
+ return this.telegram.sendInvoice(
86
+ this.chat.id,
87
+ invoice,
88
+ makeReply(this, extra)
89
+ )
90
+ },
91
+ replyWithLocation(this: Context, latitude, longitude, extra) {
92
+ this.assert(this.chat, 'replyWithLocation')
93
+ return this.telegram.sendLocation(
94
+ this.chat.id,
95
+ latitude,
96
+ longitude,
97
+ makeReply(this, extra)
98
+ )
99
+ },
100
+ replyWithMarkdown(this: Context, markdown, extra) {
101
+ this.assert(this.chat, 'replyWithMarkdown')
102
+ return this.telegram.sendMessage(this.chat.id, markdown, {
103
+ parse_mode: 'Markdown',
104
+ ...makeReply(this, extra),
105
+ })
106
+ },
107
+ replyWithMarkdownV2(this: Context, markdown, extra) {
108
+ this.assert(this.chat, 'replyWithMarkdownV2')
109
+ return this.telegram.sendMessage(this.chat.id, markdown, {
110
+ parse_mode: 'MarkdownV2',
111
+ ...makeReply(this, extra),
112
+ })
113
+ },
114
+ replyWithMediaGroup(this: Context, media, extra) {
115
+ this.assert(this.chat, 'replyWithMediaGroup')
116
+ return this.telegram.sendMediaGroup(
117
+ this.chat.id,
118
+ media,
119
+ makeReply(this, extra)
120
+ )
121
+ },
122
+ replyWithPhoto(this: Context, photo, extra) {
123
+ this.assert(this.chat, 'replyWithPhoto')
124
+ return this.telegram.sendPhoto(this.chat.id, photo, makeReply(this, extra))
125
+ },
126
+ replyWithPoll(this: Context, question, options, extra) {
127
+ this.assert(this.chat, 'replyWithPoll')
128
+ return this.telegram.sendPoll(
129
+ this.chat.id,
130
+ question,
131
+ options,
132
+ makeReply(this, extra)
133
+ )
134
+ },
135
+ replyWithQuiz(this: Context, question, options, extra) {
136
+ this.assert(this.chat, 'replyWithQuiz')
137
+ return this.telegram.sendQuiz(
138
+ this.chat.id,
139
+ question,
140
+ options,
141
+ makeReply(this, extra)
142
+ )
143
+ },
144
+ replyWithSticker(this: Context, sticker, extra) {
145
+ this.assert(this.chat, 'replyWithSticker')
146
+ return this.telegram.sendSticker(
147
+ this.chat.id,
148
+ sticker,
149
+ makeReply(this, extra)
150
+ )
151
+ },
152
+ replyWithVenue(this: Context, latitude, longitude, title, address, extra) {
153
+ this.assert(this.chat, 'replyWithVenue')
154
+ return this.telegram.sendVenue(
155
+ this.chat.id,
156
+ latitude,
157
+ longitude,
158
+ title,
159
+ address,
160
+ makeReply(this, extra)
161
+ )
162
+ },
163
+ replyWithVideo(this: Context, video, extra) {
164
+ this.assert(this.chat, 'replyWithVideo')
165
+ return this.telegram.sendVideo(this.chat.id, video, makeReply(this, extra))
166
+ },
167
+ replyWithVideoNote(this: Context, videoNote, extra) {
168
+ this.assert(this.chat, 'replyWithVideoNote')
169
+ return this.telegram.sendVideoNote(
170
+ this.chat.id,
171
+ videoNote,
172
+ makeReply(this, extra)
173
+ )
174
+ },
175
+ replyWithVoice(this: Context, voice, extra) {
176
+ this.assert(this.chat, 'replyWithVoice')
177
+ return this.telegram.sendVoice(this.chat.id, voice, makeReply(this, extra))
178
+ },
179
+ replyWithRichMessage(this: Context, options) {
180
+ this.assert(this.chat, 'replyWithRichMessage')
181
+ const { text, parseMode = 'HTML', buttons, disableLinkPreview, extra } = options
182
+ return this.telegram.sendMessage(this.chat.id, text, makeReply(this, {
183
+ parse_mode: parseMode,
184
+ link_preview_options: disableLinkPreview ? { is_disabled: true } : undefined,
185
+ reply_markup: buttons?.length ? { inline_keyboard: buttons } : undefined,
186
+ ...extra,
187
+ }))
188
+ },
189
+ replyWithRichMessageContent(this: Context, richMessage: InputRichMessage, extra?: ExtraSendRichMessage) {
190
+ this.assert(this.chat, 'replyWithRichMessageContent')
191
+ return this.telegram.sendRichMessage(this.chat.id, richMessage, makeReply(this, extra))
192
+ },
193
+ }
194
+
195
+ /**
196
+ * Sets up Context to use the new reply methods.
197
+ * This middleware makes `ctx.reply()` and `ctx.replyWith*()` methods will actually reply to the message they are replying to.
198
+ * Use `ctx.sendMessage()` to send a message in chat without replying to it.
199
+ *
200
+ * If the message to reply is deleted, `reply()` will send a normal message.
201
+ * If the update is not a message and we are unable to reply, `reply()` will send a normal message.
202
+ */
203
+ export function useNewReplies<C extends Context>(): Middleware<C> {
204
+ return (ctx, next) => {
205
+ ctx.reply = replyContext.reply
206
+ ctx.replyWithPhoto = replyContext.replyWithPhoto
207
+ ctx.replyWithMediaGroup = replyContext.replyWithMediaGroup
208
+ ctx.replyWithAudio = replyContext.replyWithAudio
209
+ ctx.replyWithDice = replyContext.replyWithDice
210
+ ctx.replyWithDocument = replyContext.replyWithDocument
211
+ ctx.replyWithSticker = replyContext.replyWithSticker
212
+ ctx.replyWithVideo = replyContext.replyWithVideo
213
+ ctx.replyWithAnimation = replyContext.replyWithAnimation
214
+ ctx.replyWithVideoNote = replyContext.replyWithVideoNote
215
+ ctx.replyWithInvoice = replyContext.replyWithInvoice
216
+ ctx.replyWithGame = replyContext.replyWithGame
217
+ ctx.replyWithVoice = replyContext.replyWithVoice
218
+ ctx.replyWithPoll = replyContext.replyWithPoll
219
+ ctx.replyWithQuiz = replyContext.replyWithQuiz
220
+ ctx.replyWithChatAction = replyContext.replyWithChatAction
221
+ ctx.replyWithLocation = replyContext.replyWithLocation
222
+ ctx.replyWithVenue = replyContext.replyWithVenue
223
+ ctx.replyWithContact = replyContext.replyWithContact
224
+ ctx.replyWithMarkdown = replyContext.replyWithMarkdown
225
+ ctx.replyWithMarkdownV2 = replyContext.replyWithMarkdownV2
226
+ ctx.replyWithHTML = replyContext.replyWithHTML
227
+ ctx.replyWithRichMessage = replyContext.replyWithRichMessage
228
+ ctx.replyWithRichMessageContent = replyContext.replyWithRichMessageContent
229
+ return next()
230
+ }
231
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ export { Telegraf } from './telegraf'
2
+ export { Context, NarrowedContext } from './context'
3
+ export { Composer } from './composer'
4
+ export { Middleware, MiddlewareFn, MiddlewareObj } from './middleware'
5
+ export { Router } from './router'
6
+ export { TelegramError } from './core/network/error'
7
+ export { Telegram } from './telegram'
8
+
9
+ export * as Types from './telegram-types'
10
+ export * as Markup from './markup'
11
+ export * as Input from './input'
12
+ export * as Format from './format'
13
+
14
+ export { deunionize } from './core/helpers/deunionize'
15
+ export { session, MemorySessionStore, SessionStore } from './session'
16
+
17
+ export * as Scenes from './scenes'
18
+ export * as RichMessage from './core/types/rich-message'
package/src/input.ts ADDED
@@ -0,0 +1,59 @@
1
+ import { InputFile } from './core/types/typegram'
2
+
3
+ /**
4
+ * The local file specified by path will be uploaded to Telegram using multipart/form-data.
5
+ *
6
+ * 10 MB max size for photos, 50 MB for other files.
7
+ */
8
+ // prettier-ignore
9
+ export const fromLocalFile = (path: string, filename?: string): InputFile => ({ source: path, filename })
10
+
11
+ /**
12
+ * The buffer will be uploaded as file to Telegram using multipart/form-data.
13
+ *
14
+ * 10 MB max size for photos, 50 MB for other files.
15
+ */
16
+ // prettier-ignore
17
+ export const fromBuffer = (buffer: Buffer, filename?: string): InputFile => ({ source: buffer, filename })
18
+
19
+ /**
20
+ * Contents of the stream will be uploaded as file to Telegram using multipart/form-data.
21
+ *
22
+ * 10 MB max size for photos, 50 MB for other files.
23
+ */
24
+ // prettier-ignore
25
+ export const fromReadableStream = (stream: NodeJS.ReadableStream, filename?: string): InputFile => ({ source: stream, filename })
26
+
27
+ /**
28
+ * Contents of the URL will be streamed to Telegram.
29
+ *
30
+ * 10 MB max size for photos, 50 MB for other files.
31
+ */
32
+ // prettier-ignore
33
+ export const fromURLStream = (url: string | URL, filename?: string): InputFile => ({ url: url.toString(), filename })
34
+
35
+ /**
36
+ * Provide Telegram with an HTTP URL for the file to be sent.
37
+ * Telegram will download and send the file.
38
+ *
39
+ * * The target file must have the correct MIME type (e.g., audio/mpeg for `sendAudio`, etc.).
40
+ * * `sendDocument` with URL will currently only work for GIF, PDF and ZIP files.
41
+ * * To use `sendVoice`, the file must have the type audio/ogg and be no more than 1MB in size.
42
+ * 1-20MB voice notes will be sent as files.
43
+ *
44
+ * 5 MB max size for photos and 20 MB max for other types of content.
45
+ */
46
+ export const fromURL = (url: string | URL): string => url.toString()
47
+
48
+ /**
49
+ * If the file is already stored somewhere on the Telegram servers, you don't need to reupload it:
50
+ * each file object has a file_id field, simply pass this file_id as a parameter instead of uploading.
51
+ *
52
+ * It is not possible to change the file type when resending by file_id.
53
+ *
54
+ * It is not possible to resend thumbnails using file_id.
55
+ * They have to be uploaded using one of the other Input methods.
56
+ *
57
+ * There are no limits for files sent this way.
58
+ */
59
+ export const fromFileId = (fileId: string): string => fileId
package/src/markup.ts ADDED
@@ -0,0 +1,142 @@
1
+ import {
2
+ ForceReply,
3
+ InlineKeyboardButton,
4
+ InlineKeyboardMarkup,
5
+ KeyboardButton,
6
+ ReplyKeyboardMarkup,
7
+ ReplyKeyboardRemove,
8
+ } from './core/types/typegram'
9
+ import { is2D } from './core/helpers/check'
10
+
11
+ type Hideable<B> = B & { hide?: boolean }
12
+ type HideableKBtn = Hideable<KeyboardButton>
13
+ type HideableIKBtn = Hideable<InlineKeyboardButton>
14
+
15
+ export class Markup<
16
+ T extends
17
+ | InlineKeyboardMarkup
18
+ | ReplyKeyboardMarkup
19
+ | ReplyKeyboardRemove
20
+ | ForceReply,
21
+ > {
22
+ constructor(readonly reply_markup: T) {}
23
+
24
+ selective<T extends ForceReply | ReplyKeyboardMarkup>(
25
+ this: Markup<T>,
26
+ value = true
27
+ ) {
28
+ return new Markup<T>({ ...this.reply_markup, selective: value })
29
+ }
30
+
31
+ placeholder<T extends ForceReply | ReplyKeyboardMarkup>(
32
+ this: Markup<T>,
33
+ placeholder: string
34
+ ) {
35
+ return new Markup<T>({
36
+ ...this.reply_markup,
37
+ input_field_placeholder: placeholder,
38
+ })
39
+ }
40
+
41
+ resize(this: Markup<ReplyKeyboardMarkup>, value = true) {
42
+ return new Markup<ReplyKeyboardMarkup>({
43
+ ...this.reply_markup,
44
+ resize_keyboard: value,
45
+ })
46
+ }
47
+
48
+ oneTime(this: Markup<ReplyKeyboardMarkup>, value = true) {
49
+ return new Markup<ReplyKeyboardMarkup>({
50
+ ...this.reply_markup,
51
+ one_time_keyboard: value,
52
+ })
53
+ }
54
+
55
+ persistent(this: Markup<ReplyKeyboardMarkup>, value = true) {
56
+ return new Markup<ReplyKeyboardMarkup>({
57
+ ...this.reply_markup,
58
+ is_persistent: value,
59
+ })
60
+ }
61
+ }
62
+
63
+ export * as button from './button'
64
+
65
+ export function removeKeyboard(): Markup<ReplyKeyboardRemove> {
66
+ return new Markup<ReplyKeyboardRemove>({ remove_keyboard: true })
67
+ }
68
+
69
+ export function forceReply(): Markup<ForceReply> {
70
+ return new Markup<ForceReply>({ force_reply: true })
71
+ }
72
+
73
+ export function keyboard(buttons: HideableKBtn[][]): Markup<ReplyKeyboardMarkup>
74
+ export function keyboard(
75
+ buttons: HideableKBtn[],
76
+ options?: Partial<KeyboardBuildingOptions<HideableKBtn>>
77
+ ): Markup<ReplyKeyboardMarkup>
78
+ export function keyboard(
79
+ buttons: HideableKBtn[] | HideableKBtn[][],
80
+ options?: Partial<KeyboardBuildingOptions<HideableKBtn>>
81
+ ): Markup<ReplyKeyboardMarkup> {
82
+ const keyboard = buildKeyboard(buttons, {
83
+ columns: 1,
84
+ ...options,
85
+ })
86
+ return new Markup<ReplyKeyboardMarkup>({ keyboard })
87
+ }
88
+
89
+ export function inlineKeyboard(
90
+ buttons: HideableIKBtn[][]
91
+ ): Markup<InlineKeyboardMarkup>
92
+ export function inlineKeyboard(
93
+ buttons: HideableIKBtn[],
94
+ options?: Partial<KeyboardBuildingOptions<HideableIKBtn>>
95
+ ): Markup<InlineKeyboardMarkup>
96
+ export function inlineKeyboard(
97
+ buttons: HideableIKBtn[] | HideableIKBtn[][],
98
+ options?: Partial<KeyboardBuildingOptions<HideableIKBtn>>
99
+ ): Markup<InlineKeyboardMarkup> {
100
+ const inlineKeyboard = buildKeyboard(buttons, {
101
+ columns: buttons.length,
102
+ ...options,
103
+ })
104
+ return new Markup<InlineKeyboardMarkup>({ inline_keyboard: inlineKeyboard })
105
+ }
106
+
107
+ interface KeyboardBuildingOptions<B extends HideableKBtn | HideableIKBtn> {
108
+ wrap?: (btn: B, index: number, currentRow: B[]) => boolean
109
+ columns: number
110
+ }
111
+
112
+ function buildKeyboard<B extends HideableKBtn | HideableIKBtn>(
113
+ buttons: B[] | B[][],
114
+ options: KeyboardBuildingOptions<B>
115
+ ): B[][] {
116
+ const result: B[][] = []
117
+ if (!Array.isArray(buttons)) {
118
+ return result
119
+ }
120
+ if (is2D(buttons)) {
121
+ return buttons.map((row) => row.filter((button) => !button.hide))
122
+ }
123
+ const wrapFn =
124
+ options.wrap !== undefined
125
+ ? options.wrap
126
+ : (_btn: B, _index: number, currentRow: B[]) =>
127
+ currentRow.length >= options.columns
128
+ let currentRow: B[] = []
129
+ let index = 0
130
+ for (const btn of buttons.filter((button) => !button.hide)) {
131
+ if (wrapFn(btn, index, currentRow) && currentRow.length > 0) {
132
+ result.push(currentRow)
133
+ currentRow = []
134
+ }
135
+ currentRow.push(btn)
136
+ index++
137
+ }
138
+ if (currentRow.length > 0) {
139
+ result.push(currentRow)
140
+ }
141
+ return result
142
+ }
@@ -0,0 +1,24 @@
1
+ import { Context } from './context'
2
+ import { Update } from './core/types/typegram'
3
+
4
+ /*
5
+ next's parameter is in a contravariant position, and thus, trying to type it
6
+ prevents assigning `MiddlewareFn<ContextMessageUpdate>`
7
+ to `MiddlewareFn<CustomContext>`.
8
+ Middleware passing the parameter should be a separate type instead.
9
+ */
10
+ export type MiddlewareFn<C extends Context<U>, U extends Update = Update> = (
11
+ ctx: C,
12
+ next: () => Promise<void>
13
+ ) => Promise<unknown> | void
14
+
15
+ export interface MiddlewareObj<
16
+ C extends Context<U>,
17
+ U extends Update = Update,
18
+ > {
19
+ middleware: () => MiddlewareFn<C, U>
20
+ }
21
+
22
+ export type Middleware<C extends Context<U>, U extends Update = Update> =
23
+ | MiddlewareFn<C, U>
24
+ | MiddlewareObj<C, U>
@@ -0,0 +1,118 @@
1
+ import { Deunionize } from './core/helpers/deunionize'
2
+ import { indexed } from './core/helpers/util'
3
+ import * as tg from './core/types/typegram'
4
+
5
+ export type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
6
+ export const Digit = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])
7
+ export type Reaction =
8
+ | tg.TelegramEmoji
9
+ | `${Digit}${string}`
10
+ | Deunionize<tg.ReactionType>
11
+
12
+ type ReactionCtx = { update: Partial<tg.Update.MessageReactionUpdate> }
13
+
14
+ const inspectReaction = (reaction: tg.ReactionType) => {
15
+ if (reaction.type === 'custom_emoji')
16
+ return `Custom(${reaction.custom_emoji_id})`
17
+ else return reaction.emoji
18
+ }
19
+
20
+ export class ReactionList {
21
+ // this is a lie, proxy will be used to access the properties
22
+ [index: number]: Deunionize<tg.ReactionType>
23
+
24
+ protected constructor(protected list: tg.ReactionType[]) {}
25
+
26
+ static fromArray(list: tg.ReactionType[] = []): ReactionList {
27
+ return indexed(
28
+ new ReactionList(list),
29
+ function (this: ReactionList, index) {
30
+ return this.list[index]
31
+ }
32
+ )
33
+ }
34
+
35
+ static has(reactions: tg.ReactionType[], reaction: Reaction): boolean {
36
+ if (typeof reaction === 'string')
37
+ if (Digit.has(reaction[0] as string))
38
+ return reactions.some(
39
+ (r: Deunionize<tg.ReactionType>) => r.custom_emoji_id === reaction
40
+ )
41
+ else
42
+ return reactions.some(
43
+ (r: Deunionize<tg.ReactionType>) => r.emoji === reaction
44
+ )
45
+
46
+ return reactions.some((r: Deunionize<tg.ReactionType>) => {
47
+ if (r.type === 'custom_emoji')
48
+ return r.custom_emoji_id === reaction.custom_emoji_id
49
+ else if (r.type === 'emoji') return r.emoji === reaction.emoji
50
+ })
51
+ }
52
+
53
+ toArray(): tg.ReactionType[] {
54
+ return [...this.list]
55
+ }
56
+
57
+ filter(
58
+ filterFn: (value: tg.ReactionType, index: number) => boolean
59
+ ): ReactionList {
60
+ return ReactionList.fromArray(this.list.filter(filterFn))
61
+ }
62
+
63
+ has(reaction: Reaction): boolean {
64
+ return ReactionList.has(this.list, reaction)
65
+ }
66
+
67
+ get count(): number {
68
+ return this.list.length
69
+ }
70
+
71
+ [Symbol.iterator]() {
72
+ return this.list[Symbol.iterator]()
73
+ }
74
+
75
+ [Symbol.for('nodejs.util.inspect.custom')]() {
76
+ const flattened = this.list.map(inspectReaction).join(', ')
77
+ return ['ReactionList {', flattened, '}'].join(' ')
78
+ }
79
+ }
80
+
81
+ export class MessageReactions extends ReactionList {
82
+ private constructor(public ctx: ReactionCtx) {
83
+ super(ctx.update.message_reaction?.new_reaction ?? [])
84
+ }
85
+
86
+ static from(ctx: ReactionCtx) {
87
+ return indexed(
88
+ new MessageReactions(ctx),
89
+ function (this: MessageReactions, index) {
90
+ return this.list[index]
91
+ }
92
+ )
93
+ }
94
+
95
+ get old() {
96
+ return ReactionList.fromArray(
97
+ this.ctx.update.message_reaction?.old_reaction
98
+ )
99
+ }
100
+
101
+ get new() {
102
+ return ReactionList.fromArray(
103
+ this.ctx.update.message_reaction?.new_reaction
104
+ )
105
+ }
106
+
107
+ get added(): ReactionList {
108
+ return this.new.filter((reaction) => !this.old.has(reaction))
109
+ }
110
+
111
+ get removed(): ReactionList {
112
+ return this.old.filter((reaction) => !this.new.has(reaction))
113
+ }
114
+
115
+ get kept(): ReactionList {
116
+ return this.new.filter((reaction) => this.old.has(reaction))
117
+ }
118
+ }
package/src/router.ts ADDED
@@ -0,0 +1,55 @@
1
+ /** @format */
2
+
3
+ import { Middleware, MiddlewareObj } from './middleware'
4
+ import Composer from './composer'
5
+ import Context from './context'
6
+
7
+ type NonemptyReadonlyArray<T> = readonly [T, ...T[]]
8
+
9
+ type RouteFn<TContext extends Context> = (ctx: TContext) => {
10
+ route: string
11
+ context?: Partial<TContext>
12
+ state?: Partial<TContext['state']>
13
+ } | null
14
+
15
+ /** @deprecated in favor of {@link Composer.dispatch} */
16
+ export class Router<C extends Context> implements MiddlewareObj<C> {
17
+ private otherwiseHandler: Middleware<C> = Composer.passThru()
18
+
19
+ constructor(
20
+ private readonly routeFn: RouteFn<C>,
21
+ public handlers = new Map<string, Middleware<C>>()
22
+ ) {
23
+ if (typeof routeFn !== 'function') {
24
+ throw new Error('Missing routing function')
25
+ }
26
+ }
27
+
28
+ on(route: string, ...fns: NonemptyReadonlyArray<Middleware<C>>) {
29
+ if (fns.length === 0) {
30
+ throw new TypeError('At least one handler must be provided')
31
+ }
32
+ this.handlers.set(route, Composer.compose(fns))
33
+ return this
34
+ }
35
+
36
+ otherwise(...fns: NonemptyReadonlyArray<Middleware<C>>) {
37
+ if (fns.length === 0) {
38
+ throw new TypeError('At least one otherwise handler must be provided')
39
+ }
40
+ this.otherwiseHandler = Composer.compose(fns)
41
+ return this
42
+ }
43
+
44
+ middleware() {
45
+ return Composer.lazy<C>((ctx) => {
46
+ const result = this.routeFn(ctx)
47
+ if (result == null) {
48
+ return this.otherwiseHandler
49
+ }
50
+ Object.assign(ctx, result.context)
51
+ Object.assign(ctx.state, result.state)
52
+ return this.handlers.get(result.route) ?? this.otherwiseHandler
53
+ })
54
+ }
55
+ }
@@ -0,0 +1,52 @@
1
+ import { Middleware, MiddlewareFn } from '../middleware'
2
+ import Composer from '../composer'
3
+ import Context from '../context'
4
+
5
+ const { compose } = Composer
6
+
7
+ export interface SceneOptions<C extends Context> {
8
+ ttl?: number
9
+ handlers: ReadonlyArray<MiddlewareFn<C>>
10
+ enterHandlers: ReadonlyArray<MiddlewareFn<C>>
11
+ leaveHandlers: ReadonlyArray<MiddlewareFn<C>>
12
+ }
13
+
14
+ export class BaseScene<C extends Context = Context> extends Composer<C> {
15
+ id: string
16
+ ttl?: number
17
+ enterHandler: MiddlewareFn<C>
18
+ leaveHandler: MiddlewareFn<C>
19
+ constructor(id: string, options?: SceneOptions<C>) {
20
+ const opts: SceneOptions<C> = {
21
+ handlers: [],
22
+ enterHandlers: [],
23
+ leaveHandlers: [],
24
+ ...options,
25
+ }
26
+ super(...opts.handlers)
27
+ this.id = id
28
+ this.ttl = opts.ttl
29
+ this.enterHandler = compose(opts.enterHandlers)
30
+ this.leaveHandler = compose(opts.leaveHandlers)
31
+ }
32
+
33
+ enter(...fns: Array<Middleware<C>>) {
34
+ this.enterHandler = compose([this.enterHandler, ...fns])
35
+ return this
36
+ }
37
+
38
+ leave(...fns: Array<Middleware<C>>) {
39
+ this.leaveHandler = compose([this.leaveHandler, ...fns])
40
+ return this
41
+ }
42
+
43
+ enterMiddleware() {
44
+ return this.enterHandler
45
+ }
46
+
47
+ leaveMiddleware() {
48
+ return this.leaveHandler
49
+ }
50
+ }
51
+
52
+ export default BaseScene