ultra-telegram-framework 1.0.3

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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/adapters/gas.d.ts +10 -0
  4. package/dist/adapters/gas.js +90 -0
  5. package/dist/adapters/node.d.ts +52 -0
  6. package/dist/adapters/node.js +150 -0
  7. package/dist/adapters/web.d.ts +4 -0
  8. package/dist/adapters/web.js +90 -0
  9. package/dist/core/base-api.d.ts +40 -0
  10. package/dist/core/base-api.js +26 -0
  11. package/dist/core/bot.d.ts +1828 -0
  12. package/dist/core/bot.js +2753 -0
  13. package/dist/core/composer.d.ts +87 -0
  14. package/dist/core/composer.js +164 -0
  15. package/dist/core/context/base-context.d.ts +24 -0
  16. package/dist/core/context/base-context.js +56 -0
  17. package/dist/core/context/reply-context.d.ts +234 -0
  18. package/dist/core/context/reply-context.js +528 -0
  19. package/dist/core/context.d.ts +8 -0
  20. package/dist/core/context.js +34 -0
  21. package/dist/core/keyboard.d.ts +76 -0
  22. package/dist/core/keyboard.js +182 -0
  23. package/dist/core/menu.d.ts +58 -0
  24. package/dist/core/menu.js +87 -0
  25. package/dist/index.d.ts +18 -0
  26. package/dist/index.js +24 -0
  27. package/dist/scenes/scene-manager.d.ts +39 -0
  28. package/dist/scenes/scene-manager.js +65 -0
  29. package/dist/scenes/stage.d.ts +8 -0
  30. package/dist/scenes/stage.js +34 -0
  31. package/dist/scenes/wizard.d.ts +18 -0
  32. package/dist/scenes/wizard.js +32 -0
  33. package/dist/session/gas-cache-storage.d.ts +34 -0
  34. package/dist/session/gas-cache-storage.js +49 -0
  35. package/dist/session/gas-hybrid-storage.d.ts +27 -0
  36. package/dist/session/gas-hybrid-storage.js +49 -0
  37. package/dist/session/gas-storage.d.ts +24 -0
  38. package/dist/session/gas-storage.js +47 -0
  39. package/dist/session/index.d.ts +28 -0
  40. package/dist/session/index.js +43 -0
  41. package/dist/session/memory-storage.d.ts +28 -0
  42. package/dist/session/memory-storage.js +35 -0
  43. package/dist/session/storage.d.ts +18 -0
  44. package/dist/session/storage.js +2 -0
  45. package/dist/types/telegram.d.ts +7560 -0
  46. package/dist/types/telegram.js +4 -0
  47. package/package.json +42 -0
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Abstract class containing common logic for all keyboard types.
3
+ * T is the button type (InlineKeyboardButton or KeyboardButton).
4
+ */
5
+ export class BaseKeyboard {
6
+ constructor() {
7
+ // Protected property (available in child classes)
8
+ this.matrix = [[]];
9
+ }
10
+ /** Starts a new row */
11
+ row() {
12
+ this.matrix.push([]);
13
+ return this;
14
+ }
15
+ /**
16
+ * Adds a custom emoji to the LAST added button.
17
+ * @param emojiId Unique emoji ID (available for Premium bots)
18
+ */
19
+ customEmoji(emojiId) {
20
+ const lastButton = this.getLastButton();
21
+ if (lastButton)
22
+ lastButton.icon_custom_emoji_id = emojiId;
23
+ return this;
24
+ }
25
+ /**
26
+ * Sets the style for the LAST added button.
27
+ * @param style 'danger' (red) | 'success' (green) | 'primary' (blue)
28
+ */
29
+ style(style) {
30
+ const lastButton = this.getLastButton();
31
+ if (lastButton)
32
+ lastButton.style = style;
33
+ return this;
34
+ }
35
+ // =====================================
36
+ // INTERNAL HELPERS
37
+ // =====================================
38
+ addButton(button) {
39
+ const currentRow = this.matrix[this.matrix.length - 1];
40
+ if (currentRow) {
41
+ currentRow.push(button);
42
+ }
43
+ return this;
44
+ }
45
+ getLastButton() {
46
+ const currentRow = this.matrix[this.matrix.length - 1];
47
+ if (!currentRow || currentRow.length === 0)
48
+ return undefined;
49
+ return currentRow[currentRow.length - 1];
50
+ }
51
+ }
52
+ // ============================================================================
53
+ // INLINE KEYBOARD (Buttons under the message)
54
+ // ============================================================================
55
+ export class InlineKeyboard extends BaseKeyboard {
56
+ text(text, callback_data) {
57
+ return this.addButton({ text, callback_data });
58
+ }
59
+ url(text, url) {
60
+ return this.addButton({ text, url });
61
+ }
62
+ webApp(text, webAppUrl) {
63
+ return this.addButton({ text, web_app: { url: webAppUrl } });
64
+ }
65
+ loginUrl(text, login_url) {
66
+ return this.addButton({ text, login_url });
67
+ }
68
+ switchInlineQuery(text, query = '') {
69
+ return this.addButton({ text, switch_inline_query: query });
70
+ }
71
+ switchInlineCurrentChat(text, query = '') {
72
+ return this.addButton({ text, switch_inline_query_current_chat: query });
73
+ }
74
+ switchInlineChosenChat(text, chosen_chat) {
75
+ return this.addButton({ text, switch_inline_query_chosen_chat: chosen_chat });
76
+ }
77
+ copyText(text, copy_text) {
78
+ return this.addButton({ text, copy_text });
79
+ }
80
+ game(text, callback_game = {}) {
81
+ return this.addButton({ text, callback_game });
82
+ }
83
+ pay(text) {
84
+ return this.addButton({ text, pay: true });
85
+ }
86
+ /**
87
+ * Special button for switching between InlineMenu pages
88
+ * @param text Button text
89
+ * @param menuId Menu ID
90
+ * @param pageId ID of the page to navigate to
91
+ */
92
+ menu(text, menuId, pageId) {
93
+ return this.addButton({ text, callback_data: `menu:${menuId}:${pageId}` });
94
+ }
95
+ /** Builds the final object (uses this.matrix from the base class) */
96
+ build() {
97
+ const cleanKeyboard = this.matrix.filter(row => row.length > 0);
98
+ return { inline_keyboard: cleanKeyboard };
99
+ }
100
+ /** Allows automatic serialization of the object to JSON */
101
+ toJSON() {
102
+ return this.build();
103
+ }
104
+ }
105
+ // ============================================================================
106
+ // REPLY KEYBOARD (Buttons instead of the phone keyboard)
107
+ // ============================================================================
108
+ export class ReplyKeyboard extends BaseKeyboard {
109
+ constructor() {
110
+ super(...arguments);
111
+ this.options = {};
112
+ }
113
+ /** Adds a regular text button */
114
+ text(text) {
115
+ return this.addButton({ text });
116
+ }
117
+ requestContact(text) {
118
+ return this.addButton({ text, request_contact: true });
119
+ }
120
+ requestLocation(text) {
121
+ return this.addButton({ text, request_location: true });
122
+ }
123
+ requestUsers(text, request_users) {
124
+ return this.addButton({ text, request_users });
125
+ }
126
+ requestChat(text, request_chat) {
127
+ return this.addButton({ text, request_chat });
128
+ }
129
+ requestPoll(text, request_poll = {}) {
130
+ return this.addButton({ text, request_poll });
131
+ }
132
+ requestManagedBot(text, request_managed_bot) {
133
+ return this.addButton({ text, request_managed_bot });
134
+ }
135
+ webApp(text, webAppUrl) {
136
+ return this.addButton({ text, web_app: { url: webAppUrl } });
137
+ }
138
+ // =====================================
139
+ // KEYBOARD SETTINGS
140
+ // =====================================
141
+ persistent(is_persistent = true) {
142
+ this.options.is_persistent = is_persistent;
143
+ return this;
144
+ }
145
+ selective(is_selective = true) {
146
+ this.options.selective = is_selective;
147
+ return this;
148
+ }
149
+ resized(is_resized = true) {
150
+ this.options.resize_keyboard = is_resized;
151
+ return this;
152
+ }
153
+ oneTime(is_one_time = true) {
154
+ this.options.one_time_keyboard = is_one_time;
155
+ return this;
156
+ }
157
+ placeholder(text) {
158
+ this.options.input_field_placeholder = text;
159
+ return this;
160
+ }
161
+ /** Builds the final object (uses this.matrix from the base class) */
162
+ build() {
163
+ const cleanKeyboard = this.matrix.filter(row => row.length > 0);
164
+ return {
165
+ keyboard: cleanKeyboard,
166
+ ...this.options
167
+ };
168
+ }
169
+ /** Allows automatic serialization of the object to JSON */
170
+ toJSON() {
171
+ return this.build();
172
+ }
173
+ // =====================================
174
+ // STATIC HELPERS
175
+ // =====================================
176
+ static remove(selective) {
177
+ return { remove_keyboard: true, selective };
178
+ }
179
+ static forceReply(selective, placeholder) {
180
+ return { force_reply: true, selective, input_field_placeholder: placeholder };
181
+ }
182
+ }
@@ -0,0 +1,58 @@
1
+ import { Context } from './context';
2
+ import { Composer, Middleware } from './composer';
3
+ import { InlineKeyboard } from './keyboard';
4
+ declare module './context' {
5
+ interface Context {
6
+ menu?: InlineMenuManager<any>;
7
+ }
8
+ }
9
+ export type MenuPageOptions = {
10
+ /** ID of the page the "Back" button leads to. If not specified, the button will not be shown. */
11
+ back?: string;
12
+ /** Text for the "Back" button (default is "⬅️ Back") */
13
+ backText?: string;
14
+ };
15
+ export type MenuPageRender<C extends Context> = (ctx: C) => Promise<{
16
+ text: string;
17
+ keyboard: InlineKeyboard;
18
+ }> | {
19
+ text: string;
20
+ keyboard: InlineKeyboard;
21
+ };
22
+ export type PageEntry<C extends Context> = {
23
+ render: MenuPageRender<C>;
24
+ options?: MenuPageOptions;
25
+ };
26
+ /**
27
+ * Class for creating interactive Inline menus.
28
+ */
29
+ export declare class InlineMenu<C extends Context = Context> extends Composer<C> {
30
+ private pages;
31
+ private id;
32
+ constructor(id?: string);
33
+ /**
34
+ * Add a page to the menu
35
+ * @param id Unique page ID
36
+ * @param renderer Function that returns text and keyboard
37
+ * @param options Page settings (e.g., Back button)
38
+ */
39
+ page(id: string, renderer: MenuPageRender<C>, options?: MenuPageOptions): this;
40
+ /**
41
+ * Main menu middleware
42
+ */
43
+ middleware(): Middleware<C>;
44
+ }
45
+ /**
46
+ * Manager for controlling menus inside handlers
47
+ */
48
+ export declare class InlineMenuManager<C extends Context = Context> {
49
+ private ctx;
50
+ private pages;
51
+ private menuId;
52
+ constructor(ctx: C, pages: Map<string, PageEntry<C>>, menuId: string);
53
+ /**
54
+ * Go to the specified menu page
55
+ */
56
+ setPage(pageId: string): Promise<void>;
57
+ url(pageId: string): string;
58
+ }
@@ -0,0 +1,87 @@
1
+ import { Composer } from './composer';
2
+ /**
3
+ * Class for creating interactive Inline menus.
4
+ */
5
+ export class InlineMenu extends Composer {
6
+ constructor(id = 'main') {
7
+ super();
8
+ this.pages = new Map();
9
+ this.id = id;
10
+ }
11
+ /**
12
+ * Add a page to the menu
13
+ * @param id Unique page ID
14
+ * @param renderer Function that returns text and keyboard
15
+ * @param options Page settings (e.g., Back button)
16
+ */
17
+ page(id, renderer, options) {
18
+ this.pages.set(id, { render: renderer, options });
19
+ return this;
20
+ }
21
+ /**
22
+ * Main menu middleware
23
+ */
24
+ middleware() {
25
+ const composerHandler = super.middleware();
26
+ return async (ctx, next) => {
27
+ var _a, _b;
28
+ // 1. Hydrate context with the menu manager
29
+ ctx.menu = new InlineMenuManager(ctx, this.pages, this.id);
30
+ // 2. First pass through Composer handlers (.action(), etc.)
31
+ let handledByComposer = false;
32
+ await composerHandler(ctx, async () => {
33
+ handledByComposer = true;
34
+ });
35
+ // If Composer caught the event - do not proceed further
36
+ if (!handledByComposer)
37
+ return;
38
+ // 3. If no handler found - check page navigation
39
+ if ((_b = (_a = ctx.callbackQuery) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.startsWith(`menu:${this.id}:`)) {
40
+ const pageId = ctx.callbackQuery.data.split(':')[2];
41
+ if (pageId) {
42
+ await ctx.menu.setPage(pageId);
43
+ return;
44
+ }
45
+ }
46
+ await next();
47
+ };
48
+ }
49
+ }
50
+ /**
51
+ * Manager for controlling menus inside handlers
52
+ */
53
+ export class InlineMenuManager {
54
+ constructor(ctx, pages, menuId) {
55
+ this.ctx = ctx;
56
+ this.pages = pages;
57
+ this.menuId = menuId;
58
+ }
59
+ /**
60
+ * Go to the specified menu page
61
+ */
62
+ async setPage(pageId) {
63
+ var _a;
64
+ const entry = this.pages.get(pageId);
65
+ if (!entry)
66
+ throw new Error(`Menu page "${pageId}" not found.`);
67
+ const { text, keyboard } = await Promise.resolve(entry.render(this.ctx));
68
+ // If a "parent" page is specified in the page settings, add a Back button
69
+ if ((_a = entry.options) === null || _a === void 0 ? void 0 : _a.back) {
70
+ keyboard.row().menu(entry.options.backText || '⬅️ Back', this.menuId, entry.options.back);
71
+ }
72
+ const rawKeyboard = keyboard.toJSON();
73
+ if (this.ctx.callbackQuery) {
74
+ // answerCbQuery is executed together with editMessage to guarantee removing the loading indicator
75
+ await Promise.all([
76
+ this.ctx.editMessage(text, { reply_markup: rawKeyboard }),
77
+ this.ctx.answerCbQuery()
78
+ ]);
79
+ }
80
+ else {
81
+ await this.ctx.reply(text, { reply_markup: rawKeyboard });
82
+ }
83
+ }
84
+ url(pageId) {
85
+ return `menu:${this.menuId}:${pageId}`;
86
+ }
87
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @link https://volodymyrdzola.github.io/tg-framework/
3
+ */
4
+ export * from './core/bot';
5
+ export * from './core/composer';
6
+ export * from './core/context';
7
+ export * from './core/base-api';
8
+ export * from './core/keyboard';
9
+ export * from './core/menu';
10
+ export * from './adapters/gas';
11
+ export * from './adapters/node';
12
+ export * from './adapters/web';
13
+ export * from './session/index';
14
+ export * from './scenes/stage';
15
+ export * from './scenes/wizard';
16
+ export * from './scenes/scene-manager';
17
+ export * from './types/telegram';
18
+ export * from './core/bot';
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @link https://volodymyrdzola.github.io/tg-framework/
3
+ */
4
+ // CORE
5
+ export * from './core/bot';
6
+ export * from './core/composer';
7
+ export * from './core/context';
8
+ export * from './core/base-api';
9
+ export * from './core/keyboard';
10
+ export * from './core/menu';
11
+ // ADAPTERS
12
+ export * from './adapters/gas';
13
+ export * from './adapters/node';
14
+ export * from './adapters/web';
15
+ // SESSIONS & STORAGE
16
+ export * from './session/index';
17
+ // SCENES
18
+ export * from './scenes/stage';
19
+ export * from './scenes/wizard';
20
+ export * from './scenes/scene-manager';
21
+ // TYPES
22
+ export * from './types/telegram';
23
+ // ALIASES
24
+ export * from './core/bot';
@@ -0,0 +1,39 @@
1
+ export interface SceneSessionData {
2
+ name?: string;
3
+ step?: number;
4
+ state?: Record<string, any>;
5
+ }
6
+ export declare class SceneManager {
7
+ private ctx;
8
+ constructor(ctx: {
9
+ session?: Record<string, any>;
10
+ });
11
+ /**
12
+ * Protected access to the system scene session object.
13
+ */
14
+ get session(): SceneSessionData;
15
+ /**
16
+ * Storage for temporary data of a specific scene.
17
+ * Cleared when leaving the scene.
18
+ */
19
+ get state(): Record<string, any>;
20
+ set state(value: Record<string, any>);
21
+ /**
22
+ * Enter a new scene
23
+ * @param name Scene name
24
+ * @param initialState Initial state (optional)
25
+ */
26
+ enter(name: string, initialState?: Record<string, any>): void;
27
+ /**
28
+ * Leave the current scene (resets FSM)
29
+ */
30
+ leave(): void;
31
+ /**
32
+ * Go to the next step in the Wizard scene
33
+ */
34
+ next(): void;
35
+ /**
36
+ * Go to a specific step by its index
37
+ */
38
+ selectStep(index: number): void;
39
+ }
@@ -0,0 +1,65 @@
1
+ // src/scenes/scene-manager.ts
2
+ export class SceneManager {
3
+ constructor(ctx) {
4
+ this.ctx = ctx;
5
+ }
6
+ /**
7
+ * Protected access to the system scene session object.
8
+ */
9
+ get session() {
10
+ var _a, _b;
11
+ var _c, _d;
12
+ (_a = (_c = this.ctx).session) !== null && _a !== void 0 ? _a : (_c.session = {});
13
+ (_b = (_d = this.ctx.session).__scene) !== null && _b !== void 0 ? _b : (_d.__scene = {});
14
+ return this.ctx.session.__scene;
15
+ }
16
+ /**
17
+ * Storage for temporary data of a specific scene.
18
+ * Cleared when leaving the scene.
19
+ */
20
+ get state() {
21
+ var _a;
22
+ var _b;
23
+ (_a = (_b = this.session).state) !== null && _a !== void 0 ? _a : (_b.state = {});
24
+ return this.session.state;
25
+ }
26
+ set state(value) {
27
+ this.session.state = value;
28
+ }
29
+ /**
30
+ * Enter a new scene
31
+ * @param name Scene name
32
+ * @param initialState Initial state (optional)
33
+ */
34
+ enter(name, initialState = {}) {
35
+ var _a;
36
+ var _b;
37
+ (_a = (_b = this.ctx).session) !== null && _a !== void 0 ? _a : (_b.session = {});
38
+ this.ctx.session.__scene = {
39
+ name,
40
+ step: 0,
41
+ state: initialState,
42
+ };
43
+ }
44
+ /**
45
+ * Leave the current scene (resets FSM)
46
+ */
47
+ leave() {
48
+ if (this.ctx.session && this.ctx.session.__scene) {
49
+ delete this.ctx.session.__scene;
50
+ }
51
+ }
52
+ /**
53
+ * Go to the next step in the Wizard scene
54
+ */
55
+ next() {
56
+ var _a;
57
+ this.session.step = ((_a = this.session.step) !== null && _a !== void 0 ? _a : 0) + 1;
58
+ }
59
+ /**
60
+ * Go to a specific step by its index
61
+ */
62
+ selectStep(index) {
63
+ this.session.step = index;
64
+ }
65
+ }
@@ -0,0 +1,8 @@
1
+ import { Composer, Middleware } from '../core/composer';
2
+ import { SceneContext, WizardScene } from './wizard';
3
+ export declare class Stage<C extends SceneContext> extends Composer<C> {
4
+ private scenes;
5
+ constructor(scenes?: WizardScene<C>[]);
6
+ register(scene: WizardScene<C>): void;
7
+ middleware(): Middleware<C>;
8
+ }
@@ -0,0 +1,34 @@
1
+ // src/scenes/stage.ts
2
+ import { Composer } from '../core/composer';
3
+ import { SceneManager } from './scene-manager';
4
+ export class Stage extends Composer {
5
+ constructor(scenes = []) {
6
+ super();
7
+ this.scenes = new Map();
8
+ scenes.forEach(scene => this.register(scene));
9
+ }
10
+ register(scene) {
11
+ this.scenes.set(scene.name, scene);
12
+ }
13
+ middleware() {
14
+ const globalHandler = super.middleware();
15
+ return async (ctx, next) => {
16
+ // 1. Hydrate context with scene manager
17
+ ctx.scene = new SceneManager(ctx);
18
+ const activeSceneName = ctx.scene.session.name;
19
+ // 2. If the user is NOT in a scene, pass the event to global bot handlers
20
+ if (!activeSceneName) {
21
+ return globalHandler(ctx, next);
22
+ }
23
+ // 3. If the scene is active, find it
24
+ const scene = this.scenes.get(activeSceneName);
25
+ if (!scene) {
26
+ // Protection against broken sessions: if the scene has been deleted from the code, exit
27
+ ctx.scene.leave();
28
+ return globalHandler(ctx, next);
29
+ }
30
+ // 4. Pass control to the active scene
31
+ await scene.middleware()(ctx, next);
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,18 @@
1
+ import { Composer, Middleware } from '../core/composer';
2
+ import { Context } from '../core/context';
3
+ import { SceneManager } from './scene-manager';
4
+ import { SessionData } from '../session';
5
+ export interface SceneContext extends Context {
6
+ session: SessionData;
7
+ scene: SceneManager;
8
+ }
9
+ export declare class WizardScene<C extends SceneContext> extends Composer<C> {
10
+ readonly name: string;
11
+ private steps;
12
+ /**
13
+ * @param name Unique name of the scene
14
+ * @param steps Handler functions for each step (Step 0, Step 1, ...)
15
+ */
16
+ constructor(name: string, ...steps: Middleware<C>[]);
17
+ middleware(): Middleware<C>;
18
+ }
@@ -0,0 +1,32 @@
1
+ // src/scenes/wizard.ts
2
+ import { Composer } from '../core/composer';
3
+ export class WizardScene extends Composer {
4
+ /**
5
+ * @param name Unique name of the scene
6
+ * @param steps Handler functions for each step (Step 0, Step 1, ...)
7
+ */
8
+ constructor(name, ...steps) {
9
+ super();
10
+ this.name = name;
11
+ this.steps = steps;
12
+ }
13
+ middleware() {
14
+ const globalHandler = super.middleware();
15
+ return async (ctx, next) => {
16
+ var _a;
17
+ let handledGlobally = false;
18
+ await globalHandler(ctx, async () => {
19
+ handledGlobally = true;
20
+ });
21
+ if (!handledGlobally)
22
+ return;
23
+ const currentStepIndex = (_a = ctx.scene.session.step) !== null && _a !== void 0 ? _a : 0;
24
+ const stepHandler = this.steps[currentStepIndex];
25
+ if (!stepHandler) {
26
+ ctx.scene.leave();
27
+ return next();
28
+ }
29
+ await stepHandler(ctx, next);
30
+ };
31
+ }
32
+ }
@@ -0,0 +1,34 @@
1
+ import { Storage } from './storage';
2
+ /**
3
+ * Session storage for Google Apps Script based on CacheService.
4
+ * Fast, but data lives for a maximum of 6 hours (21600 seconds).
5
+ */
6
+ export declare class CacheStorage<T> implements Storage<T> {
7
+ private cache;
8
+ private prefix;
9
+ private ttl;
10
+ /**
11
+ * Fast session storage for Google Apps Script based on CacheService.
12
+ * Data lives for a maximum of 6 hours (21600 seconds).
13
+ * @param cache Which cache service to use (ScriptCache by default)
14
+ * @param ttl Time to live in seconds (21600 - 6 hours by default)
15
+ * @param prefix Prefix for keys
16
+ */
17
+ constructor(cache?: GoogleAppsScript.Cache.Cache, ttl?: number, prefix?: string);
18
+ /**
19
+ * Gets the session.
20
+ * @param key Session key
21
+ */
22
+ get(key: string): T | undefined;
23
+ /**
24
+ * Saves the session.
25
+ * @param key Session key
26
+ * @param value Session value
27
+ */
28
+ set(key: string, value: T): void;
29
+ /**
30
+ * Deletes the session.
31
+ * @param key Session key
32
+ */
33
+ delete(key: string): void;
34
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Session storage for Google Apps Script based on CacheService.
3
+ * Fast, but data lives for a maximum of 6 hours (21600 seconds).
4
+ */
5
+ export class CacheStorage {
6
+ /**
7
+ * Fast session storage for Google Apps Script based on CacheService.
8
+ * Data lives for a maximum of 6 hours (21600 seconds).
9
+ * @param cache Which cache service to use (ScriptCache by default)
10
+ * @param ttl Time to live in seconds (21600 - 6 hours by default)
11
+ * @param prefix Prefix for keys
12
+ */
13
+ constructor(cache = CacheService.getScriptCache(), ttl = 21600, prefix = 'session:') {
14
+ this.cache = cache;
15
+ this.ttl = ttl;
16
+ this.prefix = prefix;
17
+ }
18
+ /**
19
+ * Gets the session.
20
+ * @param key Session key
21
+ */
22
+ get(key) {
23
+ const raw = this.cache.get(this.prefix + key);
24
+ if (!raw)
25
+ return undefined;
26
+ try {
27
+ return JSON.parse(raw);
28
+ }
29
+ catch (e) {
30
+ return undefined;
31
+ }
32
+ }
33
+ /**
34
+ * Saves the session.
35
+ * @param key Session key
36
+ * @param value Session value
37
+ */
38
+ set(key, value) {
39
+ const raw = JSON.stringify(value);
40
+ this.cache.put(this.prefix + key, raw, this.ttl);
41
+ }
42
+ /**
43
+ * Deletes the session.
44
+ * @param key Session key
45
+ */
46
+ delete(key) {
47
+ this.cache.remove(this.prefix + key);
48
+ }
49
+ }
@@ -0,0 +1,27 @@
1
+ import { Storage } from './storage';
2
+ /**
3
+ * Hybrid storage for Google Apps Script (CacheService + PropertiesService).
4
+ * Combines cache speed and properties reliability.
5
+ */
6
+ export declare class GasHybridStorage<T> implements Storage<T> {
7
+ private cache;
8
+ private properties;
9
+ /**
10
+ * Hybrid storage for Google Apps Script (CacheService + PropertiesService).
11
+ * Combines cache speed and properties reliability.
12
+ * @param options Configuration options
13
+ * @param options.cache Which cache service to use (ScriptCache by default)
14
+ * @param options.properties Which properties service to use (ScriptProperties by default)
15
+ * @param options.ttl Time to live in seconds (21600 - 6 hours by default)
16
+ * @param options.prefix Prefix for keys
17
+ */
18
+ constructor(options?: {
19
+ cache?: GoogleAppsScript.Cache.Cache;
20
+ properties?: GoogleAppsScript.Properties.Properties;
21
+ ttl?: number;
22
+ prefix?: string;
23
+ });
24
+ get(key: string): Promise<T | undefined>;
25
+ set(key: string, value: T): Promise<void>;
26
+ delete(key: string): Promise<void>;
27
+ }