@adriangalilea/utils 0.26.0 → 0.28.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 CHANGED
@@ -385,7 +385,9 @@ Then `pnpm install`. Every `ctx.send` / `ctx.sendDocument` / `ctx.reply` / etc.
385
385
  |---|---|
386
386
  | `@adriangalilea/utils/bot/ctx` | Structural ctx types (`BotMessageCtx`, `BotCallbackCtx`, …) and the `narrow<T>(ctx)` cast helper. Pure typing — no runtime state. |
387
387
  | `@adriangalilea/utils/bot/keys` | The bot-id key namespace, a **persisted contract**: `botId(ctx)`, `botStorageKey(ctx, userId)` → `bot-<id>:<userId>`, `botSubKey(ctx, sub)` → `bot-<id>:<sub>`. Every storage key the library writes derives from here, and those keys live in YOUR Redis/D1 rows — the shape never changes. Pure functions of `ctx.bot.info.id` (no env/fs), which is what keeps the whole bot surface Worker-safe. |
388
- | `@adriangalilea/utils/bot/kit` | `gracefulStart(bot, opts?)` — SIGINT/SIGTERM → `bot.stop()` → exit; force-kills if shutdown hangs. DMs the admin `@<bot> started.` / `@<bot> shutting down.` by default when `KEV.TELEGRAM_ADMIN_ID` is set (graceful only — crashes don't trigger `onStop`); pass `notifyAdmin: false` to disable or `notifyAdmin: 12345` for an explicit chat id.<br>`adminContext({ adminId? })` — reads `TELEGRAM_ADMIN_ID` from `kev` (with optional hardcoded fallback), decorates `ctx.adminId` + `ctx.isAdmin`.<br>`botSession(opts)` — **drop-in replacement for `@gramio/session`'s `session()`** that auto-namespaces every key as `bot-<id>:<senderId>` using `ctx.bot.info.id` (populated by `getMe()` at startup). Use this instead of `session()` — full stop. Multiple bots sharing one Redis stay isolated by construction; every plugin in this package derives the same prefix internally via `botStorageKey(ctx, userId)` / `botSubKey(ctx, sub)` (from `bot/keys`). No regex, no manual prefix argument, no way to forget.<br>`prefixStorage(storage, prefix)` — escape hatch for adding a top-level prefix on top of the bot-id namespace; almost never needed. |
388
+ | `@adriangalilea/utils/bot/kit` | The Node-only corner. `gracefulStart(bot, opts?)` — SIGINT/SIGTERM → `bot.stop()` → exit; force-kills if shutdown hangs. DMs the admin `@<bot> started.` / `@<bot> shutting down.` by default when `KEV.TELEGRAM_ADMIN_ID` is set (graceful only — crashes don't trigger `onStop`); pass `notifyAdmin: false` to disable or `notifyAdmin: 12345` for an explicit chat id.<br>`adminContext({ adminId? })` — reads `TELEGRAM_ADMIN_ID` from `kev` (with optional hardcoded fallback), decorates `ctx.adminId` + `ctx.isAdmin`.<br>Also re-exports `botSession` / `prefixStorage` from `bot/session` so Node consumers keep one import. |
389
+ | `@adriangalilea/utils/bot/session` | `botSession(opts)` — **drop-in replacement for `@gramio/session`'s `session()`** that auto-namespaces every key as `bot-<id>:<senderId>` using `ctx.bot.info.id` (populated by `getMe()` at startup). Use this instead of `session()` — full stop. Multiple bots sharing one Redis/D1 stay isolated by construction; every plugin in this package derives the same prefix internally via `botStorageKey(ctx, userId)` / `botSubKey(ctx, sub)` (from `bot/keys`). No regex, no manual prefix argument, no way to forget.<br>`prefixStorage(storage, prefix)` — escape hatch for adding a top-level prefix on top of the bot-id namespace; almost never needed. Worker-safe (used to live in `bot/kit`, which forced Worker bots to hand-roll the keying). |
390
+ | `@adriangalilea/utils/bot/notify` | Best-effort admin DMs, worker-safe (you pass the ids; no env, no process). `notifyAdmins(bot, adminIds, text, extra?)` — DM each admin, failures logged and swallowed (a notification must never take the bot down). `alertAdminError(bot, adminIds, label, error, throttle?)` — truncated `🚨 label\nName: message`, rate-limited through a caller-owned `alertThrottle(ms?)` so a failure storm sends one alert per window. `gracefulStart`'s start/stop DMs are built on this. |
389
391
  | `@adriangalilea/utils/bot/access-control` | Personal-bot ACL — gates non-admin/non-default users; admin gets DM with `[✅ Aprobar][❌ Denegar]` on first attempt; `/access` opens a persistent menu (revoke / reapprove / list pending). Backed by `@gramio/session` per-user + a small index. **Native alternative**: BotFather → Bot Settings → Access → "Restrict bot usage" — flat allow-list at Telegram. Use this plugin when you want in-bot approval flow instead of a BotFather round-trip; both can coexist. |
390
392
  | `@adriangalilea/utils/bot/allow-list` | Static allow-list by **id and/or @username** — stateless, no session/storage. `allowList({ ids?, usernames? })` is a plugin that decorates `ctx.allowed` (boolean); gate in your handlers (`if (!ctx.allowed) return`). `makeAllowList(...)` is the pure framework-free predicate. The light counterpart to `access-control` (which adds an approve/deny flow + revocable store, needing session+storage). Username caveat: a `@username` is optional and mutable, and the Bot API can't resolve username→id ahead of time — prefer `ids` when known, `usernames` is the pragmatic fallback. |
391
393
  | `@adriangalilea/utils/bot/coalesce` | Joins client-split inbound messages back into one. When a user pastes >4096 chars, Telegram clients fragment it into separate `message` updates with no marker. Middleware detects the burst and emits one combined event. |
@@ -398,7 +400,8 @@ Standard wiring:
398
400
  ```typescript
399
401
  import { Bot } from 'gramio'
400
402
  import { redisStorage } from '@gramio/storage-redis'
401
- import { adminContext, botSession, gracefulStart } from '@adriangalilea/utils/bot/kit'
403
+ import { adminContext, gracefulStart } from '@adriangalilea/utils/bot/kit'
404
+ import { botSession } from '@adriangalilea/utils/bot/session'
402
405
  import { accessControl } from '@adriangalilea/utils/bot/access-control'
403
406
  import { llmStream, llmHistory } from '@adriangalilea/utils/bot/llm'
404
407
 
@@ -49,6 +49,16 @@
49
49
  import { CallbackData } from "gramio";
50
50
  type FieldType = "string" | "number" | "string?" | "number?";
51
51
  type FieldDef = Readonly<Record<string, FieldType>>;
52
+ type RequiredFieldKeys<F extends FieldDef> = {
53
+ [K in keyof F]: F[K] extends "string" | "number" ? K : never;
54
+ }[keyof F];
55
+ type FieldBase<T extends FieldType> = T extends `string${string}` ? string : number;
56
+ type SchemaOf<F extends FieldDef> = {
57
+ [K in RequiredFieldKeys<F> & string]: FieldBase<F[K]>;
58
+ } & {
59
+ [K in Exclude<keyof F, RequiredFieldKeys<F>> & string]?: FieldBase<F[K]>;
60
+ };
61
+ type TypedCallbackData<F extends FieldDef> = CallbackData<SchemaOf<F>, SchemaOf<F>>;
52
62
  export type CallbackNamespace = {
53
63
  readonly prefix: string;
54
64
  /**
@@ -61,7 +71,7 @@ export type CallbackNamespace = {
61
71
  * HMR / dual-import). A second registration with different fields
62
72
  * panics — that's a programming error, not a runtime issue.
63
73
  */
64
- data: <F extends FieldDef>(name: string, fields: F) => CallbackData;
74
+ data: <F extends FieldDef>(name: string, fields: F) => TypedCallbackData<F>;
65
75
  };
66
76
  /**
67
77
  * Reserve a callback-data prefix for a plugin. Returns a namespace
@@ -1 +1 @@
1
- {"version":3,"file":"callbacks.d.ts","sourceRoot":"","sources":["../../src/bot/callbacks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,KAAK,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAE7D,KAAK,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAsCpD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;OASG;IACH,IAAI,EAAE,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,KAAK,YAAY,CAAC;CACpE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,KAAG,iBAgC3C,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO,aAAa,CAAC;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;CACjB,CAIG,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAO,IAEzC,CAAC"}
1
+ {"version":3,"file":"callbacks.d.ts","sourceRoot":"","sources":["../../src/bot/callbacks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,KAAK,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAE7D,KAAK,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAMpD,KAAK,iBAAiB,CAAC,CAAC,SAAS,QAAQ,IAAI;KAC3C,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,GAAG,CAAC,GAAG,KAAK;CAC5D,CAAC,MAAM,CAAC,CAAC,CAAC;AACX,KAAK,SAAS,CAAC,CAAC,SAAS,SAAS,IAAI,CAAC,SAAS,SAAS,MAAM,EAAE,GAC9D,MAAM,GACN,MAAM,CAAC;AACV,KAAK,QAAQ,CAAC,CAAC,SAAS,QAAQ,IAAI;KAClC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACrD,GAAG;KACF,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACxE,CAAC;AACF,KAAK,iBAAiB,CAAC,CAAC,SAAS,QAAQ,IAAI,YAAY,CACxD,QAAQ,CAAC,CAAC,CAAC,EACX,QAAQ,CAAC,CAAC,CAAC,CACX,CAAC;AAsCF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;OASG;IACH,IAAI,EAAE,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,KAAK,iBAAiB,CAAC,CAAC,CAAC,CAAC;CAC5E,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,KAAG,iBAsC3C,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAO,aAAa,CAAC;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;CACjB,CAIG,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAO,IAEzC,CAAC"}
@@ -101,10 +101,13 @@ export const callbackNs = (prefix) => {
101
101
  `with different fields ${JSON.stringify(existing.fields)}; ` +
102
102
  `re-registration tried ${JSON.stringify(fields)}.`);
103
103
  }
104
+ // fieldsEqual just proved the stored schema IS F's schema.
104
105
  return existing.cb;
105
106
  }
106
107
  const cb = buildCallbackData(fullName, fields);
107
108
  registry.set(fullName, { fullName, fields, cb });
109
+ // buildCallbackData accumulates exactly F's fields at runtime; the
110
+ // erased builder type is re-asserted here, once, at the boundary.
108
111
  return cb;
109
112
  },
110
113
  };
@@ -1 +1 @@
1
- {"version":3,"file":"callbacks.js","sourceRoot":"","sources":["../../src/bot/callbacks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"callbacks.js","sourceRoot":"","sources":["../../src/bot/callbacks.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ export * from "./kit.js";
20
20
  export * from "./language.js";
21
21
  export * from "./llm.js";
22
22
  export * from "./menu.js";
23
+ export * from "./notify.js";
23
24
  export * from "./payments/index.js";
24
25
  export * from "./storage.js";
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
package/dist/bot/index.js CHANGED
@@ -20,6 +20,7 @@ export * from "./kit.js";
20
20
  export * from "./language.js";
21
21
  export * from "./llm.js";
22
22
  export * from "./menu.js";
23
+ export * from "./notify.js";
23
24
  export * from "./payments/index.js";
24
25
  export * from "./storage.js";
25
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
package/dist/bot/kit.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Foundational helpers every bot wants.
2
+ * Foundational helpers for NODE bots — the library's deliberately
3
+ * Node-only corner (process signals, KEV env resolution). Worker bots
4
+ * use the worker-safe subpaths instead: `bot/session` (namespaced
5
+ * sessions) and `bot/notify` (admin DMs) hold the pieces that used to
6
+ * live here but never needed an OS.
3
7
  *
4
8
  * `gracefulStart(bot, opts?)` — wires SIGINT/SIGTERM to bot.stop(),
5
9
  * runs an optional shutdown hook, force-kills if it hangs. DMs the
@@ -12,15 +16,9 @@
12
16
  * every context with `ctx.adminId` (number) and `ctx.isAdmin`
13
17
  * (boolean). Throws at startup if neither source provides an id.
14
18
  *
15
- * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
16
- * `session()` that AUTOMATICALLY namespaces every key with the
17
- * calling bot's id (parsed from `ctx.bot.options.token`). When
18
- * several bots share one Redis instance, each writes to a disjoint
19
- * keyspace (`bot-<id>:<senderId>`) with no manual wiring. Every
20
- * plugin in this package (`accessControl`, `botMenu`,
21
- * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
22
- * own storage access, so the whole package stays isolated by
23
- * construction. Use this instead of `session()` — full stop.
19
+ * `botSession` / `prefixStorage` — re-exported from `bot/session` so
20
+ * Node consumers keep one import; the implementation is worker-safe
21
+ * and lives there.
24
22
  *
25
23
  * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
26
24
  *
@@ -39,10 +37,9 @@
39
37
  *
40
38
  * await gracefulStart(bot, { onShutdown: () => db.end() })
41
39
  */
42
- import { type SessionOptions, session } from "@gramio/session";
43
- import type { Storage } from "@gramio/storage";
44
40
  import type { Plugin as PluginType } from "gramio";
45
41
  import { type AnyBot } from "gramio";
42
+ export { botSession, prefixStorage } from "./session.js";
46
43
  export type GracefulStartOptions = {
47
44
  /** Runs after `bot.stop()` resolves, before `process.exit`. Close DBs, flush logs. */
48
45
  onShutdown?: () => Promise<void> | void;
@@ -88,52 +85,5 @@ export declare const adminContext: (opts?: AdminContextOptions) => PluginType<{}
88
85
  isAdmin: boolean;
89
86
  };
90
87
  }, {}>;
91
- /**
92
- * Drop-in replacement for `@gramio/session`'s `session()` that forces
93
- * the storage key to include the calling bot's id. Equivalent to
94
- * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
95
- * but baked in so no consumer can forget.
96
- *
97
- * Returns a plain `@gramio/session` plugin — same shape, same derives,
98
- * fully interchangeable in `.extend(userSession)` chains and plugin
99
- * options like `accessControl({ session: userSession, … })`.
100
- *
101
- * `getSessionKey` can still be overridden for advanced cases (e.g. a
102
- * per-chat session in groups); the override is wrapped so it still
103
- * receives the `bot-<id>:` prefix. Don't override unless you know what
104
- * you're doing — every plugin in this package expects the
105
- * `bot-<id>:<userId>` shape for cross-user storage reads.
106
- *
107
- * @example
108
- * import { redisStorage } from '@gramio/storage-redis'
109
- * import { botSession } from '@adriangalilea/utils/bot/kit'
110
- *
111
- * const storage = redisStorage() // shared across N bots, safe
112
- *
113
- * const userSession = botSession({
114
- * storage,
115
- * key: 'session',
116
- * initial: () => ({}),
117
- * })
118
- *
119
- * bot.extend(userSession)
120
- */
121
- export declare const botSession: <Data = unknown, Key extends string = "session", Lazy extends boolean = false>(opts: SessionOptions<Data, Key, Lazy>) => ReturnType<typeof session<Data, Key, Lazy>>;
122
- /**
123
- * Wraps any `@gramio/storage` adapter so every key gets a fixed
124
- * `${prefix}` prepended.
125
- *
126
- * **You almost never need this.** The library's plugins are isolated
127
- * by bot id automatically when you wire `botSession` (see above) —
128
- * `prefixStorage` exists for edge cases:
129
- *
130
- * - Sharing a Redis instance with a NON-bot system, and you want a
131
- * top-level prefix on top of the bot-id namespace.
132
- * - Migrating from an older deployment that used a custom prefix.
133
- *
134
- * In those cases the wrapper composes cleanly with `botSession`'s
135
- * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
136
- */
137
- export declare const prefixStorage: (storage: Storage, prefix: string) => Storage;
138
88
  export type { PluginType };
139
89
  //# sourceMappingURL=kit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,QAAQ,CAAC;AAM7C,MAAM,MAAM,oBAAoB,GAAG;IAClC,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC;IACtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,aAAa,GACzB,KAAK,MAAM,EACX,OAAM,oBAAyB,KAC7B,OAAO,CAAC,IAAI,CAiFd,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAM,mBAAwB;;;;;;;;MAqB1D,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,UAAU,GACtB,IAAI,GAAG,OAAO,EACd,GAAG,SAAS,MAAM,GAAG,SAAS,EAC9B,IAAI,SAAS,OAAO,GAAG,KAAK,EAE5B,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,KACnC,UAAU,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CA0B5C,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,EAAE,QAAQ,MAAM,KAAG,OAShE,CAAC;AAIF,YAAY,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,QAAQ,CAAC;AAI7C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,oBAAoB,GAAG;IAClC,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC;IACtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,aAAa,GACzB,KAAK,MAAM,EACX,OAAM,oBAAyB,KAC7B,OAAO,CAAC,IAAI,CAmEd,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAM,mBAAwB;;;;;;;;MAqB1D,CAAC;AAIF,YAAY,EAAE,UAAU,EAAE,CAAC"}
package/dist/bot/kit.js CHANGED
@@ -1,48 +1,7 @@
1
- /**
2
- * Foundational helpers every bot wants.
3
- *
4
- * `gracefulStart(bot, opts?)` — wires SIGINT/SIGTERM to bot.stop(),
5
- * runs an optional shutdown hook, force-kills if it hangs. DMs the
6
- * admin on start/stop by default (`@<bot> started.` /
7
- * `@<bot> shutting down.`) when `KEV.TELEGRAM_ADMIN_ID` is set —
8
- * pass `notifyAdmin: false` to disable.
9
- *
10
- * `adminContext({ adminId? })` — reads admin Telegram id from KEV
11
- * (`TELEGRAM_ADMIN_ID`) with optional hardcoded fallback. Decorates
12
- * every context with `ctx.adminId` (number) and `ctx.isAdmin`
13
- * (boolean). Throws at startup if neither source provides an id.
14
- *
15
- * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
16
- * `session()` that AUTOMATICALLY namespaces every key with the
17
- * calling bot's id (parsed from `ctx.bot.options.token`). When
18
- * several bots share one Redis instance, each writes to a disjoint
19
- * keyspace (`bot-<id>:<senderId>`) with no manual wiring. Every
20
- * plugin in this package (`accessControl`, `botMenu`,
21
- * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
22
- * own storage access, so the whole package stays isolated by
23
- * construction. Use this instead of `session()` — full stop.
24
- *
25
- * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
26
- *
27
- * @example
28
- * import { Bot } from 'gramio'
29
- * import { redisStorage } from '@gramio/storage-redis'
30
- * import { adminContext, gracefulStart, botSession } from '@adriangalilea/utils/bot/kit'
31
- *
32
- * const storage = redisStorage() // share across bots, safe
33
- * const userSession = botSession({ storage, initial: () => ({}) })
34
- *
35
- * const bot = new Bot(process.env.BOT_TOKEN!)
36
- * .extend(adminContext({ adminId: 190202471 })) // KEV wins, 190… is fallback
37
- * .extend(userSession)
38
- * .command('whoami', (ctx) => ctx.send(`admin? ${ctx.isAdmin}`))
39
- *
40
- * await gracefulStart(bot, { onShutdown: () => db.end() })
41
- */
42
- import { session } from "@gramio/session";
43
1
  import { Plugin } from "gramio";
44
2
  import { kev } from "../platform/kev.js";
45
- import { botId } from "./keys.js";
3
+ import { notifyAdmins } from "./notify.js";
4
+ export { botSession, prefixStorage } from "./session.js";
46
5
  export const gracefulStart = async (bot, opts = {}) => {
47
6
  const log = opts.log === false ? () => { } : (opts.log ?? ((m) => console.log(m)));
48
7
  const forceMs = opts.forceExitAfterMs ?? 10_000;
@@ -93,28 +52,8 @@ export const gracefulStart = async (bot, opts = {}) => {
93
52
  "Pass a number explicitly, set the env var, or remove notifyAdmin.");
94
53
  }
95
54
  if (adminId) {
96
- bot.onStart(async ({ info }) => {
97
- try {
98
- await bot.api.sendMessage({
99
- chat_id: adminId,
100
- text: `@${info.username} started.`,
101
- });
102
- }
103
- catch (e) {
104
- console.error("[bot] notifyAdmin (start) failed", e);
105
- }
106
- });
107
- bot.onStop(async ({ info }) => {
108
- try {
109
- await bot.api.sendMessage({
110
- chat_id: adminId,
111
- text: `@${info.username} shutting down.`,
112
- });
113
- }
114
- catch (e) {
115
- console.error("[bot] notifyAdmin (stop) failed", e);
116
- }
117
- });
55
+ bot.onStart(({ info }) => notifyAdmins(bot, [adminId], `@${info.username} started.`));
56
+ bot.onStop(({ info }) => notifyAdmins(bot, [adminId], `@${info.username} shutting down.`));
118
57
  }
119
58
  await bot.start();
120
59
  };
@@ -136,81 +75,4 @@ export const adminContext = (opts = {}) => {
136
75
  isAdmin: "senderId" in ctx && ctx.senderId === adminId,
137
76
  }));
138
77
  };
139
- // ─── botSession ────────────────────────────────────────────────────
140
- /**
141
- * Drop-in replacement for `@gramio/session`'s `session()` that forces
142
- * the storage key to include the calling bot's id. Equivalent to
143
- * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
144
- * but baked in so no consumer can forget.
145
- *
146
- * Returns a plain `@gramio/session` plugin — same shape, same derives,
147
- * fully interchangeable in `.extend(userSession)` chains and plugin
148
- * options like `accessControl({ session: userSession, … })`.
149
- *
150
- * `getSessionKey` can still be overridden for advanced cases (e.g. a
151
- * per-chat session in groups); the override is wrapped so it still
152
- * receives the `bot-<id>:` prefix. Don't override unless you know what
153
- * you're doing — every plugin in this package expects the
154
- * `bot-<id>:<userId>` shape for cross-user storage reads.
155
- *
156
- * @example
157
- * import { redisStorage } from '@gramio/storage-redis'
158
- * import { botSession } from '@adriangalilea/utils/bot/kit'
159
- *
160
- * const storage = redisStorage() // shared across N bots, safe
161
- *
162
- * const userSession = botSession({
163
- * storage,
164
- * key: 'session',
165
- * initial: () => ({}),
166
- * })
167
- *
168
- * bot.extend(userSession)
169
- */
170
- export const botSession = (opts) => {
171
- const userKeyer = opts.getSessionKey;
172
- return session({
173
- ...opts,
174
- // The session plugin types `getSessionKey` against `ContextType<BotLike, Events>`
175
- // — too narrow for our generic wrapper. We only need `bot.info.id` +
176
- // `senderId`, both of which gramio guarantees on every event the session
177
- // plugin processes (bot.info is populated by bot.start / bot.init before
178
- // any handler fires). Structural cast at the boundary keeps the public
179
- // API generic-friendly without forcing consumers to specify Event unions.
180
- getSessionKey: (async (ctxRaw) => {
181
- const ctx = ctxRaw;
182
- const prefix = `bot-${botId(ctx)}:`;
183
- if (userKeyer) {
184
- const inner = await userKeyer(ctx);
185
- return `${prefix}${inner}`;
186
- }
187
- return `${prefix}${ctx.senderId ?? ""}`;
188
- }),
189
- });
190
- };
191
- // ─── prefixStorage (escape hatch) ──────────────────────────────────
192
- /**
193
- * Wraps any `@gramio/storage` adapter so every key gets a fixed
194
- * `${prefix}` prepended.
195
- *
196
- * **You almost never need this.** The library's plugins are isolated
197
- * by bot id automatically when you wire `botSession` (see above) —
198
- * `prefixStorage` exists for edge cases:
199
- *
200
- * - Sharing a Redis instance with a NON-bot system, and you want a
201
- * top-level prefix on top of the bot-id namespace.
202
- * - Migrating from an older deployment that used a custom prefix.
203
- *
204
- * In those cases the wrapper composes cleanly with `botSession`'s
205
- * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
206
- */
207
- export const prefixStorage = (storage, prefix) => {
208
- const k = (key) => `${prefix}${String(key)}`;
209
- return {
210
- get: (key) => storage.get(k(key)),
211
- has: (key) => storage.has(k(key)),
212
- set: (key, value) => storage.set(k(key), value),
213
- delete: (key) => storage.delete(k(key)),
214
- };
215
- };
216
78
  //# sourceMappingURL=kit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Admin notifications — best-effort DMs to the bot's operator(s).
3
+ * Worker-safe: no env, no process; the caller passes the admin ids
4
+ * (from wherever its runtime keeps them: KEV on Node, D1/env on a
5
+ * Worker).
6
+ *
7
+ * Everything here is FIRE-AND-FORGET BY CONTRACT: a notification must
8
+ * never take the bot down, so failures are logged (console.error —
9
+ * visible in a Worker tail and a Node terminal alike) and swallowed.
10
+ *
11
+ * `notifyAdmins(bot, adminIds, text, extra?)` — DM each admin.
12
+ * `alertAdminError(bot, adminIds, label, error, throttle?)` — DM a
13
+ * truncated `🚨 label\nName: message`, rate-limited through a
14
+ * caller-owned throttle so a failure storm can't flood anyone.
15
+ * `alertThrottle(ms?)` — make that throttle (per-process on Node,
16
+ * per-isolate on a Worker; own one per subsystem if you want
17
+ * independent budgets).
18
+ *
19
+ * @example
20
+ * import { alertAdminError, alertThrottle, notifyAdmins } from '@adriangalilea/utils/bot/notify'
21
+ *
22
+ * const errorAlerts = alertThrottle() // 60s budget, module-level
23
+ * bot.onError(({ kind, error }) => void alertAdminError(bot, adminIds, `error [${kind}]`, error, errorAlerts))
24
+ * await notifyAdmins(bot, adminIds, `@${bot.info.username} deployed.`)
25
+ */
26
+ /** The one API surface a notification needs — satisfied by any gramio `Bot`. */
27
+ export type NotifyBot = {
28
+ api: {
29
+ sendMessage: (params: {
30
+ chat_id: number;
31
+ text: string;
32
+ }) => Promise<unknown>;
33
+ };
34
+ };
35
+ /**
36
+ * DM `text` to every admin id, best-effort: each failure is logged and
37
+ * swallowed (one unreachable admin must not block the rest, and a
38
+ * notification must never throw into the calling flow). `extra` merges
39
+ * into the sendMessage params (reply_markup, link_preview_options, …).
40
+ */
41
+ export declare function notifyAdmins(bot: NotifyBot, adminIds: readonly number[], text: string, extra?: Record<string, unknown>): Promise<void>;
42
+ /**
43
+ * Rate-limit state for {@link alertAdminError}, owned by the CALLER so
44
+ * scope is explicit (per-process on Node, per-isolate on a Worker) and
45
+ * independent subsystems can hold independent budgets.
46
+ */
47
+ export type AlertThrottle = {
48
+ ms: number;
49
+ lastAt: number;
50
+ };
51
+ /** A fresh throttle: at most one alert per `ms` (default 60s). */
52
+ export declare const alertThrottle: (ms?: number) => AlertThrottle;
53
+ /**
54
+ * DM the admins a truncated error (`🚨 label\nName: message`), gated by
55
+ * `throttle` so a failure storm sends one alert per window, not one per
56
+ * failure. No throttle = every call sends.
57
+ */
58
+ export declare function alertAdminError(bot: NotifyBot, adminIds: readonly number[], label: string, error: unknown, throttle?: AlertThrottle): Promise<void>;
59
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/bot/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG;IACvB,GAAG,EAAE;QACJ,WAAW,EAAE,CAAC,MAAM,EAAE;YACrB,OAAO,EAAE,MAAM,CAAC;YAChB,IAAI,EAAE,MAAM,CAAC;SACb,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KACvB,CAAC;CACF,CAAC;AAKF;;;;;GAKG;AACH,wBAAsB,YAAY,CACjC,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,kEAAkE;AAClE,eAAO,MAAM,aAAa,GAAI,WAAW,KAAG,aAG1C,CAAC;AAEH;;;;GAIG;AACH,wBAAsB,eAAe,CACpC,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Admin notifications — best-effort DMs to the bot's operator(s).
3
+ * Worker-safe: no env, no process; the caller passes the admin ids
4
+ * (from wherever its runtime keeps them: KEV on Node, D1/env on a
5
+ * Worker).
6
+ *
7
+ * Everything here is FIRE-AND-FORGET BY CONTRACT: a notification must
8
+ * never take the bot down, so failures are logged (console.error —
9
+ * visible in a Worker tail and a Node terminal alike) and swallowed.
10
+ *
11
+ * `notifyAdmins(bot, adminIds, text, extra?)` — DM each admin.
12
+ * `alertAdminError(bot, adminIds, label, error, throttle?)` — DM a
13
+ * truncated `🚨 label\nName: message`, rate-limited through a
14
+ * caller-owned throttle so a failure storm can't flood anyone.
15
+ * `alertThrottle(ms?)` — make that throttle (per-process on Node,
16
+ * per-isolate on a Worker; own one per subsystem if you want
17
+ * independent budgets).
18
+ *
19
+ * @example
20
+ * import { alertAdminError, alertThrottle, notifyAdmins } from '@adriangalilea/utils/bot/notify'
21
+ *
22
+ * const errorAlerts = alertThrottle() // 60s budget, module-level
23
+ * bot.onError(({ kind, error }) => void alertAdminError(bot, adminIds, `error [${kind}]`, error, errorAlerts))
24
+ * await notifyAdmins(bot, adminIds, `@${bot.info.username} deployed.`)
25
+ */
26
+ /** Telegram's hard per-message limit; longer texts are truncated, never split. */
27
+ const MAX_CHARS = 4096;
28
+ /**
29
+ * DM `text` to every admin id, best-effort: each failure is logged and
30
+ * swallowed (one unreachable admin must not block the rest, and a
31
+ * notification must never throw into the calling flow). `extra` merges
32
+ * into the sendMessage params (reply_markup, link_preview_options, …).
33
+ */
34
+ export async function notifyAdmins(bot, adminIds, text, extra) {
35
+ const body = text.slice(0, MAX_CHARS);
36
+ await Promise.all(adminIds
37
+ .filter((id) => id > 0)
38
+ .map((id) => bot.api
39
+ .sendMessage({ ...extra, chat_id: id, text: body })
40
+ .catch((e) => console.error(`[bot/notify] admin ${id} failed:`, e))));
41
+ }
42
+ /** A fresh throttle: at most one alert per `ms` (default 60s). */
43
+ export const alertThrottle = (ms = 60_000) => ({
44
+ ms,
45
+ lastAt: 0,
46
+ });
47
+ /**
48
+ * DM the admins a truncated error (`🚨 label\nName: message`), gated by
49
+ * `throttle` so a failure storm sends one alert per window, not one per
50
+ * failure. No throttle = every call sends.
51
+ */
52
+ export async function alertAdminError(bot, adminIds, label, error, throttle) {
53
+ if (throttle) {
54
+ const now = Date.now();
55
+ if (now - throttle.lastAt < throttle.ms)
56
+ return;
57
+ throttle.lastAt = now;
58
+ }
59
+ const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
60
+ await notifyAdmins(bot, adminIds, `🚨 ${label}\n${message}`);
61
+ }
62
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/bot/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAYH,kFAAkF;AAClF,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAc,EACd,QAA2B,EAC3B,IAAY,EACZ,KAA+B;IAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,OAAO,CAAC,GAAG,CAChB,QAAQ;SACN,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;SACtB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CACX,GAAG,CAAC,GAAG;SACL,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAClD,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CACpE,CACF,CAAC;AACH,CAAC;AASD,kEAAkE;AAClE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAE,GAAG,MAAM,EAAiB,EAAE,CAAC,CAAC;IAC7D,EAAE;IACF,MAAM,EAAE,CAAC;CACT,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,GAAc,EACd,QAA2B,EAC3B,KAAa,EACb,KAAc,EACd,QAAwB;IAExB,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE;YAAE,OAAO;QAChD,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC;IACvB,CAAC;IACD,MAAM,OAAO,GACZ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5E,MAAM,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC;AAC9D,CAAC"}
@@ -29,13 +29,25 @@ import type { BotCallbackCtx } from "../ctx.js";
29
29
  import type { PaymentsStores } from "./stores.js";
30
30
  import type { BotPaymentsConfig, PaymentsSession } from "./types.js";
31
31
  /** User tap on a charge entry — opens confirmation overlay then admin DM. */
32
- export declare const refundRequestCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
32
+ export declare const refundRequestCb: import("gramio").CallbackData<{
33
+ cid: string;
34
+ } & {}, {
35
+ cid: string;
36
+ } & {}>;
33
37
  /** Admin tap on the DM notification — approve. */
34
- export declare const refundApproveCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
38
+ export declare const refundApproveCb: import("gramio").CallbackData<{
39
+ cid: string;
40
+ } & {}, {
41
+ cid: string;
42
+ } & {}>;
35
43
  /** Admin tap on the DM notification — deny. */
36
- export declare const refundDenyCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
44
+ export declare const refundDenyCb: import("gramio").CallbackData<{
45
+ cid: string;
46
+ } & {}, {
47
+ cid: string;
48
+ } & {}>;
37
49
  /** Admin tap on the DM notification — close (no action). */
38
- export declare const refundCloseCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
50
+ export declare const refundCloseCb: import("gramio").CallbackData<{} & {}, {} & {}>;
39
51
  /** Strict bot.api surface this module exercises. */
40
52
  type RefundBotApi = {
41
53
  refundStarPayment: (params: {
@@ -1 +1 @@
1
- {"version":3,"file":"refund.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/refund.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACX,iBAAiB,EAEjB,eAAe,EACf,MAAM,YAAY,CAAC;AASpB,6EAA6E;AAC7E,eAAO,MAAM,eAAe,2EAA+C,CAAC;AAC5E,kDAAkD;AAClD,eAAO,MAAM,eAAe,2EAA+C,CAAC;AAC5E,+CAA+C;AAC/C,eAAO,MAAM,YAAY,2EAA4C,CAAC;AACtE,4DAA4D;AAC5D,eAAO,MAAM,aAAa,2EAA8B,CAAC;AAIzD,oDAAoD;AACpD,KAAK,YAAY,GAAG;IACnB,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,0BAA0B,EAAE,MAAM,CAAC;KACnC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,wBAAwB,EAAE,CAAC,MAAM,EAAE;QAClC,OAAO,EAAE,MAAM,CAAC;QAChB,0BAA0B,EAAE,MAAM,CAAC;QACnC,WAAW,EAAE,OAAO,CAAC;KACrB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,WAAW,EAAE,CAAC,MAAM,EAAE;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACvB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB,CAAC;AAEF,KAAK,WAAW,GAAG;IAAE,GAAG,CAAC,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;GAKG;AACH,KAAK,SAAS,GAAG,cAAc,CAAC,WAAW,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,YAAY,CAAC,GAAG;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CACjB,CAAC;AAqIF,MAAM,MAAM,qBAAqB,GAAG;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,qBAAqB,MAE3B,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAC7C,OAAO,CAAC,MAAM,GAAG,SAAS,CA8D5B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,qBAAqB,MACrB,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAiGpE,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,qBAAqB,MACrB,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAyCpE,CAAC"}
1
+ {"version":3,"file":"refund.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/refund.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACX,iBAAiB,EAEjB,eAAe,EACf,MAAM,YAAY,CAAC;AASpB,6EAA6E;AAC7E,eAAO,MAAM,eAAe;;;;OAA+C,CAAC;AAC5E,kDAAkD;AAClD,eAAO,MAAM,eAAe;;;;OAA+C,CAAC;AAC5E,+CAA+C;AAC/C,eAAO,MAAM,YAAY;;;;OAA4C,CAAC;AACtE,4DAA4D;AAC5D,eAAO,MAAM,aAAa,iDAA8B,CAAC;AAIzD,oDAAoD;AACpD,KAAK,YAAY,GAAG;IACnB,iBAAiB,EAAE,CAAC,MAAM,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,0BAA0B,EAAE,MAAM,CAAC;KACnC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,wBAAwB,EAAE,CAAC,MAAM,EAAE;QAClC,OAAO,EAAE,MAAM,CAAC;QAChB,0BAA0B,EAAE,MAAM,CAAC;QACnC,WAAW,EAAE,OAAO,CAAC;KACrB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,WAAW,EAAE,CAAC,MAAM,EAAE;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACvB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB,CAAC;AAEF,KAAK,WAAW,GAAG;IAAE,GAAG,CAAC,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;GAKG;AACH,KAAK,SAAS,GAAG,cAAc,CAAC,WAAW,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,YAAY,CAAC,GAAG;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CACjB,CAAC;AAqIF,MAAM,MAAM,qBAAqB,GAAG;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,qBAAqB,MAE3B,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAC7C,OAAO,CAAC,MAAM,GAAG,SAAS,CA8D5B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,MAAM,qBAAqB,MACrB,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAiGpE,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,qBAAqB,MACrB,KAAK,SAAS,GAAG;IAAE,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAG,OAAO,CAAC,IAAI,CAyCpE,CAAC"}
@@ -29,9 +29,13 @@ import { type BotPaymentsConfig, type PaymentsSession, type WaiverRecord } from
29
29
  * `pk` (productKey) follows the shape validated by `payload.ts` —
30
30
  * `vip.N` / `credits.N` / `perks.X` — all ASCII-safe.
31
31
  */
32
- export declare const waiverConsentCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
32
+ export declare const waiverConsentCb: import("gramio").CallbackData<{
33
+ pk: string;
34
+ } & {}, {
35
+ pk: string;
36
+ } & {}>;
33
37
  /** Fired when the user taps "❌ Cancelar" in the consent prompt. */
34
- export declare const waiverCancelCb: import("gramio").CallbackData<Record<never, never>, Record<never, never>>;
38
+ export declare const waiverCancelCb: import("gramio").CallbackData<{} & {}, {} & {}>;
35
39
  /**
36
40
  * Returns true when the stored consent matches the current waiver
37
41
  * version — i.e. no re-consent needed before a purchase.
@@ -1 +1 @@
1
- {"version":3,"file":"waiver.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/waiver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAMxC,OAAO,EACN,KAAK,iBAAiB,EAGtB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,MAAM,YAAY,CAAC;AAMpB;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,2EAA8C,CAAC;AAE3E,mEAAmE;AACnE,eAAO,MAAM,cAAc,2EAA+B,CAAC;AAI3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GACzB,QAAQ,YAAY,GAAG,SAAS,EAChC,gBAAgB,MAAM,KACpB,OAGF,CAAC;AAiBF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAC1B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,YAAY,MAAM,EAClB,MAAM,MAAM,KACV,cA8BF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC7B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,MAAM,MAAM,KACV,MAWU,CAAC;AAId;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAClC,KAAK;IAAE,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,eAAe,CAAA;KAAE,CAAA;CAAE,EAC3C,SAAS,MAAM,EACf,QAAQ,MAAM,KACZ,YAUF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,OAAO,EAChB,KAAK;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,EACrB,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,QAAQ,MAAM,KACZ,OAAO,CAAC,YAAY,CAatB,CAAC"}
1
+ {"version":3,"file":"waiver.d.ts","sourceRoot":"","sources":["../../../src/bot/payments/waiver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAMxC,OAAO,EACN,KAAK,iBAAiB,EAGtB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,MAAM,YAAY,CAAC;AAMpB;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe;;;;OAA8C,CAAC;AAE3E,mEAAmE;AACnE,eAAO,MAAM,cAAc,iDAA+B,CAAC;AAI3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GACzB,QAAQ,YAAY,GAAG,SAAS,EAChC,gBAAgB,MAAM,KACpB,OAGF,CAAC;AAiBF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAC1B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,YAAY,MAAM,EAClB,MAAM,MAAM,KACV,cA8BF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC7B,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAC9B,MAAM,MAAM,KACV,MAWU,CAAC;AAId;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAClC,KAAK;IAAE,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,eAAe,CAAA;KAAE,CAAA;CAAE,EAC3C,SAAS,MAAM,EACf,QAAQ,MAAM,KACZ,YAUF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,OAAO,EAChB,KAAK;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,EACrB,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,QAAQ,MAAM,KACZ,OAAO,CAAC,YAAY,CAatB,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bot-namespaced session storage — worker-safe (no env, no process, no
3
+ * signals; runs identically on Node and Cloudflare Workers).
4
+ *
5
+ * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
6
+ * `session()` that AUTOMATICALLY namespaces every key with the
7
+ * calling bot's id (parsed from `ctx.bot.options.token`). When
8
+ * several bots share one storage instance, each writes to a
9
+ * disjoint keyspace (`bot-<id>:<senderId>`) with no manual wiring.
10
+ * Every plugin in this package (`accessControl`, `botMenu`,
11
+ * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
12
+ * own storage access, so the whole package stays isolated by
13
+ * construction. Use this instead of `session()` — full stop.
14
+ *
15
+ * `prefixStorage(storage, prefix)` — escape hatch that prepends a
16
+ * fixed prefix to every key of any `@gramio/storage` adapter.
17
+ *
18
+ * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
19
+ *
20
+ * @example
21
+ * import { redisStorage } from '@gramio/storage-redis'
22
+ * import { botSession } from '@adriangalilea/utils/bot/session'
23
+ *
24
+ * const storage = redisStorage() // share across bots, safe
25
+ * const userSession = botSession({ storage, initial: () => ({}) })
26
+ * bot.extend(userSession)
27
+ */
28
+ import { type SessionOptions, session } from "@gramio/session";
29
+ import type { Storage } from "@gramio/storage";
30
+ /**
31
+ * Drop-in replacement for `@gramio/session`'s `session()` that forces
32
+ * the storage key to include the calling bot's id. Equivalent to
33
+ * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
34
+ * but baked in so no consumer can forget.
35
+ *
36
+ * Returns a plain `@gramio/session` plugin — same shape, same derives,
37
+ * fully interchangeable in `.extend(userSession)` chains and plugin
38
+ * options like `accessControl({ session: userSession, … })`.
39
+ *
40
+ * `getSessionKey` can still be overridden for advanced cases (e.g. a
41
+ * per-chat session in groups); the override is wrapped so it still
42
+ * receives the `bot-<id>:` prefix. Don't override unless you know what
43
+ * you're doing — every plugin in this package expects the
44
+ * `bot-<id>:<userId>` shape for cross-user storage reads.
45
+ */
46
+ export declare const botSession: <Data = unknown, Key extends string = "session", Lazy extends boolean = false>(opts: SessionOptions<Data, Key, Lazy>) => ReturnType<typeof session<Data, Key, Lazy>>;
47
+ /**
48
+ * Wraps any `@gramio/storage` adapter so every key gets a fixed
49
+ * `${prefix}` prepended.
50
+ *
51
+ * **You almost never need this.** The library's plugins are isolated
52
+ * by bot id automatically when you wire `botSession` (see above) —
53
+ * `prefixStorage` exists for edge cases:
54
+ *
55
+ * - Sharing a Redis instance with a NON-bot system, and you want a
56
+ * top-level prefix on top of the bot-id namespace.
57
+ * - Migrating from an older deployment that used a custom prefix.
58
+ *
59
+ * In those cases the wrapper composes cleanly with `botSession`'s
60
+ * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
61
+ */
62
+ export declare const prefixStorage: (storage: Storage, prefix: string) => Storage;
63
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/bot/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,GACtB,IAAI,GAAG,OAAO,EACd,GAAG,SAAS,MAAM,GAAG,SAAS,EAC9B,IAAI,SAAS,OAAO,GAAG,KAAK,EAE5B,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,KACnC,UAAU,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CA0B5C,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,EAAE,QAAQ,MAAM,KAAG,OAShE,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Bot-namespaced session storage — worker-safe (no env, no process, no
3
+ * signals; runs identically on Node and Cloudflare Workers).
4
+ *
5
+ * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
6
+ * `session()` that AUTOMATICALLY namespaces every key with the
7
+ * calling bot's id (parsed from `ctx.bot.options.token`). When
8
+ * several bots share one storage instance, each writes to a
9
+ * disjoint keyspace (`bot-<id>:<senderId>`) with no manual wiring.
10
+ * Every plugin in this package (`accessControl`, `botMenu`,
11
+ * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
12
+ * own storage access, so the whole package stays isolated by
13
+ * construction. Use this instead of `session()` — full stop.
14
+ *
15
+ * `prefixStorage(storage, prefix)` — escape hatch that prepends a
16
+ * fixed prefix to every key of any `@gramio/storage` adapter.
17
+ *
18
+ * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
19
+ *
20
+ * @example
21
+ * import { redisStorage } from '@gramio/storage-redis'
22
+ * import { botSession } from '@adriangalilea/utils/bot/session'
23
+ *
24
+ * const storage = redisStorage() // share across bots, safe
25
+ * const userSession = botSession({ storage, initial: () => ({}) })
26
+ * bot.extend(userSession)
27
+ */
28
+ import { session } from "@gramio/session";
29
+ import { botId } from "./keys.js";
30
+ // ─── botSession ────────────────────────────────────────────────────
31
+ /**
32
+ * Drop-in replacement for `@gramio/session`'s `session()` that forces
33
+ * the storage key to include the calling bot's id. Equivalent to
34
+ * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
35
+ * but baked in so no consumer can forget.
36
+ *
37
+ * Returns a plain `@gramio/session` plugin — same shape, same derives,
38
+ * fully interchangeable in `.extend(userSession)` chains and plugin
39
+ * options like `accessControl({ session: userSession, … })`.
40
+ *
41
+ * `getSessionKey` can still be overridden for advanced cases (e.g. a
42
+ * per-chat session in groups); the override is wrapped so it still
43
+ * receives the `bot-<id>:` prefix. Don't override unless you know what
44
+ * you're doing — every plugin in this package expects the
45
+ * `bot-<id>:<userId>` shape for cross-user storage reads.
46
+ */
47
+ export const botSession = (opts) => {
48
+ const userKeyer = opts.getSessionKey;
49
+ return session({
50
+ ...opts,
51
+ // The session plugin types `getSessionKey` against `ContextType<BotLike, Events>`
52
+ // — too narrow for our generic wrapper. We only need `bot.info.id` +
53
+ // `senderId`, both of which gramio guarantees on every event the session
54
+ // plugin processes (bot.info is populated by bot.start / bot.init before
55
+ // any handler fires). Structural cast at the boundary keeps the public
56
+ // API generic-friendly without forcing consumers to specify Event unions.
57
+ getSessionKey: (async (ctxRaw) => {
58
+ const ctx = ctxRaw;
59
+ const prefix = `bot-${botId(ctx)}:`;
60
+ if (userKeyer) {
61
+ const inner = await userKeyer(ctx);
62
+ return `${prefix}${inner}`;
63
+ }
64
+ return `${prefix}${ctx.senderId ?? ""}`;
65
+ }),
66
+ });
67
+ };
68
+ // ─── prefixStorage (escape hatch) ──────────────────────────────────
69
+ /**
70
+ * Wraps any `@gramio/storage` adapter so every key gets a fixed
71
+ * `${prefix}` prepended.
72
+ *
73
+ * **You almost never need this.** The library's plugins are isolated
74
+ * by bot id automatically when you wire `botSession` (see above) —
75
+ * `prefixStorage` exists for edge cases:
76
+ *
77
+ * - Sharing a Redis instance with a NON-bot system, and you want a
78
+ * top-level prefix on top of the bot-id namespace.
79
+ * - Migrating from an older deployment that used a custom prefix.
80
+ *
81
+ * In those cases the wrapper composes cleanly with `botSession`'s
82
+ * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
83
+ */
84
+ export const prefixStorage = (storage, prefix) => {
85
+ const k = (key) => `${prefix}${String(key)}`;
86
+ return {
87
+ get: (key) => storage.get(k(key)),
88
+ has: (key) => storage.has(k(key)),
89
+ set: (key, value) => storage.set(k(key), value),
90
+ delete: (key) => storage.delete(k(key)),
91
+ };
92
+ };
93
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/bot/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAuB,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAElC,sEAAsE;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAKzB,IAAqC,EACS,EAAE;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC;IAKrC,OAAO,OAAO,CAAkB;QAC/B,GAAG,IAAI;QACP,kFAAkF;QAClF,qEAAqE;QACrE,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,0EAA0E;QAC1E,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YAChC,MAAM,GAAG,GAAG,MAAgB,CAAC;YAC7B,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YACpC,IAAI,SAAS,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,MACb,SACA,CAAC,GAAG,CAAC,CAAC;gBACP,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC;YAC5B,CAAC;YACD,OAAO,GAAG,MAAM,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACzC,CAAC,CAAqD;KACtD,CAAgD,CAAC;AACnD,CAAC,CAAC;AAEF,sEAAsE;AAEtE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAgB,EAAE,MAAc,EAAW,EAAE;IAC1E,MAAM,CAAC,GAAG,CAAC,GAA6B,EAAU,EAAE,CACnD,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3B,OAAO;QACN,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;QAC/C,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;KACvC,CAAC;AACH,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adriangalilea/utils",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "TypeScript utilities - logger, currency, formatter, GramIO bot plugins, and more",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -87,6 +87,14 @@
87
87
  "./bot/callbacks": {
88
88
  "types": "./dist/bot/callbacks.d.ts",
89
89
  "default": "./dist/bot/callbacks.js"
90
+ },
91
+ "./bot/session": {
92
+ "types": "./dist/bot/session.d.ts",
93
+ "default": "./dist/bot/session.js"
94
+ },
95
+ "./bot/notify": {
96
+ "types": "./dist/bot/notify.d.ts",
97
+ "default": "./dist/bot/notify.js"
90
98
  }
91
99
  },
92
100
  "files": [