@tgify/tgify 0.1.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.
- package/LICENSE +23 -0
- package/README.md +356 -0
- package/filters.d.ts +1 -0
- package/filters.js +1 -0
- package/format.d.ts +1 -0
- package/format.js +1 -0
- package/future.d.ts +1 -0
- package/future.js +1 -0
- package/lib/button.js +100 -0
- package/lib/cli.mjs +105 -0
- package/lib/composer.js +582 -0
- package/lib/context.js +1219 -0
- package/lib/core/helpers/args.js +57 -0
- package/lib/core/helpers/check.js +55 -0
- package/lib/core/helpers/compact.js +16 -0
- package/lib/core/helpers/deunionize.js +12 -0
- package/lib/core/helpers/formatting.js +91 -0
- package/lib/core/helpers/util.js +50 -0
- package/lib/core/network/client.js +330 -0
- package/lib/core/network/error.js +21 -0
- package/lib/core/network/multipart-stream.js +71 -0
- package/lib/core/network/polling.js +87 -0
- package/lib/core/network/webhook.js +54 -0
- package/lib/core/types/typegram.js +27 -0
- package/lib/filters.js +69 -0
- package/lib/format.js +38 -0
- package/lib/future.js +149 -0
- package/lib/index.js +58 -0
- package/lib/input.js +61 -0
- package/lib/markup.js +121 -0
- package/lib/middleware.js +2 -0
- package/lib/reactions.js +84 -0
- package/lib/router.js +46 -0
- package/lib/scenes/base.js +39 -0
- package/lib/scenes/context.js +104 -0
- package/lib/scenes/index.js +21 -0
- package/lib/scenes/stage.js +49 -0
- package/lib/scenes/wizard/context.js +31 -0
- package/lib/scenes/wizard/index.js +45 -0
- package/lib/scenes.js +17 -0
- package/lib/session.js +166 -0
- package/lib/telegraf.js +256 -0
- package/lib/telegram-types.js +6 -0
- package/lib/telegram.js +1240 -0
- package/lib/types.js +2 -0
- package/lib/utils.js +5 -0
- package/markup.d.ts +1 -0
- package/markup.js +1 -0
- package/package.json +140 -0
- package/scenes.d.ts +1 -0
- package/scenes.js +1 -0
- package/session.d.ts +1 -0
- package/session.js +1 -0
- package/src/button.ts +182 -0
- package/src/composer.ts +1008 -0
- package/src/context.ts +1661 -0
- package/src/core/helpers/args.ts +63 -0
- package/src/core/helpers/check.ts +71 -0
- package/src/core/helpers/compact.ts +18 -0
- package/src/core/helpers/deunionize.ts +26 -0
- package/src/core/helpers/formatting.ts +119 -0
- package/src/core/helpers/util.ts +96 -0
- package/src/core/network/client.ts +396 -0
- package/src/core/network/error.ts +29 -0
- package/src/core/network/multipart-stream.ts +45 -0
- package/src/core/network/polling.ts +94 -0
- package/src/core/network/webhook.ts +58 -0
- package/src/core/types/typegram.ts +54 -0
- package/src/filters.ts +109 -0
- package/src/format.ts +110 -0
- package/src/future.ts +213 -0
- package/src/index.ts +17 -0
- package/src/input.ts +59 -0
- package/src/markup.ts +142 -0
- package/src/middleware.ts +24 -0
- package/src/reactions.ts +118 -0
- package/src/router.ts +55 -0
- package/src/scenes/base.ts +52 -0
- package/src/scenes/context.ts +136 -0
- package/src/scenes/index.ts +21 -0
- package/src/scenes/stage.ts +71 -0
- package/src/scenes/wizard/context.ts +58 -0
- package/src/scenes/wizard/index.ts +63 -0
- package/src/scenes.ts +1 -0
- package/src/session.ts +204 -0
- package/src/telegraf.ts +354 -0
- package/src/telegram-types.ts +219 -0
- package/src/telegram.ts +1635 -0
- package/src/types.ts +2 -0
- package/src/utils.ts +1 -0
- package/types.d.ts +1 -0
- package/types.js +1 -0
- package/typings/button.d.ts +36 -0
- package/typings/button.d.ts.map +1 -0
- package/typings/composer.d.ts +227 -0
- package/typings/composer.d.ts.map +1 -0
- package/typings/context.d.ts +655 -0
- package/typings/context.d.ts.map +1 -0
- package/typings/core/helpers/args.d.ts +11 -0
- package/typings/core/helpers/args.d.ts.map +1 -0
- package/typings/core/helpers/check.d.ts +56 -0
- package/typings/core/helpers/check.d.ts.map +1 -0
- package/typings/core/helpers/compact.d.ts +4 -0
- package/typings/core/helpers/compact.d.ts.map +1 -0
- package/typings/core/helpers/deunionize.d.ts +18 -0
- package/typings/core/helpers/deunionize.d.ts.map +1 -0
- package/typings/core/helpers/formatting.d.ts +30 -0
- package/typings/core/helpers/formatting.d.ts.map +1 -0
- package/typings/core/helpers/util.d.ts +26 -0
- package/typings/core/helpers/util.d.ts.map +1 -0
- package/typings/core/network/client.d.ts +53 -0
- package/typings/core/network/client.d.ts.map +1 -0
- package/typings/core/network/error.d.ts +16 -0
- package/typings/core/network/error.d.ts.map +1 -0
- package/typings/core/network/multipart-stream.d.ts +16 -0
- package/typings/core/network/multipart-stream.d.ts.map +1 -0
- package/typings/core/network/polling.d.ts +16 -0
- package/typings/core/network/polling.d.ts.map +1 -0
- package/typings/core/network/webhook.d.ts +6 -0
- package/typings/core/network/webhook.d.ts.map +1 -0
- package/typings/core/types/typegram.d.ts +42 -0
- package/typings/core/types/typegram.d.ts.map +1 -0
- package/typings/filters.d.ts +18 -0
- package/typings/filters.d.ts.map +1 -0
- package/typings/format.d.ts +22 -0
- package/typings/format.d.ts.map +1 -0
- package/typings/future.d.ts +12 -0
- package/typings/future.d.ts.map +1 -0
- package/typings/index.d.ts +15 -0
- package/typings/index.d.ts.map +1 -0
- package/typings/input.d.ts +50 -0
- package/typings/input.d.ts.map +1 -0
- package/typings/markup.d.ts +27 -0
- package/typings/markup.d.ts.map +1 -0
- package/typings/middleware.d.ts +8 -0
- package/typings/middleware.d.ts.map +1 -0
- package/typings/reactions.d.ts +32 -0
- package/typings/reactions.d.ts.map +1 -0
- package/typings/router.d.ts +21 -0
- package/typings/router.d.ts.map +1 -0
- package/typings/scenes/base.d.ts +22 -0
- package/typings/scenes/base.d.ts.map +1 -0
- package/typings/scenes/context.d.ts +36 -0
- package/typings/scenes/context.d.ts.map +1 -0
- package/typings/scenes/index.d.ts +11 -0
- package/typings/scenes/index.d.ts.map +1 -0
- package/typings/scenes/stage.d.ts +24 -0
- package/typings/scenes/stage.d.ts.map +1 -0
- package/typings/scenes/wizard/context.d.ts +29 -0
- package/typings/scenes/wizard/context.d.ts.map +1 -0
- package/typings/scenes/wizard/index.d.ts +16 -0
- package/typings/scenes/wizard/index.d.ts.map +1 -0
- package/typings/scenes.d.ts +2 -0
- package/typings/scenes.d.ts.map +1 -0
- package/typings/session.d.ts +55 -0
- package/typings/session.d.ts.map +1 -0
- package/typings/telegraf.d.ts +115 -0
- package/typings/telegraf.d.ts.map +1 -0
- package/typings/telegram-types.d.ts +117 -0
- package/typings/telegram-types.d.ts.map +1 -0
- package/typings/telegram.d.ts +675 -0
- package/typings/telegram.d.ts.map +1 -0
- package/typings/types.d.ts +3 -0
- package/typings/types.d.ts.map +1 -0
- package/typings/utils.d.ts +2 -0
- package/typings/utils.d.ts.map +1 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
interface Entity {
|
|
2
|
+
/** Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” ($USD), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers) */
|
|
3
|
+
type: string
|
|
4
|
+
/** Offset in UTF-16 code units to the start of the entity */
|
|
5
|
+
offset: number
|
|
6
|
+
/** Length of the entity in UTF-16 code units */
|
|
7
|
+
length: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const SINGLE_QUOTE = "'"
|
|
11
|
+
const DOUBLE_QUOTE = '"'
|
|
12
|
+
|
|
13
|
+
export function argsParser(
|
|
14
|
+
str: string,
|
|
15
|
+
entities: Entity[] = [],
|
|
16
|
+
entityOffset = 0
|
|
17
|
+
) {
|
|
18
|
+
const mentions: { [offset: string]: number } = {}
|
|
19
|
+
for (const entity of entities) // extract all text_mentions into an { offset: length } map
|
|
20
|
+
if (entity.type === 'text_mention' || entity.type === 'text_link')
|
|
21
|
+
mentions[entity.offset - entityOffset] = entity.length
|
|
22
|
+
|
|
23
|
+
const args: string[] = []
|
|
24
|
+
let done = 0
|
|
25
|
+
let inside: `'` | `"` | undefined = undefined
|
|
26
|
+
let buf = ''
|
|
27
|
+
|
|
28
|
+
function flush(to: number) {
|
|
29
|
+
if (done !== to) args.push(buf + str.slice(done, to)), (inside = undefined)
|
|
30
|
+
buf = ''
|
|
31
|
+
done = to + 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < str.length; i++) {
|
|
35
|
+
const char = str[i]
|
|
36
|
+
// quick lookup length of mention starting at i
|
|
37
|
+
const mention = mentions[i]
|
|
38
|
+
if (mention) {
|
|
39
|
+
// if we're inside a quote, eagerly flush existing state
|
|
40
|
+
flush(i)
|
|
41
|
+
// this also consumes current index, so decrement
|
|
42
|
+
done--
|
|
43
|
+
// fast forward to end of mention
|
|
44
|
+
i += mention
|
|
45
|
+
flush(i)
|
|
46
|
+
} else if (char === SINGLE_QUOTE || char === DOUBLE_QUOTE)
|
|
47
|
+
if (inside)
|
|
48
|
+
if (inside === char) flush(i)
|
|
49
|
+
else continue
|
|
50
|
+
else flush(i), (inside = char)
|
|
51
|
+
else if (char === ' ')
|
|
52
|
+
if (inside) continue
|
|
53
|
+
else flush(i)
|
|
54
|
+
else if (char === '\n') flush(i)
|
|
55
|
+
else if (char === '\\')
|
|
56
|
+
(buf += str.slice(done, i)), (done = ++i) // skip parsing the next char
|
|
57
|
+
else continue
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (done < str.length) flush(str.length)
|
|
61
|
+
|
|
62
|
+
return args
|
|
63
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
interface Mapping {
|
|
2
|
+
string: string
|
|
3
|
+
number: number
|
|
4
|
+
bigint: bigint
|
|
5
|
+
boolean: boolean
|
|
6
|
+
symbol: symbol
|
|
7
|
+
undefined: undefined
|
|
8
|
+
object: Record<string, unknown>
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
function: (...props: any[]) => any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks if a given object has a property with a given name.
|
|
15
|
+
*
|
|
16
|
+
* Example invocation:
|
|
17
|
+
* ```js
|
|
18
|
+
* let obj = { 'foo': 'bar', 'baz': () => {} }
|
|
19
|
+
* hasProp(obj, 'foo') // true
|
|
20
|
+
* hasProp(obj, 'baz') // true
|
|
21
|
+
* hasProp(obj, 'abc') // false
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @param obj An object to test
|
|
25
|
+
* @param prop The name of the property
|
|
26
|
+
*/
|
|
27
|
+
export function hasProp<O extends object, K extends PropertyKey>(
|
|
28
|
+
obj: O | undefined,
|
|
29
|
+
prop: K
|
|
30
|
+
): obj is O & Record<K, unknown> {
|
|
31
|
+
return obj !== undefined && prop in obj
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Checks if a given object has a property with a given name.
|
|
35
|
+
* Furthermore performs a `typeof` check on the property if it exists.
|
|
36
|
+
*
|
|
37
|
+
* Example invocation:
|
|
38
|
+
* ```js
|
|
39
|
+
* let obj = { 'foo': 'bar', 'baz': () => {} }
|
|
40
|
+
* hasPropType(obj, 'foo', 'string') // true
|
|
41
|
+
* hasPropType(obj, 'baz', 'function') // true
|
|
42
|
+
* hasPropType(obj, 'abc', 'number') // false
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param obj An object to test
|
|
46
|
+
* @param prop The name of the property
|
|
47
|
+
* @param type The type the property is expected to have
|
|
48
|
+
*/
|
|
49
|
+
export function hasPropType<
|
|
50
|
+
O extends object,
|
|
51
|
+
K extends PropertyKey,
|
|
52
|
+
T extends keyof Mapping,
|
|
53
|
+
V extends Mapping[T],
|
|
54
|
+
>(obj: O | undefined, prop: K, type: T): obj is O & Record<K, V> {
|
|
55
|
+
return hasProp(obj, prop) && type === typeof obj[prop]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if the supplied array has two dimensions or not.
|
|
60
|
+
*
|
|
61
|
+
* Example invocations:
|
|
62
|
+
* is2D([]) // false
|
|
63
|
+
* is2D([[]]) // true
|
|
64
|
+
* is2D([[], []]) // true
|
|
65
|
+
* is2D([42]) // false
|
|
66
|
+
*
|
|
67
|
+
* @param arr an array with one or two dimensions
|
|
68
|
+
*/
|
|
69
|
+
export function is2D<E>(arr: E[] | E[][]): arr is E[][] {
|
|
70
|
+
return Array.isArray(arr[0])
|
|
71
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function compactOptions<T extends { [key: string]: unknown }>(
|
|
2
|
+
options?: T
|
|
3
|
+
): T | undefined {
|
|
4
|
+
if (!options) {
|
|
5
|
+
return options
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const compacted: Partial<T> = {}
|
|
9
|
+
for (const key in options)
|
|
10
|
+
if (
|
|
11
|
+
// todo(mkr): replace with Object.hasOwn in v5 (Node 16+)
|
|
12
|
+
Object.prototype.hasOwnProperty.call(options, key) &&
|
|
13
|
+
options[key] !== undefined
|
|
14
|
+
)
|
|
15
|
+
compacted[key] = options[key]
|
|
16
|
+
|
|
17
|
+
return compacted as T | undefined
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type PropOr<
|
|
2
|
+
T extends object | undefined,
|
|
3
|
+
P extends string | symbol | number,
|
|
4
|
+
D = undefined,
|
|
5
|
+
> = T extends Partial<Record<P, unknown>> ? T[P] : D
|
|
6
|
+
|
|
7
|
+
export type UnionKeys<T> = T extends unknown ? keyof T : never
|
|
8
|
+
|
|
9
|
+
type AddOptionalKeys<K extends PropertyKey> = { readonly [P in K]?: never }
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @see https://millsp.github.io/ts-toolbelt/modules/union_strict.html
|
|
13
|
+
*/
|
|
14
|
+
export type Deunionize<B extends object | undefined, T = B> = T extends object
|
|
15
|
+
? T & AddOptionalKeys<Exclude<UnionKeys<B>, keyof T>>
|
|
16
|
+
: T
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Expose properties from all union variants.
|
|
20
|
+
* @deprectated
|
|
21
|
+
* @see https://github.com/telegraf/telegraf/issues/1388#issuecomment-791573609
|
|
22
|
+
* @see https://millsp.github.io/ts-toolbelt/modules/union_strict.html
|
|
23
|
+
*/
|
|
24
|
+
export function deunionize<T extends object | undefined>(t: T) {
|
|
25
|
+
return t as Deunionize<T>
|
|
26
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
|
|
2
|
+
import { MessageEntity, User } from '@telegraf/types'
|
|
3
|
+
import { Any, zip } from './util'
|
|
4
|
+
|
|
5
|
+
export type Nestable<Kind extends string> =
|
|
6
|
+
| string
|
|
7
|
+
| number
|
|
8
|
+
| boolean
|
|
9
|
+
| FmtString<Kind>
|
|
10
|
+
export type MaybeNestableList<Kind extends string> =
|
|
11
|
+
| Nestable<Kind>
|
|
12
|
+
| readonly Nestable<Kind>[]
|
|
13
|
+
|
|
14
|
+
export interface FmtString<Brand extends string> {
|
|
15
|
+
text: string
|
|
16
|
+
entities?: MessageEntity[]
|
|
17
|
+
parse_mode?: undefined
|
|
18
|
+
__to_nest: Brand
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class FmtString<Brand extends string = string>
|
|
22
|
+
implements FmtString<Brand>
|
|
23
|
+
{
|
|
24
|
+
constructor(
|
|
25
|
+
public text: string,
|
|
26
|
+
entities?: MessageEntity[]
|
|
27
|
+
) {
|
|
28
|
+
if (entities) {
|
|
29
|
+
this.entities = entities
|
|
30
|
+
// force parse_mode to undefined if entities are present
|
|
31
|
+
this.parse_mode = undefined
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
static normalise(content: Nestable<string>) {
|
|
35
|
+
if (content instanceof FmtString) return content
|
|
36
|
+
return new FmtString(String(content))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const isArray: <T>(xs: T | readonly T[]) => xs is readonly T[] = Array.isArray
|
|
41
|
+
|
|
42
|
+
/** Given a base FmtString and something to append to it, mutates the base */
|
|
43
|
+
const _add = (base: FmtString, next: FmtString | Any) => {
|
|
44
|
+
const len = base.text.length
|
|
45
|
+
if (next instanceof FmtString) {
|
|
46
|
+
base.text = `${base.text}${next.text}`
|
|
47
|
+
// next.entities could be undefined and condition will fail
|
|
48
|
+
for (let i = 0; i < (next.entities?.length || 0); i++) {
|
|
49
|
+
// because of the above condition, next.entities[i] cannot be undefined
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
51
|
+
const entity = next.entities![i]!
|
|
52
|
+
// base.entities is ensured by caller
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
54
|
+
base.entities!.push({ ...entity, offset: entity.offset + len })
|
|
55
|
+
}
|
|
56
|
+
} else base.text = `${base.text}${next}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Given an `Iterable<FmtString | string | Any>` and a separator, flattens the list into a single FmtString.
|
|
61
|
+
* Analogous to Array#join -> string, but for FmtString
|
|
62
|
+
*/
|
|
63
|
+
export const join = (
|
|
64
|
+
fragments: Iterable<FmtString | string | Any>,
|
|
65
|
+
separator?: string | FmtString
|
|
66
|
+
) => {
|
|
67
|
+
const result = new FmtString('')
|
|
68
|
+
// ensure entities array so loop doesn't need to check
|
|
69
|
+
result.entities = []
|
|
70
|
+
|
|
71
|
+
const iter = fragments[Symbol.iterator]()
|
|
72
|
+
|
|
73
|
+
let curr = iter.next()
|
|
74
|
+
while (!curr.done) {
|
|
75
|
+
_add(result, curr.value)
|
|
76
|
+
curr = iter.next()
|
|
77
|
+
if (separator && !curr.done) _add(result, separator)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// set parse_mode: undefined if entities are present
|
|
81
|
+
if (result.entities.length) result.parse_mode = undefined
|
|
82
|
+
// remove entities array if not relevant
|
|
83
|
+
else delete result.entities
|
|
84
|
+
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Internal constructor for all fmt helpers */
|
|
89
|
+
export function createFmt(kind?: MessageEntity['type'], opts?: object) {
|
|
90
|
+
return function fmt(
|
|
91
|
+
parts: MaybeNestableList<string>,
|
|
92
|
+
...items: Nestable<string>[]
|
|
93
|
+
) {
|
|
94
|
+
parts = isArray(parts) ? parts : [parts]
|
|
95
|
+
const result = join(zip(parts, items))
|
|
96
|
+
if (kind) {
|
|
97
|
+
result.entities ??= []
|
|
98
|
+
result.entities.unshift({
|
|
99
|
+
type: kind,
|
|
100
|
+
offset: 0,
|
|
101
|
+
length: result.text.length,
|
|
102
|
+
...opts,
|
|
103
|
+
} as MessageEntity)
|
|
104
|
+
result.parse_mode = undefined
|
|
105
|
+
}
|
|
106
|
+
return result
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const linkOrMention = (
|
|
111
|
+
content: Nestable<string>,
|
|
112
|
+
data:
|
|
113
|
+
| { type: 'text_link'; url: string }
|
|
114
|
+
| { type: 'text_mention'; user: User }
|
|
115
|
+
) => {
|
|
116
|
+
const { text, entities = [] } = FmtString.normalise(content)
|
|
117
|
+
entities.unshift(Object.assign(data, { offset: 0, length: text.length }))
|
|
118
|
+
return new FmtString(text, entities)
|
|
119
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { FmtString } from './formatting'
|
|
2
|
+
import { Deunionize, UnionKeys } from './deunionize'
|
|
3
|
+
|
|
4
|
+
export const env = process.env
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
7
|
+
export type Any = {} | undefined | null
|
|
8
|
+
|
|
9
|
+
export type Expand<T> = T extends object
|
|
10
|
+
? T extends infer O
|
|
11
|
+
? { [K in keyof O]: O[K] }
|
|
12
|
+
: never
|
|
13
|
+
: T
|
|
14
|
+
|
|
15
|
+
export type MaybeArray<T> = T | T[]
|
|
16
|
+
export type MaybePromise<T> = T | Promise<T>
|
|
17
|
+
export type NonemptyReadonlyArray<T> = readonly [T, ...T[]]
|
|
18
|
+
|
|
19
|
+
// prettier-ignore
|
|
20
|
+
export type ExclusiveKeys<A extends object, B extends object> = keyof Omit<A, keyof B>
|
|
21
|
+
|
|
22
|
+
export function fmtCaption<
|
|
23
|
+
Extra extends { caption?: string | FmtString } | undefined,
|
|
24
|
+
>(
|
|
25
|
+
extra?: Extra
|
|
26
|
+
): Extra extends undefined
|
|
27
|
+
? undefined
|
|
28
|
+
: Omit<Extra, 'caption'> & { caption?: string }
|
|
29
|
+
|
|
30
|
+
export function fmtCaption(extra?: { caption?: string | FmtString }) {
|
|
31
|
+
if (!extra) return
|
|
32
|
+
const caption = extra.caption
|
|
33
|
+
if (!caption || typeof caption === 'string') return extra
|
|
34
|
+
const { text, entities } = caption
|
|
35
|
+
return {
|
|
36
|
+
...extra,
|
|
37
|
+
caption: text,
|
|
38
|
+
...(entities && {
|
|
39
|
+
caption_entities: entities,
|
|
40
|
+
parse_mode: undefined,
|
|
41
|
+
}),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type DistinctKeys<T extends object> = Exclude<UnionKeys<T>, keyof T>
|
|
46
|
+
|
|
47
|
+
// prettier-ignore
|
|
48
|
+
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
|
49
|
+
export type KeyedDistinct<T extends object, K extends DistinctKeys<T>> = Record<K, {}> & Deunionize<Record<K, {}>, T>
|
|
50
|
+
|
|
51
|
+
// prettier-ignore
|
|
52
|
+
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
|
53
|
+
export type Keyed<T extends object, K extends UnionKeys<T>> = Record<K, {}> & Deunionize<Record<K, {}>, T>
|
|
54
|
+
|
|
55
|
+
/** Construct a generic type guard */
|
|
56
|
+
export type Guard<X = unknown, Y extends X = X> = (x: X) => x is Y
|
|
57
|
+
|
|
58
|
+
/** Extract the guarded type from a type guard, defaults to never. */
|
|
59
|
+
export type Guarded<F> =
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
F extends (x: any) => x is infer T ? T : never
|
|
62
|
+
|
|
63
|
+
export function* zip<X, Y>(xs: Iterable<X>, ys: Iterable<Y>): Iterable<X | Y> {
|
|
64
|
+
const x = xs[Symbol.iterator]()
|
|
65
|
+
const y = ys[Symbol.iterator]()
|
|
66
|
+
let x1 = x.next()
|
|
67
|
+
let y1 = y.next()
|
|
68
|
+
|
|
69
|
+
while (!x1.done) {
|
|
70
|
+
yield x1.value
|
|
71
|
+
if (!y1.done) yield y1.value
|
|
72
|
+
x1 = x.next()
|
|
73
|
+
y1 = y.next()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
while (!y1.done) {
|
|
77
|
+
yield y1.value
|
|
78
|
+
y1 = y.next()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function indexed<T extends object, U>(
|
|
83
|
+
target: T,
|
|
84
|
+
indexer: (index: number) => U
|
|
85
|
+
) {
|
|
86
|
+
return new Proxy(target, {
|
|
87
|
+
get: function (target, prop, receiver) {
|
|
88
|
+
if (
|
|
89
|
+
(typeof prop === 'string' || typeof prop === 'number') &&
|
|
90
|
+
!isNaN(+prop)
|
|
91
|
+
)
|
|
92
|
+
return indexer.call(target, +prop)
|
|
93
|
+
return Reflect.get(target, prop, receiver)
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
}
|