@adriangalilea/utils 0.7.0 → 0.9.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 +29 -5
- package/dist/bot/access-control.d.ts +106 -62
- package/dist/bot/access-control.d.ts.map +1 -1
- package/dist/bot/access-control.js +255 -146
- package/dist/bot/access-control.js.map +1 -1
- package/dist/bot/index.d.ts +3 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +3 -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 +305 -0
- package/dist/bot/language.d.ts.map +1 -0
- package/dist/bot/language.js +250 -0
- package/dist/bot/language.js.map +1 -0
- package/dist/bot/menu.d.ts +189 -0
- package/dist/bot/menu.d.ts.map +1 -0
- package/dist/bot/menu.js +331 -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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/say/index.d.ts +62 -0
- package/dist/say/index.d.ts.map +1 -0
- package/dist/say/index.js +59 -0
- package/dist/say/index.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/dist/bot/menu.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable settings menu — registers a single slash command, renders
|
|
3
|
+
* an `InlineKeyboard`, routes callbacks. Features (language, history,
|
|
4
|
+
* etc.) contribute items; the bot builder adds custom items inline.
|
|
5
|
+
*
|
|
6
|
+
* ## Why menu is a separate primitive
|
|
7
|
+
*
|
|
8
|
+
* The user-facing menu is just UI — composing items. Features (per-user
|
|
9
|
+
* state, recording, gates) have their OWN runtime behaviour independent
|
|
10
|
+
* of any menu.
|
|
11
|
+
*
|
|
12
|
+
* ## GDPR rights (Forget / Export)
|
|
13
|
+
*
|
|
14
|
+
* If you pass `personalData: { storage }`, the menu adds two buttons:
|
|
15
|
+
*
|
|
16
|
+
* - 🗑 Forget my data — `storage.delete(sessionKey(userId))`
|
|
17
|
+
* - 📥 Export my data — `storage.get(sessionKey(userId))` → JSON file
|
|
18
|
+
*
|
|
19
|
+
* Because all per-user state across our plugins lives in ONE shared
|
|
20
|
+
* session record (see `bot/language`, `bot/message-history`), wiping
|
|
21
|
+
* or exporting that single key covers everything in one shot. No
|
|
22
|
+
* registry, no cascade, no per-plugin coordination.
|
|
23
|
+
*
|
|
24
|
+
* The `sessionKey` option defaults to `String(userId)` — matching
|
|
25
|
+
* `@gramio/session`'s default `getSessionKey: (ctx) => `${ctx.senderId}`.
|
|
26
|
+
* If you customize the session's `getSessionKey`, pass a matching
|
|
27
|
+
* function here.
|
|
28
|
+
*
|
|
29
|
+
* ## Privacy URL
|
|
30
|
+
*
|
|
31
|
+
* `privacy` defaults to [Telegram's Standard Bot Privacy Policy](https://telegram.org/privacy-tpa).
|
|
32
|
+
* Override when you retain content or process data beyond what the
|
|
33
|
+
* standard covers.
|
|
34
|
+
*
|
|
35
|
+
* Peer deps: `gramio`, `@gramio/storage`.
|
|
36
|
+
*
|
|
37
|
+
* @example Personal LLM bot — language only, no retention
|
|
38
|
+
*
|
|
39
|
+
* import { language } from '@adriangalilea/utils/bot/language'
|
|
40
|
+
* import { botMenu } from '@adriangalilea/utils/bot/menu'
|
|
41
|
+
*
|
|
42
|
+
* const lang = language({ session: userSession, supported: ['en','es'] as const, default: 'en' })
|
|
43
|
+
*
|
|
44
|
+
* const menu = botMenu({
|
|
45
|
+
* command: 'settings',
|
|
46
|
+
* description: 'Open settings',
|
|
47
|
+
* adminContact: '@adriangalilea',
|
|
48
|
+
* items: [lang.menuItem],
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* bot.extend(userSession).extend(lang.plugin).extend(menu.plugin)
|
|
52
|
+
*
|
|
53
|
+
* @example Bot with retention — adds Forget/Export
|
|
54
|
+
*
|
|
55
|
+
* const menu = botMenu({
|
|
56
|
+
* command: 'settings',
|
|
57
|
+
* description: 'Open settings',
|
|
58
|
+
* adminContact: '@adriangalilea',
|
|
59
|
+
* privacy: 'https://yourbot.com/privacy',
|
|
60
|
+
* personalData: { storage }, // ← enables Forget/Export
|
|
61
|
+
* items: [lang.menuItem],
|
|
62
|
+
* })
|
|
63
|
+
*
|
|
64
|
+
* bot
|
|
65
|
+
* .extend(userSession)
|
|
66
|
+
* .extend(history.plugin)
|
|
67
|
+
* .extend(menu.plugin)
|
|
68
|
+
*/
|
|
69
|
+
import { CallbackData, InlineKeyboard, Plugin, } from 'gramio';
|
|
70
|
+
import { say } from '../say/index.js';
|
|
71
|
+
const FALLBACK_LANG = 'en';
|
|
72
|
+
const ctxLang = (ctx) => ctx.session?.language ?? FALLBACK_LANG;
|
|
73
|
+
const DEFAULT_COMMAND = 'settings';
|
|
74
|
+
const DEFAULT_DESCRIPTION = 'Open settings menu';
|
|
75
|
+
const DEFAULT_PRIVACY_URL = 'https://telegram.org/privacy-tpa';
|
|
76
|
+
const DEFAULT_HEADER = { en: '⚙️ Settings', es: '⚙️ Ajustes' };
|
|
77
|
+
const DEFAULT_SESSION_KEY = (userId) => String(userId);
|
|
78
|
+
// ─── callback data schemas ─────────────────────────────────────────
|
|
79
|
+
const navCb = new CallbackData('mNav').string('path');
|
|
80
|
+
const actCb = new CallbackData('mAct').string('path');
|
|
81
|
+
const forgetConfirmCb = new CallbackData('mFcfm');
|
|
82
|
+
const forgetCancelCb = new CallbackData('mFcnl');
|
|
83
|
+
const exportCb = new CallbackData('mExp');
|
|
84
|
+
export class BotMenu {
|
|
85
|
+
/** @internal */
|
|
86
|
+
_items;
|
|
87
|
+
/** @internal */
|
|
88
|
+
_opts;
|
|
89
|
+
constructor(opts) {
|
|
90
|
+
this._items = [...(opts.items ?? [])];
|
|
91
|
+
this._opts = {
|
|
92
|
+
command: opts.command ?? DEFAULT_COMMAND,
|
|
93
|
+
description: opts.description ?? DEFAULT_DESCRIPTION,
|
|
94
|
+
privacy: opts.privacy ?? DEFAULT_PRIVACY_URL,
|
|
95
|
+
header: opts.header ?? DEFAULT_HEADER,
|
|
96
|
+
adminContact: opts.adminContact,
|
|
97
|
+
personalData: opts.personalData
|
|
98
|
+
? {
|
|
99
|
+
storage: opts.personalData.storage,
|
|
100
|
+
sessionKey: opts.personalData.sessionKey ?? DEFAULT_SESSION_KEY,
|
|
101
|
+
}
|
|
102
|
+
: null,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Append a custom item. Mutates the menu. */
|
|
106
|
+
add(item) {
|
|
107
|
+
this._items.push(item);
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
/** The gramio plugin: registers the slash command + all callback handlers. */
|
|
111
|
+
get plugin() {
|
|
112
|
+
return buildMenuPlugin(this);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export const botMenu = (opts) => new BotMenu(opts);
|
|
116
|
+
// ─── internal: rendering + plugin ──────────────────────────────────
|
|
117
|
+
const labelOf = (l, ctx) => {
|
|
118
|
+
const resolved = typeof l === 'function' ? l(ctx) : l;
|
|
119
|
+
if (typeof resolved === 'string')
|
|
120
|
+
return resolved;
|
|
121
|
+
return say(resolved, ctxLang(ctx));
|
|
122
|
+
};
|
|
123
|
+
const itemsForPath = (root, path) => {
|
|
124
|
+
if (path.length === 0)
|
|
125
|
+
return root;
|
|
126
|
+
const [head, ...rest] = path;
|
|
127
|
+
const found = root.find((i) => i.id === head);
|
|
128
|
+
if (!found || !('submenu' in found))
|
|
129
|
+
return null;
|
|
130
|
+
return itemsForPath(found.submenu, rest);
|
|
131
|
+
};
|
|
132
|
+
const itemForPath = (root, path) => {
|
|
133
|
+
if (path.length === 0)
|
|
134
|
+
return null;
|
|
135
|
+
let current = root;
|
|
136
|
+
let last;
|
|
137
|
+
for (const segment of path) {
|
|
138
|
+
if (!current)
|
|
139
|
+
return null;
|
|
140
|
+
last = current.find((i) => i.id === segment);
|
|
141
|
+
if (!last)
|
|
142
|
+
return null;
|
|
143
|
+
current = 'submenu' in last ? last.submenu : undefined;
|
|
144
|
+
}
|
|
145
|
+
return last ?? null;
|
|
146
|
+
};
|
|
147
|
+
const renderKeyboard = (menu, items, ctx, parentPath) => {
|
|
148
|
+
const kb = new InlineKeyboard();
|
|
149
|
+
const sorted = [...items]
|
|
150
|
+
.filter((i) => (i.visible ? i.visible(ctx) : true))
|
|
151
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
152
|
+
for (const item of sorted) {
|
|
153
|
+
const path = [...parentPath, item.id].join('.');
|
|
154
|
+
const label = labelOf(item.label, ctx);
|
|
155
|
+
if ('action' in item) {
|
|
156
|
+
kb.text(label, actCb.pack({ path }));
|
|
157
|
+
}
|
|
158
|
+
else if ('url' in item) {
|
|
159
|
+
kb.url(label, item.url);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
kb.text(label, navCb.pack({ path }));
|
|
163
|
+
}
|
|
164
|
+
kb.row();
|
|
165
|
+
}
|
|
166
|
+
const lang = ctxLang(ctx);
|
|
167
|
+
if (parentPath.length === 0) {
|
|
168
|
+
// GDPR rights buttons at the root view.
|
|
169
|
+
if (menu._opts.personalData) {
|
|
170
|
+
kb.text(say({ en: '🗑 Forget my data', es: '🗑 Olvidar mis datos' }, lang), actCb.pack({ path: '_forget' }));
|
|
171
|
+
kb.row();
|
|
172
|
+
kb.text(say({ en: '📥 Export my data', es: '📥 Exportar mis datos' }, lang), exportCb.pack({}));
|
|
173
|
+
kb.row();
|
|
174
|
+
}
|
|
175
|
+
// Privacy link.
|
|
176
|
+
kb.url(say({ en: '📖 Privacy', es: '📖 Privacidad' }, lang), menu._opts.privacy);
|
|
177
|
+
kb.row();
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const backPath = parentPath.slice(0, -1).join('.');
|
|
181
|
+
kb.text(say({ en: '⬅️ Back', es: '⬅️ Volver' }, lang), navCb.pack({ path: backPath || '_root' }));
|
|
182
|
+
}
|
|
183
|
+
return kb;
|
|
184
|
+
};
|
|
185
|
+
const renderConfirmForget = (lang) => new InlineKeyboard()
|
|
186
|
+
.text(say({ en: '✅ Confirm delete', es: '✅ Confirmar borrado' }, lang), forgetConfirmCb.pack({}))
|
|
187
|
+
.row()
|
|
188
|
+
.text(say({ en: '⬅️ Cancel', es: '⬅️ Cancelar' }, lang), forgetCancelCb.pack({}));
|
|
189
|
+
const buildMenuPlugin = (menu) => {
|
|
190
|
+
const { command, description, header, personalData, adminContact } = menu._opts;
|
|
191
|
+
return new Plugin('@adriangalilea/utils/bot/menu')
|
|
192
|
+
.command(command, { description }, async (ctx) => {
|
|
193
|
+
const kb = renderKeyboard(menu, menu._items, ctx, []);
|
|
194
|
+
await ctx.send(labelOf(header, ctx), { reply_markup: kb });
|
|
195
|
+
})
|
|
196
|
+
// Navigate (root / submenu)
|
|
197
|
+
.callbackQuery(navCb, async (ctx) => {
|
|
198
|
+
const lang = ctxLang(ctx);
|
|
199
|
+
const raw = ctx.queryData.path;
|
|
200
|
+
const segments = raw === '_root' ? [] : raw.split('.');
|
|
201
|
+
const items = itemsForPath(menu._items, segments);
|
|
202
|
+
if (!items) {
|
|
203
|
+
await ctx.answer({
|
|
204
|
+
text: say({ en: 'Menu out of date.', es: 'Menú obsoleto.' }, lang),
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
await ctx.answer({});
|
|
209
|
+
const kb = renderKeyboard(menu, items, ctx, segments);
|
|
210
|
+
try {
|
|
211
|
+
await ctx.editText(labelOf(header, ctx), { reply_markup: kb });
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// message too old to edit
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
// Action items + the forget pre-confirmation
|
|
218
|
+
.callbackQuery(actCb, async (ctx) => {
|
|
219
|
+
const lang = ctxLang(ctx);
|
|
220
|
+
const raw = ctx.queryData.path;
|
|
221
|
+
if (raw === '_forget') {
|
|
222
|
+
await ctx.answer({});
|
|
223
|
+
try {
|
|
224
|
+
await ctx.editText(say({
|
|
225
|
+
en: '⚠️ Delete all your data?\n\n' +
|
|
226
|
+
'This removes the session record we keep about you ' +
|
|
227
|
+
'(preferences, history, access state). Not reversible.',
|
|
228
|
+
es: '⚠️ ¿Borrar todos tus datos?\n\n' +
|
|
229
|
+
'Esto elimina el registro de sesión que guardamos sobre ti ' +
|
|
230
|
+
'(preferencias, historial, estado de acceso). No se puede deshacer.',
|
|
231
|
+
}, lang), { reply_markup: renderConfirmForget(lang) });
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
/* ignore */
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const item = itemForPath(menu._items, raw.split('.'));
|
|
239
|
+
if (!item || !('action' in item)) {
|
|
240
|
+
await ctx.answer({
|
|
241
|
+
text: say({ en: 'Item not found.', es: 'Elemento no encontrado.' }, lang),
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
await ctx.answer({});
|
|
246
|
+
await item.action(ctx);
|
|
247
|
+
})
|
|
248
|
+
// Forget — confirm path
|
|
249
|
+
.callbackQuery(forgetConfirmCb, async (ctx) => {
|
|
250
|
+
const lang = ctxLang(ctx);
|
|
251
|
+
if (!personalData) {
|
|
252
|
+
await ctx.answer({
|
|
253
|
+
text: say({ en: 'Not configured.', es: 'No configurado.' }, lang),
|
|
254
|
+
show_alert: true,
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const userId = ctx.from?.id;
|
|
259
|
+
if (userId === undefined)
|
|
260
|
+
return ctx.answer({
|
|
261
|
+
text: say({ en: 'No user.', es: 'Sin usuario.' }, lang),
|
|
262
|
+
});
|
|
263
|
+
try {
|
|
264
|
+
await personalData.storage.delete(personalData.sessionKey(userId));
|
|
265
|
+
await ctx.answer({
|
|
266
|
+
text: say({ en: 'Deleted.', es: 'Borrado.' }, lang),
|
|
267
|
+
});
|
|
268
|
+
try {
|
|
269
|
+
await ctx.editText(say({
|
|
270
|
+
en: '✅ Your data has been deleted.',
|
|
271
|
+
es: '✅ Tus datos han sido borrados.',
|
|
272
|
+
}, lang));
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
/* ignore */
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
console.error('[menu] /forget failed', e);
|
|
280
|
+
await ctx.answer({
|
|
281
|
+
text: say({ en: 'Failed.', es: 'Falló.' }, lang),
|
|
282
|
+
});
|
|
283
|
+
await ctx.send(say({
|
|
284
|
+
en: `❌ Could not delete your data.\n\nPlease contact ${adminContact}.`,
|
|
285
|
+
es: `❌ No se han podido borrar tus datos.\n\nContacta con ${adminContact}.`,
|
|
286
|
+
}, lang));
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
.callbackQuery(forgetCancelCb, async (ctx) => {
|
|
290
|
+
await ctx.answer({});
|
|
291
|
+
const kb = renderKeyboard(menu, menu._items, ctx, []);
|
|
292
|
+
try {
|
|
293
|
+
await ctx.editText(labelOf(header, ctx), { reply_markup: kb });
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
/* ignore */
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
// Export — JSON file with the user's whole session record
|
|
300
|
+
.callbackQuery(exportCb, async (ctx) => {
|
|
301
|
+
const lang = ctxLang(ctx);
|
|
302
|
+
if (!personalData) {
|
|
303
|
+
await ctx.answer({
|
|
304
|
+
text: say({ en: 'Not configured.', es: 'No configurado.' }, lang),
|
|
305
|
+
show_alert: true,
|
|
306
|
+
});
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const userId = ctx.from?.id;
|
|
310
|
+
if (userId === undefined)
|
|
311
|
+
return ctx.answer({
|
|
312
|
+
text: say({ en: 'No user.', es: 'Sin usuario.' }, lang),
|
|
313
|
+
});
|
|
314
|
+
const record = (await personalData.storage.get(personalData.sessionKey(userId))) ?? {};
|
|
315
|
+
const file = new File([JSON.stringify({ userId, exportedAt: Date.now(), data: record }, null, 2)], `my-data-${userId}-${Date.now()}.json`, { type: 'application/json' });
|
|
316
|
+
await ctx.answer({});
|
|
317
|
+
try {
|
|
318
|
+
await ctx.sendDocument(file, {
|
|
319
|
+
caption: say({ en: '📥 Your data export', es: '📥 Exportación de tus datos' }, lang),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
console.error('[menu] /export sendDocument failed', e);
|
|
324
|
+
await ctx.send(say({
|
|
325
|
+
en: `❌ Could not send your data export.\n\nPlease contact ${adminContact}.`,
|
|
326
|
+
es: `❌ No se ha podido enviar la exportación de tus datos.\n\nContacta con ${adminContact}.`,
|
|
327
|
+
}, lang));
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/bot/menu.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user rolling message history for GramIO bots — opt-in, with
|
|
3
|
+
* retention.
|
|
4
|
+
*
|
|
5
|
+
* Follows the same shared-session pattern as `bot/language`: the
|
|
6
|
+
* user creates a `session()` once at bot level, and each feature
|
|
7
|
+
* plugin (including this one) declares it as a dependency. gramio
|
|
8
|
+
* dedupes the runtime extension; the types flow.
|
|
9
|
+
*
|
|
10
|
+
* ## What this plugin owns
|
|
11
|
+
*
|
|
12
|
+
* - Appends each incoming user message to `ctx.session.history.items`
|
|
13
|
+
* - Prunes entries older than `retentionDays` or beyond `maxMessages`
|
|
14
|
+
* - Exposes a read-only pruned snapshot at `ctx.history` for handlers
|
|
15
|
+
*
|
|
16
|
+
* ## GDPR caveat — retention IS personal data
|
|
17
|
+
*
|
|
18
|
+
* Unlike `bot/language` (preference data, covered by Telegram's standard
|
|
19
|
+
* privacy policy), retaining user **message content** is the one thing
|
|
20
|
+
* the [standard policy](https://telegram.org/privacy-tpa) does NOT cover
|
|
21
|
+
* by default. If you extend `messageHistory`, you should:
|
|
22
|
+
*
|
|
23
|
+
* 1. Set a custom `privacy` URL on your `botMenu` describing what you
|
|
24
|
+
* retain and for how long.
|
|
25
|
+
* 2. Pass `personalData: { storage }` to your `botMenu` so 🗑 Forget
|
|
26
|
+
* and 📥 Export buttons appear — letting users see and delete
|
|
27
|
+
* the data you keep about them.
|
|
28
|
+
*
|
|
29
|
+
* These are not enforced by this plugin (would couple it to menu); they
|
|
30
|
+
* are documented as the bot author's legal responsibility.
|
|
31
|
+
*
|
|
32
|
+
* ## Storage layout
|
|
33
|
+
*
|
|
34
|
+
* Lives entirely inside the shared session record:
|
|
35
|
+
*
|
|
36
|
+
* storage[String(senderId)] = {
|
|
37
|
+
* ...other plugins' fields,
|
|
38
|
+
* history: { items: [HistoryEntry, ...] }
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* Peer deps: `gramio`, `@gramio/session`.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* import { Bot } from 'gramio'
|
|
45
|
+
* import { session } from '@gramio/session'
|
|
46
|
+
* import { redisStorage } from '@gramio/storage-redis'
|
|
47
|
+
* import { messageHistory } from '@adriangalilea/utils/bot/message-history'
|
|
48
|
+
*
|
|
49
|
+
* const userSession = session({ storage: redisStorage(), key: 'session', initial: () => ({}) })
|
|
50
|
+
*
|
|
51
|
+
* const history = messageHistory({
|
|
52
|
+
* session: userSession,
|
|
53
|
+
* maxMessages: 100,
|
|
54
|
+
* retentionDays: 7,
|
|
55
|
+
* })
|
|
56
|
+
*
|
|
57
|
+
* const bot = new Bot(process.env.BOT_TOKEN!)
|
|
58
|
+
* .extend(userSession)
|
|
59
|
+
* .extend(history.plugin)
|
|
60
|
+
* .command('replay', (ctx) => {
|
|
61
|
+
* const last = ctx.history.slice(-3).map((e) => e.text).join('\n---\n')
|
|
62
|
+
* return ctx.send(last || '(no history)')
|
|
63
|
+
* })
|
|
64
|
+
*/
|
|
65
|
+
import { type DeriveDefinitions, Plugin } from 'gramio';
|
|
66
|
+
import { session } from '@gramio/session';
|
|
67
|
+
export type HistoryEntry = {
|
|
68
|
+
/** Telegram message id. */
|
|
69
|
+
messageId: number;
|
|
70
|
+
/** Unix seconds (Telegram's `message.date`). */
|
|
71
|
+
date: number;
|
|
72
|
+
/** Message text, or empty string if non-text. */
|
|
73
|
+
text: string;
|
|
74
|
+
};
|
|
75
|
+
export type HistoryRecord = {
|
|
76
|
+
items: HistoryEntry[];
|
|
77
|
+
};
|
|
78
|
+
/** Loose session shape — this plugin only touches the `history` field. */
|
|
79
|
+
type SessionLike = {
|
|
80
|
+
history?: HistoryRecord;
|
|
81
|
+
};
|
|
82
|
+
/** @internal — kept unexported so it doesn't clash with peers' refs. */
|
|
83
|
+
type HistorySessionPluginRef = ReturnType<typeof session<SessionLike, 'session'>>;
|
|
84
|
+
export type MessageHistoryOptions = {
|
|
85
|
+
/**
|
|
86
|
+
* Shared session plugin. This plugin extends it for the type flow;
|
|
87
|
+
* gramio's runtime dedup ensures it only runs once per update.
|
|
88
|
+
*/
|
|
89
|
+
session: HistorySessionPluginRef;
|
|
90
|
+
/** Ring buffer cap. Oldest entries dropped when exceeded. */
|
|
91
|
+
maxMessages: number;
|
|
92
|
+
/** Entries older than this (in days) are dropped on read. */
|
|
93
|
+
retentionDays: number;
|
|
94
|
+
};
|
|
95
|
+
export type MessageHistoryFeature = {
|
|
96
|
+
plugin: ReturnType<typeof buildHistoryPlugin>;
|
|
97
|
+
};
|
|
98
|
+
type HistoryDerives = {
|
|
99
|
+
history: ReadonlyArray<HistoryEntry>;
|
|
100
|
+
};
|
|
101
|
+
export declare const messageHistory: (opts: MessageHistoryOptions) => MessageHistoryFeature;
|
|
102
|
+
declare const buildHistoryPlugin: (args: {
|
|
103
|
+
sessionPlugin: HistorySessionPluginRef;
|
|
104
|
+
maxMessages: number;
|
|
105
|
+
retentionDays: number;
|
|
106
|
+
}) => Plugin<{}, DeriveDefinitions & {
|
|
107
|
+
global: HistoryDerives;
|
|
108
|
+
} & {
|
|
109
|
+
message: {
|
|
110
|
+
session: SessionLike & {
|
|
111
|
+
$clear: () => Promise<void>;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
channel_post: {
|
|
115
|
+
session: SessionLike & {
|
|
116
|
+
$clear: () => Promise<void>;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
inline_query: {
|
|
120
|
+
session: SessionLike & {
|
|
121
|
+
$clear: () => Promise<void>;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
chosen_inline_result: {
|
|
125
|
+
session: SessionLike & {
|
|
126
|
+
$clear: () => Promise<void>;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
callback_query: {
|
|
130
|
+
session: SessionLike & {
|
|
131
|
+
$clear: () => Promise<void>;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
shipping_query: {
|
|
135
|
+
session: SessionLike & {
|
|
136
|
+
$clear: () => Promise<void>;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
pre_checkout_query: {
|
|
140
|
+
session: SessionLike & {
|
|
141
|
+
$clear: () => Promise<void>;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
poll_answer: {
|
|
145
|
+
session: SessionLike & {
|
|
146
|
+
$clear: () => Promise<void>;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
chat_join_request: {
|
|
150
|
+
session: SessionLike & {
|
|
151
|
+
$clear: () => Promise<void>;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
new_chat_members: {
|
|
155
|
+
session: SessionLike & {
|
|
156
|
+
$clear: () => Promise<void>;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
new_chat_title: {
|
|
160
|
+
session: SessionLike & {
|
|
161
|
+
$clear: () => Promise<void>;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
new_chat_photo: {
|
|
165
|
+
session: SessionLike & {
|
|
166
|
+
$clear: () => Promise<void>;
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
delete_chat_photo: {
|
|
170
|
+
session: SessionLike & {
|
|
171
|
+
$clear: () => Promise<void>;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
group_chat_created: {
|
|
175
|
+
session: SessionLike & {
|
|
176
|
+
$clear: () => Promise<void>;
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
message_auto_delete_timer_changed: {
|
|
180
|
+
session: SessionLike & {
|
|
181
|
+
$clear: () => Promise<void>;
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
migrate_to_chat_id: {
|
|
185
|
+
session: SessionLike & {
|
|
186
|
+
$clear: () => Promise<void>;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
migrate_from_chat_id: {
|
|
190
|
+
session: SessionLike & {
|
|
191
|
+
$clear: () => Promise<void>;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
pinned_message: {
|
|
195
|
+
session: SessionLike & {
|
|
196
|
+
$clear: () => Promise<void>;
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
invoice: {
|
|
200
|
+
session: SessionLike & {
|
|
201
|
+
$clear: () => Promise<void>;
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
successful_payment: {
|
|
205
|
+
session: SessionLike & {
|
|
206
|
+
$clear: () => Promise<void>;
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
chat_shared: {
|
|
210
|
+
session: SessionLike & {
|
|
211
|
+
$clear: () => Promise<void>;
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
proximity_alert_triggered: {
|
|
215
|
+
session: SessionLike & {
|
|
216
|
+
$clear: () => Promise<void>;
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
video_chat_scheduled: {
|
|
220
|
+
session: SessionLike & {
|
|
221
|
+
$clear: () => Promise<void>;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
video_chat_started: {
|
|
225
|
+
session: SessionLike & {
|
|
226
|
+
$clear: () => Promise<void>;
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
video_chat_ended: {
|
|
230
|
+
session: SessionLike & {
|
|
231
|
+
$clear: () => Promise<void>;
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
video_chat_participants_invited: {
|
|
235
|
+
session: SessionLike & {
|
|
236
|
+
$clear: () => Promise<void>;
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
web_app_data: {
|
|
240
|
+
session: SessionLike & {
|
|
241
|
+
$clear: () => Promise<void>;
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
location: {
|
|
245
|
+
session: SessionLike & {
|
|
246
|
+
$clear: () => Promise<void>;
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
passport_data: {
|
|
250
|
+
session: SessionLike & {
|
|
251
|
+
$clear: () => Promise<void>;
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
} & {
|
|
255
|
+
message: HistoryDerives;
|
|
256
|
+
callback_query: HistoryDerives;
|
|
257
|
+
}, {}>;
|
|
258
|
+
export {};
|
|
259
|
+
//# sourceMappingURL=message-history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-history.d.ts","sourceRoot":"","sources":["../../src/bot/message-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAIzC,MAAM,MAAM,YAAY,GAAG;IACzB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB,CAAA;AAED,0EAA0E;AAC1E,KAAK,WAAW,GAAG;IAAE,OAAO,CAAC,EAAE,aAAa,CAAA;CAAE,CAAA;AAE9C,wEAAwE;AACxE,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAA;AAEjF,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,OAAO,EAAE,uBAAuB,CAAA;IAChC,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;CAC9C,CAAA;AAID,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;CACrC,CAAA;AAgBD,eAAO,MAAM,cAAc,GAAI,MAAM,qBAAqB,KAAG,qBAW5D,CAAA;AAID,QAAA,MAAM,kBAAkB,GAAI,MAAM;IAChC,aAAa,EAAE,uBAAuB,CAAA;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;CACtB;YAGqD,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA8BnE,CAAA"}
|