@adriangalilea/utils 0.6.0 → 0.8.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 +2 -5
- package/dist/bot/access-control.d.ts +91 -58
- package/dist/bot/access-control.d.ts.map +1 -1
- package/dist/bot/access-control.js +105 -75
- package/dist/bot/access-control.js.map +1 -1
- package/dist/bot/coalesce.d.ts +107 -0
- package/dist/bot/coalesce.d.ts.map +1 -0
- package/dist/bot/coalesce.js +155 -0
- package/dist/bot/coalesce.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.map +1 -1
- package/dist/bot/kit.js +6 -0
- package/dist/bot/kit.js.map +1 -1
- package/dist/bot/language.d.ts +279 -0
- package/dist/bot/language.d.ts.map +1 -0
- package/dist/bot/language.js +222 -0
- package/dist/bot/language.js.map +1 -0
- package/dist/bot/menu.d.ts +175 -0
- package/dist/bot/menu.d.ts.map +1 -0
- package/dist/bot/menu.js +284 -0
- package/dist/bot/menu.js.map +1 -0
- package/dist/bot/message-history.d.ts +259 -0
- package/dist/bot/message-history.d.ts.map +1 -0
- package/dist/bot/message-history.js +111 -0
- package/dist/bot/message-history.js.map +1 -0
- package/package.json +17 -1
- package/dist/currency/crypto-symbols-data.d.ts +0 -10
- package/dist/currency/crypto-symbols-data.d.ts.map +0 -1
- package/dist/currency/crypto-symbols-data.js +0 -13765
- package/dist/currency/crypto-symbols-data.js.map +0 -1
- package/dist/currency/crypto-symbols.d.ts +0 -20
- package/dist/currency/crypto-symbols.d.ts.map +0 -1
- package/dist/currency/crypto-symbols.js +0 -23
- package/dist/currency/crypto-symbols.js.map +0 -1
- package/dist/currency/download-crypto-list.d.ts +0 -10
- package/dist/currency/download-crypto-list.d.ts.map +0 -1
- package/dist/currency/download-crypto-list.js +0 -69
- package/dist/currency/download-crypto-list.js.map +0 -1
- package/dist/currency/index.d.ts +0 -84
- package/dist/currency/index.d.ts.map +0 -1
- package/dist/currency/index.js +0 -230
- package/dist/currency/index.js.map +0 -1
- package/dist/dir.d.ts +0 -40
- package/dist/dir.d.ts.map +0 -1
- package/dist/dir.js +0 -108
- package/dist/dir.js.map +0 -1
- package/dist/file.d.ts +0 -53
- package/dist/file.d.ts.map +0 -1
- package/dist/file.js +0 -211
- package/dist/file.js.map +0 -1
- package/dist/format.d.ts +0 -40
- package/dist/format.d.ts.map +0 -1
- package/dist/format.js +0 -83
- package/dist/format.js.map +0 -1
- package/dist/kev.d.ts +0 -149
- package/dist/kev.d.ts.map +0 -1
- package/dist/kev.js +0 -761
- package/dist/kev.js.map +0 -1
- package/dist/log.d.ts +0 -91
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -300
- package/dist/log.js.map +0 -1
- package/dist/logger.d.ts +0 -91
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -269
- package/dist/logger.js.map +0 -1
- package/dist/path.d.ts +0 -67
- package/dist/path.d.ts.map +0 -1
- package/dist/path.js +0 -107
- package/dist/path.js.map +0 -1
- package/dist/project.d.ts +0 -35
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -154
- package/dist/project.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,8 +8,6 @@ TypeScript utilities - logger, currency, offensive programming, file operations,
|
|
|
8
8
|
pnpm add @adriangalilea/utils
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Also available on [JSR](https://jsr.io/@adriangalilea/utils) but the JSR publish pipeline is not automated — versions may lag behind npm.
|
|
12
|
-
|
|
13
11
|
## Usage
|
|
14
12
|
|
|
15
13
|
### Logger
|
|
@@ -262,6 +260,7 @@ pnpm add @adriangalilea/utils gramio @gramio/storage @gramio/session
|
|
|
262
260
|
|---|---|
|
|
263
261
|
| `@adriangalilea/utils/bot/kit` | `gracefulStart(bot)` — SIGINT/SIGTERM → `bot.stop()` → exit; force-kills if shutdown hangs.<br>`adminContext({ adminId? })` — reads `TELEGRAM_ADMIN_ID` from `kev` (with optional hardcoded fallback), decorates `ctx.adminId` + `ctx.isAdmin`. |
|
|
264
262
|
| `@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. |
|
|
263
|
+
| `@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. |
|
|
265
264
|
| `@adriangalilea/utils/bot/llm-stream` | `ctx.startStream()` for LLM token streams. Debounced `editMessageText`, splits at 4000 chars on paragraph/line/word boundary, parses Markdown locally so malformed mid-stream markup degrades to plain text instead of failing. |
|
|
266
265
|
|
|
267
266
|
Standard wiring:
|
|
@@ -296,15 +295,13 @@ See `src/bot/CLAUDE.md` for storage layout, design decisions, and gotchas.
|
|
|
296
295
|
|
|
297
296
|
## Release
|
|
298
297
|
|
|
299
|
-
Bump version in `package.json
|
|
298
|
+
Bump version in `package.json`, push to `main`. CI handles everything:
|
|
300
299
|
|
|
301
300
|
1. Type-check, lint, build
|
|
302
301
|
2. Publish to npm via [OIDC trusted publishing](https://docs.npmjs.com/generating-provenance-statements) (no tokens — GitHub Actions proves identity directly to npm)
|
|
303
302
|
3. Create git tag `vX.Y.Z`
|
|
304
303
|
4. Generate changelog via [git-cliff](https://github.com/orhun/git-cliff) and create GitHub release
|
|
305
304
|
|
|
306
|
-
JSR publishing is not automated — see `jsr.json` TODO.
|
|
307
|
-
|
|
308
305
|
## License
|
|
309
306
|
|
|
310
307
|
MIT
|
|
@@ -17,45 +17,58 @@
|
|
|
17
17
|
* │
|
|
18
18
|
* stranger's session updated · stranger gets DM
|
|
19
19
|
*
|
|
20
|
-
* **Storage layout.**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* the `/access` admin menu can list without scanning the whole DB.
|
|
20
|
+
* **Storage layout.** This plugin stores its per-user record under
|
|
21
|
+
* the `access` field of the shared session record (see
|
|
22
|
+
* `bot/CLAUDE.md` § "Shared session, one record per user"). All
|
|
23
|
+
* per-user state across our plugins coexists in the same record:
|
|
25
24
|
*
|
|
26
|
-
* storage
|
|
27
|
-
* access
|
|
28
|
-
*
|
|
25
|
+
* storage[String(userId)] = {
|
|
26
|
+
* access: { status, approvedAt, … }, // ← this plugin
|
|
27
|
+
* language: 'es', // ← bot/language
|
|
28
|
+
* history: { items: [...] }, // ← bot/message-history
|
|
29
|
+
* }
|
|
29
30
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* own record. To mutate Pepe's record we hit the storage at the same
|
|
33
|
-
* key format we registered the session with (`access:<id>`) and
|
|
34
|
-
* update the index. This isn't a hack — it's our own module
|
|
35
|
-
* coordinating with itself.
|
|
31
|
+
* Plus one tiny admin-side index so `/access` can list pending /
|
|
32
|
+
* approved / denied without scanning every user:
|
|
36
33
|
*
|
|
37
|
-
*
|
|
38
|
-
* extended first or `bot.start()` throws. Inside this plugin,
|
|
39
|
-
* `ctx.adminId` and `ctx.isAdmin` are typed.
|
|
34
|
+
* storage['ac:index'] = { pending: [...ids], approved: [...], denied: [...] }
|
|
40
35
|
*
|
|
41
|
-
*
|
|
36
|
+
* **Cross-user mutations.** When the admin taps `[✅ Aprobar]` on
|
|
37
|
+
* Pepe's notification, `ctx` is the admin's, so `ctx.session` is the
|
|
38
|
+
* admin's record — useless for mutating Pepe. We reach for Pepe's
|
|
39
|
+
* record directly via `storage.get(String(pepeId))`, preserve other
|
|
40
|
+
* plugins' fields in it (read-modify-write), and put it back.
|
|
41
|
+
*
|
|
42
|
+
* **Composes with**:
|
|
43
|
+
* - `adminContext` (kit.ts) — required, gives us `ctx.adminId` /
|
|
44
|
+
* `ctx.isAdmin`. Declared as a runtime dependency.
|
|
45
|
+
* - `@gramio/session` — the user creates ONE session at bot level
|
|
46
|
+
* and passes it to this plugin (and the other session-using
|
|
47
|
+
* ones). gramio's runtime dedup ensures the session derive runs
|
|
48
|
+
* exactly once per update.
|
|
49
|
+
*
|
|
50
|
+
* Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
|
|
42
51
|
*
|
|
43
52
|
* @example
|
|
44
53
|
* import { Bot } from 'gramio'
|
|
54
|
+
* import { session } from '@gramio/session'
|
|
45
55
|
* import { redisStorage } from '@gramio/storage-redis'
|
|
46
56
|
* import { adminContext, gracefulStart } from '@adriangalilea/utils/bot/kit'
|
|
47
57
|
* import { accessControl } from '@adriangalilea/utils/bot/access-control'
|
|
48
58
|
*
|
|
49
59
|
* const storage = redisStorage()
|
|
60
|
+
* const userSession = session({ storage, key: 'session', initial: () => ({}) })
|
|
50
61
|
*
|
|
51
62
|
* const bot = new Bot(process.env.BOT_TOKEN!)
|
|
52
63
|
* .extend(adminContext({ adminId: 190202471 }))
|
|
53
|
-
* .extend(
|
|
54
|
-
* .
|
|
64
|
+
* .extend(userSession)
|
|
65
|
+
* .extend(accessControl({ session: userSession, storage, defaults: [1158734055] }))
|
|
66
|
+
* .command('start', (ctx) => ctx.send(`source=${ctx.access.source ?? 'denied'}`))
|
|
55
67
|
*
|
|
56
68
|
* await gracefulStart(bot)
|
|
57
69
|
*/
|
|
58
70
|
import { type AnyBot, type DeriveDefinitions, Plugin } from 'gramio';
|
|
71
|
+
import { session } from '@gramio/session';
|
|
59
72
|
import { type Storage } from '@gramio/storage';
|
|
60
73
|
export type AccessStatus = 'unknown' | 'pending' | 'approved' | 'denied';
|
|
61
74
|
export type AccessUser = {
|
|
@@ -65,8 +78,10 @@ export type AccessUser = {
|
|
|
65
78
|
username?: string;
|
|
66
79
|
};
|
|
67
80
|
/**
|
|
68
|
-
*
|
|
69
|
-
* `
|
|
81
|
+
* What this plugin persists under `ctx.session.access` per user. When
|
|
82
|
+
* `ctx.session.access` is `undefined`, the user has never interacted
|
|
83
|
+
* (or has been wiped via /forget). The plugin treats that as
|
|
84
|
+
* status='unknown' for gating purposes.
|
|
70
85
|
*/
|
|
71
86
|
export type AccessRecord = {
|
|
72
87
|
status: AccessStatus;
|
|
@@ -105,9 +120,25 @@ export type AccessInfo = {
|
|
|
105
120
|
allowed: false;
|
|
106
121
|
reason: 'denied' | 'pending' | 'unknown' | 'no-sender';
|
|
107
122
|
};
|
|
123
|
+
/** Loose session shape — this plugin only touches the `access` field. */
|
|
124
|
+
type SessionLike = {
|
|
125
|
+
access?: AccessRecord;
|
|
126
|
+
};
|
|
127
|
+
/** @internal — kept unexported so it doesn't clash with peers' refs. */
|
|
128
|
+
type AcSessionPluginRef = ReturnType<typeof session<SessionLike, 'session'>>;
|
|
108
129
|
export type AccessControlOptions = {
|
|
109
|
-
/**
|
|
110
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Shared session plugin. This plugin extends it for type flow;
|
|
132
|
+
* gramio's runtime dedup ensures it only runs once per update.
|
|
133
|
+
* `ctx.session.access` is where the per-user access record lives.
|
|
134
|
+
*/
|
|
135
|
+
session: AcSessionPluginRef;
|
|
136
|
+
/**
|
|
137
|
+
* Storage backend for cross-user mutations (admin approves Pepe →
|
|
138
|
+
* write to Pepe's session record from admin's ctx). Must be the
|
|
139
|
+
* same storage instance passed to `session()`.
|
|
140
|
+
*/
|
|
141
|
+
storage: Storage;
|
|
111
142
|
/** Always-allowed user ids, hardcoded. Bypass the entire flow. */
|
|
112
143
|
defaults?: ReadonlyArray<number>;
|
|
113
144
|
/** Reply sent to denied users on first attempt. `false` to silence. */
|
|
@@ -132,157 +163,159 @@ type AdminDerives = {
|
|
|
132
163
|
adminId: number;
|
|
133
164
|
isAdmin: boolean;
|
|
134
165
|
};
|
|
135
|
-
type AccessSessionDerives = {
|
|
136
|
-
_accessSession: AccessRecord;
|
|
137
|
-
};
|
|
138
166
|
type AccessDerives = {
|
|
139
167
|
access: AccessInfo;
|
|
140
168
|
};
|
|
141
|
-
|
|
142
|
-
|
|
169
|
+
type SessionDerives = {
|
|
170
|
+
session: SessionLike & {
|
|
171
|
+
$clear: () => Promise<void>;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
export declare const accessControl: (opts: AccessControlOptions) => Plugin<{}, DeriveDefinitions & {
|
|
175
|
+
global: AdminDerives & AccessDerives & SessionDerives;
|
|
143
176
|
} & {
|
|
144
177
|
message: {
|
|
145
|
-
|
|
178
|
+
session: SessionLike & {
|
|
146
179
|
$clear: () => Promise<void>;
|
|
147
180
|
};
|
|
148
181
|
};
|
|
149
182
|
channel_post: {
|
|
150
|
-
|
|
183
|
+
session: SessionLike & {
|
|
151
184
|
$clear: () => Promise<void>;
|
|
152
185
|
};
|
|
153
186
|
};
|
|
154
187
|
inline_query: {
|
|
155
|
-
|
|
188
|
+
session: SessionLike & {
|
|
156
189
|
$clear: () => Promise<void>;
|
|
157
190
|
};
|
|
158
191
|
};
|
|
159
192
|
chosen_inline_result: {
|
|
160
|
-
|
|
193
|
+
session: SessionLike & {
|
|
161
194
|
$clear: () => Promise<void>;
|
|
162
195
|
};
|
|
163
196
|
};
|
|
164
197
|
callback_query: {
|
|
165
|
-
|
|
198
|
+
session: SessionLike & {
|
|
166
199
|
$clear: () => Promise<void>;
|
|
167
200
|
};
|
|
168
201
|
};
|
|
169
202
|
shipping_query: {
|
|
170
|
-
|
|
203
|
+
session: SessionLike & {
|
|
171
204
|
$clear: () => Promise<void>;
|
|
172
205
|
};
|
|
173
206
|
};
|
|
174
207
|
pre_checkout_query: {
|
|
175
|
-
|
|
208
|
+
session: SessionLike & {
|
|
176
209
|
$clear: () => Promise<void>;
|
|
177
210
|
};
|
|
178
211
|
};
|
|
179
212
|
poll_answer: {
|
|
180
|
-
|
|
213
|
+
session: SessionLike & {
|
|
181
214
|
$clear: () => Promise<void>;
|
|
182
215
|
};
|
|
183
216
|
};
|
|
184
217
|
chat_join_request: {
|
|
185
|
-
|
|
218
|
+
session: SessionLike & {
|
|
186
219
|
$clear: () => Promise<void>;
|
|
187
220
|
};
|
|
188
221
|
};
|
|
189
222
|
new_chat_members: {
|
|
190
|
-
|
|
223
|
+
session: SessionLike & {
|
|
191
224
|
$clear: () => Promise<void>;
|
|
192
225
|
};
|
|
193
226
|
};
|
|
194
227
|
new_chat_title: {
|
|
195
|
-
|
|
228
|
+
session: SessionLike & {
|
|
196
229
|
$clear: () => Promise<void>;
|
|
197
230
|
};
|
|
198
231
|
};
|
|
199
232
|
new_chat_photo: {
|
|
200
|
-
|
|
233
|
+
session: SessionLike & {
|
|
201
234
|
$clear: () => Promise<void>;
|
|
202
235
|
};
|
|
203
236
|
};
|
|
204
237
|
delete_chat_photo: {
|
|
205
|
-
|
|
238
|
+
session: SessionLike & {
|
|
206
239
|
$clear: () => Promise<void>;
|
|
207
240
|
};
|
|
208
241
|
};
|
|
209
242
|
group_chat_created: {
|
|
210
|
-
|
|
243
|
+
session: SessionLike & {
|
|
211
244
|
$clear: () => Promise<void>;
|
|
212
245
|
};
|
|
213
246
|
};
|
|
214
247
|
message_auto_delete_timer_changed: {
|
|
215
|
-
|
|
248
|
+
session: SessionLike & {
|
|
216
249
|
$clear: () => Promise<void>;
|
|
217
250
|
};
|
|
218
251
|
};
|
|
219
252
|
migrate_to_chat_id: {
|
|
220
|
-
|
|
253
|
+
session: SessionLike & {
|
|
221
254
|
$clear: () => Promise<void>;
|
|
222
255
|
};
|
|
223
256
|
};
|
|
224
257
|
migrate_from_chat_id: {
|
|
225
|
-
|
|
258
|
+
session: SessionLike & {
|
|
226
259
|
$clear: () => Promise<void>;
|
|
227
260
|
};
|
|
228
261
|
};
|
|
229
262
|
pinned_message: {
|
|
230
|
-
|
|
263
|
+
session: SessionLike & {
|
|
231
264
|
$clear: () => Promise<void>;
|
|
232
265
|
};
|
|
233
266
|
};
|
|
234
267
|
invoice: {
|
|
235
|
-
|
|
268
|
+
session: SessionLike & {
|
|
236
269
|
$clear: () => Promise<void>;
|
|
237
270
|
};
|
|
238
271
|
};
|
|
239
272
|
successful_payment: {
|
|
240
|
-
|
|
273
|
+
session: SessionLike & {
|
|
241
274
|
$clear: () => Promise<void>;
|
|
242
275
|
};
|
|
243
276
|
};
|
|
244
277
|
chat_shared: {
|
|
245
|
-
|
|
278
|
+
session: SessionLike & {
|
|
246
279
|
$clear: () => Promise<void>;
|
|
247
280
|
};
|
|
248
281
|
};
|
|
249
282
|
proximity_alert_triggered: {
|
|
250
|
-
|
|
283
|
+
session: SessionLike & {
|
|
251
284
|
$clear: () => Promise<void>;
|
|
252
285
|
};
|
|
253
286
|
};
|
|
254
287
|
video_chat_scheduled: {
|
|
255
|
-
|
|
288
|
+
session: SessionLike & {
|
|
256
289
|
$clear: () => Promise<void>;
|
|
257
290
|
};
|
|
258
291
|
};
|
|
259
292
|
video_chat_started: {
|
|
260
|
-
|
|
293
|
+
session: SessionLike & {
|
|
261
294
|
$clear: () => Promise<void>;
|
|
262
295
|
};
|
|
263
296
|
};
|
|
264
297
|
video_chat_ended: {
|
|
265
|
-
|
|
298
|
+
session: SessionLike & {
|
|
266
299
|
$clear: () => Promise<void>;
|
|
267
300
|
};
|
|
268
301
|
};
|
|
269
302
|
video_chat_participants_invited: {
|
|
270
|
-
|
|
303
|
+
session: SessionLike & {
|
|
271
304
|
$clear: () => Promise<void>;
|
|
272
305
|
};
|
|
273
306
|
};
|
|
274
307
|
web_app_data: {
|
|
275
|
-
|
|
308
|
+
session: SessionLike & {
|
|
276
309
|
$clear: () => Promise<void>;
|
|
277
310
|
};
|
|
278
311
|
};
|
|
279
312
|
location: {
|
|
280
|
-
|
|
313
|
+
session: SessionLike & {
|
|
281
314
|
$clear: () => Promise<void>;
|
|
282
315
|
};
|
|
283
316
|
};
|
|
284
317
|
passport_data: {
|
|
285
|
-
|
|
318
|
+
session: SessionLike & {
|
|
286
319
|
$clear: () => Promise<void>;
|
|
287
320
|
};
|
|
288
321
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../../src/bot/access-control.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../../src/bot/access-control.ts"],"names":[],"mappings":"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"}
|