@adriangalilea/utils 0.17.1 → 0.19.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/README.md +9 -9
- package/dist/bot/access-control.d.ts.map +1 -1
- package/dist/bot/access-control.js +62 -45
- package/dist/bot/access-control.js.map +1 -1
- package/dist/bot/callbacks.d.ts +89 -0
- package/dist/bot/callbacks.d.ts.map +1 -0
- package/dist/bot/callbacks.js +128 -0
- package/dist/bot/callbacks.js.map +1 -0
- package/dist/bot/ctx.d.ts +219 -0
- package/dist/bot/ctx.d.ts.map +1 -0
- package/dist/bot/ctx.js +52 -0
- package/dist/bot/ctx.js.map +1 -0
- package/dist/bot/index.d.ts +4 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +4 -0
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/kit.d.ts +93 -41
- package/dist/bot/kit.d.ts.map +1 -1
- package/dist/bot/kit.js +153 -30
- package/dist/bot/kit.js.map +1 -1
- package/dist/bot/menu.d.ts +31 -11
- package/dist/bot/menu.d.ts.map +1 -1
- package/dist/bot/menu.js +34 -9
- package/dist/bot/menu.js.map +1 -1
- package/dist/bot/payments/callbacks.d.ts +28 -0
- package/dist/bot/payments/callbacks.d.ts.map +1 -0
- package/dist/bot/payments/callbacks.js +102 -0
- package/dist/bot/payments/callbacks.js.map +1 -0
- package/dist/bot/payments/commands.d.ts +26 -0
- package/dist/bot/payments/commands.d.ts.map +1 -0
- package/dist/bot/payments/commands.js +41 -0
- package/dist/bot/payments/commands.js.map +1 -0
- package/dist/bot/payments/config.d.ts +30 -0
- package/dist/bot/payments/config.d.ts.map +1 -0
- package/dist/bot/payments/config.js +166 -0
- package/dist/bot/payments/config.js.map +1 -0
- package/dist/bot/payments/derive.d.ts +75 -0
- package/dist/bot/payments/derive.d.ts.map +1 -0
- package/dist/bot/payments/derive.js +151 -0
- package/dist/bot/payments/derive.js.map +1 -0
- package/dist/bot/payments/handlers.d.ts +113 -0
- package/dist/bot/payments/handlers.d.ts.map +1 -0
- package/dist/bot/payments/handlers.js +284 -0
- package/dist/bot/payments/handlers.js.map +1 -0
- package/dist/bot/payments/index.d.ts +17 -0
- package/dist/bot/payments/index.d.ts.map +1 -0
- package/dist/bot/payments/index.js +14 -0
- package/dist/bot/payments/index.js.map +1 -0
- package/dist/bot/payments/invoice.d.ts +101 -0
- package/dist/bot/payments/invoice.d.ts.map +1 -0
- package/dist/bot/payments/invoice.js +213 -0
- package/dist/bot/payments/invoice.js.map +1 -0
- package/dist/bot/payments/menu-item.d.ts +41 -0
- package/dist/bot/payments/menu-item.d.ts.map +1 -0
- package/dist/bot/payments/menu-item.js +259 -0
- package/dist/bot/payments/menu-item.js.map +1 -0
- package/dist/bot/payments/payload.d.ts +42 -0
- package/dist/bot/payments/payload.d.ts.map +1 -0
- package/dist/bot/payments/payload.js +69 -0
- package/dist/bot/payments/payload.js.map +1 -0
- package/dist/bot/payments/payouts.d.ts +88 -0
- package/dist/bot/payments/payouts.d.ts.map +1 -0
- package/dist/bot/payments/payouts.js +133 -0
- package/dist/bot/payments/payouts.js.map +1 -0
- package/dist/bot/payments/plugin.d.ts +271 -0
- package/dist/bot/payments/plugin.d.ts.map +1 -0
- package/dist/bot/payments/plugin.js +141 -0
- package/dist/bot/payments/plugin.js.map +1 -0
- package/dist/bot/payments/refund.d.ts +124 -0
- package/dist/bot/payments/refund.d.ts.map +1 -0
- package/dist/bot/payments/refund.js +308 -0
- package/dist/bot/payments/refund.js.map +1 -0
- package/dist/bot/payments/schemas.d.ts +58 -0
- package/dist/bot/payments/schemas.d.ts.map +1 -0
- package/dist/bot/payments/schemas.js +55 -0
- package/dist/bot/payments/schemas.js.map +1 -0
- package/dist/bot/payments/state.d.ts +90 -0
- package/dist/bot/payments/state.d.ts.map +1 -0
- package/dist/bot/payments/state.js +149 -0
- package/dist/bot/payments/state.js.map +1 -0
- package/dist/bot/payments/stores.d.ts +44 -0
- package/dist/bot/payments/stores.d.ts.map +1 -0
- package/dist/bot/payments/stores.js +39 -0
- package/dist/bot/payments/stores.js.map +1 -0
- package/dist/bot/payments/types.d.ts +299 -0
- package/dist/bot/payments/types.d.ts.map +1 -0
- package/dist/bot/payments/types.js +39 -0
- package/dist/bot/payments/types.js.map +1 -0
- package/dist/bot/payments/waiver.d.ts +81 -0
- package/dist/bot/payments/waiver.d.ts.map +1 -0
- package/dist/bot/payments/waiver.js +145 -0
- package/dist/bot/payments/waiver.js.map +1 -0
- package/dist/bot/storage.d.ts +154 -0
- package/dist/bot/storage.d.ts.map +1 -0
- package/dist/bot/storage.js +222 -0
- package/dist/bot/storage.js.map +1 -0
- package/package.json +24 -3
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback-query handler bodies — extracted from `plugin.ts` per the
|
|
3
|
+
* `bot/CLAUDE.md` plugin file convention (callbacks.ts owns the
|
|
4
|
+
* `.callbackQuery()` handler bodies; plugin.ts is wiring only).
|
|
5
|
+
*
|
|
6
|
+
* The CallbackData schemas themselves live with their owning feature
|
|
7
|
+
* (waiver.ts, refund.ts) — those modules also export `buildXxxHandler`
|
|
8
|
+
* factories for handlers that need richer dependencies (refund's flow
|
|
9
|
+
* needs `stores + storage + cfg`). This file holds the simpler
|
|
10
|
+
* inline-style handlers that didn't justify their own factory.
|
|
11
|
+
*
|
|
12
|
+
* - `buildWaiverConsentHandler({ cfg, catalog })`
|
|
13
|
+
* persists consent, deletes the prompt message, re-enters the
|
|
14
|
+
* invoice flow for the carried productKey.
|
|
15
|
+
* - `buildWaiverCancelHandler()`
|
|
16
|
+
* acknowledges the tap and edits the prompt to a "canceled"
|
|
17
|
+
* message.
|
|
18
|
+
* - `buildRefundCloseHandler()`
|
|
19
|
+
* admin-only: deletes the DM notification on the close button.
|
|
20
|
+
*/
|
|
21
|
+
import { say } from "../../say/index.js";
|
|
22
|
+
import { narrow } from "../ctx.js";
|
|
23
|
+
import { presentInvoice } from "./invoice.js";
|
|
24
|
+
import { persistWaiverOnSession } from "./waiver.js";
|
|
25
|
+
const FALLBACK_LANG = "en";
|
|
26
|
+
// ─── waiver consent ───────────────────────────────────────────────
|
|
27
|
+
export const buildWaiverConsentHandler = (args) => async (ctx) => {
|
|
28
|
+
const c = narrow(ctx);
|
|
29
|
+
const lang = c.session.language ?? FALLBACK_LANG;
|
|
30
|
+
const userId = c.from?.id;
|
|
31
|
+
if (!userId) {
|
|
32
|
+
await c.answer({
|
|
33
|
+
text: say({
|
|
34
|
+
en: "Could not identify you.",
|
|
35
|
+
es: "No se pudo identificarte.",
|
|
36
|
+
}, lang),
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const product = args.catalog.byKey.get(c.queryData.pk);
|
|
41
|
+
if (!product) {
|
|
42
|
+
await c.answer({
|
|
43
|
+
text: say({ en: "Unknown product.", es: "Producto desconocido." }, lang),
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Persist consent on the user's session (cur ctx IS the user
|
|
48
|
+
// since they're tapping their own message).
|
|
49
|
+
persistWaiverOnSession(narrow(c), args.cfg.waiver.version, lang);
|
|
50
|
+
// Acknowledge the tap with a toast, then DELETE the consent
|
|
51
|
+
// prompt entirely. Previously we edited the body to
|
|
52
|
+
// "✅ Consent recorded. Sending invoice…" which goes stale the
|
|
53
|
+
// moment the invoice payment sheet appears underneath — visible
|
|
54
|
+
// noise in the chat history forever after. The toast confirms
|
|
55
|
+
// what happened; the user's next interaction is the Telegram
|
|
56
|
+
// payment sheet itself.
|
|
57
|
+
await c.answer({
|
|
58
|
+
text: say({ en: "✅ Consent recorded.", es: "✅ Consentimiento registrado." }, lang),
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
await c.message?.delete?.();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
/* message too old to delete — ignore */
|
|
65
|
+
}
|
|
66
|
+
// Re-enter the invoice flow now that consent is fresh.
|
|
67
|
+
await presentInvoice(narrow(c), args.cfg, args.catalog, product.id);
|
|
68
|
+
};
|
|
69
|
+
// ─── waiver cancel ────────────────────────────────────────────────
|
|
70
|
+
export const buildWaiverCancelHandler = () => async (ctx) => {
|
|
71
|
+
const c = narrow(ctx);
|
|
72
|
+
const lang = c.session.language ?? FALLBACK_LANG;
|
|
73
|
+
await c.answer({
|
|
74
|
+
text: say({ en: "Canceled.", es: "Cancelado." }, lang),
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
await c.editText(say({ en: "❌ Purchase canceled.", es: "❌ Compra cancelada." }, lang));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* ignore — message too old to edit */
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
// ─── refund close (admin-only) ────────────────────────────────────
|
|
84
|
+
export const buildRefundCloseHandler = () => async (ctx) => {
|
|
85
|
+
const c = narrow(ctx);
|
|
86
|
+
const lang = c.session.language ?? FALLBACK_LANG;
|
|
87
|
+
if (!c.isAdmin) {
|
|
88
|
+
await c.answer({
|
|
89
|
+
text: say({ en: "Admin only.", es: "Solo admin." }, lang),
|
|
90
|
+
show_alert: true,
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
await c.answer({});
|
|
95
|
+
try {
|
|
96
|
+
await c.message?.delete?.();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
/* ignore — message too old to delete */
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=callbacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callbacks.js","sourceRoot":"","sources":["../../../src/bot/payments/callbacks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash-command handlers. The `/paysupport` command is mandated by
|
|
3
|
+
* Telegram Bot Developer ToS §6.5 — every bot accepting payments must
|
|
4
|
+
* expose a way for users to reach support about charges.
|
|
5
|
+
*
|
|
6
|
+
* The actual command registration (`.command("paysupport", { … }, fn)`)
|
|
7
|
+
* happens in `plugin.ts`; this module owns the body + the response
|
|
8
|
+
* text so wording stays in one place.
|
|
9
|
+
*/
|
|
10
|
+
import type { BotPaymentsConfig } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Mandatory `/paysupport` text. ToS §6.5 requires every bot accepting
|
|
13
|
+
* payments to expose this. Brief message pointing the user to the menu
|
|
14
|
+
* (where the refund flow lives) and to the human admin contact.
|
|
15
|
+
*
|
|
16
|
+
* Exported because tests + future admin tooling may want to render the
|
|
17
|
+
* exact wording the bot uses without going through the command handler.
|
|
18
|
+
*/
|
|
19
|
+
export declare const buildPaysupportText: (cfg: BotPaymentsConfig<string>, lang: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the `/paysupport` command handler. Pure factory — `plugin.ts`
|
|
22
|
+
* calls this once at construction and passes the result to gramio's
|
|
23
|
+
* `.command()`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const buildPaysupportCommand: (cfg: BotPaymentsConfig<string>) => (ctx: unknown) => Promise<void>;
|
|
26
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAC;AAMrE;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,MAAM,MAAM,KACV,MAeD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,iBAAiB,CAAC,MAAM,CAAC,MACxB,KAAK,OAAO,KAAG,OAAO,CAAC,IAAI,CAIjC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash-command handlers. The `/paysupport` command is mandated by
|
|
3
|
+
* Telegram Bot Developer ToS §6.5 — every bot accepting payments must
|
|
4
|
+
* expose a way for users to reach support about charges.
|
|
5
|
+
*
|
|
6
|
+
* The actual command registration (`.command("paysupport", { … }, fn)`)
|
|
7
|
+
* happens in `plugin.ts`; this module owns the body + the response
|
|
8
|
+
* text so wording stays in one place.
|
|
9
|
+
*/
|
|
10
|
+
import { say } from "../../say/index.js";
|
|
11
|
+
import { narrow } from "../ctx.js";
|
|
12
|
+
const FALLBACK_LANG = "en";
|
|
13
|
+
/**
|
|
14
|
+
* Mandatory `/paysupport` text. ToS §6.5 requires every bot accepting
|
|
15
|
+
* payments to expose this. Brief message pointing the user to the menu
|
|
16
|
+
* (where the refund flow lives) and to the human admin contact.
|
|
17
|
+
*
|
|
18
|
+
* Exported because tests + future admin tooling may want to render the
|
|
19
|
+
* exact wording the bot uses without going through the command handler.
|
|
20
|
+
*/
|
|
21
|
+
export const buildPaysupportText = (cfg, lang) => say({
|
|
22
|
+
en: `Need help with a payment?\n\n` +
|
|
23
|
+
`• Open /settings → 💎 VIP → 📜 History to see your charges and request a refund.\n` +
|
|
24
|
+
`• Or contact: ${cfg.paysupport}\n\n` +
|
|
25
|
+
`Refund disputes are handled by the admin within Telegram.`,
|
|
26
|
+
es: `¿Necesitas ayuda con un pago?\n\n` +
|
|
27
|
+
`• Abre /settings → 💎 VIP → 📜 Historial para ver tus cargos y solicitar reembolso.\n` +
|
|
28
|
+
`• O contacta: ${cfg.paysupport}\n\n` +
|
|
29
|
+
`Las disputas las gestiona el admin dentro de Telegram.`,
|
|
30
|
+
}, lang);
|
|
31
|
+
/**
|
|
32
|
+
* Returns the `/paysupport` command handler. Pure factory — `plugin.ts`
|
|
33
|
+
* calls this once at construction and passes the result to gramio's
|
|
34
|
+
* `.command()`.
|
|
35
|
+
*/
|
|
36
|
+
export const buildPaysupportCommand = (cfg) => async (ctx) => {
|
|
37
|
+
const c = narrow(ctx);
|
|
38
|
+
const lang = c.session.language ?? FALLBACK_LANG;
|
|
39
|
+
await c.send(buildPaysupportText(cfg, lang));
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../../src/bot/payments/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAsB,MAAM,EAAE,MAAM,WAAW,CAAC;AAGvD,MAAM,aAAa,GAAG,IAAI,CAAC;AAI3B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAClC,GAA8B,EAC9B,IAAY,EACH,EAAE,CACX,GAAG,CACF;IACC,EAAE,EACD,+BAA+B;QAC/B,oFAAoF;QACpF,iBAAiB,GAAG,CAAC,UAAU,MAAM;QACrC,2DAA2D;IAC5D,EAAE,EACD,mCAAmC;QACnC,uFAAuF;QACvF,iBAAiB,GAAG,CAAC,UAAU,MAAM;QACrC,wDAAwD;CACzD,EACD,IAAI,CACJ,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAClC,CAAC,GAA8B,EAAE,EAAE,CACnC,KAAK,EAAE,GAAY,EAAiB,EAAE;IACrC,MAAM,CAAC,GAAG,MAAM,CAA6B,GAAG,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI,aAAa,CAAC;IACjD,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;AAC9C,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Construction-time validation + catalog build.
|
|
3
|
+
*
|
|
4
|
+
* `validateConfig(cfg)` is the gate. It throws `Panic` on every shape
|
|
5
|
+
* problem the bot author can introduce — a misconfigured payments
|
|
6
|
+
* plugin should scream at startup, not silently misbehave at runtime
|
|
7
|
+
* (see `src/offensive.ts` and CLAUDE.md §"Failure modes").
|
|
8
|
+
*
|
|
9
|
+
* `buildCatalog(cfg)` produces the normalized `ProductCatalog` the rest
|
|
10
|
+
* of the plugin consults (positional ids resolved, polyglots passed
|
|
11
|
+
* through, O(1) lookup by ProductKey).
|
|
12
|
+
*
|
|
13
|
+
* Both are pure — no gramio, no storage, no side effects.
|
|
14
|
+
*/
|
|
15
|
+
import { type BotPaymentsConfig, type ProductCatalog } from "./types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Validates the config and returns the **locales set** that the plugin
|
|
18
|
+
* will use for cross-checking other Polyglots. Derived from
|
|
19
|
+
* `waiver.text` (the single mandatory Polyglot) — every other Polyglot
|
|
20
|
+
* in the config must cover the same locales.
|
|
21
|
+
*
|
|
22
|
+
* Throws `Panic` on any structural problem.
|
|
23
|
+
*/
|
|
24
|
+
export declare const validateConfig: (cfg: BotPaymentsConfig<string>) => ReadonlySet<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Materialize the validated config into the runtime catalog. Assumes
|
|
27
|
+
* `validateConfig(cfg)` has already been called and passed.
|
|
28
|
+
*/
|
|
29
|
+
export declare const buildCatalog: (cfg: BotPaymentsConfig<string>) => ProductCatalog;
|
|
30
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EACN,KAAK,iBAAiB,EAGtB,KAAK,cAAc,EAGnB,MAAM,YAAY,CAAC;AAwCpB;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAC1B,KAAK,iBAAiB,CAAC,MAAM,CAAC,KAC5B,WAAW,CAAC,MAAM,CAuFpB,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,YAAY,GACxB,KAAK,iBAAiB,CAAC,MAAM,CAAC,KAC5B,cA2CF,CAAC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Construction-time validation + catalog build.
|
|
3
|
+
*
|
|
4
|
+
* `validateConfig(cfg)` is the gate. It throws `Panic` on every shape
|
|
5
|
+
* problem the bot author can introduce — a misconfigured payments
|
|
6
|
+
* plugin should scream at startup, not silently misbehave at runtime
|
|
7
|
+
* (see `src/offensive.ts` and CLAUDE.md §"Failure modes").
|
|
8
|
+
*
|
|
9
|
+
* `buildCatalog(cfg)` produces the normalized `ProductCatalog` the rest
|
|
10
|
+
* of the plugin consults (positional ids resolved, polyglots passed
|
|
11
|
+
* through, O(1) lookup by ProductKey).
|
|
12
|
+
*
|
|
13
|
+
* Both are pure — no gramio, no storage, no side effects.
|
|
14
|
+
*/
|
|
15
|
+
import { panic } from "../../offensive.js";
|
|
16
|
+
import { SUBSCRIPTION_PERIOD_SECONDS, } from "./types.js";
|
|
17
|
+
// ─── validation ────────────────────────────────────────────────────
|
|
18
|
+
const requireNonEmptyString = (value, where) => {
|
|
19
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
20
|
+
panic(`bot/payments: ${where} must be a non-empty string (got ${value})`);
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
};
|
|
24
|
+
const requirePositiveInt = (value, where) => {
|
|
25
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
26
|
+
panic(`bot/payments: ${where} must be a positive integer (got ${value})`);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
};
|
|
30
|
+
const polyglotKeys = (p) => Object.keys(p).filter((k) => typeof p[k] === "string");
|
|
31
|
+
const requirePolyglotCovers = (p, locales, where) => {
|
|
32
|
+
if (!p || typeof p !== "object") {
|
|
33
|
+
panic(`bot/payments: ${where} must be a Polyglot object`);
|
|
34
|
+
}
|
|
35
|
+
if (locales.size === 0)
|
|
36
|
+
return; // no required-locales declared — accept anything non-empty
|
|
37
|
+
for (const loc of locales) {
|
|
38
|
+
const v = p[loc];
|
|
39
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
40
|
+
panic(`bot/payments: ${where} is missing a non-empty entry for locale "${loc}"`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Validates the config and returns the **locales set** that the plugin
|
|
46
|
+
* will use for cross-checking other Polyglots. Derived from
|
|
47
|
+
* `waiver.text` (the single mandatory Polyglot) — every other Polyglot
|
|
48
|
+
* in the config must cover the same locales.
|
|
49
|
+
*
|
|
50
|
+
* Throws `Panic` on any structural problem.
|
|
51
|
+
*/
|
|
52
|
+
export const validateConfig = (cfg) => {
|
|
53
|
+
if (!cfg || typeof cfg !== "object") {
|
|
54
|
+
panic("bot/payments: config must be an object");
|
|
55
|
+
}
|
|
56
|
+
requireNonEmptyString(cfg.paysupport, "paysupport");
|
|
57
|
+
requireNonEmptyString(cfg.legal?.sellerName, "legal.sellerName");
|
|
58
|
+
requireNonEmptyString(cfg.legal?.nif, "legal.nif");
|
|
59
|
+
// termsUrl and privacyUrl are OPTIONAL. privacyUrl gets a sensible
|
|
60
|
+
// Telegram-side default at use time (waiver.ts). termsUrl, when
|
|
61
|
+
// omitted, hides the 📖 Terms button from the consent prompt — no
|
|
62
|
+
// canonical Telegram ToS to fall back to.
|
|
63
|
+
if (cfg.legal?.termsUrl !== undefined) {
|
|
64
|
+
requireNonEmptyString(cfg.legal.termsUrl, "legal.termsUrl");
|
|
65
|
+
}
|
|
66
|
+
if (cfg.legal?.privacyUrl !== undefined) {
|
|
67
|
+
requireNonEmptyString(cfg.legal.privacyUrl, "legal.privacyUrl");
|
|
68
|
+
}
|
|
69
|
+
requireNonEmptyString(cfg.waiver?.version, "waiver.version");
|
|
70
|
+
const waiverKeys = polyglotKeys(cfg.waiver?.text ?? {});
|
|
71
|
+
if (waiverKeys.length === 0) {
|
|
72
|
+
panic("bot/payments: waiver.text must declare at least one locale with non-empty content");
|
|
73
|
+
}
|
|
74
|
+
const locales = new Set(waiverKeys);
|
|
75
|
+
const hasVip = Array.isArray(cfg.vip) && cfg.vip.length > 0;
|
|
76
|
+
const hasCredits = !!cfg.credits &&
|
|
77
|
+
Array.isArray(cfg.credits.packs) &&
|
|
78
|
+
cfg.credits.packs.length > 0;
|
|
79
|
+
const hasPerks = !!cfg.perks && Object.keys(cfg.perks).length > 0;
|
|
80
|
+
if (!hasVip && !hasCredits && !hasPerks) {
|
|
81
|
+
panic("bot/payments: declare at least one of `vip`, `credits.packs`, or `perks` — " +
|
|
82
|
+
"a payments plugin with no products has nothing to sell");
|
|
83
|
+
}
|
|
84
|
+
if (hasVip) {
|
|
85
|
+
cfg.vip?.forEach((rung, i) => {
|
|
86
|
+
const at = `vip[${i}]`;
|
|
87
|
+
requirePositiveInt(rung.xtr, `${at}.xtr`);
|
|
88
|
+
if (rung.period !== "30d") {
|
|
89
|
+
panic(`bot/payments: ${at}.period must be "30d" (Telegram supports no other ` +
|
|
90
|
+
`subscription period as of 2026) — got "${rung.period}"`);
|
|
91
|
+
}
|
|
92
|
+
requirePolyglotCovers(rung.name, locales, `${at}.name`);
|
|
93
|
+
if (rung.grants?.credits !== undefined) {
|
|
94
|
+
requirePositiveInt(rung.grants.credits, `${at}.grants.credits`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (hasCredits) {
|
|
99
|
+
const c = cfg.credits;
|
|
100
|
+
if (!c)
|
|
101
|
+
panic("bot/payments: credits config disappeared mid-validation");
|
|
102
|
+
requirePolyglotCovers(c.unit, locales, "credits.unit");
|
|
103
|
+
c.packs.forEach((pack, i) => {
|
|
104
|
+
const at = `credits.packs[${i}]`;
|
|
105
|
+
requirePositiveInt(pack.xtr, `${at}.xtr`);
|
|
106
|
+
requirePositiveInt(pack.grants?.credits, `${at}.grants.credits`);
|
|
107
|
+
if (pack.name) {
|
|
108
|
+
requirePolyglotCovers(pack.name, locales, `${at}.name`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (hasPerks && cfg.perks) {
|
|
113
|
+
for (const [key, perk] of Object.entries(cfg.perks)) {
|
|
114
|
+
const at = `perks.${key}`;
|
|
115
|
+
if (!/^[a-z][a-z0-9_]*$/i.test(key)) {
|
|
116
|
+
panic(`bot/payments: perk key "${key}" must match /^[a-z][a-z0-9_]*$/i (used in ` +
|
|
117
|
+
`payload encoding, must be safe)`);
|
|
118
|
+
}
|
|
119
|
+
requirePositiveInt(perk.xtr, `${at}.xtr`);
|
|
120
|
+
requirePolyglotCovers(perk.name, locales, `${at}.name`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return locales;
|
|
124
|
+
};
|
|
125
|
+
// ─── catalog build ─────────────────────────────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Materialize the validated config into the runtime catalog. Assumes
|
|
128
|
+
* `validateConfig(cfg)` has already been called and passed.
|
|
129
|
+
*/
|
|
130
|
+
export const buildCatalog = (cfg) => {
|
|
131
|
+
const vip = (cfg.vip ?? []).map((rung, i) => ({
|
|
132
|
+
id: `vip.${i + 1}`,
|
|
133
|
+
rank: i + 1,
|
|
134
|
+
xtr: rung.xtr,
|
|
135
|
+
periodSeconds: SUBSCRIPTION_PERIOD_SECONDS[rung.period],
|
|
136
|
+
name: rung.name,
|
|
137
|
+
creditsGranted: rung.grants?.credits ?? 0,
|
|
138
|
+
}));
|
|
139
|
+
const creditsPacks = (cfg.credits?.packs ?? []).map((pack, i) => ({
|
|
140
|
+
id: `credits.${i + 1}`,
|
|
141
|
+
xtr: pack.xtr,
|
|
142
|
+
creditsGranted: pack.grants.credits,
|
|
143
|
+
name: pack.name,
|
|
144
|
+
}));
|
|
145
|
+
const perks = Object.entries(cfg.perks ?? {}).map(([key, perk]) => ({
|
|
146
|
+
id: `perks.${key}`,
|
|
147
|
+
key,
|
|
148
|
+
xtr: perk.xtr,
|
|
149
|
+
name: perk.name,
|
|
150
|
+
}));
|
|
151
|
+
const byKey = new Map();
|
|
152
|
+
for (const r of vip)
|
|
153
|
+
byKey.set(r.id, r);
|
|
154
|
+
for (const p of creditsPacks)
|
|
155
|
+
byKey.set(p.id, p);
|
|
156
|
+
for (const p of perks)
|
|
157
|
+
byKey.set(p.id, p);
|
|
158
|
+
return {
|
|
159
|
+
vip,
|
|
160
|
+
creditsUnit: cfg.credits?.unit,
|
|
161
|
+
creditsPacks,
|
|
162
|
+
perks,
|
|
163
|
+
byKey,
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/bot/payments/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx.payments` derive — the per-event ctx decoration.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `plugin.ts` to keep that file pure wiring per the
|
|
5
|
+
* `bot/CLAUDE.md` plugin file convention. Everything that reads or
|
|
6
|
+
* mutates session state inside an event handler goes through one of
|
|
7
|
+
* the methods this module attaches:
|
|
8
|
+
*
|
|
9
|
+
* - `ctx.payments.atLeast(id)` — tier gate (lazy expiry)
|
|
10
|
+
* - `ctx.payments.tier()` — current rung (`'free' | 'vip.N'`)
|
|
11
|
+
* - `ctx.payments.tier.level()` — integer rank
|
|
12
|
+
* - `ctx.payments.tier.label()` — resolved Polyglot label
|
|
13
|
+
* - `ctx.payments.credits.*` — balance / consume / tryConsume
|
|
14
|
+
* - `ctx.payments.has(perkId)` — perk ownership boolean
|
|
15
|
+
* - `ctx.payments.require(...)` — gate + localized upgrade prompt
|
|
16
|
+
* - `ctx.payments.invoice(key)` — purchase entry (waiver-gated)
|
|
17
|
+
*
|
|
18
|
+
* Returned shape is a function so `plugin.ts` can drop it directly
|
|
19
|
+
* into `.derive(["message", "callback_query"], buildPaymentsDerive({...}))`.
|
|
20
|
+
*/
|
|
21
|
+
import { type BotPaymentsConfig, type ProductCatalog } from "./types.js";
|
|
22
|
+
type TierKey = "free" | `vip.${number}`;
|
|
23
|
+
/**
|
|
24
|
+
* Callable `tier` namespace. Matches `PaymentsCtx['tier']` from `plugin.ts`
|
|
25
|
+
* — exported there as the public surface. Repeated minimally here so
|
|
26
|
+
* derive.ts isn't import-cycle dependent on plugin.ts.
|
|
27
|
+
*/
|
|
28
|
+
export type TierFn = (() => TierKey) & {
|
|
29
|
+
level: () => number;
|
|
30
|
+
label: () => string | undefined;
|
|
31
|
+
};
|
|
32
|
+
export type CreditsApi = {
|
|
33
|
+
balance: () => number;
|
|
34
|
+
consume: (n: number) => void;
|
|
35
|
+
tryConsume: (n: number) => boolean;
|
|
36
|
+
};
|
|
37
|
+
export type RequireOpts = {
|
|
38
|
+
feature?: {
|
|
39
|
+
en: string;
|
|
40
|
+
es: string;
|
|
41
|
+
} | string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* What `buildPaymentsDerive` attaches under `ctx.payments`. Plugin.ts
|
|
45
|
+
* casts this to the typed `PaymentsCtx<Cfg>` (with positional ladder
|
|
46
|
+
* narrowing) before exposing it to bot authors — TypeScript can't
|
|
47
|
+
* preserve the generic narrowing through a function-shaped derive
|
|
48
|
+
* factory cleanly, so the strict type lives at the public surface.
|
|
49
|
+
*/
|
|
50
|
+
export type PaymentsDerived = {
|
|
51
|
+
atLeast: (id: string) => boolean;
|
|
52
|
+
tier: TierFn;
|
|
53
|
+
has: (perkId: string) => boolean;
|
|
54
|
+
credits: CreditsApi;
|
|
55
|
+
require: (id: string, opts?: RequireOpts) => Promise<boolean>;
|
|
56
|
+
invoice: (productKey: string) => Promise<"invoice_sent" | "waiver_prompt_sent" | "unknown_product">;
|
|
57
|
+
};
|
|
58
|
+
export type BuildPaymentsDeriveArgs = {
|
|
59
|
+
/** Resolved product catalogue (rungs, packs, perks). */
|
|
60
|
+
catalog: ProductCatalog;
|
|
61
|
+
/**
|
|
62
|
+
* Full config — currently only consulted inside `invoice()` for the
|
|
63
|
+
* waiver wording + product display strings via `presentInvoice`.
|
|
64
|
+
*/
|
|
65
|
+
cfg: BotPaymentsConfig<string>;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Returns the function you pass to gramio's `.derive([events], fn)`.
|
|
69
|
+
* The returned function takes ctx, returns `{ payments }`.
|
|
70
|
+
*/
|
|
71
|
+
export declare const buildPaymentsDerive: ({ cfg, catalog, }: BuildPaymentsDeriveArgs) => (ctx: unknown) => {
|
|
72
|
+
payments: PaymentsDerived;
|
|
73
|
+
};
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/derive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,EACN,KAAK,iBAAiB,EAGtB,KAAK,cAAc,EACnB,MAAM,YAAY,CAAC;AAMpB,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,MAAM,EAAE,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG;IACtC,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACzB,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAC9C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,OAAO,EAAE,CACR,UAAU,EAAE,MAAM,KACd,OAAO,CAAC,cAAc,GAAG,oBAAoB,GAAG,iBAAiB,CAAC,CAAC;CACxE,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,wDAAwD;IACxD,OAAO,EAAE,cAAc,CAAC;IACxB;;;OAGG;IACH,GAAG,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,mBAGjC,uBAAuB,MACjB,KAAK,OAAO,KAAG;IAAE,QAAQ,EAAE,eAAe,CAAA;CA+IlD,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ctx.payments` derive — the per-event ctx decoration.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `plugin.ts` to keep that file pure wiring per the
|
|
5
|
+
* `bot/CLAUDE.md` plugin file convention. Everything that reads or
|
|
6
|
+
* mutates session state inside an event handler goes through one of
|
|
7
|
+
* the methods this module attaches:
|
|
8
|
+
*
|
|
9
|
+
* - `ctx.payments.atLeast(id)` — tier gate (lazy expiry)
|
|
10
|
+
* - `ctx.payments.tier()` — current rung (`'free' | 'vip.N'`)
|
|
11
|
+
* - `ctx.payments.tier.level()` — integer rank
|
|
12
|
+
* - `ctx.payments.tier.label()` — resolved Polyglot label
|
|
13
|
+
* - `ctx.payments.credits.*` — balance / consume / tryConsume
|
|
14
|
+
* - `ctx.payments.has(perkId)` — perk ownership boolean
|
|
15
|
+
* - `ctx.payments.require(...)` — gate + localized upgrade prompt
|
|
16
|
+
* - `ctx.payments.invoice(key)` — purchase entry (waiver-gated)
|
|
17
|
+
*
|
|
18
|
+
* Returned shape is a function so `plugin.ts` can drop it directly
|
|
19
|
+
* into `.derive(["message", "callback_query"], buildPaymentsDerive({...}))`.
|
|
20
|
+
*/
|
|
21
|
+
import { say } from "../../say/index.js";
|
|
22
|
+
import { narrow } from "../ctx.js";
|
|
23
|
+
import { menuNavCb } from "../menu.js";
|
|
24
|
+
import { presentInvoice } from "./invoice.js";
|
|
25
|
+
import { InsufficientCredits, } from "./types.js";
|
|
26
|
+
const FALLBACK_LANG = "en";
|
|
27
|
+
/**
|
|
28
|
+
* Returns the function you pass to gramio's `.derive([events], fn)`.
|
|
29
|
+
* The returned function takes ctx, returns `{ payments }`.
|
|
30
|
+
*/
|
|
31
|
+
export const buildPaymentsDerive = ({ cfg, catalog, }) => {
|
|
32
|
+
return (ctx) => {
|
|
33
|
+
// Single narrow at the top — every subsequent reference uses
|
|
34
|
+
// `c.<field>` and reads cleanly. The structural type is the
|
|
35
|
+
// intersection of message + callback_query event ctxs so this
|
|
36
|
+
// derive works for both event scopes (gramio's runtime dedup
|
|
37
|
+
// guarantees one execution per update).
|
|
38
|
+
const c = narrow(ctx);
|
|
39
|
+
// Lazy expiry — vip is only "active" while its expiresAt is in
|
|
40
|
+
// the future (CLAUDE.md §"sweep" deferred to v2). Pure read; no
|
|
41
|
+
// cleanup of stale `session.pay.vip` records.
|
|
42
|
+
const isVipActive = () => {
|
|
43
|
+
const v = c.session.pay?.vip;
|
|
44
|
+
if (!v)
|
|
45
|
+
return false;
|
|
46
|
+
return v.expiresAt * 1000 > Date.now();
|
|
47
|
+
};
|
|
48
|
+
const effectiveRung = () => {
|
|
49
|
+
if (!isVipActive())
|
|
50
|
+
return 0;
|
|
51
|
+
return c.session.pay?.vip?.rung ?? 0;
|
|
52
|
+
};
|
|
53
|
+
const atLeast = (id) => {
|
|
54
|
+
if (id === "free")
|
|
55
|
+
return true;
|
|
56
|
+
const rung = effectiveRung();
|
|
57
|
+
if (id === "vip")
|
|
58
|
+
return rung > 0;
|
|
59
|
+
if (id.startsWith("vip.")) {
|
|
60
|
+
const want = Number.parseInt(id.slice(4), 10);
|
|
61
|
+
return rung >= want;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
const tierFn = (() => {
|
|
66
|
+
const rung = effectiveRung();
|
|
67
|
+
if (rung === 0)
|
|
68
|
+
return "free";
|
|
69
|
+
return `vip.${rung}`;
|
|
70
|
+
});
|
|
71
|
+
tierFn.level = () => effectiveRung();
|
|
72
|
+
tierFn.label = () => {
|
|
73
|
+
const rung = effectiveRung();
|
|
74
|
+
if (rung === 0)
|
|
75
|
+
return undefined;
|
|
76
|
+
const r = catalog.vip[rung - 1];
|
|
77
|
+
if (!r)
|
|
78
|
+
return undefined;
|
|
79
|
+
const lang = c.session.language ?? FALLBACK_LANG;
|
|
80
|
+
return (r.name[lang] ??
|
|
81
|
+
r.name[FALLBACK_LANG]);
|
|
82
|
+
};
|
|
83
|
+
const credits = {
|
|
84
|
+
balance: () => c.session.pay?.credits ?? 0,
|
|
85
|
+
consume: (n) => {
|
|
86
|
+
const current = c.session.pay?.credits ?? 0;
|
|
87
|
+
if (current < n)
|
|
88
|
+
throw new InsufficientCredits(n, current);
|
|
89
|
+
c.session.pay ??= {};
|
|
90
|
+
c.session.pay.credits = current - n;
|
|
91
|
+
},
|
|
92
|
+
tryConsume: (n) => {
|
|
93
|
+
const current = c.session.pay?.credits ?? 0;
|
|
94
|
+
if (current < n)
|
|
95
|
+
return false;
|
|
96
|
+
c.session.pay ??= {};
|
|
97
|
+
c.session.pay.credits = current - n;
|
|
98
|
+
return true;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
const has = (perkId) => !!c.session.pay?.perks?.[perkId];
|
|
102
|
+
const requireFn = async (id, opts) => {
|
|
103
|
+
if (atLeast(id))
|
|
104
|
+
return true;
|
|
105
|
+
const lang = c.session.language ?? FALLBACK_LANG;
|
|
106
|
+
const featureName = opts?.feature === undefined
|
|
107
|
+
? undefined
|
|
108
|
+
: typeof opts.feature === "string"
|
|
109
|
+
? opts.feature
|
|
110
|
+
: (opts.feature[lang] ?? opts.feature.en);
|
|
111
|
+
const body = featureName !== undefined
|
|
112
|
+
? say({
|
|
113
|
+
en: `💎 "${featureName}" needs VIP. Tap below to upgrade.`,
|
|
114
|
+
es: `💎 "${featureName}" necesita VIP. Toca abajo para mejorar.`,
|
|
115
|
+
}, lang)
|
|
116
|
+
: say({
|
|
117
|
+
en: "💎 This needs VIP. Tap below to upgrade.",
|
|
118
|
+
es: "💎 Esto necesita VIP. Toca abajo para mejorar.",
|
|
119
|
+
}, lang);
|
|
120
|
+
// Deep-link into our menuItem (id='pay') via botMenu's
|
|
121
|
+
// shared menuNavCb schema. v1 opens VIP root; deep-linking to
|
|
122
|
+
// a specific rung is a v2 TODO (see CLAUDE.md).
|
|
123
|
+
const button = say({ en: "💎 Open VIP plans", es: "💎 Ver planes VIP" }, lang);
|
|
124
|
+
await c.send(body, {
|
|
125
|
+
reply_markup: {
|
|
126
|
+
inline_keyboard: [
|
|
127
|
+
[
|
|
128
|
+
{
|
|
129
|
+
text: button,
|
|
130
|
+
callback_data: menuNavCb.pack({ path: "pay" }),
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
return false;
|
|
137
|
+
};
|
|
138
|
+
const invoiceFn = (productKey) => presentInvoice(narrow(c), cfg, catalog, productKey);
|
|
139
|
+
return {
|
|
140
|
+
payments: {
|
|
141
|
+
atLeast,
|
|
142
|
+
tier: tierFn,
|
|
143
|
+
has,
|
|
144
|
+
credits,
|
|
145
|
+
require: requireFn,
|
|
146
|
+
invoice: invoiceFn,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","sourceRoot":"","sources":["../../../src/bot/payments/derive.ts"],"names":[],"mappings":"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"}
|