@mtcute/dispatcher 0.12.3 → 0.13.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/cjs/callback-data-builder.d.ts +3 -1
- package/cjs/callback-data-builder.js +10 -9
- package/cjs/callback-data-builder.js.map +1 -1
- package/cjs/context/scene-transition.d.ts +24 -0
- package/cjs/context/scene-transition.js +52 -0
- package/cjs/context/scene-transition.js.map +1 -0
- package/cjs/dispatcher.d.ts +23 -1
- package/cjs/dispatcher.js +62 -10
- package/cjs/dispatcher.js.map +1 -1
- package/cjs/propagation.d.ts +2 -1
- package/cjs/propagation.js +2 -1
- package/cjs/propagation.js.map +1 -1
- package/esm/callback-data-builder.d.ts +3 -1
- package/esm/callback-data-builder.js +10 -9
- package/esm/callback-data-builder.js.map +1 -1
- package/esm/context/scene-transition.d.ts +24 -0
- package/esm/context/scene-transition.js +48 -0
- package/esm/context/scene-transition.js.map +1 -0
- package/esm/dispatcher.d.ts +23 -1
- package/esm/dispatcher.js +62 -10
- package/esm/dispatcher.js.map +1 -1
- package/esm/propagation.d.ts +2 -1
- package/esm/propagation.js +2 -1
- package/esm/propagation.js.map +1 -1
- package/package.json +2 -2
|
@@ -26,8 +26,10 @@ export declare class CallbackDataBuilder<T extends string> {
|
|
|
26
26
|
* Parse callback data to object
|
|
27
27
|
*
|
|
28
28
|
* @param data Callback data as string
|
|
29
|
+
* @param safe If `true`, will return `null` instead of throwing on invalid data
|
|
29
30
|
*/
|
|
30
|
-
parse(data: string): Record<T, string>;
|
|
31
|
+
parse(data: string, safe?: false): Record<T, string>;
|
|
32
|
+
parse(data: string, safe: true): Record<T, string> | null;
|
|
31
33
|
/**
|
|
32
34
|
* Create a filter for this callback data.
|
|
33
35
|
*
|
|
@@ -43,18 +43,17 @@ class CallbackDataBuilder {
|
|
|
43
43
|
}
|
|
44
44
|
return ret;
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
* Parse callback data to object
|
|
48
|
-
*
|
|
49
|
-
* @param data Callback data as string
|
|
50
|
-
*/
|
|
51
|
-
parse(data) {
|
|
46
|
+
parse(data, safe = false) {
|
|
52
47
|
const parts = data.split(this.sep);
|
|
53
48
|
if (parts[0] !== this.prefix) {
|
|
54
|
-
|
|
49
|
+
if (safe)
|
|
50
|
+
return null;
|
|
51
|
+
throw new core_1.MtArgumentError(`Invalid data passed: "${data}" (bad prefix, expected ${this.prefix}, got ${parts[0]})`);
|
|
55
52
|
}
|
|
56
53
|
if (parts.length !== this._fields.length + 1) {
|
|
57
|
-
|
|
54
|
+
if (safe)
|
|
55
|
+
return null;
|
|
56
|
+
throw new core_1.MtArgumentError(`Invalid data passed: "${data}" (bad parts count, expected ${this._fields.length}, got ${parts.length - 1})`);
|
|
58
57
|
}
|
|
59
58
|
const ret = {};
|
|
60
59
|
parts.forEach((it, idx) => {
|
|
@@ -82,7 +81,9 @@ class CallbackDataBuilder {
|
|
|
82
81
|
return async (query) => {
|
|
83
82
|
if (!query.dataStr)
|
|
84
83
|
return false;
|
|
85
|
-
const data = this.parse(query.dataStr);
|
|
84
|
+
const data = this.parse(query.dataStr, true);
|
|
85
|
+
if (!data)
|
|
86
|
+
return false;
|
|
86
87
|
const fnResult = await params(query, data);
|
|
87
88
|
if (typeof fnResult === 'boolean') {
|
|
88
89
|
query.match = data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callback-data-builder.js","sourceRoot":"","sources":["../../src/callback-data-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { CallbackQuery, MaybeArray, MaybePromise, MtArgumentError } from '@mtcute/core'\n\nimport { UpdateFilter } from './filters/types.js'\n\n/**\n * Callback data builder, inspired by [aiogram](https://github.com/aiogram/aiogram).\n *\n * This can be used to simplify management of different callbacks.\n *\n * [Learn more in the docs](/guide/topics/keyboards.html#callback-data-builders)\n */\nexport class CallbackDataBuilder<T extends string> {\n private readonly _fields: T[]\n\n sep = ':'\n\n /**\n * @param prefix Prefix for the data. Use something unique across your bot.\n * @param fields Field names in the order they will be serialized.\n */\n constructor(\n public prefix: string,\n ...fields: T[]\n ) {\n this._fields = fields\n }\n\n /**\n * Build a callback data string\n *\n * @param obj Object containing the data\n */\n build(obj: Record<T, string>): string {\n const ret =\n this.prefix +\n this.sep +\n this._fields\n .map((f) => {\n const val = obj[f]\n\n if (val.includes(this.sep)) {\n throw new MtArgumentError(\n `Value for ${f} ${val} contains separator ${this.sep} and cannot be used.`,\n )\n }\n\n return val\n })\n .join(this.sep)\n\n if (ret.length > 64) {\n throw new MtArgumentError('Resulting callback data is too long.')\n }\n\n return ret\n }\n\n /**\n * Parse callback data to object\n *\n * @param data Callback data as string\n */\n parse(data: string): Record<T, string> {\n const parts = data.split(this.sep)\n\n if (parts[0] !== this.prefix) {\n throw new MtArgumentError('Invalid data passed')\n }\n\n if (parts.length !== this._fields.length + 1) {\n throw new MtArgumentError('Invalid data passed')\n }\n\n const ret = {} as Record<T, string>\n parts.forEach((it, idx) => {\n if (idx === 0) return // skip prefix\n\n ret[this._fields[idx - 1]] = it\n })\n\n return ret\n }\n\n /**\n * Create a filter for this callback data.\n *\n * You can either pass an object with field names as keys and values as strings or regexes,\n * which will be compiled to a RegExp, or a function that will be called with the parsed data.\n * Note that the strings will be passed to `RegExp` **directly**, so you may want to escape them.\n *\n * When using a function, you can either return a boolean, or an object with field names as keys\n * and values as strings or regexes. In the latter case, the resulting object will be matched\n * against the parsed data the same way as if you passed it directly.\n *\n * @param params\n */\n filter(\n params:\n | ((\n upd: CallbackQuery,\n parsed: Record<T, string>,\n ) => MaybePromise<Partial<Record<T, MaybeArray<string | RegExp>>> | boolean>)\n | Partial<Record<T, MaybeArray<string | RegExp>>> = {},\n ): UpdateFilter<\n CallbackQuery,\n {\n match: Record<T, string>\n }\n > {\n if (typeof params === 'function') {\n return async (query) => {\n if (!query.dataStr) return false\n\n const data = this.parse(query.dataStr)\n const fnResult = await params(query, data)\n\n if (typeof fnResult === 'boolean') {\n (\n query as CallbackQuery & {\n match: Record<T, string>\n }\n ).match = data\n\n return fnResult\n }\n\n // validate result\n for (const key in fnResult) {\n const value = data[key]\n if (value === undefined) return false\n\n let matchers = fnResult[key] as MaybeArray<string | RegExp>\n if (!Array.isArray(matchers)) matchers = [matchers]\n\n for (const matcher of matchers) {\n if (typeof matcher === 'string') {\n if (value !== matcher) return false\n } else if (!matcher.test(value)) return false\n }\n }\n\n (\n query as CallbackQuery & {\n match: Record<T, string>\n }\n ).match = data\n\n return true\n }\n }\n\n const parts: string[] = []\n\n this._fields.forEach((field) => {\n if (!(field in params)) {\n parts.push(`[^${this.sep}]*?`)\n\n return\n }\n\n const value = params[field]\n\n if (Array.isArray(value)) {\n parts.push(`(${value.map((i) => (typeof i === 'string' ? i : i.source)).join('|')})`)\n } else {\n // noinspection SuspiciousTypeOfGuard\n parts.push(typeof value === 'string' ? value : (value as RegExp).source)\n }\n })\n\n const regex = new RegExp(`^${this.prefix}${this.sep}${parts.join(this.sep)}$`)\n\n return (query) => {\n const m = query.dataStr?.match(regex)\n if (!m) return false\n ;(\n query as CallbackQuery & {\n match: Record<T, string>\n }\n ).match = this.parse(m[0])\n\n return true\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"callback-data-builder.js","sourceRoot":"","sources":["../../src/callback-data-builder.ts"],"names":[],"mappings":";;;AAAA,uCAAuF;AAIvF;;;;;;GAMG;AACH,MAAa,mBAAmB;IAUjB;IATM,OAAO,CAAK;IAE7B,GAAG,GAAG,GAAG,CAAA;IAET;;;OAGG;IACH,YACW,MAAc,EACrB,GAAG,MAAW;QADP,WAAM,GAAN,MAAM,CAAQ;QAGrB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACzB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAsB;QACxB,MAAM,GAAG,GACL,IAAI,CAAC,MAAM;YACX,IAAI,CAAC,GAAG;YACR,IAAI,CAAC,OAAO;iBACP,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACP,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;gBAElB,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACzB,MAAM,IAAI,sBAAe,CACrB,aAAa,CAAC,IAAI,GAAG,uBAAuB,IAAI,CAAC,GAAG,sBAAsB,CAC7E,CAAA;gBACL,CAAC;gBAED,OAAO,GAAG,CAAA;YACd,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEvB,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,sBAAe,CAAC,sCAAsC,CAAC,CAAA;QACrE,CAAC;QAED,OAAO,GAAG,CAAA;IACd,CAAC;IAUD,KAAK,CAAC,IAAY,EAAE,IAAI,GAAG,KAAK;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAElC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3B,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAA;YACrB,MAAM,IAAI,sBAAe,CACrB,yBAAyB,IAAI,2BAA2B,IAAI,CAAC,MAAM,SAAS,KAAK,CAAC,CAAC,CAAC,GAAG,CAC1F,CAAA;QACL,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAA;YACrB,MAAM,IAAI,sBAAe,CACrB,yBAAyB,IAAI,gCAAgC,IAAI,CAAC,OAAO,CAAC,MAAM,SAC5E,KAAK,CAAC,MAAM,GAAG,CACnB,GAAG,CACN,CAAA;QACL,CAAC;QAED,MAAM,GAAG,GAAG,EAAuB,CAAA;QACnC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;YACtB,IAAI,GAAG,KAAK,CAAC;gBAAE,OAAM,CAAC,cAAc;YAEpC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,CAAA;IACd,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,MAAM,CACF,SAKwD,EAAE;QAO1D,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,KAAK,EAAE,KAAK,EAAE,EAAE;gBACnB,IAAI,CAAC,KAAK,CAAC,OAAO;oBAAE,OAAO,KAAK,CAAA;gBAEhC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;gBAC5C,IAAI,CAAC,IAAI;oBAAE,OAAO,KAAK,CAAA;gBAEvB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;gBAE1C,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAE5B,KAGH,CAAC,KAAK,GAAG,IAAI,CAAA;oBAEd,OAAO,QAAQ,CAAA;gBACnB,CAAC;gBAED,kBAAkB;gBAClB,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;oBACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;oBACvB,IAAI,KAAK,KAAK,SAAS;wBAAE,OAAO,KAAK,CAAA;oBAErC,IAAI,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAgC,CAAA;oBAC3D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;wBAAE,QAAQ,GAAG,CAAC,QAAQ,CAAC,CAAA;oBAEnD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;wBAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;4BAC9B,IAAI,KAAK,KAAK,OAAO;gCAAE,OAAO,KAAK,CAAA;wBACvC,CAAC;6BAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;4BAAE,OAAO,KAAK,CAAA;oBACjD,CAAC;gBACL,CAAC;gBAGG,KAGH,CAAC,KAAK,GAAG,IAAI,CAAA;gBAEd,OAAO,IAAI,CAAA;YACf,CAAC,CAAA;QACL,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAA;QAE1B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC3B,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,EAAE,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;gBAE9B,OAAM;YACV,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;YAE3B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACzF,CAAC;iBAAM,CAAC;gBACJ,qCAAqC;gBACrC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,KAAgB,CAAC,MAAM,CAAC,CAAA;YAC5E,CAAC;QACL,CAAC,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAE9E,OAAO,CAAC,KAAK,EAAE,EAAE;YACb,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;YACrC,IAAI,CAAC,CAAC;gBAAE,OAAO,KAAK,CACnB;YACG,KAGH,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAE1B,OAAO,IAAI,CAAA;QACf,CAAC,CAAA;IACL,CAAC;CACJ;AA1LD,kDA0LC","sourcesContent":["import { CallbackQuery, MaybeArray, MaybePromise, MtArgumentError } from '@mtcute/core'\n\nimport { UpdateFilter } from './filters/types.js'\n\n/**\n * Callback data builder, inspired by [aiogram](https://github.com/aiogram/aiogram).\n *\n * This can be used to simplify management of different callbacks.\n *\n * [Learn more in the docs](/guide/topics/keyboards.html#callback-data-builders)\n */\nexport class CallbackDataBuilder<T extends string> {\n private readonly _fields: T[]\n\n sep = ':'\n\n /**\n * @param prefix Prefix for the data. Use something unique across your bot.\n * @param fields Field names in the order they will be serialized.\n */\n constructor(\n public prefix: string,\n ...fields: T[]\n ) {\n this._fields = fields\n }\n\n /**\n * Build a callback data string\n *\n * @param obj Object containing the data\n */\n build(obj: Record<T, string>): string {\n const ret =\n this.prefix +\n this.sep +\n this._fields\n .map((f) => {\n const val = obj[f]\n\n if (val.includes(this.sep)) {\n throw new MtArgumentError(\n `Value for ${f} ${val} contains separator ${this.sep} and cannot be used.`,\n )\n }\n\n return val\n })\n .join(this.sep)\n\n if (ret.length > 64) {\n throw new MtArgumentError('Resulting callback data is too long.')\n }\n\n return ret\n }\n\n /**\n * Parse callback data to object\n *\n * @param data Callback data as string\n * @param safe If `true`, will return `null` instead of throwing on invalid data\n */\n parse(data: string, safe?: false): Record<T, string>\n parse(data: string, safe: true): Record<T, string> | null\n parse(data: string, safe = false): Record<T, string> | null {\n const parts = data.split(this.sep)\n\n if (parts[0] !== this.prefix) {\n if (safe) return null\n throw new MtArgumentError(\n `Invalid data passed: \"${data}\" (bad prefix, expected ${this.prefix}, got ${parts[0]})`,\n )\n }\n\n if (parts.length !== this._fields.length + 1) {\n if (safe) return null\n throw new MtArgumentError(\n `Invalid data passed: \"${data}\" (bad parts count, expected ${this._fields.length}, got ${\n parts.length - 1\n })`,\n )\n }\n\n const ret = {} as Record<T, string>\n parts.forEach((it, idx) => {\n if (idx === 0) return // skip prefix\n\n ret[this._fields[idx - 1]] = it\n })\n\n return ret\n }\n\n /**\n * Create a filter for this callback data.\n *\n * You can either pass an object with field names as keys and values as strings or regexes,\n * which will be compiled to a RegExp, or a function that will be called with the parsed data.\n * Note that the strings will be passed to `RegExp` **directly**, so you may want to escape them.\n *\n * When using a function, you can either return a boolean, or an object with field names as keys\n * and values as strings or regexes. In the latter case, the resulting object will be matched\n * against the parsed data the same way as if you passed it directly.\n *\n * @param params\n */\n filter(\n params:\n | ((\n upd: CallbackQuery,\n parsed: Record<T, string>,\n ) => MaybePromise<Partial<Record<T, MaybeArray<string | RegExp>>> | boolean>)\n | Partial<Record<T, MaybeArray<string | RegExp>>> = {},\n ): UpdateFilter<\n CallbackQuery,\n {\n match: Record<T, string>\n }\n > {\n if (typeof params === 'function') {\n return async (query) => {\n if (!query.dataStr) return false\n\n const data = this.parse(query.dataStr, true)\n if (!data) return false\n\n const fnResult = await params(query, data)\n\n if (typeof fnResult === 'boolean') {\n (\n query as CallbackQuery & {\n match: Record<T, string>\n }\n ).match = data\n\n return fnResult\n }\n\n // validate result\n for (const key in fnResult) {\n const value = data[key]\n if (value === undefined) return false\n\n let matchers = fnResult[key] as MaybeArray<string | RegExp>\n if (!Array.isArray(matchers)) matchers = [matchers]\n\n for (const matcher of matchers) {\n if (typeof matcher === 'string') {\n if (value !== matcher) return false\n } else if (!matcher.test(value)) return false\n }\n }\n\n (\n query as CallbackQuery & {\n match: Record<T, string>\n }\n ).match = data\n\n return true\n }\n }\n\n const parts: string[] = []\n\n this._fields.forEach((field) => {\n if (!(field in params)) {\n parts.push(`[^${this.sep}]*?`)\n\n return\n }\n\n const value = params[field]\n\n if (Array.isArray(value)) {\n parts.push(`(${value.map((i) => (typeof i === 'string' ? i : i.source)).join('|')})`)\n } else {\n // noinspection SuspiciousTypeOfGuard\n parts.push(typeof value === 'string' ? value : (value as RegExp).source)\n }\n })\n\n const regex = new RegExp(`^${this.prefix}${this.sep}${parts.join(this.sep)}$`)\n\n return (query) => {\n const m = query.dataStr?.match(regex)\n if (!m) return false\n ;(\n query as CallbackQuery & {\n match: Record<T, string>\n }\n ).match = this.parse(m[0])\n\n return true\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BusinessMessageContext } from './business-message.js';
|
|
2
|
+
import { CallbackQueryContext, InlineCallbackQueryContext } from './callback-query.js';
|
|
3
|
+
import { MessageContext } from './message.js';
|
|
4
|
+
import { UpdateContextType } from './parse.js';
|
|
5
|
+
/** Update which is dispatched whenever scene is entered or exited */
|
|
6
|
+
export declare class SceneTransitionContext {
|
|
7
|
+
/** Name of the previous scene, if any */
|
|
8
|
+
readonly previousScene: string | null;
|
|
9
|
+
/** Update, handler for which triggered the transition */
|
|
10
|
+
readonly update: UpdateContextType;
|
|
11
|
+
constructor(
|
|
12
|
+
/** Name of the previous scene, if any */
|
|
13
|
+
previousScene: string | null,
|
|
14
|
+
/** Update, handler for which triggered the transition */
|
|
15
|
+
update: UpdateContextType);
|
|
16
|
+
/** Get {@link update}, asserting it is a message-related update */
|
|
17
|
+
get message(): MessageContext;
|
|
18
|
+
/** Get {@link update}, asserting it is a business message-related update */
|
|
19
|
+
get businessMessage(): BusinessMessageContext;
|
|
20
|
+
/** Get {@link update}, asserting it is a callback query update */
|
|
21
|
+
get callbackQuery(): CallbackQueryContext;
|
|
22
|
+
/** Get {@link update}, asserting it is an inline callback query update */
|
|
23
|
+
get inlineCallbackQuery(): InlineCallbackQueryContext;
|
|
24
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SceneTransitionContext = void 0;
|
|
4
|
+
const core_1 = require("@mtcute/core");
|
|
5
|
+
const utils_js_1 = require("@mtcute/core/utils.js");
|
|
6
|
+
const business_message_js_1 = require("./business-message.js");
|
|
7
|
+
const callback_query_js_1 = require("./callback-query.js");
|
|
8
|
+
const message_js_1 = require("./message.js");
|
|
9
|
+
/** Update which is dispatched whenever scene is entered or exited */
|
|
10
|
+
class SceneTransitionContext {
|
|
11
|
+
previousScene;
|
|
12
|
+
update;
|
|
13
|
+
constructor(
|
|
14
|
+
/** Name of the previous scene, if any */
|
|
15
|
+
previousScene,
|
|
16
|
+
/** Update, handler for which triggered the transition */
|
|
17
|
+
update) {
|
|
18
|
+
this.previousScene = previousScene;
|
|
19
|
+
this.update = update;
|
|
20
|
+
}
|
|
21
|
+
/** Get {@link update}, asserting it is a message-related update */
|
|
22
|
+
get message() {
|
|
23
|
+
if (this.update instanceof message_js_1.MessageContext) {
|
|
24
|
+
return this.update;
|
|
25
|
+
}
|
|
26
|
+
throw new core_1.MtTypeAssertionError('SceneTransitionContext.message', 'message', this.update._name);
|
|
27
|
+
}
|
|
28
|
+
/** Get {@link update}, asserting it is a business message-related update */
|
|
29
|
+
get businessMessage() {
|
|
30
|
+
if (this.update instanceof business_message_js_1.BusinessMessageContext) {
|
|
31
|
+
return this.update;
|
|
32
|
+
}
|
|
33
|
+
throw new core_1.MtTypeAssertionError('SceneTransitionContext.businessMessage', 'business message', this.update._name);
|
|
34
|
+
}
|
|
35
|
+
/** Get {@link update}, asserting it is a callback query update */
|
|
36
|
+
get callbackQuery() {
|
|
37
|
+
if (this.update instanceof callback_query_js_1.CallbackQueryContext) {
|
|
38
|
+
return this.update;
|
|
39
|
+
}
|
|
40
|
+
throw new core_1.MtTypeAssertionError('SceneTransitionContext.callbackQuery', 'callback query', this.update._name);
|
|
41
|
+
}
|
|
42
|
+
/** Get {@link update}, asserting it is an inline callback query update */
|
|
43
|
+
get inlineCallbackQuery() {
|
|
44
|
+
if (this.update instanceof callback_query_js_1.InlineCallbackQueryContext) {
|
|
45
|
+
return this.update;
|
|
46
|
+
}
|
|
47
|
+
throw new core_1.MtTypeAssertionError('SceneTransitionContext.inlineCallbackQuery', 'inline callback query', this.update._name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.SceneTransitionContext = SceneTransitionContext;
|
|
51
|
+
(0, utils_js_1.makeInspectable)(SceneTransitionContext);
|
|
52
|
+
//# sourceMappingURL=scene-transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-transition.js","sourceRoot":"","sources":["../../../src/context/scene-transition.ts"],"names":[],"mappings":";;;AAAA,uCAAmD;AACnD,oDAAuD;AAEvD,+DAA8D;AAC9D,2DAAsF;AACtF,6CAA6C;AAG7C,qEAAqE;AACrE,MAAa,sBAAsB;IAGlB;IAEA;IAJb;IACI,yCAAyC;IAChC,aAA4B;IACrC,yDAAyD;IAChD,MAAyB;QAFzB,kBAAa,GAAb,aAAa,CAAe;QAE5B,WAAM,GAAN,MAAM,CAAmB;IACnC,CAAC;IAEJ,mEAAmE;IACnE,IAAI,OAAO;QACP,IAAI,IAAI,CAAC,MAAM,YAAY,2BAAc,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC,MAAM,CAAA;QACtB,CAAC;QAED,MAAM,IAAI,2BAAoB,CAAC,gCAAgC,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAClG,CAAC;IAED,4EAA4E;IAC5E,IAAI,eAAe;QACf,IAAI,IAAI,CAAC,MAAM,YAAY,4CAAsB,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC,MAAM,CAAA;QACtB,CAAC;QAED,MAAM,IAAI,2BAAoB,CAAC,wCAAwC,EAAE,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACnH,CAAC;IAED,kEAAkE;IAClE,IAAI,aAAa;QACb,IAAI,IAAI,CAAC,MAAM,YAAY,wCAAoB,EAAE,CAAC;YAC9C,OAAO,IAAI,CAAC,MAAM,CAAA;QACtB,CAAC;QAED,MAAM,IAAI,2BAAoB,CAAC,sCAAsC,EAAE,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC/G,CAAC;IAED,0EAA0E;IAC1E,IAAI,mBAAmB;QACnB,IAAI,IAAI,CAAC,MAAM,YAAY,8CAA0B,EAAE,CAAC;YACpD,OAAO,IAAI,CAAC,MAAM,CAAA;QACtB,CAAC;QAED,MAAM,IAAI,2BAAoB,CAC1B,4CAA4C,EAC5C,uBAAuB,EACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CACpB,CAAA;IACL,CAAC;CACJ;AA/CD,wDA+CC;AAED,IAAA,0BAAe,EAAC,sBAAsB,CAAC,CAAA","sourcesContent":["import { MtTypeAssertionError } from '@mtcute/core'\nimport { makeInspectable } from '@mtcute/core/utils.js'\n\nimport { BusinessMessageContext } from './business-message.js'\nimport { CallbackQueryContext, InlineCallbackQueryContext } from './callback-query.js'\nimport { MessageContext } from './message.js'\nimport { UpdateContextType } from './parse.js'\n\n/** Update which is dispatched whenever scene is entered or exited */\nexport class SceneTransitionContext {\n constructor(\n /** Name of the previous scene, if any */\n readonly previousScene: string | null,\n /** Update, handler for which triggered the transition */\n readonly update: UpdateContextType,\n ) {}\n\n /** Get {@link update}, asserting it is a message-related update */\n get message(): MessageContext {\n if (this.update instanceof MessageContext) {\n return this.update\n }\n\n throw new MtTypeAssertionError('SceneTransitionContext.message', 'message', this.update._name)\n }\n\n /** Get {@link update}, asserting it is a business message-related update */\n get businessMessage(): BusinessMessageContext {\n if (this.update instanceof BusinessMessageContext) {\n return this.update\n }\n\n throw new MtTypeAssertionError('SceneTransitionContext.businessMessage', 'business message', this.update._name)\n }\n\n /** Get {@link update}, asserting it is a callback query update */\n get callbackQuery(): CallbackQueryContext {\n if (this.update instanceof CallbackQueryContext) {\n return this.update\n }\n\n throw new MtTypeAssertionError('SceneTransitionContext.callbackQuery', 'callback query', this.update._name)\n }\n\n /** Get {@link update}, asserting it is an inline callback query update */\n get inlineCallbackQuery(): InlineCallbackQueryContext {\n if (this.update instanceof InlineCallbackQueryContext) {\n return this.update\n }\n\n throw new MtTypeAssertionError(\n 'SceneTransitionContext.inlineCallbackQuery',\n 'inline callback query',\n this.update._name,\n )\n }\n}\n\nmakeInspectable(SceneTransitionContext)\n"]}
|
package/cjs/dispatcher.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { TelegramClient } from '@mtcute/core/client.js';
|
|
|
3
3
|
import { UpdateContext } from './context/base.js';
|
|
4
4
|
import { BusinessMessageContext } from './context/business-message.js';
|
|
5
5
|
import { CallbackQueryContext, ChatJoinRequestUpdateContext, ChosenInlineResultContext, InlineCallbackQueryContext, InlineQueryContext, MessageContext, PreCheckoutQueryContext } from './context/index.js';
|
|
6
|
+
import { SceneTransitionContext } from './context/scene-transition.js';
|
|
6
7
|
import { filters, UpdateFilter } from './filters/index.js';
|
|
7
8
|
import { BotChatJoinRequestHandler, BotReactionCountUpdateHandler, BotReactionUpdateHandler, BotStoppedHandler, BusinessConnectionUpdateHandler, BusinessMessageGroupHandler, CallbackQueryHandler, ChatJoinRequestHandler, ChatMemberUpdateHandler, ChosenInlineResultHandler, DeleteBusinessMessageHandler, DeleteMessageHandler, DeleteStoryHandler, EditBusinessMessageHandler, EditMessageHandler, HistoryReadHandler, InlineCallbackQueryHandler, InlineQueryHandler, MessageGroupHandler, NewBusinessMessageHandler, NewMessageHandler, PollUpdateHandler, PollVoteHandler, PreCheckoutQueryHandler, RawUpdateHandler, StoryUpdateHandler, UpdateHandler, UserStatusUpdateHandler, UserTypingHandler } from './handler.js';
|
|
8
9
|
import { PropagationAction } from './propagation.js';
|
|
@@ -47,11 +48,14 @@ export declare class Dispatcher<State extends object = never> {
|
|
|
47
48
|
private _errorHandler?;
|
|
48
49
|
private _preUpdateHandler?;
|
|
49
50
|
private _postUpdateHandler?;
|
|
51
|
+
private _sceneTransitionHandler?;
|
|
50
52
|
protected constructor(client?: TelegramClient, params?: DispatcherParams);
|
|
51
53
|
/**
|
|
52
54
|
* Create a new dispatcher and bind it to the client.
|
|
53
55
|
*/
|
|
54
|
-
static for<State extends object = never>(client: TelegramClient, params?: DispatcherParams
|
|
56
|
+
static for<State extends object = never>(client: TelegramClient, ...args: [State] extends [never] ? [params?: DispatcherParams] : [params: DispatcherParams & {
|
|
57
|
+
storage: IStateStorageProvider;
|
|
58
|
+
}]): Dispatcher<State>;
|
|
55
59
|
/**
|
|
56
60
|
* Create a new child dispatcher.
|
|
57
61
|
*/
|
|
@@ -340,6 +344,24 @@ export declare class Dispatcher<State extends object = never> {
|
|
|
340
344
|
* @param group Handler group index
|
|
341
345
|
*/
|
|
342
346
|
onRawUpdate(filter: RawUpdateHandler['check'], handler: RawUpdateHandler['callback'], group?: number): void;
|
|
347
|
+
/**
|
|
348
|
+
* Register a scene transition handler
|
|
349
|
+
*
|
|
350
|
+
* This handler is called whenever a scene transition occurs
|
|
351
|
+
* in the context of the scene that is being entered,
|
|
352
|
+
* and before any of the its own handlers are called,
|
|
353
|
+
* and can be used to customize the transition behavior:
|
|
354
|
+
* - `Stop` to prevent dispatching the update any further **even if ToScene/ToRoot was used**
|
|
355
|
+
* - `Continue` same as Stop, but still dispatch the update to children
|
|
356
|
+
* - `ToScene` to prevent the transition and dispatch the update to the scene entered in the transition handler
|
|
357
|
+
*
|
|
358
|
+
* > **Note**: if multiple `state.enter()` calls were made within the same update,
|
|
359
|
+
* > this handler will only be called for the last one.
|
|
360
|
+
*
|
|
361
|
+
* @param handler Raw update handler
|
|
362
|
+
* @param group Handler group index
|
|
363
|
+
*/
|
|
364
|
+
onSceneTransition(handler: ((ctx: SceneTransitionContext, state: UpdateState<State>) => MaybePromise<PropagationAction | void>) | null): void;
|
|
343
365
|
/**
|
|
344
366
|
* Register a new message handler without any filters
|
|
345
367
|
*
|
package/cjs/dispatcher.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.Dispatcher = void 0;
|
|
8
8
|
const core_1 = require("@mtcute/core");
|
|
9
9
|
const parse_js_1 = require("./context/parse.js");
|
|
10
|
+
const scene_transition_js_1 = require("./context/scene-transition.js");
|
|
10
11
|
const index_js_1 = require("./state/index.js");
|
|
11
12
|
const service_js_1 = require("./state/service.js");
|
|
12
13
|
/**
|
|
@@ -29,6 +30,7 @@ class Dispatcher {
|
|
|
29
30
|
_errorHandler;
|
|
30
31
|
_preUpdateHandler;
|
|
31
32
|
_postUpdateHandler;
|
|
33
|
+
_sceneTransitionHandler;
|
|
32
34
|
constructor(client, params) {
|
|
33
35
|
this.dispatchRawUpdate = this.dispatchRawUpdate.bind(this);
|
|
34
36
|
this.dispatchUpdate = this.dispatchUpdate.bind(this);
|
|
@@ -57,9 +59,6 @@ class Dispatcher {
|
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Create a new dispatcher and bind it to the client.
|
|
62
|
-
*/
|
|
63
62
|
static for(client, params) {
|
|
64
63
|
return new Dispatcher(client, params);
|
|
65
64
|
}
|
|
@@ -245,7 +244,10 @@ class Dispatcher {
|
|
|
245
244
|
(update.name === 'new_message' ||
|
|
246
245
|
update.name === 'edit_message' ||
|
|
247
246
|
update.name === 'callback_query' ||
|
|
248
|
-
update.name === 'message_group'
|
|
247
|
+
update.name === 'message_group' ||
|
|
248
|
+
update.name === 'new_business_message' ||
|
|
249
|
+
update.name === 'edit_business_message' ||
|
|
250
|
+
update.name === 'business_message_group')) {
|
|
249
251
|
// no need to fetch scene if there are no registered scenes
|
|
250
252
|
if (!parsedContext)
|
|
251
253
|
parsedContext = (0, parse_js_1._parsedUpdateToContext)(this._client, update);
|
|
@@ -281,7 +283,10 @@ class Dispatcher {
|
|
|
281
283
|
(update.name === 'new_message' ||
|
|
282
284
|
update.name === 'edit_message' ||
|
|
283
285
|
update.name === 'callback_query' ||
|
|
284
|
-
update.name === 'message_group'
|
|
286
|
+
update.name === 'message_group' ||
|
|
287
|
+
update.name === 'new_business_message' ||
|
|
288
|
+
update.name === 'edit_business_message' ||
|
|
289
|
+
update.name === 'business_message_group')) {
|
|
285
290
|
if (!parsedContext)
|
|
286
291
|
parsedContext = (0, parse_js_1._parsedUpdateToContext)(this._client, update);
|
|
287
292
|
const key = await this._stateKeyDelegate(parsedContext);
|
|
@@ -327,6 +332,31 @@ class Dispatcher {
|
|
|
327
332
|
}
|
|
328
333
|
else
|
|
329
334
|
continue;
|
|
335
|
+
if (parsedState && this._scenes) {
|
|
336
|
+
// check if scene transition was made
|
|
337
|
+
const newScene = parsedState.scene;
|
|
338
|
+
if (parsedScene !== newScene) {
|
|
339
|
+
const nextDp = newScene ? this._scenes.get(newScene) : this._parent;
|
|
340
|
+
if (!nextDp) {
|
|
341
|
+
throw new core_1.MtArgumentError(`Scene ${newScene} not found`);
|
|
342
|
+
}
|
|
343
|
+
if (nextDp._sceneTransitionHandler) {
|
|
344
|
+
const transition = new scene_transition_js_1.SceneTransitionContext(parsedScene, parsedContext);
|
|
345
|
+
const transitionResult = await nextDp._sceneTransitionHandler?.(transition, parsedState);
|
|
346
|
+
switch (transitionResult) {
|
|
347
|
+
case 'stop':
|
|
348
|
+
return true;
|
|
349
|
+
case 'continue':
|
|
350
|
+
continue;
|
|
351
|
+
case 'scene': {
|
|
352
|
+
const scene = parsedState.scene;
|
|
353
|
+
const dp = scene ? nextDp._scenes.get(scene) : nextDp._parent;
|
|
354
|
+
return dp._dispatchUpdateNowImpl(update, undefined, scene, true);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
330
360
|
switch (result) {
|
|
331
361
|
case 'continue':
|
|
332
362
|
continue;
|
|
@@ -339,11 +369,9 @@ class Dispatcher {
|
|
|
339
369
|
if (!parsedState) {
|
|
340
370
|
throw new core_1.MtArgumentError('Cannot use ToScene without state');
|
|
341
371
|
}
|
|
342
|
-
const scene = parsedState
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
return this._scenes.get(scene)._dispatchUpdateNowImpl(update, undefined, scene, true);
|
|
372
|
+
const scene = parsedState.scene;
|
|
373
|
+
const dp = scene ? this._scenes.get(scene) : this._parent;
|
|
374
|
+
return dp._dispatchUpdateNowImpl(update, undefined, scene, true);
|
|
347
375
|
}
|
|
348
376
|
}
|
|
349
377
|
break;
|
|
@@ -513,6 +541,7 @@ class Dispatcher {
|
|
|
513
541
|
child._client = this._client;
|
|
514
542
|
child._storage = this._storage;
|
|
515
543
|
child._deps = this._deps;
|
|
544
|
+
child._scenes = this._scenes;
|
|
516
545
|
child._stateKeyDelegate = this._stateKeyDelegate;
|
|
517
546
|
child._customStorage ??= this._customStorage;
|
|
518
547
|
child._customStateKeyDelegate ??= this._customStateKeyDelegate;
|
|
@@ -730,6 +759,29 @@ class Dispatcher {
|
|
|
730
759
|
onRawUpdate(filter, handler, group) {
|
|
731
760
|
this._addKnownHandler('raw', filter, handler, group);
|
|
732
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* Register a scene transition handler
|
|
764
|
+
*
|
|
765
|
+
* This handler is called whenever a scene transition occurs
|
|
766
|
+
* in the context of the scene that is being entered,
|
|
767
|
+
* and before any of the its own handlers are called,
|
|
768
|
+
* and can be used to customize the transition behavior:
|
|
769
|
+
* - `Stop` to prevent dispatching the update any further **even if ToScene/ToRoot was used**
|
|
770
|
+
* - `Continue` same as Stop, but still dispatch the update to children
|
|
771
|
+
* - `ToScene` to prevent the transition and dispatch the update to the scene entered in the transition handler
|
|
772
|
+
*
|
|
773
|
+
* > **Note**: if multiple `state.enter()` calls were made within the same update,
|
|
774
|
+
* > this handler will only be called for the last one.
|
|
775
|
+
*
|
|
776
|
+
* @param handler Raw update handler
|
|
777
|
+
* @param group Handler group index
|
|
778
|
+
*/
|
|
779
|
+
onSceneTransition(handler) {
|
|
780
|
+
if (handler)
|
|
781
|
+
this._sceneTransitionHandler = handler;
|
|
782
|
+
else
|
|
783
|
+
this._sceneTransitionHandler = undefined;
|
|
784
|
+
}
|
|
733
785
|
/** @internal */
|
|
734
786
|
onNewMessage(filter, handler, group) {
|
|
735
787
|
this._addKnownHandler('new_message', filter, handler, group);
|