Skip to content

Commit

Permalink
Merge pull request #41 from beabee-communityrm/feat/persistent-session
Browse files Browse the repository at this point in the history
Feat(persistent-session): Implementation
  • Loading branch information
wpf500 authored Oct 14, 2024
2 parents 5954536 + f939c20 commit fcc8171
Show file tree
Hide file tree
Showing 31 changed files with 350 additions and 376 deletions.
9 changes: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,6 @@ dist
.yarn/install-state.gz
.pnp.*

# SQLlite files
*.sqlite

# deno2node
deno_cache/

Expand All @@ -140,4 +137,8 @@ telegram-bot/telegram-bot

.credentials.json

# Temporary type build files
# Database files
*.sqlite
*.db
*.db-shm
*.db-wal
2 changes: 1 addition & 1 deletion telegram-bot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ parts of the application:
implement a new Telegram Command and the `EventManager` to implement a new
event manger.
- `data`: Contains data which can be persisted in docker volumes such the
`database.sql`.
`kv.db`.
- `deps`: In Deno it is common to import all dependencies in a central deps.ts
file, we want to follow this but due to many dependencies we have split them
cleanly in this folder.
Expand Down
27 changes: 27 additions & 0 deletions telegram-bot/adapters/key-value-storage-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { StorageAdapter } from "../deps/index.ts";

/**
* Key-value storage adapter for Deno.
* Based on https://github.com/grammyjs/storages/blob/main/packages/denokv/src/adapter.ts
* @see https://docs.deno.com/examples/kv/
* @see https://github.com/grammyjs/storages/tree/main/packages/denokv
*/
export class KeyValueStorageAdapter<T> implements StorageAdapter<T> {
constructor(
private kv: Deno.Kv,
private prefix: Deno.KvKeyPart[] = ["sessions"],
) {}

async read(key: string): Promise<T | undefined> {
const result = await this.kv.get([...this.prefix, key]);
return result.value !== null ? result.value as T : undefined;
}

async write(key: string, value: T) {
await this.kv.set([...this.prefix, key], value);
}

async delete(key: string) {
await this.kv.delete([...this.prefix, key]);
}
}
6 changes: 3 additions & 3 deletions telegram-bot/commands/reset.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ export class ResetCommand extends BaseCommand {
async action(ctx: AppContext) {
// Always allow this command to reset the state even if an error occurs, so we not use `this.checkAction` here
const session = await ctx.session;
const abortController = session._data.abortController;
const nonPersisted = await this.stateMachine.getNonPersisted(ctx);
let successful = true;

if (abortController) {
if (nonPersisted.abortController) {
// Already cancelled
if (abortController.signal.aborted) {
if (nonPersisted.abortController.signal.aborted) {
await this.communication.send(
ctx,
this.messageRenderer.resetCancelledMessage(),
Expand Down
4 changes: 2 additions & 2 deletions telegram-bot/core/base.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { ChatState } from "../enums/index.ts";
import type { I18nService } from "../services/i18n.service.ts";
import type { CommunicationService } from "../services/communication.service.ts";
import type { MessageRenderer } from "../renderer/message.renderer.ts";
import type { AppContext, StateSession } from "../types/index.ts";
import type { AppContext, SessionPersisted } from "../types/index.ts";

/**
* Base class for all bot commands
Expand Down Expand Up @@ -69,7 +69,7 @@ export abstract class BaseCommand implements BotCommand {
* @param session The current session
* @returns True if the command is usable, false otherwise
*/
public isCommandUsable(session: StateSession): boolean {
public isCommandUsable(session: SessionPersisted): boolean {
return this.visibleOnStates.length === 0 ||
this.visibleOnStates.includes(session.state);
}
Expand Down
8 changes: 4 additions & 4 deletions telegram-bot/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
}
},
"tasks": {
"dev": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi --watch ./main.ts",
"start": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi ./main.ts",
"compile": "deno compile --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi ./main.ts",
"dev": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi --unstable-kv --watch ./main.ts",
"start": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi --unstable-kv ./main.ts",
"compile": "deno compile --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi --unstable-kv ./main.ts",
"generate:index": "deno run --allow-read --allow-write scripts/generate-index.ts && deno task format",
"test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi",
"test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi --unstable-kv",
"lint": "deno lint",
"format": "deno fmt",
"check": "deno task check:types && deno task check:format",
Expand Down
67 changes: 67 additions & 0 deletions telegram-bot/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion telegram-bot/deps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ export * from "./jsonc.ts";
export * from "./markdown-it.ts";
export * from "./std.ts";
export * from "./typeorm.ts";
export * from "./valtio.ts";
2 changes: 0 additions & 2 deletions telegram-bot/deps/valtio.ts

This file was deleted.

5 changes: 5 additions & 0 deletions telegram-bot/enums/abort-controller-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum AbortControllerState {
ACTIVE = "active",
ABORTED = "aborted",
NULL = "null",
}
1 change: 1 addition & 0 deletions telegram-bot/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./abort-controller-state.ts";
export * from "./bot-command-scope.ts";
export * from "./chat-state.ts";
export * from "./i18n-event.ts";
Expand Down
20 changes: 5 additions & 15 deletions telegram-bot/event-managers/callout-response.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class CalloutResponseEventManager extends BaseEventManager {
protected async onCalloutParticipateKeyboardPressed(ctx: AppContext) {
const data = ctx.callbackQuery?.data?.split(":");
// const slug = data?.[1];
const shortSlug = data?.[1];
const id = data?.[1];
// const startResponse = data?.[2] as "continue" | "cancel" === "continue";
const startResponse =
data?.[2] as typeof TRUTHY_MESSAGE_KEY | typeof FALSY_MESSAGE_KEY ===
Expand All @@ -76,17 +76,7 @@ export class CalloutResponseEventManager extends BaseEventManager {
return;
}

if (!shortSlug) {
await this.communication.send(
ctx,
this.messageRenderer.calloutNotFound(),
);
return;
}

const slug = this.callout.getSlug(shortSlug);

if (!slug) {
if (!id) {
await this.communication.send(
ctx,
this.messageRenderer.calloutNotFound(),
Expand All @@ -97,11 +87,11 @@ export class CalloutResponseEventManager extends BaseEventManager {
console.debug(
"onCalloutParticipateKeyboardPressed",
data,
slug,
id,
// startResponse,
);

const calloutWithForm = await this.callout.get(slug, ["form"]);
const calloutWithForm = await this.callout.get(id, ["form"]);

// Render the callout with the form
const questions = this.calloutResponseRenderer
Expand Down Expand Up @@ -142,7 +132,7 @@ export class CalloutResponseEventManager extends BaseEventManager {

try {
// TODO: Ask for contact details if callout requires it
await this.callout.createResponse(slug, {
await this.callout.createResponse(id, {
answers,
//guestName: ctx.from?.username,
// guestEmail: "test@beabee.io",
Expand Down
18 changes: 4 additions & 14 deletions telegram-bot/event-managers/callout.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,18 @@ export class CalloutEventManager extends BaseEventManager {
}

protected async onShowCalloutKeyboardPressed(ctx: AppContext) {
const shortSlug = ctx.callbackQuery?.data?.split(":")[1];
const id = ctx.callbackQuery?.data?.split(":")[1];

// Remove the inline keyboard
await this.keyboard.removeInlineKeyboard(ctx);

const noSlugMessage =
"This button has not a callout slug associated with it";

if (!shortSlug) {
await ctx.reply(noSlugMessage);
return;
}

const slug = this.callout.getSlug(shortSlug);

if (!slug) {
await ctx.reply(noSlugMessage);
if (!id) {
await ctx.reply("This button has not a callout id associated with it");
return;
}

try {
const callout = await this.callout.get(slug, ["form"]);
const callout = await this.callout.get(id, ["form"]);
const calloutDetailsRender = await this.calloutRenderer.calloutDetails(
callout,
);
Expand Down
13 changes: 6 additions & 7 deletions telegram-bot/renderer/callout.renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { KeyboardService } from "../services/keyboard.service.ts";
import { I18nService } from "../services/i18n.service.ts";

import type {
CalloutDataExt,
GetCalloutDataExt,
GetCalloutDataWithExt,
Render,
Expand Down Expand Up @@ -40,17 +39,17 @@ export class CalloutRenderer {
* @returns
*/
public startResponseKeyboard(
callout: CalloutDataExt,
callout: GetCalloutDataExt,
): Render {
const keyboardMessageMd = `_${
escapeMd(this.i18n.t("bot.response.messages.calloutStartResponse"))
}_`;
const yesNoInlineKeyboard = this.keyboard.inlineYesNo(
`${INLINE_BUTTON_CALLBACK_CALLOUT_PARTICIPATE}:${callout.shortSlug}`,
`${INLINE_BUTTON_CALLBACK_CALLOUT_PARTICIPATE}:${callout.id}`,
);

const result: Render = {
key: `callout:start-response:${callout.shortSlug}`,
key: `callout:start-response:${callout.id}`,
type: RenderType.MARKDOWN,
markdown: keyboardMessageMd,
inlineKeyboard: yesNoInlineKeyboard,
Expand Down Expand Up @@ -93,7 +92,7 @@ export class CalloutRenderer {
listChar = escapeMd(listChar);

const result: Render = {
key: `callout:list:${callout.shortSlug}`,
key: `callout:list:${callout.id}`,
type: RenderType.MARKDOWN,
markdown: `${listChar} ${this.title(callout).markdown}\n`,
accepted: this.condition.replayConditionNone(),
Expand Down Expand Up @@ -160,7 +159,7 @@ export class CalloutRenderer {
*/
public title(callout: GetCalloutDataExt, withUrl = true) {
const result: Render = {
key: `callout:title:${callout.shortSlug}`,
key: `callout:title:${callout.id}`,
type: RenderType.MARKDOWN,
markdown: "",
accepted: this.condition.replayConditionNone(),
Expand Down Expand Up @@ -213,7 +212,7 @@ export class CalloutRenderer {
});

const calloutResult: Render = {
key: `callout:photo:${callout.shortSlug}`,
key: `callout:photo:${callout.id}`,
type: RenderType.PHOTO,
photo: calloutImage,
accepted: this.condition.replayConditionNone(),
Expand Down
Loading

0 comments on commit fcc8171

Please sign in to comment.