@tgify/tgify 0.1.0 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -23
- package/README.md +356 -356
- package/lib/cli.mjs +9 -9
- package/package.json +1 -1
- package/src/button.ts +182 -182
- package/src/composer.ts +1008 -1008
- package/src/context.ts +1661 -1661
- package/src/core/helpers/args.ts +63 -63
- package/src/core/helpers/check.ts +71 -71
- package/src/core/helpers/compact.ts +18 -18
- package/src/core/helpers/deunionize.ts +26 -26
- package/src/core/helpers/formatting.ts +119 -119
- package/src/core/helpers/util.ts +96 -96
- package/src/core/network/client.ts +396 -396
- package/src/core/network/error.ts +29 -29
- package/src/core/network/multipart-stream.ts +45 -45
- package/src/core/network/polling.ts +94 -94
- package/src/core/network/webhook.ts +58 -58
- package/src/core/types/typegram.ts +54 -54
- package/src/filters.ts +109 -109
- package/src/format.ts +110 -110
- package/src/future.ts +213 -213
- package/src/index.ts +17 -17
- package/src/input.ts +59 -59
- package/src/markup.ts +142 -142
- package/src/middleware.ts +24 -24
- package/src/reactions.ts +118 -118
- package/src/router.ts +55 -55
- package/src/scenes/base.ts +52 -52
- package/src/scenes/context.ts +136 -136
- package/src/scenes/index.ts +21 -21
- package/src/scenes/stage.ts +71 -71
- package/src/scenes/wizard/context.ts +58 -58
- package/src/scenes/wizard/index.ts +63 -63
- package/src/scenes.ts +1 -1
- package/src/session.ts +204 -204
- package/src/telegraf.ts +354 -354
- package/src/telegram-types.ts +219 -219
- package/src/telegram.ts +1635 -1635
- package/src/types.ts +2 -2
- package/src/utils.ts +1 -1
- package/typings/telegraf.d.ts.map +1 -1
package/src/markup.ts
CHANGED
|
@@ -1,142 +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
|
-
}
|
|
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
|
+
}
|
package/src/middleware.ts
CHANGED
|
@@ -1,24 +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>
|
|
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>
|
package/src/reactions.ts
CHANGED
|
@@ -1,118 +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
|
-
}
|
|
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
CHANGED
|
@@ -1,55 +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
|
-
}
|
|
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
|
+
}
|