@adriangalilea/utils 1.8.0 → 1.10.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
@@ -443,6 +443,7 @@ Then `pnpm install`. Every `ctx.send` / `ctx.sendDocument` / `ctx.reply` / etc.
443
443
  | `@adriangalilea/utils/bot/profile` | `syncBotProfile(bot, { name?, description?, about?, photo?, commands?, expects?, adminIds? })` — the bot's Telegram-facing identity as CODE, never BotFather: localized name / description / About / public command list, reconciled idempotently on every boot (get → compare → set per field per language; unchanged values cost one read, so cold-start firing is rate-limit-free). `expects: { inline: true }` declares the BotFather-only capabilities the code assumes — a mismatch (inline mode off on an inline-dependent bot) DMs the admins, since the API can only detect it, not fix it. Never throws; failures log and the bot keeps running. |
444
444
  | `@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. |
445
445
  | `@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. |
446
+ | `@adriangalilea/utils/bot/groups` | Group-chat identity as plain functions: `isGroupChat(ctx)` / `isPrivateChat(ctx)` + `isGroupAdmin(ctx, { chatId?, userId? }?)` — the `getChatMember` creator/administrator check behind every admin-gated group setting (auto-summary toggles, "dismiss" buttons on group welcomes), chat/user defaulted from the ctx. Takes real gramio ctxs and `MenuCtx` with no cast; a miswired ctx panics, an API rejection fails closed (`false`). Composition over policy: bot-owner override stays at your call site (`ctx.isAdmin \|\| await isGroupAdmin(ctx)`). |
446
447
  | `@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. |
447
448
  | `@adriangalilea/utils/bot/llm` | The full LLM-chatbot pipeline in one module. **Input:** `streamChat(response)` parses OpenAI-compatible SSE (OpenAI, vllm, mlx-lm, llama.cpp, Together, Groq, …) into a typed `AsyncGenerator<{type: 'content' \| 'reasoning', text}>`. **Output:** `ctx.startStream()` (low-level: debounced markdown to Telegram, 4000-char split, exposes `wasPartial` after `.end()`). `ctx.startChatStream(response)` (high-level: consumes the stream, renders reasoning as a Telegram `expandable_blockquote` entity + content as streamed markdown — both go through `markdownToFormattable` with graceful degradation — returns `{ content, reasoning }`). **History:** `llmHistory({...})` returns `.plugin` (decorates `ctx.llm` with `.add() / .get() / .clear() / .all() / .clearAll()`, per-(user, thread) OpenAI `ChatMessage` shape, persisted in the shared session record so the menu's 🗑 Forget wipes it automatically) AND `.menuItem` (drop-in "🗑 Delete this thread" for `botMenu` — wipes the LLM history AND calls `deleteForumTopic` so the Telegram thread + all its messages disappear from the chat; falls back to Redis-only clear when no `threadId` is present). |
448
449
  | `@adriangalilea/utils/bot/menu` | `botMenu({ command, description, items, privacy?, personalData?, adminContact })` — `/settings` command + InlineKeyboard router. Root view always renders a `🛡️ Privacy & data` submenu button that wraps the privacy policy link plus (if `personalData: { storage }`) 🗑 Forget + 📥 Export buttons. Items take `keepRow` (render on the same row as the next item — e.g. a two-per-row language picker) and `rootExtra` (render at the bottom of the root menu, below Privacy & data). `label` / `header` / `style` / `visible` resolvers may be **async** (read your db at render time — never cache render strings in the session); `parseMode: 'HTML'` renders the header formatted (you own escaping); `personalData.onForget(ctx, userId)` wipes YOUR tables inside the same try as the session delete, so Forget either forgets everything or reports failure. `toggleMenuItem({ id, read, write, label: { off, on }, toast? })` — convenience factory for boolean-toggle items with dynamic label + optional toast, storage-agnostic via `read`/`write` closures. |
@@ -19,6 +19,18 @@ export type FlagSpec<K extends FlagKind = FlagKind> = {
19
19
  help?: string;
20
20
  /** Code default — a scalar, or a tier map for per-tier values. */
21
21
  default: KindValue[K] | TierMap<KindValue[K]>;
22
+ /** Number flags: inclusive write-time bounds. Many writers touch a flag (bot admin
23
+ * taps, web consoles, fat fingers) — bounds make an insane value unwritable at the
24
+ * shared rule ({@link flagValueError}) instead of trusting every panel separately. */
25
+ min?: number;
26
+ max?: number;
27
+ /**
28
+ * String flags: the closed set of allowed values — the flag becomes an ENUM. Panels
29
+ * render a picker instead of free text, and in-bot admin menus can ROTATE through the
30
+ * values on tap (off → admins → all → off …). The audience-gating convention lives in
31
+ * {@link audienceAllows}.
32
+ */
33
+ choices?: readonly string[];
22
34
  };
23
35
  /** What `describe()` returns per flag — JSON-safe, for panels/consoles. */
24
36
  export type FlagDescriptor = {
@@ -29,6 +41,9 @@ export type FlagDescriptor = {
29
41
  default: unknown;
30
42
  /** Whether the DECLARED default is per-tier (panels offer tier inputs). */
31
43
  tiered: boolean;
44
+ min?: number;
45
+ max?: number;
46
+ choices?: readonly string[];
32
47
  };
33
48
  export type FlagsBackend = {
34
49
  /** Read the bot's operator-config record (plain JSON object). */
@@ -66,12 +81,34 @@ export type Flags<Spec extends Record<string, FlagSpec>> = {
66
81
  * external panels can render a tier map as per-tier inputs. */
67
82
  export declare const isTierMap: (v: unknown) => v is TierMap<unknown>;
68
83
  /**
69
- * A scalar of the right kind, or a tier map whose every value is. THE write
70
- * rule — `flags.set` enforces it, and it's exported so any external writer
71
- * (a web console patching the same config record directly) validates with
72
- * the SAME function instead of restating it.
84
+ * A scalar of the right kind, or a tier map whose every value is. THE shape half
85
+ * of the write rule; see {@link flagValueError} for the full rule with constraints.
73
86
  */
74
87
  export declare const flagValueOk: (kind: FlagKind, v: unknown) => boolean;
88
+ /** The constraint surface {@link flagValueError} checks — a FlagDescriptor satisfies it. */
89
+ export type FlagConstraints = {
90
+ kind: FlagKind;
91
+ min?: number;
92
+ max?: number;
93
+ choices?: readonly string[];
94
+ };
95
+ /**
96
+ * THE write rule, complete: shape (kind, tier maps) plus constraints (bounds, choices).
97
+ * Returns null when the value is writable, else a human-readable reason. `flags.set`
98
+ * enforces it, and it's exported so any external writer (a web console patching the
99
+ * same config record directly, an in-bot input prompt) validates with the SAME function
100
+ * instead of restating it. Constraints apply at WRITE time only — reads keep shape-checking
101
+ * so tightening a bound later never bricks an already-stored value.
102
+ */
103
+ export declare const flagValueError: (spec: FlagConstraints, v: unknown) => string | null;
104
+ /**
105
+ * The audience-gating convention for a `choices: ["off", "admins", "all"]` flag:
106
+ * a staged rollout an in-bot menu rotates through on tap. `off` gates everyone,
107
+ * `admins` opens it to operators/testers, `all` ships it.
108
+ */
109
+ export declare const audienceAllows: (value: string, opts: {
110
+ admin: boolean;
111
+ }) => boolean;
75
112
  export declare function defineFlags<Spec extends Record<string, FlagSpec>>(spec: Spec, backend: FlagsBackend): Flags<Spec>;
76
113
  export {};
77
114
  //# sourceMappingURL=flags.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/bot/flags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/bot/flags.ts"],"names":[],"mappings":"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"}
package/dist/bot/flags.js CHANGED
@@ -88,23 +88,62 @@ const resolveTiered = (value, tier) => {
88
88
  };
89
89
  const kindOk = (kind, v) => kind === "bool" ? typeof v === "boolean" : typeof v === kind;
90
90
  /**
91
- * A scalar of the right kind, or a tier map whose every value is. THE write
92
- * rule — `flags.set` enforces it, and it's exported so any external writer
93
- * (a web console patching the same config record directly) validates with
94
- * the SAME function instead of restating it.
91
+ * A scalar of the right kind, or a tier map whose every value is. THE shape half
92
+ * of the write rule; see {@link flagValueError} for the full rule with constraints.
95
93
  */
96
94
  export const flagValueOk = (kind, v) => {
97
95
  if (isTierMap(v))
98
96
  return Object.values(v).every((t) => kindOk(kind, t));
99
97
  return kindOk(kind, v);
100
98
  };
99
+ /**
100
+ * THE write rule, complete: shape (kind, tier maps) plus constraints (bounds, choices).
101
+ * Returns null when the value is writable, else a human-readable reason. `flags.set`
102
+ * enforces it, and it's exported so any external writer (a web console patching the
103
+ * same config record directly, an in-bot input prompt) validates with the SAME function
104
+ * instead of restating it. Constraints apply at WRITE time only — reads keep shape-checking
105
+ * so tightening a bound later never bricks an already-stored value.
106
+ */
107
+ export const flagValueError = (spec, v) => {
108
+ if (!flagValueOk(spec.kind, v))
109
+ return `not a ${spec.kind} (or a tier map of them)`;
110
+ const scalars = isTierMap(v) ? Object.values(v) : [v];
111
+ for (const s of scalars) {
112
+ if (spec.kind === "number") {
113
+ const n = s;
114
+ if (!Number.isFinite(n))
115
+ return "not a finite number";
116
+ if (spec.min !== undefined && n < spec.min)
117
+ return `below the minimum (${spec.min})`;
118
+ if (spec.max !== undefined && n > spec.max)
119
+ return `above the maximum (${spec.max})`;
120
+ }
121
+ if (spec.choices && !spec.choices.includes(s)) {
122
+ return `must be one of: ${spec.choices.join(" · ")}`;
123
+ }
124
+ }
125
+ return null;
126
+ };
127
+ /**
128
+ * The audience-gating convention for a `choices: ["off", "admins", "all"]` flag:
129
+ * a staged rollout an in-bot menu rotates through on tap. `off` gates everyone,
130
+ * `admins` opens it to operators/testers, `all` ships it.
131
+ */
132
+ export const audienceAllows = (value, opts) => {
133
+ if (value === "all")
134
+ return true;
135
+ if (value === "admins")
136
+ return opts.admin;
137
+ return false;
138
+ };
101
139
  // ─── the factory ─────────────────────────────────────────────────────
102
140
  export function defineFlags(spec, backend) {
103
141
  for (const [key, s] of Object.entries(spec)) {
104
142
  if (RESERVED.has(key))
105
143
  panic(`flags: "${key}" is a reserved name`);
106
- if (!flagValueOk(s.kind, s.default))
107
- panic(`flags: "${key}" default doesn't match kind "${s.kind}":`, s.default);
144
+ const reason = flagValueError(s, s.default);
145
+ if (reason !== null)
146
+ panic(`flags: "${key}" default rejected: ${reason}`, s.default);
108
147
  }
109
148
  // One config read per update: multiple flag reads on the same ctx share
110
149
  // the same in-flight promise (keyed on ctx object identity).
@@ -140,6 +179,9 @@ export function defineFlags(spec, backend) {
140
179
  ...(s.help !== undefined ? { help: s.help } : {}),
141
180
  default: s.default,
142
181
  tiered: isTierMap(s.default),
182
+ ...(s.min !== undefined ? { min: s.min } : {}),
183
+ ...(s.max !== undefined ? { max: s.max } : {}),
184
+ ...(s.choices !== undefined ? { choices: s.choices } : {}),
143
185
  })),
144
186
  overrides: async (ctx) => {
145
187
  const stored = await configFor(ctx);
@@ -156,11 +198,12 @@ export function defineFlags(spec, backend) {
156
198
  const s = spec[key] ?? panic(`flags: unknown flag "${key}"`);
157
199
  if (!backend.write)
158
200
  panic("flags: set() needs a write backend");
159
- if (value !== null && !flagValueOk(s.kind, value))
201
+ const reason = value === null ? null : flagValueError(s, value);
202
+ if (reason !== null)
160
203
  throw new SourcedError({
161
204
  source: "flags",
162
205
  operation: "set",
163
- message: `value for "${key}" doesn't match kind "${s.kind}"`,
206
+ message: `value for "${key}" rejected: ${reason}`,
164
207
  context: { key, value },
165
208
  });
166
209
  await backend.write(ctx, { [key]: value });
@@ -1 +1 @@
1
- {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/bot/flags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/bot/flags.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /** Minimum ctx shape the chat-type predicates read. */
2
+ export type ChatTypeCtx = {
3
+ chat?: {
4
+ type?: string;
5
+ };
6
+ };
7
+ /** Minimum ctx shape `isGroupAdmin` reads. `bot` is the running gramio Bot — typed
8
+ * `unknown` so MenuCtx and every real ctx assign without a cast; its
9
+ * `api.getChatMember` is asserted at call time. */
10
+ export type GroupAdminCtx = {
11
+ bot: unknown;
12
+ chat?: {
13
+ id?: number;
14
+ };
15
+ from?: {
16
+ id?: number;
17
+ };
18
+ };
19
+ /** True for a group or supergroup chat. */
20
+ export declare const isGroupChat: (ctx: ChatTypeCtx) => boolean;
21
+ /** True for a 1:1 private chat. */
22
+ export declare const isPrivateChat: (ctx: ChatTypeCtx) => boolean;
23
+ /**
24
+ * Whether a user is a creator/administrator of a group, per `getChatMember`. Chat and
25
+ * user default to the ctx's own (`ctx.chat.id`, `ctx.from.id` — gramio's own
26
+ * parameter-defaulting idiom); pass `chatId` / `userId` for cross-chat checks. Missing
27
+ * ids (an actor-less service event) or an API rejection answer `false`.
28
+ */
29
+ export declare function isGroupAdmin(ctx: GroupAdminCtx, opts?: {
30
+ chatId?: number;
31
+ userId?: number;
32
+ }): Promise<boolean>;
33
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/bot/groups.ts"],"names":[],"mappings":"AAqCA,uDAAuD;AACvD,MAAM,MAAM,WAAW,GAAG;IACzB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACzB,CAAC;AAEF;;oDAEoD;AACpD,MAAM,MAAM,aAAa,GAAG;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvB,CAAC;AAcF,2CAA2C;AAC3C,eAAO,MAAM,WAAW,GAAI,KAAK,WAAW,KAAG,OACe,CAAC;AAE/D,mCAAmC;AACnC,eAAO,MAAM,aAAa,GAAI,KAAK,WAAW,KAAG,OACpB,CAAC;AAE9B;;;;;GAKG;AACH,wBAAsB,YAAY,CACjC,GAAG,EAAE,aAAa,EAClB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7C,OAAO,CAAC,OAAO,CAAC,CAkBlB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Group-chat identity — chat-type predicates and the group-admin check. Worker-safe
3
+ * (no env, no OS).
4
+ *
5
+ * Telegram's Bot API has no "is this user a group admin?" primitive and neither does
6
+ * gramio: the raw material is `getChatMember` returning a member whose `status` may be
7
+ * `"creator"` or `"administrator"`. Every bot with an admin-gated group setting re-rolls
8
+ * that check; this module rolls it ONCE.
9
+ *
10
+ * These take minimum structural ctx shapes (the `bot/ctx.ts` philosophy) that real
11
+ * gramio contexts AND `bot/menu`'s `MenuCtx` satisfy by duck typing — a menu action
12
+ * gates with `isGroupAdmin(ctx)`, no cast. `ctx.bot` stays `unknown` for exactly that
13
+ * assignability (MenuCtx's own choice); a ctx whose bot has no `api.getChatMember`
14
+ * PANICS — a miswire screams, it doesn't read as "not admin".
15
+ *
16
+ * `isGroupAdmin` FAILS CLOSED on the API call itself: a `getChatMember` rejection (user
17
+ * never seen in the chat, network, the bot removed mid-tap) answers `false`, never
18
+ * throws — it is a permission gate, and a gate that throws breaks the surface it guards
19
+ * (a menu tap's single answerCallbackQuery, a command reply). That rejection is the
20
+ * messy real world; denial is the only honest answer to "I couldn't verify".
21
+ *
22
+ * Anonymous-admin caveat: an admin posting anonymously wears the group's own identity
23
+ * in `message.from` (the GroupAnonymousBot), so a MESSAGE from them fails this check.
24
+ * Callback taps always carry the real user, so inline-button gates (the settings case)
25
+ * are unaffected.
26
+ *
27
+ * @example
28
+ * // Gate a group-wide toggle: the bot's own admins always may, otherwise the
29
+ * // tapper must hold power over THIS group. (Policy composes at the call site.)
30
+ * if (!ctx.isAdmin && !(await isGroupAdmin(ctx))) return tr("groupAdminsOnly")
31
+ *
32
+ * @example
33
+ * // Cross-chat: is user 42 an admin of chat -100123?
34
+ * await isGroupAdmin(ctx, { chatId: -100123, userId: 42 })
35
+ */
36
+ import { assert } from "../offensive.js";
37
+ // The two chat-member statuses that hold power over a group. Not exported: the
38
+ // contract is the question `isGroupAdmin` answers, never status comparison.
39
+ const GROUP_ADMIN_STATUSES = new Set(["creator", "administrator"]);
40
+ /** True for a group or supergroup chat. */
41
+ export const isGroupChat = (ctx) => ctx.chat?.type === "group" || ctx.chat?.type === "supergroup";
42
+ /** True for a 1:1 private chat. */
43
+ export const isPrivateChat = (ctx) => ctx.chat?.type === "private";
44
+ /**
45
+ * Whether a user is a creator/administrator of a group, per `getChatMember`. Chat and
46
+ * user default to the ctx's own (`ctx.chat.id`, `ctx.from.id` — gramio's own
47
+ * parameter-defaulting idiom); pass `chatId` / `userId` for cross-chat checks. Missing
48
+ * ids (an actor-less service event) or an API rejection answer `false`.
49
+ */
50
+ export async function isGroupAdmin(ctx, opts = {}) {
51
+ const chatId = opts.chatId ?? ctx.chat?.id;
52
+ const userId = opts.userId ?? ctx.from?.id;
53
+ if (chatId === undefined || userId === undefined)
54
+ return false;
55
+ const bot = ctx.bot;
56
+ assert(typeof bot?.api?.getChatMember === "function", "isGroupAdmin: ctx.bot.api.getChatMember missing — not a gramio ctx?");
57
+ try {
58
+ const member = await bot.api.getChatMember({
59
+ chat_id: chatId,
60
+ user_id: userId,
61
+ });
62
+ return GROUP_ADMIN_STATUSES.has(member.status ?? "");
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ }
68
+ //# sourceMappingURL=groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/bot/groups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAwBzC,+EAA+E;AAC/E,4EAA4E;AAC5E,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;AAEnE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAgB,EAAW,EAAE,CACxD,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,YAAY,CAAC;AAE/D,mCAAmC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAgB,EAAW,EAAE,CAC1D,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAkB,EAClB,OAA6C,EAAE;IAE/C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAsD,CAAC;IACvE,MAAM,CACL,OAAO,GAAG,EAAE,GAAG,EAAE,aAAa,KAAK,UAAU,EAC7C,qEAAqE,CACrE,CAAC;IACF,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC;YAC1C,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,MAAM;SACf,CAAC,CAAC;QACH,OAAO,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC"}
@@ -16,6 +16,7 @@ export * from "./admin.js";
16
16
  export * from "./callbacks.js";
17
17
  export * from "./coalesce.js";
18
18
  export * from "./ctx.js";
19
+ export * from "./groups.js";
19
20
  export * from "./keys.js";
20
21
  export * from "./kit.js";
21
22
  export * from "./language.js";
@@ -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,YAAY,CAAC;AAC3B,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,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
@@ -16,6 +16,7 @@ export * from "./admin.js";
16
16
  export * from "./callbacks.js";
17
17
  export * from "./coalesce.js";
18
18
  export * from "./ctx.js";
19
+ export * from "./groups.js";
19
20
  export * from "./keys.js";
20
21
  export * from "./kit.js";
21
22
  export * from "./language.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adriangalilea/utils",
3
- "version": "1.8.0",
3
+ "version": "1.10.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",
@@ -127,6 +127,10 @@
127
127
  "./bot/flags": {
128
128
  "types": "./dist/bot/flags.d.ts",
129
129
  "default": "./dist/bot/flags.js"
130
+ },
131
+ "./bot/groups": {
132
+ "types": "./dist/bot/groups.d.ts",
133
+ "default": "./dist/bot/groups.js"
130
134
  }
131
135
  },
132
136
  "files": [
@@ -151,7 +155,8 @@
151
155
  "test:worker-safe": "pnpm run build && tsx tests/worker-safe-test.ts",
152
156
  "demo:bot": "tsx tests/create-demo.ts",
153
157
  "test:tg-html": "tsx tests/tg-html-test.ts",
154
- "test:flags": "tsx tests/flags-test.ts"
158
+ "test:flags": "tsx tests/flags-test.ts",
159
+ "test:groups": "tsx tests/groups-test.ts"
155
160
  },
156
161
  "keywords": [
157
162
  "typescript",