From b9ca39c543bd3e0fda3ecdc31bdd04d4b3974e88 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 9 Feb 2024 18:08:23 +0100 Subject: [PATCH 01/17] feat(validation): Basic scaffolding for callout validation started --- .../services/callout-communication.service.ts | 24 +++++++++++++++++++ .../services/callout-condition.service.ts | 16 +++++++++++++ telegram-bot/services/condition.service.ts | 8 +++---- 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 telegram-bot/services/callout-communication.service.ts create mode 100644 telegram-bot/services/callout-condition.service.ts diff --git a/telegram-bot/services/callout-communication.service.ts b/telegram-bot/services/callout-communication.service.ts new file mode 100644 index 0000000..db355e0 --- /dev/null +++ b/telegram-bot/services/callout-communication.service.ts @@ -0,0 +1,24 @@ +import { Singleton } from "../deps.ts"; +import { EventService } from "./event.service.ts"; +import { TransformService } from "./transform.service.ts"; +import { CalloutConditionService } from "./callout-condition.service.ts"; +import { CommunicationService } from "./communication.service.ts"; +import { MessageRenderer } from "../renderer/message.renderer.ts"; + +/** + * Service to handle the callout replay communication with the telegram bot and the telegram user. + * This service waits for a response until a response is received that fulfils a CalloutComponentSchema condition. + * TODO: Currently not implemented. + */ +@Singleton() +export class CalloutCommunicationService extends CommunicationService { + constructor( + protected readonly event: EventService, + protected readonly messageRenderer: MessageRenderer, + protected readonly transform: TransformService, + protected readonly condition: CalloutConditionService, + ) { + super(event, messageRenderer, transform, condition); + console.debug(`${this.constructor.name} created`); + } +} diff --git a/telegram-bot/services/callout-condition.service.ts b/telegram-bot/services/callout-condition.service.ts new file mode 100644 index 0000000..a78d25c --- /dev/null +++ b/telegram-bot/services/callout-condition.service.ts @@ -0,0 +1,16 @@ +import { Singleton } from "../deps.ts"; + +import { ConditionService } from "./condition.service.ts"; + +/** + * Define and check `CalloutComponentSchema` conditions for a replay. + * This class checks replays messages for `CalloutComponentSchema` conditions. + * TODO: Currently not implemented. + */ +@Singleton() +export class CalloutConditionService extends ConditionService { + constructor() { + super(); + console.debug(`${this.constructor.name} created`); + } +} diff --git a/telegram-bot/services/condition.service.ts b/telegram-bot/services/condition.service.ts index a7d707a..b591daf 100644 --- a/telegram-bot/services/condition.service.ts +++ b/telegram-bot/services/condition.service.ts @@ -2,14 +2,12 @@ import { Singleton } from "../deps.ts"; import { RelayAcceptedFileType, ReplayType } from "../enums/index.ts"; import { extractNumbers, + filterMimeTypesByPatterns, getFileIdFromMessage, + getSimpleMimeTypes, getTextFromMessage, isNumber, } from "../utils/index.ts"; -import { - filterMimeTypesByPatterns, - getSimpleMimeTypes, -} from "../utils/index.ts"; import type { Message, @@ -25,7 +23,7 @@ import type { ReplayConditionSelection, ReplayConditionText, } from "../types/index.ts"; -import { Context } from "../types/grammy.ts"; +import type { Context } from "../types/grammy.ts"; /** * Define and check conditions for a replay. From 17aa9591c14bcf109d90a99100f1247c9c33640d Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Wed, 28 Feb 2024 19:47:49 +0100 Subject: [PATCH 02/17] feat(validation): Make use of calloutComponentValidator from beabee-common --- beabee-client/deno.json | 5 +- beabee-client/src/deps.ts | 1 + beabee-client/src/types/content-join.ts | 7 +- beabee-client/src/types/index.ts | 1 - beabee-client/src/types/stripe-fee-country.ts | 1 - beabee-common | 2 +- telegram-bot-deno.code-workspace | 11 +- telegram-bot/deno.json | 3 +- telegram-bot/deps.ts | 159 +------ telegram-bot/deps/alosaur.ts | 1 + telegram-bot/deps/ammonia.ts | 7 + telegram-bot/deps/beabee-client.ts | 1 + telegram-bot/deps/beabee-common.ts | 1 + telegram-bot/deps/djwt.ts | 2 + telegram-bot/deps/googleapis.ts | 1 + telegram-bot/deps/grammy.ts | 1 + telegram-bot/deps/index.ts | 11 + telegram-bot/deps/jsonc.ts | 1 + telegram-bot/deps/markdown-it.ts | 5 + telegram-bot/deps/std.ts | 9 + telegram-bot/deps/typeorm.ts | 1 + telegram-bot/enums/parsed-response-type.ts | 7 +- telegram-bot/enums/replay-type.ts | 2 + .../renderer/callout-response.renderer.ts | 106 ++--- telegram-bot/renderer/callout.renderer.ts | 5 +- telegram-bot/scripts/generate-index.ts | 1 + .../services/callout-communication.service.ts | 24 - .../services/callout-condition.service.ts | 16 - .../services/communication.service.ts | 30 +- telegram-bot/services/condition.service.ts | 341 +------------ telegram-bot/services/transform.service.ts | 177 +++---- telegram-bot/services/validation.service.ts | 450 ++++++++++++++++++ telegram-bot/types/index.ts | 4 +- .../types/render-response-parsed-address.ts | 12 - ...ender-response-parsed-callout-component.ts | 13 + telegram-bot/types/render-response-parsed.ts | 6 +- telegram-bot/types/replay-accepted-base.ts | 4 +- ...eplay-accepted-callout-component-schema.ts | 11 + telegram-bot/types/replay-accepted-file.ts | 2 + telegram-bot/types/replay-accepted-text.ts | 2 +- telegram-bot/types/replay-accepted.ts | 4 +- ...play-condition-callout-component-schema.ts | 14 + telegram-bot/types/replay-condition.ts | 4 +- telegram-bot/utils/callouts.ts | 23 +- telegram-bot/utils/number.ts | 4 +- 45 files changed, 744 insertions(+), 749 deletions(-) delete mode 100644 beabee-client/src/types/stripe-fee-country.ts create mode 100644 telegram-bot/deps/alosaur.ts create mode 100644 telegram-bot/deps/ammonia.ts create mode 100644 telegram-bot/deps/beabee-client.ts create mode 100644 telegram-bot/deps/beabee-common.ts create mode 100644 telegram-bot/deps/djwt.ts create mode 100644 telegram-bot/deps/googleapis.ts create mode 100644 telegram-bot/deps/grammy.ts create mode 100644 telegram-bot/deps/index.ts create mode 100644 telegram-bot/deps/jsonc.ts create mode 100644 telegram-bot/deps/markdown-it.ts create mode 100644 telegram-bot/deps/std.ts create mode 100644 telegram-bot/deps/typeorm.ts delete mode 100644 telegram-bot/services/callout-communication.service.ts delete mode 100644 telegram-bot/services/callout-condition.service.ts create mode 100644 telegram-bot/services/validation.service.ts delete mode 100644 telegram-bot/types/render-response-parsed-address.ts create mode 100644 telegram-bot/types/render-response-parsed-callout-component.ts create mode 100644 telegram-bot/types/replay-accepted-callout-component-schema.ts create mode 100644 telegram-bot/types/replay-condition-callout-component-schema.ts diff --git a/beabee-client/deno.json b/beabee-client/deno.json index f76a023..9519444 100644 --- a/beabee-client/deno.json +++ b/beabee-client/deno.json @@ -13,10 +13,11 @@ "test:deno": "deno test --allow-net --allow-read --allow-write --allow-env --allow-ffi ./test/deno", "test:node": "npm run test:node", "test:web": "npm run test:web", - "lint": "deno lint", + "lint": "deno lint mod.ts src", "format": "deno fmt", - "check": "deno task check:format", + "check": "deno task check:types && deno task check:format", "check:format": "deno task format --check", + "check:types": "deno check mod.ts", "cache": "deno task cache:deno && deno task cache:node", "cache:deno": "deno cache --reload mod.ts", "cache:node": "npm install", diff --git a/beabee-client/src/deps.ts b/beabee-client/src/deps.ts index dac25aa..5b51885 100644 --- a/beabee-client/src/deps.ts +++ b/beabee-client/src/deps.ts @@ -16,4 +16,5 @@ export type { PaymentStatus, RoleType, RuleGroup, + StripeFeeCountry, } from "@beabee/beabee-common"; diff --git a/beabee-client/src/types/content-join.ts b/beabee-client/src/types/content-join.ts index 86eec86..0a90793 100644 --- a/beabee-client/src/types/content-join.ts +++ b/beabee-client/src/types/content-join.ts @@ -1,5 +1,8 @@ -import type { ContributionPeriod, PaymentMethod } from "../deps.ts"; -import type { StripeFeeCountry } from "./index.ts"; +import type { + ContributionPeriod, + PaymentMethod, + StripeFeeCountry, +} from "../deps.ts"; export interface ContentJoin { title: string; diff --git a/beabee-client/src/types/index.ts b/beabee-client/src/types/index.ts index 62ff6b0..4e41867 100644 --- a/beabee-client/src/types/index.ts +++ b/beabee-client/src/types/index.ts @@ -74,7 +74,6 @@ export * from "./set-contribution-data.ts"; export * from "./start-contribution-data.ts"; export * from "./stripe-bacs-payment-source.ts"; export * from "./stripe-card-payment-source.ts"; -export * from "./stripe-fee-country.ts"; export * from "./stripe-sepa-payment-source.ts"; export * from "./update-callout-data.ts"; export * from "./update-callout-response-comment-data.ts"; diff --git a/beabee-client/src/types/stripe-fee-country.ts b/beabee-client/src/types/stripe-fee-country.ts deleted file mode 100644 index 9779d1b..0000000 --- a/beabee-client/src/types/stripe-fee-country.ts +++ /dev/null @@ -1 +0,0 @@ -export type StripeFeeCountry = "eu" | "gb" | "ca"; diff --git a/beabee-common b/beabee-common index 1714d3e..4b1e89c 160000 --- a/beabee-common +++ b/beabee-common @@ -1 +1 @@ -Subproject commit 1714d3ec5ff5905e3f4b714b559beb75398be563 +Subproject commit 4b1e89c1a2bf29a491defb1f15b4350032dfabe5 diff --git a/telegram-bot-deno.code-workspace b/telegram-bot-deno.code-workspace index 6f4389f..f00e8bc 100644 --- a/telegram-bot-deno.code-workspace +++ b/telegram-bot-deno.code-workspace @@ -2,7 +2,7 @@ "folders": [ { "path": ".", - "name": "root" + "name": "_root" }, { "path": "./telegram-bot", @@ -11,6 +11,10 @@ { "path": "./beabee-client", "name": "beabee-client" + }, + { + "path": "./beabee-common", + "name": "beabee-common" } ], "settings": { @@ -23,6 +27,8 @@ "denodb", "selectboxes" ], + "prettier.enable": false, + "eslint.enable": false, "deno.enable": true, "deno.lint": true, "deno.unstable": true, @@ -35,13 +41,12 @@ "./beabee-client/jest.config.web.js", "./beabee-client/jest.config.node.js", "./beabee-common/dist", - "./beabee-common/scripts", "./beabee-common/test/node", "./beabee-common/jest.config.js" ], "editor.defaultFormatter": "denoland.vscode-deno", "editor.formatOnSave": true, - "typescript.format.enable": true, + "typescript.format.enable": true }, "launch": { "version": "0.2.0", diff --git a/telegram-bot/deno.json b/telegram-bot/deno.json index 67c0398..ffc7e80 100644 --- a/telegram-bot/deno.json +++ b/telegram-bot/deno.json @@ -31,8 +31,9 @@ "test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi", "lint": "deno lint", "format": "deno fmt", - "check": "deno task check:format", + "check": "deno task check:types && deno task check:format", "check:format": "deno task format --check", + "check:types": "deno check main.ts", "cache": "deno cache --reload main.ts", "docker:build": "docker build -f ../Dockerfile -t beabee/telegram-bot:latest ..", "docker:start": "docker run -it --init -p 3003:3003 beabee/telegram-bot", diff --git a/telegram-bot/deps.ts b/telegram-bot/deps.ts index f890b38..56ded58 100644 --- a/telegram-bot/deps.ts +++ b/telegram-bot/deps.ts @@ -1,158 +1 @@ -// AMMONIA - -export { - Ammonia, - AmmoniaBuilder, - clean as ammoniaClean, - cleanText as ammoniaCleanText, - init as ammoniaInit, -} from "https://deno.land/x/ammonia@0.3.1/mod.ts"; - -// DENO-STD - -import { db as mediaTypeDb } from "https://deno.land/std@0.208.0/media_types/_db.ts"; -import * as _mediaTypes from "https://deno.land/std@0.208.0/media_types/mod.ts"; -export const mediaTypes = { ..._mediaTypes, db: mediaTypeDb }; - -export { - dirname, - fromFileUrl, - join, -} from "https://deno.land/std@0.211.0/path/mod.ts"; - -// GOOGLEAPIS - -export { google, sheets_v4 } from "npm:googleapis@131.0.0"; - -// ALOSAUR - -export { container, Singleton } from "alosaur/mod.ts"; - -// MARKDOWN-IT - -// @deno-types="npm:@types/markdown-it" -import MarkdownIt from "npm:markdown-it"; -import type MarkdownRenderer from "npm:@types/markdown-it/lib/renderer"; - -export { MarkdownIt, MarkdownRenderer }; - -// GRAMMY - -export { Bot, InlineKeyboard } from "grammy/mod.ts"; - -// TYPEORM - -export { DataSource } from "typeorm"; - -// BEABEE - -export { - ApiError, - CalloutClient, - CalloutResponseClient, - ContentClient, -} from "../beabee-client/mod.ts"; - -export type { - CalloutData, - Content, - ContentId, - CreateCalloutResponseData, - GetCalloutData, - GetCalloutDataWith, - GetCalloutsQuery, - GetCalloutWith, -} from "../beabee-client/mod.ts"; - -export { - CalloutComponentBaseType, - calloutComponentContentValidator, - calloutComponentInputAddressValidator, - calloutComponentInputCheckboxValidator, - calloutComponentInputCurrencyValidator, - calloutComponentInputDateTimeValidator, - calloutComponentInputEmailValidator, - calloutComponentInputFileValidator, - calloutComponentInputNumberValidator, - calloutComponentInputPhoneNumberValidator, - calloutComponentInputSelectableTypes, - calloutComponentInputSelectableValidator, - calloutComponentInputSelectValidator, - calloutComponentInputSignatureValidator, - calloutComponentInputTextTypes, - calloutComponentInputTextValidator, - calloutComponentInputTimeValidator, - calloutComponentInputTypes, - calloutComponentInputUrlValidator, - calloutComponentNestableTypes, - calloutComponentNestableValidator, - CalloutComponentType, - calloutComponentTypes, - calloutComponentValidator, - isCalloutComponentOfBaseType, - isCalloutComponentOfType, - ItemStatus, -} from "../beabee-common/mod.ts"; - -export type { - CalloutComponentBaseInputSchema, - CalloutComponentBaseInputSelectableSchema, - CalloutComponentBaseInputTextSchema, - CalloutComponentBaseMap, - CalloutComponentBaseNestableSchema, - CalloutComponentBaseRules, - CalloutComponentBaseSchema, - CalloutComponentContentSchema, - CalloutComponentInputAddressRules, - CalloutComponentInputAddressSchema, - CalloutComponentInputCheckboxRules, - CalloutComponentInputCheckboxSchema, - CalloutComponentInputCurrencyRules, - CalloutComponentInputCurrencySchema, - CalloutComponentInputDateTimeRules, - CalloutComponentInputDateTimeSchema, - CalloutComponentInputEmailRules, - CalloutComponentInputEmailSchema, - CalloutComponentInputFileRules, - CalloutComponentInputFileSchema, - CalloutComponentInputNumberRules, - CalloutComponentInputNumberSchema, - CalloutComponentInputPhoneNumberRules, - CalloutComponentInputPhoneNumberSchema, - CalloutComponentInputSchema, - CalloutComponentInputSelectableRadioRules, - CalloutComponentInputSelectableRadioSchema, - CalloutComponentInputSelectableSchema, - CalloutComponentInputSelectableSelectboxesSchema, - CalloutComponentInputSelectRules, - CalloutComponentInputSelectSchema, - CalloutComponentInputSignatureRules, - CalloutComponentInputSignatureSchema, - CalloutComponentInputTextAreaSchema, - CalloutComponentInputTextFieldSchema, - CalloutComponentInputTextRules, - CalloutComponentInputTextSchema, - CalloutComponentInputTimeRules, - CalloutComponentInputTimeSchema, - CalloutComponentInputUrlRules, - CalloutComponentInputUrlSchema, - CalloutComponentMap, - CalloutComponentNestablePanelSchema, - CalloutComponentNestableSchema, - CalloutComponentNestableTabsSchema, - CalloutComponentNestableWellSchema, - CalloutComponentSchema, - CalloutComponentSelectboxesRules, - CalloutResponseAnswer, - CalloutResponseAnswerAddress, - CalloutResponseAnswerFileUpload, - CalloutResponseAnswersNestable, - CalloutResponseAnswersSlide, - CalloutSlideSchema, - Paginated, -} from "../beabee-common/mod.ts"; - -export { parse as parseJsonc } from "https://deno.land/x/jsonc@1/main.ts"; - -// JSON Web Token -export * as djwt from "https://deno.land/x/djwt@v3.0.1/mod.ts"; +export * from "./deps/index.ts"; diff --git a/telegram-bot/deps/alosaur.ts b/telegram-bot/deps/alosaur.ts new file mode 100644 index 0000000..2cffb3e --- /dev/null +++ b/telegram-bot/deps/alosaur.ts @@ -0,0 +1 @@ +export { container, Singleton } from "alosaur/mod.ts"; diff --git a/telegram-bot/deps/ammonia.ts b/telegram-bot/deps/ammonia.ts new file mode 100644 index 0000000..c2ea7cf --- /dev/null +++ b/telegram-bot/deps/ammonia.ts @@ -0,0 +1,7 @@ +export { + Ammonia, + AmmoniaBuilder, + clean as ammoniaClean, + cleanText as ammoniaCleanText, + init as ammoniaInit, +} from "https://deno.land/x/ammonia@0.3.1/mod.ts"; diff --git a/telegram-bot/deps/beabee-client.ts b/telegram-bot/deps/beabee-client.ts new file mode 100644 index 0000000..f548c2c --- /dev/null +++ b/telegram-bot/deps/beabee-client.ts @@ -0,0 +1 @@ +export * from "../../beabee-client/mod.ts"; diff --git a/telegram-bot/deps/beabee-common.ts b/telegram-bot/deps/beabee-common.ts new file mode 100644 index 0000000..d73535e --- /dev/null +++ b/telegram-bot/deps/beabee-common.ts @@ -0,0 +1 @@ +export * from "../../beabee-common/mod.ts"; diff --git a/telegram-bot/deps/djwt.ts b/telegram-bot/deps/djwt.ts new file mode 100644 index 0000000..07fbe39 --- /dev/null +++ b/telegram-bot/deps/djwt.ts @@ -0,0 +1,2 @@ +// JSON Web Token +export * as djwt from "https://deno.land/x/djwt@v3.0.1/mod.ts"; diff --git a/telegram-bot/deps/googleapis.ts b/telegram-bot/deps/googleapis.ts new file mode 100644 index 0000000..e07985d --- /dev/null +++ b/telegram-bot/deps/googleapis.ts @@ -0,0 +1 @@ +export { google, sheets_v4 } from "npm:googleapis@131.0.0"; diff --git a/telegram-bot/deps/grammy.ts b/telegram-bot/deps/grammy.ts new file mode 100644 index 0000000..8ed604a --- /dev/null +++ b/telegram-bot/deps/grammy.ts @@ -0,0 +1 @@ +export { Bot, InlineKeyboard } from "grammy/mod.ts"; diff --git a/telegram-bot/deps/index.ts b/telegram-bot/deps/index.ts new file mode 100644 index 0000000..8ec8ba6 --- /dev/null +++ b/telegram-bot/deps/index.ts @@ -0,0 +1,11 @@ +export * from "./alosaur.ts"; +export * from "./ammonia.ts"; +export * from "./beabee-client.ts"; +export * from "./beabee-common.ts"; +export * from "./djwt.ts"; +export * from "./googleapis.ts"; +export * from "./grammy.ts"; +export * from "./jsonc.ts"; +export * from "./markdown-it.ts"; +export * from "./std.ts"; +export * from "./typeorm.ts"; diff --git a/telegram-bot/deps/jsonc.ts b/telegram-bot/deps/jsonc.ts new file mode 100644 index 0000000..5b9b6f4 --- /dev/null +++ b/telegram-bot/deps/jsonc.ts @@ -0,0 +1 @@ +export { parse as parseJsonc } from "https://deno.land/x/jsonc@1/main.ts"; diff --git a/telegram-bot/deps/markdown-it.ts b/telegram-bot/deps/markdown-it.ts new file mode 100644 index 0000000..1c2b548 --- /dev/null +++ b/telegram-bot/deps/markdown-it.ts @@ -0,0 +1,5 @@ +// @deno-types="npm:@types/markdown-it" +import MarkdownIt from "npm:markdown-it"; +import type MarkdownRenderer from "npm:@types/markdown-it/lib/renderer"; + +export { MarkdownIt, MarkdownRenderer }; diff --git a/telegram-bot/deps/std.ts b/telegram-bot/deps/std.ts new file mode 100644 index 0000000..191f29f --- /dev/null +++ b/telegram-bot/deps/std.ts @@ -0,0 +1,9 @@ +import { db as mediaTypeDb } from "https://deno.land/std@0.208.0/media_types/_db.ts"; +import * as _mediaTypes from "https://deno.land/std@0.208.0/media_types/mod.ts"; +export const mediaTypes = { ..._mediaTypes, db: mediaTypeDb }; + +export { + dirname, + fromFileUrl, + join, +} from "https://deno.land/std@0.211.0/path/mod.ts"; diff --git a/telegram-bot/deps/typeorm.ts b/telegram-bot/deps/typeorm.ts new file mode 100644 index 0000000..0e5a71d --- /dev/null +++ b/telegram-bot/deps/typeorm.ts @@ -0,0 +1 @@ +export { DataSource } from "typeorm"; diff --git a/telegram-bot/enums/parsed-response-type.ts b/telegram-bot/enums/parsed-response-type.ts index 2728c86..f6b293c 100644 --- a/telegram-bot/enums/parsed-response-type.ts +++ b/telegram-bot/enums/parsed-response-type.ts @@ -4,12 +4,7 @@ export enum ParsedResponseType { NUMBER = "number", BOOLEAN = "boolean", SELECTION = "selection", - ADDRESS = "address", - - // TODO: implement these? - // DATE = "date", - // TIME = "time", - // ... + CALLOUT_COMPONENT = "callout-component", ANY = "any", NONE = "none", } diff --git a/telegram-bot/enums/replay-type.ts b/telegram-bot/enums/replay-type.ts index 8b8632b..2d3fa86 100644 --- a/telegram-bot/enums/replay-type.ts +++ b/telegram-bot/enums/replay-type.ts @@ -7,6 +7,8 @@ export enum ReplayType { ANY = "any", /** Select one of one or more options */ SELECTION = "selection", + /** Accept or wait for callout answer replay */ + CALLOUT_COMPONENT_SCHEMA = "callout-component-schema", /** No answer is needed */ NONE = "none", } diff --git a/telegram-bot/renderer/callout-response.renderer.ts b/telegram-bot/renderer/callout-response.renderer.ts index 1393029..39cbc74 100644 --- a/telegram-bot/renderer/callout-response.renderer.ts +++ b/telegram-bot/renderer/callout-response.renderer.ts @@ -84,10 +84,7 @@ export class CalloutResponseRenderer { /** * Render a component description in Markdown */ - protected descriptionMd( - component: CalloutComponentSchema, - prefix: string, - ) { + protected descriptionMd(component: CalloutComponentSchema, prefix: string) { if (typeof component.description !== "string" || !component.description) { return EMPTY_RENDER; } @@ -107,10 +104,7 @@ export class CalloutResponseRenderer { * Render an input component placeholder in Markdown * @param input The input component to render */ - protected placeholderMd( - input: CalloutComponentInputSchema, - prefix: string, - ) { + protected placeholderMd(input: CalloutComponentInputSchema, prefix: string) { const result: Render = { key: createCalloutGroupKey(input.key, prefix), type: RenderType.MARKDOWN, @@ -123,7 +117,9 @@ export class CalloutResponseRenderer { if (placeholder) { result.markdown = `_${ - escapeMd(this.i18n.t("info.messages.placeholder", { placeholder })) + escapeMd( + this.i18n.t("info.messages.placeholder", { placeholder }), + ) }_`; } @@ -235,28 +231,23 @@ export class CalloutResponseRenderer { key: createCalloutGroupKey(base.key, prefix), type: RenderType.MARKDOWN, markdown: ``, - accepted: this.condition.replayConditionText( + accepted: this.condition.replayConditionCalloutConponent( multiple, - undefined, + base, multiple ? [this.i18n.t("reactions.messages.done")] : [], ), - parseType: calloutComponentTypeToParsedResponseType(base), + parseType: ParsedResponseType.CALLOUT_COMPONENT, }; // Label const label = this.labelMd(base, prefix); - if ( - label.type === RenderType.MARKDOWN && label.markdown - ) { + if (label.type === RenderType.MARKDOWN && label.markdown) { result.markdown += `${label.markdown}\n`; } // Description const desc = this.descriptionMd(base, prefix); - if ( - desc.type === RenderType.MARKDOWN && - desc.markdown - ) { + if (desc.type === RenderType.MARKDOWN && desc.markdown) { result.markdown += `${desc.markdown}\n`; } @@ -293,10 +284,7 @@ export class CalloutResponseRenderer { ); if (file.placeholder) { - result.markdown += `\n\n${ - this.placeholderMd(file, prefix) - .markdown - }`; + result.markdown += `\n\n${this.placeholderMd(file, prefix).markdown}`; } if (multiple) { @@ -306,6 +294,18 @@ export class CalloutResponseRenderer { return result; } + /** + * Render an input signature component in Markdown + * @param signature The input signature component to render + * @param prefix The prefix, used to group the answers later (only used to group slides) + */ + protected inputSignatureComponent( + signature: CalloutComponentInputSignatureSchema, + prefix: string, + ) { + return this.inputFileComponent(signature, prefix); + } + /** A content component only prints a text and does not expect an answer */ protected contentComponent( content: CalloutComponentContentSchema, @@ -325,7 +325,7 @@ export class CalloutResponseRenderer { type: RenderType.HTML, html, accepted: this.condition.replayConditionNone(false), - parseType: calloutComponentTypeToParsedResponseType(content), + parseType: ParsedResponseType.NONE, }; return result; @@ -336,10 +336,7 @@ export class CalloutResponseRenderer { * @param input The input component to render * @param prefix The prefix, used to group the answers later (only used to group slides) */ - protected inputComponent( - input: CalloutComponentInputSchema, - prefix: string, - ) { + protected inputComponent(input: CalloutComponentInputSchema, prefix: string) { const result = this.baseComponent(input, prefix); result.markdown += `\n\n`; @@ -382,6 +379,7 @@ export class CalloutResponseRenderer { : this.i18n.t("info.messages.only-one-email-allowed"), ) }_`; + break; } case CalloutComponentType.INPUT_NUMBER: { @@ -510,7 +508,8 @@ export class CalloutResponseRenderer { case "selectboxes": { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.multiple-selections-allowed") + "\n\n" + + this.i18n.t("info.messages.multiple-selections-allowed") + + "\n\n" + this.messageRenderer.writeDoneMessage( this.i18n.t("reactions.messages.done"), ).text, @@ -589,30 +588,26 @@ export class CalloutResponseRenderer { const results: Render[] = []; if (isCalloutComponentOfType(component, CalloutComponentType.CONTENT)) { - results.push(this.contentComponent( - component, - prefix, - )); + results.push(this.contentComponent(component, prefix)); return results; } if (isCalloutComponentOfType(component, CalloutComponentType.INPUT_FILE)) { - results.push(this.inputFileComponent( - component, - prefix, - )); + results.push(this.inputFileComponent(component, prefix)); + return results; + } + + if ( + isCalloutComponentOfType(component, CalloutComponentType.INPUT_SIGNATURE) + ) { + results.push(this.inputSignatureComponent(component, prefix)); return results; } if ( isCalloutComponentOfType(component, CalloutComponentType.INPUT_SELECT) ) { - results.push( - this.selectComponent( - component, - prefix, - ), - ); + results.push(this.selectComponent(component, prefix)); return results; } @@ -622,36 +617,21 @@ export class CalloutResponseRenderer { CalloutComponentBaseType.INPUT_SELECTABLE, ) ) { - results.push( - this.selectableComponent( - component, - prefix, - ), - ); + results.push(this.selectableComponent(component, prefix)); return results; } if ( isCalloutComponentOfBaseType(component, CalloutComponentBaseType.NESTABLE) ) { - results.push( - ...this.nestableComponent( - component, - prefix, - ), - ); + results.push(...this.nestableComponent(component, prefix)); return results; } if ( isCalloutComponentOfBaseType(component, CalloutComponentBaseType.INPUT) ) { - results.push( - this.inputComponent( - component, - prefix, - ), - ); + results.push(this.inputComponent(component, prefix)); return results; } @@ -725,9 +705,7 @@ export class CalloutResponseRenderer { * @param callout The callout to render * @returns */ - public full( - callout: GetCalloutDataWithExt<"form">, - ) { + public full(callout: GetCalloutDataWithExt<"form">) { const form = callout.formSchema; const slidesRenders: Render[] = []; diff --git a/telegram-bot/renderer/callout.renderer.ts b/telegram-bot/renderer/callout.renderer.ts index 3821df1..d7d7496 100644 --- a/telegram-bot/renderer/callout.renderer.ts +++ b/telegram-bot/renderer/callout.renderer.ts @@ -11,10 +11,11 @@ import { I18nService } from "../services/i18n.service.ts"; import type { CalloutDataExt, GetCalloutDataExt, - Paginated, Render, } from "../types/index.ts"; +import type { Paginated } from "../deps.ts"; + /** * Render callouts for Telegram in Markdown */ @@ -147,7 +148,7 @@ export class CalloutRenderer { } /** - * Render a callout as a photo. + * Render a callout with photo. * * @param callout * @returns diff --git a/telegram-bot/scripts/generate-index.ts b/telegram-bot/scripts/generate-index.ts index c1be125..d939e44 100644 --- a/telegram-bot/scripts/generate-index.ts +++ b/telegram-bot/scripts/generate-index.ts @@ -5,6 +5,7 @@ const paths = [ "./controllers", "./constants", "./core", + "./deps", "./enums", "./event-managers", "./models", diff --git a/telegram-bot/services/callout-communication.service.ts b/telegram-bot/services/callout-communication.service.ts deleted file mode 100644 index db355e0..0000000 --- a/telegram-bot/services/callout-communication.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Singleton } from "../deps.ts"; -import { EventService } from "./event.service.ts"; -import { TransformService } from "./transform.service.ts"; -import { CalloutConditionService } from "./callout-condition.service.ts"; -import { CommunicationService } from "./communication.service.ts"; -import { MessageRenderer } from "../renderer/message.renderer.ts"; - -/** - * Service to handle the callout replay communication with the telegram bot and the telegram user. - * This service waits for a response until a response is received that fulfils a CalloutComponentSchema condition. - * TODO: Currently not implemented. - */ -@Singleton() -export class CalloutCommunicationService extends CommunicationService { - constructor( - protected readonly event: EventService, - protected readonly messageRenderer: MessageRenderer, - protected readonly transform: TransformService, - protected readonly condition: CalloutConditionService, - ) { - super(event, messageRenderer, transform, condition); - console.debug(`${this.constructor.name} created`); - } -} diff --git a/telegram-bot/services/callout-condition.service.ts b/telegram-bot/services/callout-condition.service.ts deleted file mode 100644 index a78d25c..0000000 --- a/telegram-bot/services/callout-condition.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Singleton } from "../deps.ts"; - -import { ConditionService } from "./condition.service.ts"; - -/** - * Define and check `CalloutComponentSchema` conditions for a replay. - * This class checks replays messages for `CalloutComponentSchema` conditions. - * TODO: Currently not implemented. - */ -@Singleton() -export class CalloutConditionService extends ConditionService { - constructor() { - super(); - console.debug(`${this.constructor.name} created`); - } -} diff --git a/telegram-bot/services/communication.service.ts b/telegram-bot/services/communication.service.ts index 013f2ac..6f61902 100644 --- a/telegram-bot/services/communication.service.ts +++ b/telegram-bot/services/communication.service.ts @@ -3,6 +3,7 @@ import { ParsedResponseType, RenderType, ReplayType } from "../enums/index.ts"; import { EventService } from "./event.service.ts"; import { TransformService } from "./transform.service.ts"; import { ConditionService } from "./condition.service.ts"; +import { ValidationService } from "./validation.service.ts"; import { getIdentifier } from "../utils/index.ts"; import { MessageRenderer } from "../renderer/message.renderer.ts"; @@ -26,6 +27,7 @@ export class CommunicationService { protected readonly messageRenderer: MessageRenderer, protected readonly transform: TransformService, protected readonly condition: ConditionService, + protected readonly validation: ValidationService, ) { console.debug(`${this.constructor.name} created`); } @@ -81,10 +83,7 @@ export class CommunicationService { * @param render * @returns */ - protected async acceptedUntilSpecificMessage( - ctx: Context, - render: Render, - ) { + protected async acceptedUntilSpecificMessage(ctx: Context, render: Render) { let context: Context; let message: Message | undefined; const replays: ReplayAccepted[] = []; @@ -104,9 +103,9 @@ export class CommunicationService { continue; } - replayAccepted = this.condition.messageIsAccepted( - render.accepted, + replayAccepted = this.validation.messageIsAccepted( context, + render.accepted, ); if (!replayAccepted.accepted) { @@ -146,9 +145,7 @@ export class CommunicationService { render: Render, ): Promise> { // Do not wait for a specific message - if ( - !render.accepted || render.accepted.type === ReplayType.NONE - ) { + if (!render.accepted || render.accepted.type === ReplayType.NONE) { return { type: ParsedResponseType.NONE, multiple: false, @@ -158,10 +155,7 @@ export class CommunicationService { } // Receive all messages of specific type until a message of specific type is received - const replays = await this.acceptedUntilSpecificMessage( - ctx, - render, - ); + const replays = await this.acceptedUntilSpecificMessage(ctx, render); // Parse multiple messages if (render.accepted.multiple) { @@ -195,10 +189,7 @@ export class CommunicationService { public async sendAndReceive(ctx: Context, render: Render) { await this.send(ctx, render); - const responses = await this.receive( - ctx, - render, - ); + const responses = await this.receive(ctx, render); const response: RenderResponse = { render, responses, @@ -211,10 +202,7 @@ export class CommunicationService { * @param ctx * @param renders */ - public async sendAndReceiveAll( - ctx: Context, - renders: Render[], - ) { + public async sendAndReceiveAll(ctx: Context, renders: Render[]) { const responses: RenderResponse[] = []; for (const render of renders) { const response = await this.sendAndReceive(ctx, render); diff --git a/telegram-bot/services/condition.service.ts b/telegram-bot/services/condition.service.ts index b591daf..a5b5a2a 100644 --- a/telegram-bot/services/condition.service.ts +++ b/telegram-bot/services/condition.service.ts @@ -1,33 +1,18 @@ -import { Singleton } from "../deps.ts"; -import { RelayAcceptedFileType, ReplayType } from "../enums/index.ts"; -import { - extractNumbers, - filterMimeTypesByPatterns, - getFileIdFromMessage, - getSimpleMimeTypes, - getTextFromMessage, - isNumber, -} from "../utils/index.ts"; +import { CalloutComponentSchema, Singleton } from "../deps.ts"; +import { ReplayType } from "../enums/index.ts"; +import { filterMimeTypesByPatterns } from "../utils/index.ts"; import type { - Message, - ReplayAccepted, - ReplayAcceptedFile, - ReplayAcceptedNone, - ReplayAcceptedSelection, - ReplayAcceptedText, - ReplayCondition, ReplayConditionAny, + ReplayConditionCalloutComponentSchema, ReplayConditionFile, ReplayConditionNone, ReplayConditionSelection, ReplayConditionText, } from "../types/index.ts"; -import type { Context } from "../types/grammy.ts"; /** - * Define and check conditions for a replay. - * This class checks replays messages for general conditions which can also be used for other things than Callouts. + * Define conditions for a replay. */ @Singleton() export class ConditionService { @@ -147,314 +132,16 @@ export class ConditionService { return this.replayConditionFile(multiple, mimeTypes, doneTexts); } - protected messageIsAudioFile(message: Message) { - return !!message.audio?.file_id || !!message.voice?.file_id || - message.document?.mime_type?.startsWith("audio"); - } - - protected messageIsPhotoFile(message: Message) { - return !!message.photo?.length || - message.document?.mime_type?.startsWith("image"); - } - - protected messageIsVideoFile(message: Message) { - return !!message.video?.file_id || !!message.animation?.file_id || - message.document?.mime_type?.startsWith("video"); - } - - protected messageIsDocumentFile(message: Message) { - return !!message.document?.file_id; - } - - protected messageIsContact(message: Message) { - return !!message.contact; - } - - protected messageIsLocation(message: Message) { - return !!message.venue?.location || !!message.location; - } - - protected messageIsAddress(message: Message) { - return !!message.venue?.address; // + message.venue?.title - } - - protected messageIsAnyFile(message: Message) { - return this.messageIsPhotoFile(message) || - this.messageIsDocumentFile(message) || - this.messageIsVideoFile(message) || - this.messageIsAudioFile(message) || false; - // TODO: - // this.messageIsLocation(message) || - // this.messageIsContact(message) || - // this.messageIsAddress(message); - } - - /** - * Check if a message is a file message and if the mime type is accepted - */ - protected messageIsFile( - context: Context, - accepted: ReplayConditionFile, - ): ReplayAcceptedFile { - const message = context.message; - let fileType: RelayAcceptedFileType = RelayAcceptedFileType.ANY; - // Is not a file message - if (!message) { - console.warn("Message is undefined"); - return { - type: ReplayType.FILE, - accepted: false, - fileType, - isDone: false, - context, - }; - } - // Is a file message and all mime types are accepted - if (!accepted.mimeTypes || !accepted.mimeTypes.length) { - const isAccepted = this.messageIsAnyFile(message); - return { - type: ReplayType.FILE, - accepted: isAccepted, - isDone: isAccepted && !accepted.multiple, - context, - }; - } - const simpleTypes = getSimpleMimeTypes(accepted.mimeTypes); - const photoAccepted = simpleTypes.some((m) => m === "image"); - const documentAccepted = simpleTypes.some((m) => m === "document"); - const videoAccepted = simpleTypes.some((m) => m === "video"); - const audioAccepted = simpleTypes.some((m) => m === "audio"); - // TODO: Can we do this this way? Note: We can reply directly with the location using Telegram - const locationAccepted = simpleTypes.some((m) => m === "location"); - // TODO: What is the mime type for contact? Note: We can share contacts in Telegram - const contactAccepted = simpleTypes.some((m) => m === "contact"); - // TODO: Can we do this this way? Note: We can reply directly with the location using Telegram - const addressAccepted = simpleTypes.some((m) => m === "address"); - - if (photoAccepted && this.messageIsPhotoFile(message)) { - fileType = RelayAcceptedFileType.PHOTO; - } - if (documentAccepted && this.messageIsDocumentFile(message)) { - fileType = RelayAcceptedFileType.DOCUMENT; - } - if (videoAccepted && this.messageIsVideoFile(message)) { - fileType = RelayAcceptedFileType.VIDEO; // or animation or document with mime type video - } - if (audioAccepted && this.messageIsAudioFile(message)) { - fileType = RelayAcceptedFileType.AUDIO; // or voice or document with mime type audio - } - - // TODO: This are not file types... - if (locationAccepted && this.messageIsLocation(message)) { - fileType = RelayAcceptedFileType.LOCATION; - } - if (contactAccepted && this.messageIsContact(message)) { - fileType = RelayAcceptedFileType.CONTACT; - } - if (addressAccepted && this.messageIsAddress(message)) { - fileType = RelayAcceptedFileType.ADDRESS; - } - - // A file message with accepted mime type - const isAccepted = fileType !== RelayAcceptedFileType.ANY; - return { - type: ReplayType.FILE, - accepted: isAccepted, - isDone: isAccepted && !accepted.multiple, - fileType, - fileId: getFileIdFromMessage(message), - context, - }; - } - - protected messageIsDoneText( - context: Context, - accepted: ReplayCondition, - ): ReplayAcceptedText | ReplayAcceptedNone { - // Capitalisation should not play a role for done messages, see https://github.com/beabee-communityrm/telegram-bot/issues/5 - const textMessage = getTextFromMessage(context.message)?.toLowerCase() - .trim(); - - // Is not a text message or empty - if (!textMessage) { - return { - type: ReplayType.NONE, - accepted: false, - isDone: false, - context, - }; - } - - if (accepted.multiple && accepted.doneTexts?.length) { - const doneTexts = accepted.doneTexts.map((t) => t.toLowerCase().trim()); - const isDone = doneTexts.some((t) => t === textMessage); - if (isDone) { - return { - type: ReplayType.TEXT, - accepted: true, - isDone, - text: textMessage, - context, - }; - } - } - - return { - type: ReplayType.TEXT, - accepted: false, - isDone: false, - context, - }; - } - - /** - * Check if a message is a text message and if the text is accepted - */ - protected messageIsText( - context: Context, - accepted: ReplayConditionText, - ): ReplayAcceptedText { - const message = context.message; - const texts = accepted.texts?.map((t) => t.toLowerCase().trim()); - const originalText = message?.text?.trim(); - if (message?.text) { - message.text = message.text.toLowerCase().trim(); - } - - // Is not a text message - if (!message || !message.text) { - return { - type: ReplayType.TEXT, - accepted: false, - isDone: false, - text: originalText, - context, - }; - } - - // Is a text message and all texts are accepted - if (!texts || !texts.length) { - return { - type: ReplayType.TEXT, - accepted: true, - isDone: !accepted.multiple, - text: originalText, - context, - }; - } - // Is a text message and one of the texts is accepted - // TODO: This is the DONE_MESSAGE, we should make this clear - return { - type: ReplayType.TEXT, - accepted: true, - isDone: texts.some((t) => t === message.text), - text: originalText, - context, - }; - } - - /** - * A selection message is accepted if the message is a number - * and the number is in the range of the options - * or the message is the value of the option. - * @param message - * @param accepted - * @returns - */ - protected messageIsSelection( - context: Context, - accepted: ReplayConditionSelection, - ): ReplayAcceptedSelection { - const message = context.message; - const text = getTextFromMessage(message).toLowerCase(); - - // The answer message can be the index of the value but starts with 1 - if (isNumber(text)) { - const index1 = extractNumbers(text); - const keys = Object.keys(accepted.valueLabel); - const value = keys[index1 - 1]; - if (value) { - return { - type: ReplayType.SELECTION, - accepted: true, - isDone: !accepted.multiple, - value, - context, - }; - } - } - - // The answer message can be the value directly - const acceptedValue = Object.keys(accepted.valueLabel).find( - (key) => accepted.valueLabel[key].toLowerCase() === text, - ); - const isAccepted = !!acceptedValue; + public replayConditionCalloutConponent( + multiple: boolean, + schema: CalloutComponentSchema, + doneTexts: string[] = [], + ): ReplayConditionCalloutComponentSchema { return { - type: ReplayType.SELECTION, - accepted: isAccepted, - isDone: isAccepted && !accepted.multiple, - value: acceptedValue, - context, + type: ReplayType.CALLOUT_COMPONENT_SCHEMA, + multiple, + doneTexts, + schema, }; } - - public messageIsAccepted( - accepted: ReplayCondition, - context: Context, - ): ReplayAccepted { - // If multiple messages are accepted, check if the message is a done text - if (accepted.multiple && accepted.doneTexts.length) { - const isText = this.messageIsDoneText(context, accepted); - if (isText.isDone) { - return isText; - } - } - - // Any response is accepted - if ( - accepted.type === ReplayType.ANY - ) { - return { - type: ReplayType.ANY, - accepted: true, - isDone: !accepted.multiple, - context, - }; - } - - // No response is accepted - if (accepted.type === ReplayType.NONE) { - return { - type: ReplayType.NONE, - accepted: false, - isDone: true, - context, - }; - } - - // File response is accepted - if (accepted.type === ReplayType.FILE) { - const isFile = this.messageIsFile(context, accepted); - return isFile; - } - - // Text response is accepted - if (accepted.type === ReplayType.TEXT) { - const isText = this.messageIsText(context, accepted); - return isText; - } - - // Selection response is accepted - if (accepted.type === ReplayType.SELECTION) { - const isSelection = this.messageIsSelection( - context, - accepted, - ); - return isSelection; - } - - throw new Error( - `Unknown replay until type: "${(accepted as ReplayCondition)?.type}"`, - ); - } } diff --git a/telegram-bot/services/transform.service.ts b/telegram-bot/services/transform.service.ts index 334320c..f52c2e9 100644 --- a/telegram-bot/services/transform.service.ts +++ b/telegram-bot/services/transform.service.ts @@ -15,7 +15,6 @@ import type { Render, RenderResponse, RenderResponseParsed, - RenderResponseParsedAddress, RenderResponseParsedAny, RenderResponseParsedBoolean, RenderResponseParsedFile, @@ -30,6 +29,9 @@ import type { CalloutResponseAnswer, CalloutResponseAnswersSlide, } from "../deps.ts"; +import { ReplayAcceptedCalloutComponentSchema } from "../types/index.ts"; +import { Context } from "../types/grammy.ts"; +import { CalloutResponseAnswerAddress } from "../../beabee-client/src/deps.ts"; /** * Service to transform message responses @@ -41,9 +43,9 @@ export class TransformService { } public parseResponseFile( - replay: ReplayAccepted, + context: Context, ): RenderResponseParsedFile["data"] { - const fileId = getFileIdFromMessage(replay.context.message); + const fileId = getFileIdFromMessage(context.message); if (!fileId) { throw new Error("No file id found in message"); } @@ -54,58 +56,50 @@ export class TransformService { } public parseResponsesFile( - replays: ReplayAccepted[], + contexts: Context[], ): RenderResponseParsedFile["data"] { - replays = Array.isArray(replays) ? replays : [replays]; - const res = replays.map(this.parseResponseFile); + contexts = Array.isArray(contexts) ? contexts : [contexts]; + const res = contexts.map(this.parseResponseFile); return res; } public parseResponseText( - replay: ReplayAccepted, + context: Context, ): RenderResponseParsedText["data"] { - return getTextFromMessage(replay.context.message); + return getTextFromMessage(context.message); } public parseResponsesText( - replays: ReplayAccepted[], + contexts: Context[], ): RenderResponseParsedText["data"] { - replays = Array.isArray(replays) ? replays : [replays]; - const texts = replays.filter((replay) => replay.context.message?.text) - .map(( - replay, - ) => getTextFromMessage(replay.context.message)); + contexts = Array.isArray(contexts) ? contexts : [contexts]; + const texts = contexts + .filter((context) => context.message?.text) + .map((context) => getTextFromMessage(context.message)); return texts; } public parseResponseSelection( replay: ReplayAccepted, - render: Render, + valueLabel: Record, otherFalse = true, ): RenderResponseParsedSelection["data"] { - if (render.accepted.type !== ReplayType.SELECTION) { - throw new Error( - `Unsupported accepted type for selection: "${render.parseType}"`, - ); - } - const res: RenderResponseParsedSelection["data"] = {}; - if (replay.type !== ReplayType.SELECTION || !replay.value) { - console.warn( - `Unsupported replay type for multi selection: "${render.accepted.type}"`, + if (replay.type !== ReplayType.SELECTION) { + throw new Error( + `Unsupported accepted type for selection: "${replay.type}"`, ); - const value = getTextFromMessage(replay.context.message); - res[value] = true; - return res; } - res[replay.value] = true; + if (replay.value) { + res[replay.value] = true; + } // Set all values to false that are not selected if (otherFalse) { - for (const value of Object.keys(render.accepted.valueLabel)) { + for (const value of Object.keys(valueLabel)) { res[value] ||= false; } } @@ -127,9 +121,14 @@ export class TransformService { let res: RenderResponseParsedSelection["data"] = {}; for ( - const ctx of replays.filter((replay) => replay.context.message?.text) + const rpl of replays.filter( + (replay) => replay.context.message?.text, + ) ) { - res = { ...res, ...this.parseResponseSelection(ctx, render, false) }; + res = { + ...res, + ...this.parseResponseSelection(rpl, render.accepted.valueLabel, false), + }; } // Set all values to false that are not selected @@ -141,9 +140,9 @@ export class TransformService { } public parseResponseBoolean( - replay: ReplayAccepted, + context: Context, ): RenderResponseParsedBoolean["data"] { - const boolStr = getTextFromMessage(replay.context.message).toLowerCase(); + const boolStr = getTextFromMessage(context.message).toLowerCase(); let bool = false; const truthyStr = this.i18n.t("reactions.messages.truthy").toLowerCase(); const falsyStr = this.i18n.t("reactions.messages.falsy").toLowerCase(); @@ -158,42 +157,43 @@ export class TransformService { } public parseResponsesBoolean( - replays: ReplayAccepted[], + contexts: Context[], ): RenderResponseParsedBoolean["data"] { - replays = Array.isArray(replays) ? replays : [replays]; - const booleans = replays.filter((replay) => replay.context.message?.text) - .map( - this.parseResponseBoolean, - ); + contexts = Array.isArray(contexts) ? contexts : [contexts]; + const booleans = contexts + .filter((context) => context.message?.text) + .map(this.parseResponseBoolean); return booleans; } public parseResponseNumber( - replay: ReplayAccepted, + context: Context, ): RenderResponseParsedNumber["data"] { - const text = getTextFromMessage(replay.context.message); + const text = getTextFromMessage(context.message); return extractNumbers(text); } public parseResponsesNumber( - replays: ReplayAccepted[], + contexts: Context[], ): RenderResponseParsedNumber["data"] { - replays = Array.isArray(replays) ? replays : [replays]; - const texts = replays.filter((replay) => replay.context.message?.text).map( - this.parseResponseNumber, - ); + contexts = Array.isArray(contexts) ? contexts : [contexts]; + const texts = contexts + .filter((context) => context.message?.text) + .map(this.parseResponseNumber); return texts; } - public parseResponseAddress( - replay: ReplayAccepted, - ): RenderResponseParsedAddress["data"] { - const location = getLocationFromMessage(replay.context.message); - const address: RenderResponseParsedAddress["data"] = { - formatted_address: getTextFromMessage(replay.context.message) || - location.address || location.title || "", + public parseResponseCalloutComponentAddress( + context: Context, + ): CalloutResponseAnswerAddress { + const location = getLocationFromMessage(context.message); + const address: CalloutResponseAnswerAddress = { + formatted_address: getTextFromMessage(context.message) || + location.address || + location.title || + "", geometry: { location: { lat: location.location?.latitude || 0, @@ -204,31 +204,30 @@ export class TransformService { return address; } - public parseResponsesAddress( - replays: ReplayAccepted[], - ): RenderResponseParsedAddress["data"] { - replays = Array.isArray(replays) ? replays : [replays]; - const addresses: RenderResponseParsedAddress["data"] = replays - .filter((replay) => replay.context.message?.text).map( - this.parseResponseAddress, - ); + public parseResponsesCalloutComponentAddress( + contexts: Context[], + ): CalloutResponseAnswerAddress[] { + contexts = Array.isArray(contexts) ? contexts : [contexts]; + const addresses: CalloutResponseAnswerAddress[] = contexts + .filter((context) => context.message?.text) + .map(this.parseResponseCalloutComponentAddress); return addresses; } public parseResponseAny( - context: ReplayAccepted, + context: Context, ): RenderResponseParsedAny["data"] { return this.parseResponseText(context) || this.parseResponseFile(context); } public parseResponsesAny( - replays: ReplayAccepted[], + contexts: Context[], ): RenderResponseParsedAny["data"] { - replays = Array.isArray(replays) ? replays : [replays]; - return replays.filter((replay) => replay.context.message?.text).map( - this.parseResponseAny, - ); + contexts = Array.isArray(contexts) ? contexts : [contexts]; + return contexts + .filter((context) => context.message?.text) + .map(this.parseResponseAny); } public responseNone(): RenderResponseParsedNone["data"] { @@ -248,20 +247,26 @@ export class TransformService { render: Render, ): RenderResponseParsed["data"] { switch (render.parseType) { + case ParsedResponseType.CALLOUT_COMPONENT: + // Already parsed for validation + return (replay as ReplayAcceptedCalloutComponentSchema).answer; case ParsedResponseType.FILE: - return this.parseResponseFile(replay); + return this.parseResponseFile(replay.context); case ParsedResponseType.TEXT: - return this.parseResponseText(replay); + return this.parseResponseText(replay.context); case ParsedResponseType.SELECTION: - return this.parseResponseSelection(replay, render); + if (render.accepted.type !== ReplayType.SELECTION) { + throw new Error( + `Unsupported accepted type for selection: "${render.accepted.type}"`, + ); + } + return this.parseResponseSelection(replay, render.accepted.valueLabel); case ParsedResponseType.BOOLEAN: - return this.parseResponseBoolean(replay); + return this.parseResponseBoolean(replay.context); case ParsedResponseType.NUMBER: - return this.parseResponseNumber(replay); - case ParsedResponseType.ADDRESS: - return this.parseResponseAddress(replay); + return this.parseResponseNumber(replay.context); case ParsedResponseType.ANY: - return this.parseResponseAny(replay); + return this.parseResponseAny(replay.context); case ParsedResponseType.NONE: return this.responseNone(); default: @@ -277,21 +282,25 @@ export class TransformService { replays: ReplayAccepted[], render: Render, ): RenderResponseParsed["data"] { + const contexts = replays.map((replay) => replay.context); switch (render.parseType) { + case ParsedResponseType.CALLOUT_COMPONENT: + // Already parsed for validation + return (replays as ReplayAcceptedCalloutComponentSchema[]).map( + (r) => r.answer, + ); case ParsedResponseType.FILE: - return this.parseResponsesFile(replays); + return this.parseResponsesFile(contexts); case ParsedResponseType.TEXT: - return this.parseResponsesText(replays); + return this.parseResponsesText(contexts); case ParsedResponseType.SELECTION: return this.parseResponsesSelection(replays, render); case ParsedResponseType.BOOLEAN: - return this.parseResponsesBoolean(replays); + return this.parseResponsesBoolean(contexts); case ParsedResponseType.NUMBER: - return this.parseResponsesNumber(replays); - case ParsedResponseType.ADDRESS: - return this.parseResponsesAddress(replays); + return this.parseResponsesNumber(contexts); case ParsedResponseType.ANY: - return this.parseResponsesAny(replays); + return this.parseResponsesAny(contexts); case ParsedResponseType.NONE: return this.responsesNone(); default: @@ -334,9 +343,7 @@ export class TransformService { * @param responses The responses to convert * @returns */ - public parseCalloutFormResponses( - responses: RenderResponse[], - ) { + public parseCalloutFormResponses(responses: RenderResponse[]) { const slideResponses = this.groupResponsesByGroupKey(responses); const answers: CalloutResponseAnswersSlide = {}; for (const [slideId, responses] of Object.entries(slideResponses)) { diff --git a/telegram-bot/services/validation.service.ts b/telegram-bot/services/validation.service.ts new file mode 100644 index 0000000..53dd743 --- /dev/null +++ b/telegram-bot/services/validation.service.ts @@ -0,0 +1,450 @@ +import { + CalloutComponentType, + calloutComponentValidator, + Singleton, +} from "../deps.ts"; +import { RelayAcceptedFileType, ReplayType } from "../enums/index.ts"; +import { + extractNumbers, + getFileIdFromMessage, + getSimpleMimeTypes, + getTextFromMessage, + isNumber, +} from "../utils/index.ts"; + +import { TransformService } from "./transform.service.ts"; + +import type { + Message, + ReplayAccepted, + ReplayAcceptedFile, + ReplayAcceptedNone, + ReplayAcceptedSelection, + ReplayAcceptedText, + ReplayCondition, + ReplayConditionCalloutComponentSchema, + ReplayConditionFile, + ReplayConditionSelection, + ReplayConditionText, +} from "../types/index.ts"; +import type { Context } from "../types/grammy.ts"; +import { ReplayAcceptedCalloutComponentSchema } from "../types/replay-accepted-callout-component-schema.ts"; + +/** + * Check conditions for a replay. + * This class checks replays messages for conditions which can also be used for other things than Callouts. + */ +@Singleton() +export class ValidationService { + constructor(protected readonly transform: TransformService) { + console.debug(`${this.constructor.name} created`); + } + + protected messageIsAudioFile(message: Message) { + return ( + !!message.audio?.file_id || + !!message.voice?.file_id || + message.document?.mime_type?.startsWith("audio") + ); + } + + protected messageIsPhotoFile(message: Message) { + return ( + !!message.photo?.length || + message.document?.mime_type?.startsWith("image") + ); + } + + protected messageIsVideoFile(message: Message) { + return ( + !!message.video?.file_id || + !!message.animation?.file_id || + message.document?.mime_type?.startsWith("video") + ); + } + + protected messageIsDocumentFile(message: Message) { + return !!message.document?.file_id; + } + + protected messageIsContact(message: Message) { + return !!message.contact; + } + + protected messageIsLocation(message: Message) { + return !!message.venue?.location || !!message.location; + } + + protected messageIsAddress(message: Message) { + return !!message.venue?.address; // + message.venue?.title + } + + protected messageIsAnyFile(message: Message) { + return ( + this.messageIsPhotoFile(message) || + this.messageIsDocumentFile(message) || + this.messageIsVideoFile(message) || + this.messageIsAudioFile(message) || + false + ); + // TODO: + // this.messageIsLocation(message) || + // this.messageIsContact(message) || + // this.messageIsAddress(message); + } + + /** + * Check if a message is a file message and if the mime type is accepted + */ + protected messageIsFile( + context: Context, + accepted: ReplayConditionFile, + ): ReplayAcceptedFile { + const message = context.message; + let fileType: RelayAcceptedFileType = RelayAcceptedFileType.ANY; + // Is not a file message + if (!message) { + console.warn("Message is undefined"); + return { + type: ReplayType.FILE, + accepted: false, + fileType, + isDone: false, + context, + }; + } + // Is a file message and all mime types are accepted + if (!accepted.mimeTypes || !accepted.mimeTypes.length) { + const isAccepted = this.messageIsAnyFile(message); + return { + type: ReplayType.FILE, + accepted: isAccepted, + isDone: isAccepted && !accepted.multiple, + context, + }; + } + const simpleTypes = getSimpleMimeTypes(accepted.mimeTypes); + const photoAccepted = simpleTypes.some((m) => m === "image"); + const documentAccepted = simpleTypes.some((m) => m === "document"); + const videoAccepted = simpleTypes.some((m) => m === "video"); + const audioAccepted = simpleTypes.some((m) => m === "audio"); + // TODO: Can we do this this way? Note: We can reply directly with the location using Telegram + const locationAccepted = simpleTypes.some((m) => m === "location"); + // TODO: What is the mime type for contact? Note: We can share contacts in Telegram + const contactAccepted = simpleTypes.some((m) => m === "contact"); + // TODO: Can we do this this way? Note: We can reply directly with the location using Telegram + const addressAccepted = simpleTypes.some((m) => m === "address"); + + if (photoAccepted && this.messageIsPhotoFile(message)) { + fileType = RelayAcceptedFileType.PHOTO; + } + if (documentAccepted && this.messageIsDocumentFile(message)) { + fileType = RelayAcceptedFileType.DOCUMENT; + } + if (videoAccepted && this.messageIsVideoFile(message)) { + fileType = RelayAcceptedFileType.VIDEO; // or animation or document with mime type video + } + if (audioAccepted && this.messageIsAudioFile(message)) { + fileType = RelayAcceptedFileType.AUDIO; // or voice or document with mime type audio + } + + // TODO: This are not file types... + if (locationAccepted && this.messageIsLocation(message)) { + fileType = RelayAcceptedFileType.LOCATION; + } + if (contactAccepted && this.messageIsContact(message)) { + fileType = RelayAcceptedFileType.CONTACT; + } + if (addressAccepted && this.messageIsAddress(message)) { + fileType = RelayAcceptedFileType.ADDRESS; + } + + // A file message with accepted mime type + const isAccepted = fileType !== RelayAcceptedFileType.ANY; + return { + type: ReplayType.FILE, + accepted: isAccepted, + isDone: isAccepted && !accepted.multiple, + fileType, + fileId: getFileIdFromMessage(message), + context, + }; + } + + protected messageIsDoneText( + context: Context, + accepted: ReplayCondition, + ): ReplayAcceptedText | ReplayAcceptedNone { + // Capitalisation should not play a role for done messages, see https://github.com/beabee-communityrm/telegram-bot/issues/5 + const textMessage = getTextFromMessage(context.message) + ?.toLowerCase() + .trim(); + + // Is not a text message or empty + if (!textMessage) { + return { + type: ReplayType.NONE, + accepted: false, + isDone: false, + context, + }; + } + + if (accepted.multiple && accepted.doneTexts?.length) { + const doneTexts = accepted.doneTexts.map((t) => t.toLowerCase().trim()); + const isDone = doneTexts.some((t) => t === textMessage); + if (isDone) { + return { + type: ReplayType.TEXT, + accepted: true, + isDone, + text: textMessage, + context, + }; + } + } + + return { + type: ReplayType.NONE, + accepted: false, + isDone: false, + context, + }; + } + + /** + * Check if a message is a text message and if the text is accepted + */ + protected messageIsText( + context: Context, + accepted: ReplayConditionText, + ): ReplayAcceptedText | ReplayAcceptedNone { + const message = context.message; + const texts = accepted.texts?.map((t) => t.toLowerCase().trim()); + const originalText = message?.text?.trim(); + if (message?.text) { + message.text = message.text.toLowerCase().trim(); + } + + // Is not a text message + if (!message || !originalText || !message.text) { + return { + type: ReplayType.NONE, + accepted: false, + isDone: false, + context, + }; + } + + // If not texts are defined, all texts are accepted + if (!texts || !texts.length) { + return { + type: ReplayType.TEXT, + accepted: true, + isDone: !accepted.multiple, + text: originalText, + context, + }; + } + // Is a text message and one of the texts is accepted + return { + type: ReplayType.TEXT, + accepted: true, + isDone: texts.some((t) => t === message.text), + text: originalText, + context, + }; + } + + /** + * A selection message is accepted if the message is a number + * and the number is in the range of the options + * or the message is the value of the option. + * @param context + * @param accepted + * @returns + */ + protected messageIsSelection( + context: Context, + accepted: ReplayConditionSelection, + ): ReplayAcceptedSelection { + const message = context.message; + const text = getTextFromMessage(message).toLowerCase(); + + // The answer message can be the index of the value but starts with 1 + if (isNumber(text)) { + const index1 = extractNumbers(text); + const keys = Object.keys(accepted.valueLabel); + const value = keys[index1 - 1]; + if (value) { + return { + type: ReplayType.SELECTION, + accepted: true, + isDone: !accepted.multiple, + value, + context, + }; + } + } + + // The answer message can be the value directly + const acceptedValue = Object.keys(accepted.valueLabel).find( + (key) => accepted.valueLabel[key].toLowerCase() === text, + ); + const isAccepted = !!acceptedValue; + return { + type: ReplayType.SELECTION, + accepted: isAccepted, + isDone: isAccepted && !accepted.multiple, + value: acceptedValue, + context, + }; + } + + /** + * A callout component message is accepted if the message passes the callout validator. + * @param context + * @param accepted + * @returns + */ + protected messageIsCalloutComponent( + context: Context, + accepted: ReplayConditionCalloutComponentSchema, + ): ReplayAcceptedCalloutComponentSchema | ReplayAcceptedNone { + const result: ReplayAcceptedCalloutComponentSchema = { + accepted: false, + context, + isDone: false, + type: ReplayType.CALLOUT_COMPONENT_SCHEMA, + answer: undefined, + }; + + switch (accepted.schema.type) { + case CalloutComponentType.CONTENT: { + // No answer is expected + return { + type: ReplayType.NONE, + accepted: false, + isDone: false, + context, + }; + } + case CalloutComponentType.INPUT_CHECKBOX: { + result.answer = this.transform.parseResponseBoolean(context); + break; + } + case CalloutComponentType.INPUT_FILE: + case CalloutComponentType.INPUT_SIGNATURE: { + result.answer = this.transform.parseResponseFile(context); + break; + } + case CalloutComponentType.INPUT_ADDRESS: + case CalloutComponentType.INPUT_CURRENCY: + case CalloutComponentType.INPUT_DATE_TIME: + case CalloutComponentType.INPUT_EMAIL: + case CalloutComponentType.INPUT_PHONE_NUMBER: + case CalloutComponentType.INPUT_TEXT_AREA: + case CalloutComponentType.INPUT_TEXT_FIELD: + case CalloutComponentType.INPUT_TIME: + case CalloutComponentType.INPUT_URL: { + result.answer = this.transform.parseResponseText(context); + break; + } + case CalloutComponentType.INPUT_NUMBER: { + result.answer = this.transform.parseResponseNumber(context); + break; + } + + case CalloutComponentType.INPUT_SELECT: + case CalloutComponentType.INPUT_SELECTABLE_RADIO: + case CalloutComponentType.INPUT_SELECTABLE_SELECTBOXES: { + throw new Error("Not implemented"); + // this.messageIsSelection(context, accepted); + // result.answer = this.transform.parseResponseSelection( + // context, + // accepted, + // ); + // break; + } + + case CalloutComponentType.NESTABLE_PANEL: + case CalloutComponentType.NESTABLE_TABS: + case CalloutComponentType.NESTABLE_WELL: + throw new Error("Not implemented"); + default: + throw new Error(`Unknown callout component type`); + } + + const isValid = calloutComponentValidator(accepted.schema, result.answer); + result.accepted = isValid; + result.isDone = isValid; + + return result; + } + + /** + * Check if a message is accepted by a condition + * @param accepted + * @param context + * @returns + */ + public messageIsAccepted( + context: Context, + accepted: ReplayCondition, + ): ReplayAccepted { + // If multiple messages are accepted, check if the message is a done text + if (accepted.multiple && accepted.doneTexts.length) { + const isText = this.messageIsDoneText(context, accepted); + if (isText.isDone) { + return isText; + } + } + + // Any response is accepted + if (accepted.type === ReplayType.ANY) { + return { + type: ReplayType.ANY, + accepted: true, + isDone: !accepted.multiple, + context, + }; + } + + // No response is accepted + if (accepted.type === ReplayType.NONE) { + return { + type: ReplayType.NONE, + accepted: false, + isDone: true, + context, + }; + } + + // File response is accepted + if (accepted.type === ReplayType.FILE) { + const isFile = this.messageIsFile(context, accepted); + return isFile; + } + + // Text response is accepted + if (accepted.type === ReplayType.TEXT) { + const isText = this.messageIsText(context, accepted); + return isText; + } + + // Selection response is accepted + if (accepted.type === ReplayType.SELECTION) { + const isSelection = this.messageIsSelection(context, accepted); + return isSelection; + } + + if (accepted.type === ReplayType.CALLOUT_COMPONENT_SCHEMA) { + const isCalloutAnswer = this.messageIsCalloutComponent(context, accepted); + return isCalloutAnswer; + } + + throw new Error( + `Unknown replay until type: "${(accepted as ReplayCondition)?.type}"`, + ); + } +} diff --git a/telegram-bot/types/index.ts b/telegram-bot/types/index.ts index 0e963d5..813a03e 100644 --- a/telegram-bot/types/index.ts +++ b/telegram-bot/types/index.ts @@ -16,10 +16,10 @@ export * from "./render-empty.ts"; export * from "./render-html.ts"; export * from "./render-markdown.ts"; export * from "./render-photo.ts"; -export * from "./render-response-parsed-address.ts"; export * from "./render-response-parsed-any.ts"; export * from "./render-response-parsed-base.ts"; export * from "./render-response-parsed-boolean.ts"; +export * from "./render-response-parsed-callout-component.ts"; export * from "./render-response-parsed-file.ts"; export * from "./render-response-parsed-none.ts"; export * from "./render-response-parsed-number.ts"; @@ -31,6 +31,7 @@ export * from "./render-text.ts"; export * from "./render.ts"; export * from "./replay-accepted-any.ts"; export * from "./replay-accepted-base.ts"; +export * from "./replay-accepted-callout-component-schema.ts"; export * from "./replay-accepted-file.ts"; export * from "./replay-accepted-none.ts"; export * from "./replay-accepted-selection.ts"; @@ -38,6 +39,7 @@ export * from "./replay-accepted-text.ts"; export * from "./replay-accepted.ts"; export * from "./replay-condition-any.ts"; export * from "./replay-condition-base.ts"; +export * from "./replay-condition-callout-component-schema.ts"; export * from "./replay-condition-file.ts"; export * from "./replay-condition-none.ts"; export * from "./replay-condition-selection.ts"; diff --git a/telegram-bot/types/render-response-parsed-address.ts b/telegram-bot/types/render-response-parsed-address.ts deleted file mode 100644 index 38ddc70..0000000 --- a/telegram-bot/types/render-response-parsed-address.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { RenderResponseParsedBase } from "./index.ts"; -import { CalloutResponseAnswerAddress } from "../deps.ts"; -import type { ParsedResponseType } from "../enums/index.ts"; - -export interface RenderResponseParsedAddress - extends RenderResponseParsedBase { - /** The type of the parsed data */ - type: ParsedResponseType.ADDRESS; - /** The parsed data */ - data: MULTI extends true ? CalloutResponseAnswerAddress[] - : CalloutResponseAnswerAddress; -} diff --git a/telegram-bot/types/render-response-parsed-callout-component.ts b/telegram-bot/types/render-response-parsed-callout-component.ts new file mode 100644 index 0000000..2c36eb4 --- /dev/null +++ b/telegram-bot/types/render-response-parsed-callout-component.ts @@ -0,0 +1,13 @@ +import type { RenderResponseParsedBase } from "./index.ts"; +import { CalloutResponseAnswer } from "../deps.ts"; +import type { ParsedResponseType } from "../enums/index.ts"; + +export interface RenderResponseParsedCalloutComponent< + MULTI extends boolean = boolean, +> extends RenderResponseParsedBase { + /** The type of the parsed data */ + type: ParsedResponseType.CALLOUT_COMPONENT; + // componentType: CalloutComponentType; + /** The parsed data */ + data: MULTI extends true ? CalloutResponseAnswer[] : CalloutResponseAnswer; +} diff --git a/telegram-bot/types/render-response-parsed.ts b/telegram-bot/types/render-response-parsed.ts index 5689ab8..1a4aa8d 100644 --- a/telegram-bot/types/render-response-parsed.ts +++ b/telegram-bot/types/render-response-parsed.ts @@ -1,7 +1,7 @@ import type { - RenderResponseParsedAddress, RenderResponseParsedAny, RenderResponseParsedBoolean, + RenderResponseParsedCalloutComponent, RenderResponseParsedFile, RenderResponseParsedNone, RenderResponseParsedNumber, @@ -12,9 +12,9 @@ import type { export type RenderResponseParsed = | RenderResponseParsedText | RenderResponseParsedNumber - | RenderResponseParsedAddress | RenderResponseParsedBoolean | RenderResponseParsedSelection | RenderResponseParsedFile | RenderResponseParsedAny - | RenderResponseParsedNone; + | RenderResponseParsedNone + | RenderResponseParsedCalloutComponent; diff --git a/telegram-bot/types/replay-accepted-base.ts b/telegram-bot/types/replay-accepted-base.ts index a0c40e0..9bbd274 100644 --- a/telegram-bot/types/replay-accepted-base.ts +++ b/telegram-bot/types/replay-accepted-base.ts @@ -4,9 +4,9 @@ import type { Context } from "./index.ts"; export interface ReplayAcceptedBase { /** The type of the replay. */ type: ReplayType; - /** True if the message was accepted, false if not. */ + /** True if the replay was accepted, false if not. */ accepted: boolean; - /** True if the question is answered, false if not. */ + /** True if the replay is done and nor more replays are needed, false if not. */ isDone: boolean; /** The original replay Telegram context */ context: Context; diff --git a/telegram-bot/types/replay-accepted-callout-component-schema.ts b/telegram-bot/types/replay-accepted-callout-component-schema.ts new file mode 100644 index 0000000..3c20a50 --- /dev/null +++ b/telegram-bot/types/replay-accepted-callout-component-schema.ts @@ -0,0 +1,11 @@ +import type { ReplayAcceptedBase } from "./index.ts"; +import type { ReplayType } from "../enums/index.ts"; +import type { CalloutResponseAnswer } from "../deps.ts"; + +export interface ReplayAcceptedCalloutComponentSchema + extends ReplayAcceptedBase { + type: ReplayType.CALLOUT_COMPONENT_SCHEMA; + accepted: boolean; + /** The accepted callout answer */ + answer?: CalloutResponseAnswer; +} diff --git a/telegram-bot/types/replay-accepted-file.ts b/telegram-bot/types/replay-accepted-file.ts index c041187..dcd34de 100644 --- a/telegram-bot/types/replay-accepted-file.ts +++ b/telegram-bot/types/replay-accepted-file.ts @@ -3,6 +3,8 @@ import type { ReplayAcceptedBase } from "./index.ts"; export interface ReplayAcceptedFile extends ReplayAcceptedBase { type: ReplayType.FILE; + /** True if file was accepted, false if not. */ + accepted: boolean; fileType?: RelayAcceptedFileType; /** The accepted telegram file id */ fileId?: string; diff --git a/telegram-bot/types/replay-accepted-text.ts b/telegram-bot/types/replay-accepted-text.ts index 6f7c574..9330187 100644 --- a/telegram-bot/types/replay-accepted-text.ts +++ b/telegram-bot/types/replay-accepted-text.ts @@ -4,6 +4,6 @@ import type { ReplayType } from "../enums/index.ts"; export interface ReplayAcceptedText extends ReplayAcceptedBase { type: ReplayType.TEXT; accepted: boolean; - /** The accepted text */ + /** The text message that was accepted or undefined if the replay was not accepted */ text?: string; } diff --git a/telegram-bot/types/replay-accepted.ts b/telegram-bot/types/replay-accepted.ts index c5e45b0..b63d2dd 100644 --- a/telegram-bot/types/replay-accepted.ts +++ b/telegram-bot/types/replay-accepted.ts @@ -1,5 +1,6 @@ import type { ReplayAcceptedAny, + ReplayAcceptedCalloutComponentSchema, ReplayAcceptedFile, ReplayAcceptedNone, ReplayAcceptedSelection, @@ -11,4 +12,5 @@ export type ReplayAccepted = | ReplayAcceptedText | ReplayAcceptedAny | ReplayAcceptedNone - | ReplayAcceptedSelection; + | ReplayAcceptedSelection + | ReplayAcceptedCalloutComponentSchema; diff --git a/telegram-bot/types/replay-condition-callout-component-schema.ts b/telegram-bot/types/replay-condition-callout-component-schema.ts new file mode 100644 index 0000000..01a0de2 --- /dev/null +++ b/telegram-bot/types/replay-condition-callout-component-schema.ts @@ -0,0 +1,14 @@ +import { ReplayConditionBase } from "./index.ts"; +import type { ReplayType } from "../enums/index.ts"; +import type { CalloutComponentSchema } from "../deps.ts"; + +/** + * Accept or wait for a callout answer replay. + */ +export interface ReplayConditionCalloutComponentSchema + extends ReplayConditionBase { + /** Accept or wait for callout answer replay */ + type: ReplayType.CALLOUT_COMPONENT_SCHEMA; + /** The callout component schema to validate the callout answer */ + schema: CalloutComponentSchema; +} diff --git a/telegram-bot/types/replay-condition.ts b/telegram-bot/types/replay-condition.ts index 9679e07..7b62a08 100644 --- a/telegram-bot/types/replay-condition.ts +++ b/telegram-bot/types/replay-condition.ts @@ -1,5 +1,6 @@ import { ReplayConditionAny, + ReplayConditionCalloutComponentSchema, ReplayConditionFile, ReplayConditionNone, ReplayConditionSelection, @@ -14,4 +15,5 @@ export type ReplayCondition = | ReplayConditionFile | ReplayConditionSelection | ReplayConditionAny - | ReplayConditionNone; + | ReplayConditionNone + | ReplayConditionCalloutComponentSchema; diff --git a/telegram-bot/utils/callouts.ts b/telegram-bot/utils/callouts.ts index 7f3737b..f39474c 100644 --- a/telegram-bot/utils/callouts.ts +++ b/telegram-bot/utils/callouts.ts @@ -23,6 +23,11 @@ export const isCalloutGroupKey = (key: string) => { return key.includes(CALLOUT_RESPONSE_GROUP_KEY_SEPARATOR); }; +/** + * @deprecated Use `ParsedResponseType.CALLOUT_COMPONENT` instead + * @param component + * @returns + */ export const calloutComponentTypeToParsedResponseType = ( component: CalloutComponentSchema, ): ParsedResponseType => { @@ -32,10 +37,12 @@ export const calloutComponentTypeToParsedResponseType = ( case CalloutComponentType.INPUT_TEXT_AREA: case CalloutComponentType.INPUT_PHONE_NUMBER: case CalloutComponentType.INPUT_CURRENCY: - case CalloutComponentType.INPUT_DATE_TIME: // TODO: parse date - case CalloutComponentType.INPUT_TIME: // TODO: parse time - case CalloutComponentType.INPUT_URL: { - return ParsedResponseType.TEXT; + case CalloutComponentType.INPUT_DATE_TIME: + case CalloutComponentType.INPUT_TIME: + case CalloutComponentType.INPUT_URL: + case CalloutComponentType.INPUT_NUMBER: + case CalloutComponentType.INPUT_ADDRESS: { + return ParsedResponseType.CALLOUT_COMPONENT; } case CalloutComponentType.CONTENT: { @@ -46,14 +53,6 @@ export const calloutComponentTypeToParsedResponseType = ( return ParsedResponseType.BOOLEAN; } - case CalloutComponentType.INPUT_NUMBER: { - return ParsedResponseType.NUMBER; - } - - case CalloutComponentType.INPUT_ADDRESS: { - return ParsedResponseType.ADDRESS; - } - case CalloutComponentType.INPUT_FILE: case CalloutComponentType.INPUT_SIGNATURE: { return ParsedResponseType.FILE; diff --git a/telegram-bot/utils/number.ts b/telegram-bot/utils/number.ts index 4654866..97bad64 100644 --- a/telegram-bot/utils/number.ts +++ b/telegram-bot/utils/number.ts @@ -9,7 +9,7 @@ export const isNumber = ( } // deno-lint-ignore no-explicit-any - return !isNaN(parseFloat(value as any)) && !isNaN(value as any - 0); + return !isNaN(parseFloat(value as any)) && !isNaN((value as any) - 0); }; /** @@ -21,7 +21,7 @@ export const extractNumbers = (str: string | number) => { } const num = str.replace(/[^-\d.]/g, ""); if (!isNumber(num)) { - return 0; + return NaN; } else { return Number(num); } From 377f21a3274ddc82a6d65843c6d5b218874e82b4 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Wed, 28 Feb 2024 19:53:44 +0100 Subject: [PATCH 03/17] chore: Update beabee-common --- beabee-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beabee-common b/beabee-common index 4b1e89c..fdafa1d 160000 --- a/beabee-common +++ b/beabee-common @@ -1 +1 @@ -Subproject commit 4b1e89c1a2bf29a491defb1f15b4350032dfabe5 +Subproject commit fdafa1d69aa35e692d37a26e5aff4bef5ccae1fe From ed5540e065c7790e63da1f4b4e3f442af5336ca7 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 29 Feb 2024 11:02:07 +0100 Subject: [PATCH 04/17] chore: Upgrade beabee-common --- beabee-client/deno.json | 2 +- beabee-client/deno.lock | 4 ++-- beabee-common | 2 +- deno.jsonc | 2 +- telegram-bot/deno.json | 2 +- telegram-bot/deno.lock | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/beabee-client/deno.json b/beabee-client/deno.json index 9519444..717c0a4 100644 --- a/beabee-client/deno.json +++ b/beabee-client/deno.json @@ -27,6 +27,6 @@ "std/": "https://deno.land/std@0.207.0/", "@beabee/beabee-common": "../beabee-common/mod.ts", "@beabee/client": "./mod.ts", - "date-fns": "npm:date-fns@3.2.0" + "date-fns": "npm:date-fns@3.3.1" } } diff --git a/beabee-client/deno.lock b/beabee-client/deno.lock index 1dbeed3..62e04ed 100644 --- a/beabee-client/deno.lock +++ b/beabee-client/deno.lock @@ -4,7 +4,7 @@ "specifiers": { "npm:@beabee/beabee-common@1.18.1": "npm:@beabee/beabee-common@1.18.1", "npm:@gjsify/esbuild-plugin-transform-ext@0.0.4": "npm:@gjsify/esbuild-plugin-transform-ext@0.0.4", - "npm:date-fns@3.2.0": "npm:date-fns@3.2.0" + "npm:date-fns@3.3.1": "npm:date-fns@3.3.1" }, "npm": { "@babel/runtime@7.23.4": { @@ -453,7 +453,7 @@ }, "workspace": { "dependencies": [ - "npm:date-fns@3.2.0" + "npm:date-fns@3.3.1" ], "packageJson": { "dependencies": [ diff --git a/beabee-common b/beabee-common index fdafa1d..42a6e0c 160000 --- a/beabee-common +++ b/beabee-common @@ -1 +1 @@ -Subproject commit fdafa1d69aa35e692d37a26e5aff4bef5ccae1fe +Subproject commit 42a6e0cfdb06e2d0d2fd3f976ed5e839767fae76 diff --git a/deno.jsonc b/deno.jsonc index c282a82..55b4a0c 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -24,7 +24,7 @@ "sqlite3": "https://deno.land/x/sqlite3@0.10.0/mod.ts", "@beabee/beabee-common": "./beabee-common/mod.ts", "@beabee/beabee-client": "../beabee-client/mod.ts", - "date-fns": "npm:date-fns@3.2.0" + "date-fns": "npm:date-fns@3.3.1" }, "scopes": { "https://raw.githubusercontent.com/Zhomart/dex/": { diff --git a/telegram-bot/deno.json b/telegram-bot/deno.json index ffc7e80..b4c4065 100644 --- a/telegram-bot/deno.json +++ b/telegram-bot/deno.json @@ -14,7 +14,7 @@ "std/": "https://deno.land/std@0.207.0/", "typeorm": "npm:typeorm@0.3.17", "sqlite3": "https://deno.land/x/sqlite3@0.10.0/mod.ts", - "date-fns": "npm:date-fns@3.2.0", + "date-fns": "npm:date-fns@3.3.1", "@beabee/beabee-common": "../beabee-common/mod.ts", "@beabee/beabee-client": "../beabee-client/mod.ts" }, diff --git a/telegram-bot/deno.lock b/telegram-bot/deno.lock index ad2522a..f7a640a 100644 --- a/telegram-bot/deno.lock +++ b/telegram-bot/deno.lock @@ -3,7 +3,7 @@ "packages": { "specifiers": { "npm:@types/markdown-it": "npm:@types/markdown-it@13.0.7", - "npm:date-fns@3.2.0": "npm:date-fns@3.2.0", + "npm:date-fns@3.3.1": "npm:date-fns@3.3.1", "npm:googleapis@131.0.0": "npm:googleapis@131.0.0", "npm:jsonwebtoken": "npm:jsonwebtoken@9.0.2", "npm:markdown-it": "npm:markdown-it@14.0.0", @@ -1081,7 +1081,7 @@ }, "workspace": { "dependencies": [ - "npm:date-fns@3.2.0", + "npm:date-fns@3.3.1", "npm:typeorm@0.3.17" ] } From 0a6b744f0e2511842fa34592377dbfbbbfb78346 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 29 Feb 2024 11:18:14 +0100 Subject: [PATCH 05/17] chore: Fix deno.lock files --- beabee-client/deno.lock | 102 +--------------- beabee-client/package-lock.json | 4 +- telegram-bot/deno.lock | 208 ++++++++++++-------------------- 3 files changed, 79 insertions(+), 235 deletions(-) diff --git a/beabee-client/deno.lock b/beabee-client/deno.lock index 62e04ed..dcda292 100644 --- a/beabee-client/deno.lock +++ b/beabee-client/deno.lock @@ -2,39 +2,16 @@ "version": "3", "packages": { "specifiers": { - "npm:@beabee/beabee-common@1.18.1": "npm:@beabee/beabee-common@1.18.1", "npm:@gjsify/esbuild-plugin-transform-ext@0.0.4": "npm:@gjsify/esbuild-plugin-transform-ext@0.0.4", "npm:date-fns@3.3.1": "npm:date-fns@3.3.1" }, "npm": { - "@babel/runtime@7.23.4": { - "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", - "dependencies": { - "regenerator-runtime": "regenerator-runtime@0.14.0" - } - }, - "@beabee/beabee-common@1.18.1": { - "integrity": "sha512-plPsV6r1NA+EVvyvS6UzFH52KWXhzWIvIH79EoioldA2WmvjhUU4Zs9yff9agXMT4MP4LFOUBPXKrN2zvvhIVg==", - "dependencies": { - "date-fns": "date-fns@2.30.0" - } - }, "@gjsify/esbuild-plugin-transform-ext@0.0.4": { "integrity": "sha512-LusIqHZVmxThA1rkl6ZNEFaKZ0r9nnBY/3YKqdgvEdhhN2g65I1ZVL+zfTUekZ/Tw3KKzT/wXsAONliTn1tJkw==", "dependencies": {} }, - "date-fns@2.30.0": { - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "@babel/runtime@7.23.4" - } - }, - "date-fns@3.2.0": { - "integrity": "sha512-E4KWKavANzeuusPi0jUjpuI22SURAznGkx7eZV+4i6x2A+IZxAMcajgkvuDAU1bg40+xuhW1zRdVIIM/4khuIg==", - "dependencies": {} - }, - "regenerator-runtime@0.14.0": { - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "date-fns@3.3.1": { + "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", "dependencies": {} } } @@ -59,49 +36,6 @@ "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", - "https://deno.land/std@0.186.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/std@0.186.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", - "https://deno.land/std@0.186.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", - "https://deno.land/std@0.186.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", - "https://deno.land/std@0.186.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", - "https://deno.land/std@0.186.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", - "https://deno.land/std@0.186.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", - "https://deno.land/std@0.186.0/path/mod.ts": "ee161baec5ded6510ee1d1fb6a75a0f5e4b41f3f3301c92c716ecbdf7dae910d", - "https://deno.land/std@0.186.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", - "https://deno.land/std@0.186.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", - "https://deno.land/std@0.186.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", - "https://deno.land/std@0.207.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", - "https://deno.land/std@0.207.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", - "https://deno.land/std@0.207.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.207.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", - "https://deno.land/std@0.207.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", - "https://deno.land/std@0.207.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", - "https://deno.land/std@0.207.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", - "https://deno.land/std@0.207.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", - "https://deno.land/std@0.207.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", - "https://deno.land/std@0.207.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", - "https://deno.land/std@0.207.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", - "https://deno.land/std@0.207.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", - "https://deno.land/std@0.207.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", - "https://deno.land/std@0.207.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", - "https://deno.land/std@0.207.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", - "https://deno.land/std@0.207.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", - "https://deno.land/std@0.207.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", - "https://deno.land/std@0.207.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", - "https://deno.land/std@0.207.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", - "https://deno.land/std@0.207.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", - "https://deno.land/std@0.207.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", - "https://deno.land/std@0.207.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", - "https://deno.land/std@0.207.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", - "https://deno.land/std@0.207.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", - "https://deno.land/std@0.207.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", - "https://deno.land/std@0.207.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", - "https://deno.land/std@0.207.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", - "https://deno.land/std@0.207.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", - "https://deno.land/std@0.207.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", - "https://deno.land/std@0.207.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", - "https://deno.land/std@0.207.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", - "https://deno.land/std@0.207.0/fmt/colors.ts": "34b3f77432925eb72cf0bfb351616949746768620b8e5ead66da532f93d10ba2", "https://deno.land/std@0.211.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5", "https://deno.land/std@0.211.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", "https://deno.land/std@0.211.0/encoding/_util.ts": "beacef316c1255da9bc8e95afb1fa56ed69baef919c88dc06ae6cb7a6103d376", @@ -386,30 +320,6 @@ "https://deno.land/std@0.213.0/path/windows/separator.ts": "e51c5522140eff4f8402617c5c68a201fdfa3a1a8b28dc23587cff931b665e43", "https://deno.land/std@0.213.0/path/windows/to_file_url.ts": "1cd63fd35ec8d1370feaa4752eccc4cc05ea5362a878be8dc7db733650995484", "https://deno.land/std@0.213.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", - "https://deno.land/x/deno_cache@0.5.2/auth_tokens.ts": "5d1d56474c54a9d152e44d43ea17c2e6a398dd1e9682c69811a313567c01ee1e", - "https://deno.land/x/deno_cache@0.5.2/cache.ts": "92ce8511e1e5c00fdf53a41619aa77d632ea8e0fc711324322e4d5ebf8133911", - "https://deno.land/x/deno_cache@0.5.2/deno_dir.ts": "1ea355b8ba11c630d076b222b197cfc937dd81e5a4a260938997da99e8ff93a0", - "https://deno.land/x/deno_cache@0.5.2/deps.ts": "26a75905652510b76e54b6d5ef3cf824d1062031e00782efcd768978419224e7", - "https://deno.land/x/deno_cache@0.5.2/dirs.ts": "009c6f54e0b610914d6ce9f72f6f6ccfffd2d47a79a19061e0a9eb4253836069", - "https://deno.land/x/deno_cache@0.5.2/disk_cache.ts": "66a1e604a8d564b6dd0500326cac33d08b561d331036bf7272def80f2f7952aa", - "https://deno.land/x/deno_cache@0.5.2/file_fetcher.ts": "89616c50b6df73fb04e73d0b7cd99e5f2ed7967386913d65b9e8baa4238501f7", - "https://deno.land/x/deno_cache@0.5.2/http_cache.ts": "407135eaf2802809ed373c230d57da7ef8dff923c4abf205410b9b99886491fd", - "https://deno.land/x/deno_cache@0.5.2/lib/deno_cache_dir.generated.js": "18b6526d0c50791a73dd0eb894e99de1ac05ee79dcbd53298ff5b5b6b0757fe6", - "https://deno.land/x/deno_cache@0.5.2/lib/snippets/deno_cache_dir-77bed54ace8005e0/fs.js": "cbe3a976ed63c72c7cb34ef845c27013033a3b11f9d8d3e2c4aa5dda2c0c7af6", - "https://deno.land/x/deno_cache@0.5.2/mod.ts": "0b4d071ad095128bdc2b1bc6e5d2095222dcbae08287261690ee9757e6300db6", - "https://deno.land/x/deno_cache@0.5.2/util.ts": "f3f5a0cfc60051f09162942fb0ee87a0e27b11a12aec4c22076e3006be4cc1e2", - "https://deno.land/x/deno_cache@0.6.0/auth_tokens.ts": "5d1d56474c54a9d152e44d43ea17c2e6a398dd1e9682c69811a313567c01ee1e", - "https://deno.land/x/deno_cache@0.6.0/cache.ts": "58b53c128b742757efcad10af9a3871f23b4e200674cb5b0ddf61164fb9b2fe7", - "https://deno.land/x/deno_cache@0.6.0/deno_dir.ts": "1ea355b8ba11c630d076b222b197cfc937dd81e5a4a260938997da99e8ff93a0", - "https://deno.land/x/deno_cache@0.6.0/deps.ts": "12cca94516cf2d3ed42fccd4b721ecd8060679253f077d83057511045b0081aa", - "https://deno.land/x/deno_cache@0.6.0/dirs.ts": "009c6f54e0b610914d6ce9f72f6f6ccfffd2d47a79a19061e0a9eb4253836069", - "https://deno.land/x/deno_cache@0.6.0/disk_cache.ts": "66a1e604a8d564b6dd0500326cac33d08b561d331036bf7272def80f2f7952aa", - "https://deno.land/x/deno_cache@0.6.0/file_fetcher.ts": "6d7e84572ceabf5b662980d255187f75164048c77c5e6a658389ce1c0c11a261", - "https://deno.land/x/deno_cache@0.6.0/http_cache.ts": "407135eaf2802809ed373c230d57da7ef8dff923c4abf205410b9b99886491fd", - "https://deno.land/x/deno_cache@0.6.0/lib/deno_cache_dir.generated.js": "59f8defac32e8ebf2a30f7bc77e9d88f0e60098463fb1b75e00b9791a4bbd733", - "https://deno.land/x/deno_cache@0.6.0/lib/snippets/deno_cache_dir-a2aecaa9536c9402/fs.js": "cbe3a976ed63c72c7cb34ef845c27013033a3b11f9d8d3e2c4aa5dda2c0c7af6", - "https://deno.land/x/deno_cache@0.6.0/mod.ts": "b4004287e1c6123d7f07fe9b5b3e94ce6d990c4102949a89c527c68b19627867", - "https://deno.land/x/deno_cache@0.6.0/util.ts": "f3f5a0cfc60051f09162942fb0ee87a0e27b11a12aec4c22076e3006be4cc1e2", "https://deno.land/x/deno_cache@0.6.2/auth_tokens.ts": "5d1d56474c54a9d152e44d43ea17c2e6a398dd1e9682c69811a313567c01ee1e", "https://deno.land/x/deno_cache@0.6.2/cache.ts": "58b53c128b742757efcad10af9a3871f23b4e200674cb5b0ddf61164fb9b2fe7", "https://deno.land/x/deno_cache@0.6.2/deno_dir.ts": "1ea355b8ba11c630d076b222b197cfc937dd81e5a4a260938997da99e8ff93a0", @@ -425,10 +335,6 @@ "https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6", "https://deno.land/x/denoflate@1.2.1/pkg/denoflate.js": "b9f9ad9457d3f12f28b1fb35c555f57443427f74decb403113d67364e4f2caf4", "https://deno.land/x/denoflate@1.2.1/pkg/denoflate_bg.wasm.js": "d581956245407a2115a3d7e8d85a9641c032940a8e810acbd59ca86afd34d44d", - "https://deno.land/x/dir@1.5.1/data_local_dir/mod.ts": "91eb1c4bfadfbeda30171007bac6d85aadacd43224a5ed721bbe56bc64e9eb66", - "https://deno.land/x/emit@0.28.0/_utils.ts": "98412edc7aa29e77d592b54fbad00bdec1b05d0c25eb772a5f8edc9813e08d88", - "https://deno.land/x/emit@0.28.0/emit.generated.js": "6ca765a2a7a1c2dab97e747981e6a9fb98a5acd7a0aa78a003e596fdd0632480", - "https://deno.land/x/emit@0.28.0/mod.ts": "817cb45fcd94d15a43de826161049eee89e3be4bcf06b3a2f60e8cf459e559f1", "https://deno.land/x/esbuild@v0.20.0/mod.js": "5c6fc2e98424ced3a4159c2f267c6bc62b49906e9dc86f724b3d88a482c55c70", "https://deno.land/x/esbuild_deno_loader@0.8.5/deps.ts": "9bc65a79ff37923c11d7470823aaa0ba9cd2e044046619a81feae074d3a2969d", "https://deno.land/x/esbuild_deno_loader@0.8.5/mod.ts": "28524460bef46d487221b01ade6ed913d2e127de7eeee025ab75b34b491283da", @@ -447,9 +353,7 @@ "https://deno.land/x/esbuild_deno_loader@0.9.0/src/plugin_deno_resolver.ts": "3964d48a8e4ca4c29f8e5efe85817c95f1d16c62994701168f0417afb25285ff", "https://deno.land/x/esbuild_deno_loader@0.9.0/src/shared.ts": "d717f56adafcee47834d20b77d603f646d36575273c766fcb34d6315e759392f", "https://deno.land/x/importmap@0.2.1/_util.ts": "ada9a9618b537e6c0316c048a898352396c882b9f2de38aba18fd3f2950ede89", - "https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade", - "https://deno.land/x/wasmbuild@0.14.1/cache.ts": "89eea5f3ce6035a1164b3e655c95f21300498920575ade23161421f5b01967f4", - "https://deno.land/x/wasmbuild@0.14.1/loader.ts": "d98d195a715f823151cbc8baa3f32127337628379a02d9eb2a3c5902dbccfc02" + "https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade" }, "workspace": { "dependencies": [ diff --git a/beabee-client/package-lock.json b/beabee-client/package-lock.json index 7e162ac..828b02b 100644 --- a/beabee-client/package-lock.json +++ b/beabee-client/package-lock.json @@ -30,10 +30,10 @@ }, "../beabee-common": { "name": "@beabee/beabee-common", - "version": "1.19.6", + "version": "0.20.1", "license": "AGPL-3.0", "dependencies": { - "date-fns": "^3.2.0" + "date-fns": "^3.3.1" }, "devDependencies": { "@tsconfig/recommended": "^1.0.3", diff --git a/telegram-bot/deno.lock b/telegram-bot/deno.lock index f7a640a..173d6ef 100644 --- a/telegram-bot/deno.lock +++ b/telegram-bot/deno.lock @@ -5,13 +5,12 @@ "npm:@types/markdown-it": "npm:@types/markdown-it@13.0.7", "npm:date-fns@3.3.1": "npm:date-fns@3.3.1", "npm:googleapis@131.0.0": "npm:googleapis@131.0.0", - "npm:jsonwebtoken": "npm:jsonwebtoken@9.0.2", "npm:markdown-it": "npm:markdown-it@14.0.0", "npm:typeorm@0.3.17": "npm:typeorm@0.3.17" }, "npm": { - "@babel/runtime@7.23.8": { - "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "@babel/runtime@7.24.0": { + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "dependencies": { "regenerator-runtime": "regenerator-runtime@0.14.1" } @@ -92,12 +91,14 @@ "ieee754": "ieee754@1.2.1" } }, - "call-bind@1.0.5": { - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "call-bind@1.0.7": { + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "es-define-property@1.0.0", + "es-errors": "es-errors@1.3.0", "function-bind": "function-bind@1.1.2", - "get-intrinsic": "get-intrinsic@1.2.2", - "set-function-length": "set-function-length@1.2.0" + "get-intrinsic": "get-intrinsic@1.2.4", + "set-function-length": "set-function-length@1.2.1" } }, "chalk@4.1.2": { @@ -147,11 +148,11 @@ "date-fns@2.30.0": { "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dependencies": { - "@babel/runtime": "@babel/runtime@7.23.8" + "@babel/runtime": "@babel/runtime@7.24.0" } }, - "date-fns@3.2.0": { - "integrity": "sha512-E4KWKavANzeuusPi0jUjpuI22SURAznGkx7eZV+4i6x2A+IZxAMcajgkvuDAU1bg40+xuhW1zRdVIIM/4khuIg==", + "date-fns@3.3.1": { + "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", "dependencies": {} }, "debug@4.3.4": { @@ -160,16 +161,16 @@ "ms": "ms@2.1.2" } }, - "define-data-property@1.1.1": { - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "define-data-property@1.1.4": { + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "get-intrinsic@1.2.2", - "gopd": "gopd@1.0.1", - "has-property-descriptors": "has-property-descriptors@1.0.1" + "es-define-property": "es-define-property@1.0.0", + "es-errors": "es-errors@1.3.0", + "gopd": "gopd@1.0.1" } }, - "dotenv@16.3.1": { - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dotenv@16.4.5": { + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dependencies": {} }, "ecdsa-sig-formatter@1.0.11": { @@ -186,8 +187,18 @@ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dependencies": {} }, - "escalade@3.1.1": { - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "es-define-property@1.0.0": { + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "get-intrinsic@1.2.4" + } + }, + "es-errors@1.3.0": { + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dependencies": {} + }, + "escalade@3.1.2": { + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dependencies": {} }, "extend@3.0.2": { @@ -202,11 +213,11 @@ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dependencies": {} }, - "gaxios@6.1.1": { - "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", + "gaxios@6.3.0": { + "integrity": "sha512-p+ggrQw3fBwH2F5N/PAI4k/G/y1art5OxKpb2J2chwNNHM4hHuAOtivjPuirMF4KNKwTTUal/lPfL2+7h2mEcg==", "dependencies": { "extend": "extend@3.0.2", - "https-proxy-agent": "https-proxy-agent@7.0.2", + "https-proxy-agent": "https-proxy-agent@7.0.4", "is-stream": "is-stream@2.0.1", "node-fetch": "node-fetch@2.7.0" } @@ -214,7 +225,7 @@ "gcp-metadata@6.1.0": { "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", "dependencies": { - "gaxios": "gaxios@6.1.1", + "gaxios": "gaxios@6.3.0", "json-bigint": "json-bigint@1.0.0" } }, @@ -222,13 +233,14 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dependencies": {} }, - "get-intrinsic@1.2.2": { - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "get-intrinsic@1.2.4": { + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "es-errors@1.3.0", "function-bind": "function-bind@1.1.2", - "has-proto": "has-proto@1.0.1", + "has-proto": "has-proto@1.0.3", "has-symbols": "has-symbols@1.0.3", - "hasown": "hasown@2.0.0" + "hasown": "hasown@2.0.1" } }, "glob@8.1.0": { @@ -241,14 +253,14 @@ "once": "once@1.4.0" } }, - "google-auth-library@9.4.2": { - "integrity": "sha512-rTLO4gjhqqo3WvYKL5IdtlCvRqeQ4hxUx/p4lObobY2xotFW3bCQC+Qf1N51CYOfiqfMecdMwW9RIo7dFWYjqw==", + "google-auth-library@9.6.3": { + "integrity": "sha512-4CacM29MLC2eT9Cey5GDVK4Q8t+MMp8+OEdOaqD9MG6b0dOyLORaaeJMPQ7EESVgm/+z5EKYyFLxgzBJlJgyHQ==", "dependencies": { "base64-js": "base64-js@1.5.1", "ecdsa-sig-formatter": "ecdsa-sig-formatter@1.0.11", - "gaxios": "gaxios@6.1.1", + "gaxios": "gaxios@6.3.0", "gcp-metadata": "gcp-metadata@6.1.0", - "gtoken": "gtoken@7.0.1", + "gtoken": "gtoken@7.1.0", "jws": "jws@4.0.0" } }, @@ -256,8 +268,8 @@ "integrity": "sha512-mgt5zsd7zj5t5QXvDanjWguMdHAcJmmDrF9RkInCecNsyV7S7YtGqm5v2IWONNID88osb7zmx5FtrAP12JfD0w==", "dependencies": { "extend": "extend@3.0.2", - "gaxios": "gaxios@6.1.1", - "google-auth-library": "google-auth-library@9.4.2", + "gaxios": "gaxios@6.3.0", + "google-auth-library": "google-auth-library@9.6.3", "qs": "qs@6.11.2", "url-template": "url-template@2.0.8", "uuid": "uuid@9.0.1" @@ -266,20 +278,20 @@ "googleapis@131.0.0": { "integrity": "sha512-fa4kdkY0VwHDw/04ItpQv2tlvlPIwbh6NjHDoWAVrV52GuaZbYCMOC5Y+hRmprp5HHIMRODmyb2YujlbZSRUbQ==", "dependencies": { - "google-auth-library": "google-auth-library@9.4.2", + "google-auth-library": "google-auth-library@9.6.3", "googleapis-common": "googleapis-common@7.0.1" } }, "gopd@1.0.1": { "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { - "get-intrinsic": "get-intrinsic@1.2.2" + "get-intrinsic": "get-intrinsic@1.2.4" } }, - "gtoken@7.0.1": { - "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", + "gtoken@7.1.0": { + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "dependencies": { - "gaxios": "gaxios@6.1.1", + "gaxios": "gaxios@6.3.0", "jws": "jws@4.0.0" } }, @@ -287,22 +299,22 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dependencies": {} }, - "has-property-descriptors@1.0.1": { - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "has-property-descriptors@1.0.2": { + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "get-intrinsic@1.2.2" + "es-define-property": "es-define-property@1.0.0" } }, - "has-proto@1.0.1": { - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "has-proto@1.0.3": { + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dependencies": {} }, "has-symbols@1.0.3": { "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dependencies": {} }, - "hasown@2.0.0": { - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "hasown@2.0.1": { + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dependencies": { "function-bind": "function-bind@1.1.2" } @@ -311,8 +323,8 @@ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dependencies": {} }, - "https-proxy-agent@7.0.2": { - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "https-proxy-agent@7.0.4": { + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dependencies": { "agent-base": "agent-base@7.1.0", "debug": "debug@4.3.4" @@ -347,29 +359,6 @@ "bignumber.js": "bignumber.js@9.1.2" } }, - "jsonwebtoken@9.0.2": { - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "jws@3.2.2", - "lodash.includes": "lodash.includes@4.3.0", - "lodash.isboolean": "lodash.isboolean@3.0.3", - "lodash.isinteger": "lodash.isinteger@4.0.4", - "lodash.isnumber": "lodash.isnumber@3.0.3", - "lodash.isplainobject": "lodash.isplainobject@4.0.6", - "lodash.isstring": "lodash.isstring@4.0.1", - "lodash.once": "lodash.once@4.1.1", - "ms": "ms@2.1.2", - "semver": "semver@7.5.4" - } - }, - "jwa@1.4.1": { - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "buffer-equal-constant-time@1.0.1", - "ecdsa-sig-formatter": "ecdsa-sig-formatter@1.0.11", - "safe-buffer": "safe-buffer@5.2.1" - } - }, "jwa@2.0.0": { "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "dependencies": { @@ -378,13 +367,6 @@ "safe-buffer": "safe-buffer@5.2.1" } }, - "jws@3.2.2": { - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "jwa@1.4.1", - "safe-buffer": "safe-buffer@5.2.1" - } - }, "jws@4.0.0": { "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "dependencies": { @@ -398,40 +380,6 @@ "uc.micro": "uc.micro@2.0.0" } }, - "lodash.includes@4.3.0": { - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "dependencies": {} - }, - "lodash.isboolean@3.0.3": { - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "dependencies": {} - }, - "lodash.isinteger@4.0.4": { - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "dependencies": {} - }, - "lodash.isnumber@3.0.3": { - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "dependencies": {} - }, - "lodash.isplainobject@4.0.6": { - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dependencies": {} - }, - "lodash.isstring@4.0.1": { - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dependencies": {} - }, - "lodash.once@4.1.1": { - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dependencies": {} - }, - "lru-cache@6.0.0": { - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "yallist@4.0.0" - } - }, "markdown-it@14.0.0": { "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", "dependencies": { @@ -510,7 +458,7 @@ "qs@6.11.2": { "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dependencies": { - "side-channel": "side-channel@1.0.4" + "side-channel": "side-channel@1.0.5" } }, "reflect-metadata@0.1.14": { @@ -529,20 +477,15 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dependencies": {} }, - "semver@7.5.4": { - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "set-function-length@1.2.1": { + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dependencies": { - "lru-cache": "lru-cache@6.0.0" - } - }, - "set-function-length@1.2.0": { - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", - "dependencies": { - "define-data-property": "define-data-property@1.1.1", + "define-data-property": "define-data-property@1.1.4", + "es-errors": "es-errors@1.3.0", "function-bind": "function-bind@1.1.2", - "get-intrinsic": "get-intrinsic@1.2.2", + "get-intrinsic": "get-intrinsic@1.2.4", "gopd": "gopd@1.0.1", - "has-property-descriptors": "has-property-descriptors@1.0.1" + "has-property-descriptors": "has-property-descriptors@1.0.2" } }, "sha.js@2.4.11": { @@ -552,11 +495,12 @@ "safe-buffer": "safe-buffer@5.2.1" } }, - "side-channel@1.0.4": { - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "side-channel@1.0.5": { + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", "dependencies": { - "call-bind": "call-bind@1.0.5", - "get-intrinsic": "get-intrinsic@1.2.2", + "call-bind": "call-bind@1.0.7", + "es-errors": "es-errors@1.3.0", + "get-intrinsic": "get-intrinsic@1.2.4", "object-inspect": "object-inspect@1.13.1" } }, @@ -610,7 +554,7 @@ "cli-highlight": "cli-highlight@2.1.11", "date-fns": "date-fns@2.30.0", "debug": "debug@4.3.4", - "dotenv": "dotenv@16.3.1", + "dotenv": "dotenv@16.4.5", "glob": "glob@8.1.0", "mkdirp": "mkdirp@2.1.6", "reflect-metadata": "reflect-metadata@0.1.14", @@ -659,10 +603,6 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dependencies": {} }, - "yallist@4.0.0": { - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dependencies": {} - }, "yargs-parser@20.2.9": { "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dependencies": {} @@ -675,7 +615,7 @@ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dependencies": { "cliui": "cliui@7.0.4", - "escalade": "escalade@3.1.1", + "escalade": "escalade@3.1.2", "get-caller-file": "get-caller-file@2.0.5", "require-directory": "require-directory@2.1.1", "string-width": "string-width@4.2.3", @@ -687,7 +627,7 @@ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { "cliui": "cliui@8.0.1", - "escalade": "escalade@3.1.1", + "escalade": "escalade@3.1.2", "get-caller-file": "get-caller-file@2.0.5", "require-directory": "require-directory@2.1.1", "string-width": "string-width@4.2.3", From 5506d680128730c3fa0d27c082200d219d1d2fd8 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 29 Feb 2024 14:21:48 +0100 Subject: [PATCH 06/17] fix(validation): Fix callout validation parseTypes --- beabee-client/deno.lock | 32 +++++ telegram-bot/deno.lock | 75 ++++++++++ .../renderer/callout-response.renderer.ts | 130 +++++++++++------- telegram-bot/renderer/message.renderer.ts | 23 +++- telegram-bot/services/transform.service.ts | 13 +- telegram-bot/services/validation.service.ts | 29 ++-- telegram-bot/types/replay-condition-text.ts | 3 +- 7 files changed, 237 insertions(+), 68 deletions(-) diff --git a/beabee-client/deno.lock b/beabee-client/deno.lock index dcda292..28e7f74 100644 --- a/beabee-client/deno.lock +++ b/beabee-client/deno.lock @@ -36,6 +36,38 @@ "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757", "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", + "https://deno.land/std@0.207.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.207.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", + "https://deno.land/std@0.207.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.207.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.207.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.207.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.207.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.207.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.207.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", + "https://deno.land/std@0.207.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.207.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.207.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.207.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.207.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.207.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.207.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.207.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.207.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.207.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.207.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.207.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.207.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.207.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.207.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.207.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.207.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.207.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.207.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.207.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.207.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.207.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.207.0/fmt/colors.ts": "34b3f77432925eb72cf0bfb351616949746768620b8e5ead66da532f93d10ba2", "https://deno.land/std@0.211.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5", "https://deno.land/std@0.211.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8", "https://deno.land/std@0.211.0/encoding/_util.ts": "beacef316c1255da9bc8e95afb1fa56ed69baef919c88dc06ae6cb7a6103d376", diff --git a/telegram-bot/deno.lock b/telegram-bot/deno.lock index 173d6ef..aae2abb 100644 --- a/telegram-bot/deno.lock +++ b/telegram-bot/deno.lock @@ -5,6 +5,7 @@ "npm:@types/markdown-it": "npm:@types/markdown-it@13.0.7", "npm:date-fns@3.3.1": "npm:date-fns@3.3.1", "npm:googleapis@131.0.0": "npm:googleapis@131.0.0", + "npm:jsonwebtoken": "npm:jsonwebtoken@9.0.2", "npm:markdown-it": "npm:markdown-it@14.0.0", "npm:typeorm@0.3.17": "npm:typeorm@0.3.17" }, @@ -359,6 +360,29 @@ "bignumber.js": "bignumber.js@9.1.2" } }, + "jsonwebtoken@9.0.2": { + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "jws@3.2.2", + "lodash.includes": "lodash.includes@4.3.0", + "lodash.isboolean": "lodash.isboolean@3.0.3", + "lodash.isinteger": "lodash.isinteger@4.0.4", + "lodash.isnumber": "lodash.isnumber@3.0.3", + "lodash.isplainobject": "lodash.isplainobject@4.0.6", + "lodash.isstring": "lodash.isstring@4.0.1", + "lodash.once": "lodash.once@4.1.1", + "ms": "ms@2.1.2", + "semver": "semver@7.6.0" + } + }, + "jwa@1.4.1": { + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "buffer-equal-constant-time@1.0.1", + "ecdsa-sig-formatter": "ecdsa-sig-formatter@1.0.11", + "safe-buffer": "safe-buffer@5.2.1" + } + }, "jwa@2.0.0": { "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "dependencies": { @@ -367,6 +391,13 @@ "safe-buffer": "safe-buffer@5.2.1" } }, + "jws@3.2.2": { + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "jwa@1.4.1", + "safe-buffer": "safe-buffer@5.2.1" + } + }, "jws@4.0.0": { "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "dependencies": { @@ -380,6 +411,40 @@ "uc.micro": "uc.micro@2.0.0" } }, + "lodash.includes@4.3.0": { + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dependencies": {} + }, + "lodash.isboolean@3.0.3": { + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dependencies": {} + }, + "lodash.isinteger@4.0.4": { + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dependencies": {} + }, + "lodash.isnumber@3.0.3": { + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dependencies": {} + }, + "lodash.isplainobject@4.0.6": { + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dependencies": {} + }, + "lodash.isstring@4.0.1": { + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dependencies": {} + }, + "lodash.once@4.1.1": { + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dependencies": {} + }, + "lru-cache@6.0.0": { + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "yallist@4.0.0" + } + }, "markdown-it@14.0.0": { "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", "dependencies": { @@ -477,6 +542,12 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dependencies": {} }, + "semver@7.6.0": { + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "lru-cache@6.0.0" + } + }, "set-function-length@1.2.1": { "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dependencies": { @@ -603,6 +674,10 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dependencies": {} }, + "yallist@4.0.0": { + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dependencies": {} + }, "yargs-parser@20.2.9": { "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dependencies": {} diff --git a/telegram-bot/renderer/callout-response.renderer.ts b/telegram-bot/renderer/callout-response.renderer.ts index 39cbc74..9c9f25a 100644 --- a/telegram-bot/renderer/callout-response.renderer.ts +++ b/telegram-bot/renderer/callout-response.renderer.ts @@ -1,6 +1,7 @@ import { CalloutComponentBaseType, CalloutComponentContentSchema, + CalloutComponentInputCheckboxSchema, CalloutComponentInputFileSchema, CalloutComponentInputSchema, CalloutComponentInputSelectableSchema, @@ -331,6 +332,44 @@ export class CalloutResponseRenderer { return result; } + protected inputCheckboxComponent( + input: CalloutComponentInputCheckboxSchema, + prefix: string, + ) { + const result = this.baseComponent(input, prefix); + result.parseType = ParsedResponseType.BOOLEAN; + result.markdown += `\n\n`; + + const truthyMessage = this.i18n.t("reactions.messages.truthy"); + const falsyMessage = this.i18n.t("reactions.messages.falsy"); + const doneMessage = this.i18n.t("reactions.messages.done"); + + result.markdown += `_${ + escapeMd( + this.i18n.t("response.messages.answer-with-truthy-or-falsy", { + truthy: truthyMessage, + falsy: falsyMessage, + }), + ) + }_`; + + result.accepted = this.condition.replayConditionText( + result.accepted.multiple, + [truthyMessage, falsyMessage], + result.accepted.multiple ? [doneMessage] : [], + ); + + if (input.placeholder) { + result.markdown += `\n\n${this.placeholderMd(input, prefix).markdown}`; + } + + if (input.multiple) { + result.markdown += `\n\n${this.multipleMd(input, prefix).markdown}`; + } + + return result; + } + /** * Render an input component in Markdown * @param input The input component to render @@ -351,26 +390,6 @@ export class CalloutResponseRenderer { }_`; break; } - case CalloutComponentType.INPUT_CHECKBOX: { - const truthyMessage = this.i18n.t("reactions.messages.truthy"); - const falsyMessage = this.i18n.t("reactions.messages.falsy"); - const doneMessage = this.i18n.t("reactions.messages.done"); - - result.markdown += `_${ - escapeMd( - this.i18n.t("response.messages.answer-with-truthy-or-falsy", { - truthy: truthyMessage, - falsy: falsyMessage, - }), - ) - }_`; - result.accepted = this.condition.replayConditionText( - result.accepted.multiple, - [truthyMessage, falsyMessage], - result.accepted.multiple ? [doneMessage] : [], - ); - break; - } case CalloutComponentType.INPUT_EMAIL: { result.markdown += `_${ escapeMd( @@ -468,6 +487,38 @@ export class CalloutResponseRenderer { return result; } + /** + * Render a select component in Markdown. + * Note: A select component is a dropdown menu in the frontend. + * @param select The select component to render + * @param prefix The prefix, used to group the answers later (only used to group slides) + */ + protected selectComponent( + select: CalloutComponentInputSelectSchema, + prefix: string, + ): RenderMarkdown { + const result = this.baseComponent(select, prefix); + result.parseType = ParsedResponseType.SELECTION; + result.markdown += `\n\n`; + result.accepted = { + ...result.accepted, + ...this.condition.replayConditionSelection( + result.accepted.multiple, + this.selectValuesToValueLabelPairs(select.data.values), + ), + }; + result.markdown += `\n${this.selectValues(select, prefix).markdown}`; + + result.markdown += `\n\n`; + + result.markdown += `_${ + escapeMd( + this.i18n.t("info.messages.only-one-selection-allowed"), + ) + }_`; + return result; + } + /** * Render a radio component in Markdown * @param radio The radio component to render @@ -478,7 +529,7 @@ export class CalloutResponseRenderer { prefix: string, ): RenderMarkdown { const result = this.baseComponent(selectable, prefix); - + result.parseType = ParsedResponseType.SELECTION; const multiple = result.accepted.multiple; result.accepted = { @@ -553,36 +604,6 @@ export class CalloutResponseRenderer { return nestableResults; } - /** - * Render a select component in Markdown. - * Note: A select component is a dropdown menu in the frontend. - * @param select The select component to render - * @param prefix The prefix, used to group the answers later (only used to group slides) - */ - protected selectComponent( - select: CalloutComponentInputSelectSchema, - prefix: string, - ): RenderMarkdown { - const result = this.baseComponent(select, prefix); - result.accepted = { - ...result.accepted, - ...this.condition.replayConditionSelection( - result.accepted.multiple, - this.selectValuesToValueLabelPairs(select.data.values), - ), - }; - result.markdown += `\n${this.selectValues(select, prefix).markdown}`; - - result.markdown += `\n\n`; - - result.markdown += `_${ - escapeMd( - this.i18n.t("info.messages.only-one-selection-allowed"), - ) - }_`; - return result; - } - public component(component: CalloutComponentSchema, prefix: string) { console.debug("Rendering component", component); const results: Render[] = []; @@ -611,6 +632,13 @@ export class CalloutResponseRenderer { return results; } + if ( + isCalloutComponentOfType(component, CalloutComponentType.INPUT_CHECKBOX) + ) { + results.push(this.inputCheckboxComponent(component, prefix)); + return results; + } + if ( isCalloutComponentOfBaseType( component, diff --git a/telegram-bot/renderer/message.renderer.ts b/telegram-bot/renderer/message.renderer.ts index 6e8998f..b29b1e8 100644 --- a/telegram-bot/renderer/message.renderer.ts +++ b/telegram-bot/renderer/message.renderer.ts @@ -1,4 +1,4 @@ -import { Singleton } from "alosaur/mod.ts"; +import { Singleton } from "../deps.ts"; import { RenderType } from "../enums/index.ts"; import { getSimpleMimeTypes } from "../utils/index.ts"; import { ConditionService } from "../services/condition.service.ts"; @@ -10,6 +10,7 @@ import type { ReplayAccepted, ReplayCondition, } from "../types/index.ts"; +import type { CalloutComponentSchema } from "../deps.ts"; import { ReplayType } from "../enums/replay-type.ts"; import { ParsedResponseType } from "../enums/parsed-response-type.ts"; @@ -97,6 +98,18 @@ export class MessageRenderer { } as RenderText; } + public notACalloutComponentMessage(schema: CalloutComponentSchema) { + const tKey = + `response.messages.not-a-callout-${schema.type}-component-message`; + return { + type: RenderType.TEXT, + text: this.i18n.t(tKey, { type: schema.type }), + key: tKey, + accepted: this.condition.replayConditionNone(), + parseType: ParsedResponseType.NONE, + } as RenderText; + } + public notAcceptedMessage( accepted: ReplayAccepted, condition: ReplayCondition, @@ -104,6 +117,7 @@ export class MessageRenderer { if (accepted.accepted) { throw new Error("This message was accepted but should not be"); } + if (condition.type === ReplayType.TEXT) { return this.notATextMessage(); } @@ -118,7 +132,12 @@ export class MessageRenderer { } return this.notAFileMessage(); } - throw new Error("Unknown accepted type"); + + if (condition.type === ReplayType.CALLOUT_COMPONENT_SCHEMA) { + return this.notACalloutComponentMessage(condition.schema); + } + + throw new Error("Unknown accepted type: " + condition.type); } public writeDoneMessage(doneText: string) { diff --git a/telegram-bot/services/transform.service.ts b/telegram-bot/services/transform.service.ts index f52c2e9..c09f5cf 100644 --- a/telegram-bot/services/transform.service.ts +++ b/telegram-bot/services/transform.service.ts @@ -142,10 +142,12 @@ export class TransformService { public parseResponseBoolean( context: Context, ): RenderResponseParsedBoolean["data"] { - const boolStr = getTextFromMessage(context.message).toLowerCase(); + const boolStr = getTextFromMessage(context.message).toLowerCase().trim(); let bool = false; - const truthyStr = this.i18n.t("reactions.messages.truthy").toLowerCase(); - const falsyStr = this.i18n.t("reactions.messages.falsy").toLowerCase(); + const truthyStr = this.i18n.t("reactions.messages.truthy").toLowerCase() + .trim(); + const falsyStr = this.i18n.t("reactions.messages.falsy").toLowerCase() + .trim(); if (boolStr === truthyStr) { bool = true; } else if (boolStr === falsyStr) { @@ -248,6 +250,11 @@ export class TransformService { ): RenderResponseParsed["data"] { switch (render.parseType) { case ParsedResponseType.CALLOUT_COMPONENT: + if (replay.type !== ReplayType.CALLOUT_COMPONENT_SCHEMA) { + throw new Error( + `Unsupported accepted type for callout component: "${replay.type}"`, + ); + } // Already parsed for validation return (replay as ReplayAcceptedCalloutComponentSchema).answer; case ParsedResponseType.FILE: diff --git a/telegram-bot/services/validation.service.ts b/telegram-bot/services/validation.service.ts index 53dd743..ba6c213 100644 --- a/telegram-bot/services/validation.service.ts +++ b/telegram-bot/services/validation.service.ts @@ -222,12 +222,10 @@ export class ValidationService { const message = context.message; const texts = accepted.texts?.map((t) => t.toLowerCase().trim()); const originalText = message?.text?.trim(); - if (message?.text) { - message.text = message.text.toLowerCase().trim(); - } + const lowerCaseText = originalText?.toLowerCase(); // Is not a text message - if (!message || !originalText || !message.text) { + if (!message || !originalText || !lowerCaseText) { return { type: ReplayType.NONE, accepted: false, @@ -247,13 +245,23 @@ export class ValidationService { }; } // Is a text message and one of the texts is accepted + const match = texts.some((t) => t === message.text); + if (!match) { + return { + type: ReplayType.NONE, + accepted: false, + isDone: false, + context, + } as ReplayAcceptedNone; + } + return { type: ReplayType.TEXT, accepted: true, - isDone: texts.some((t) => t === message.text), + isDone: !accepted.multiple, text: originalText, context, - }; + } as ReplayAcceptedText; } /** @@ -312,11 +320,11 @@ export class ValidationService { accepted: ReplayConditionCalloutComponentSchema, ): ReplayAcceptedCalloutComponentSchema | ReplayAcceptedNone { const result: ReplayAcceptedCalloutComponentSchema = { + type: ReplayType.CALLOUT_COMPONENT_SCHEMA, accepted: false, - context, isDone: false, - type: ReplayType.CALLOUT_COMPONENT_SCHEMA, answer: undefined, + context, }; switch (accepted.schema.type) { @@ -325,7 +333,7 @@ export class ValidationService { return { type: ReplayType.NONE, accepted: false, - isDone: false, + isDone: true, context, }; } @@ -377,7 +385,7 @@ export class ValidationService { const isValid = calloutComponentValidator(accepted.schema, result.answer); result.accepted = isValid; - result.isDone = isValid; + result.isDone = isValid && !accepted.multiple; return result; } @@ -438,6 +446,7 @@ export class ValidationService { return isSelection; } + // Callout component response answer is accepted if (accepted.type === ReplayType.CALLOUT_COMPONENT_SCHEMA) { const isCalloutAnswer = this.messageIsCalloutComponent(context, accepted); return isCalloutAnswer; diff --git a/telegram-bot/types/replay-condition-text.ts b/telegram-bot/types/replay-condition-text.ts index cb6c51e..771e17a 100644 --- a/telegram-bot/types/replay-condition-text.ts +++ b/telegram-bot/types/replay-condition-text.ts @@ -19,8 +19,7 @@ export interface ReplayConditionText extends ReplayConditionBase { type: ReplayType.TEXT; /** * Define this to wait for a specific message or leave it undefined to wait for any message. - * - If you define multiple possible messages, the logic will wait for the first message that matches. - * - Leave this undefined or empty to wait for only the first message. + * - Currently used for "yes" or "no" questions. */ texts?: string[]; } From 1143fcb8dcc09b952fbc17e7654f5c732eeb73af Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 29 Feb 2024 16:49:36 +0100 Subject: [PATCH 07/17] feat(i18n): Use camelCase for translation keys --- telegram-bot/README.md | 2 +- telegram-bot/core/command.ts | 4 +- telegram-bot/locales/de.json | 168 +++++++++--------- telegram-bot/locales/de@informal.json | 168 +++++++++--------- telegram-bot/locales/en.json | 168 +++++++++--------- telegram-bot/locales/nl.json | 168 +++++++++--------- telegram-bot/locales/pt.json | 168 +++++++++--------- telegram-bot/locales/ru.json | 168 +++++++++--------- .../renderer/callout-response.renderer.ts | 73 ++++---- telegram-bot/renderer/callout.renderer.ts | 8 +- telegram-bot/renderer/message.renderer.ts | 6 +- telegram-bot/scripts/i18n.ts | 9 +- telegram-bot/services/i18n.service.ts | 5 +- telegram-bot/services/keyboard.service.ts | 8 +- telegram-bot/services/transform.service.ts | 4 +- telegram-bot/utils/string.ts | 22 +++ 16 files changed, 590 insertions(+), 559 deletions(-) diff --git a/telegram-bot/README.md b/telegram-bot/README.md index 4ff5d97..58d0b14 100644 --- a/telegram-bot/README.md +++ b/telegram-bot/README.md @@ -63,7 +63,7 @@ import { container } from "../deps.ts"; import { I18nService } from "./services/index.ts"; const i18n = container.resolve(I18nService); // Get the Singleton instance of I18nService -const translated = i18n.t("info.messages.placeholder", { placeholder: "Hello World!" } +const translated = i18n.t("bot.info.messages.placeholder", { placeholder: "Hello World!" } ``` ### Testing diff --git a/telegram-bot/core/command.ts b/telegram-bot/core/command.ts index bb54b85..63bdb00 100644 --- a/telegram-bot/core/command.ts +++ b/telegram-bot/core/command.ts @@ -40,9 +40,9 @@ export abstract class Command implements BotCommand { */ changeLocale(lang: string) { // FIXME: This is not working on runtime - // this.command = this.i18n.t(`commands.${this.key}.command`, {}, lang); + // this.command = this.i18n.t(`bot.commands.${this.key}.command`, {}, lang); this.description = this.i18n.t( - `commands.${this.key}.description`, + `bot.commands.${this.key}.description`, {}, lang, ); diff --git a/telegram-bot/locales/de.json b/telegram-bot/locales/de.json index 0471cad..3faa4c1 100644 --- a/telegram-bot/locales/de.json +++ b/telegram-bot/locales/de.json @@ -1,94 +1,94 @@ { - "commands": { - "list": { - "command": "liste", - "description": "Auflistung aktiver Callouts" + "bot": { + "commands": { + "list": { + "command": "liste", + "description": "Auflistung aktiver Callouts" + }, + "show": { + "command": "zeigen", + "description": "Zeigt Informationen über einen spezifischen Callout" + }, + "start": { + "command": "starten", + "description": "Starte den Bot" + }, + "subscribe": { + "command": "abonnieren", + "description": "Abonniere einen Callout" + }, + "unsubscribe": { + "command": "abmelden", + "description": "Abmelden von einem Callout" + } }, - "show": { - "command": "zeigen", - "description": "Zeigt Informationen über einen spezifischen Callout" + "info": { + "messages": { + "done": "ℹ️ Wenn Sie mit Ihrer Antwort fertig sind, tippen Sie bitte \"{done}\".", + "enterAmountOfMoney": "ℹ️ Bitte geben Sie einen Geldbetrag ein.", + "enterDate": "ℹ️ Bitte geben Sie ein Datum ein.", + "enterLotsOfText": "ℹ️ Sie können in mehreren Zeilen antworten. Bitte senden Sie Ihre Antwort erst, nachdem Sie fertig geschrieben haben. Markdown-Formatierung wird unterstützt.", + "enterTelephoneNumber": "ℹ️ Bitte geben Sie eine Telefonnummer ein.", + "enterText": "ℹ️ Bitte halten Sie Ihre Antwort kurz, idealerweise in einem Satz.", + "enterTime": "ℹ️ Bitte geben Sie eine Uhrzeit ein.", + "enterUrl": "ℹ️ Bitte geben Sie eine URL ein.", + "multipleAddressesAllowed": "ℹ️ Sie können eine oder mehrere Adressen eingeben.", + "multipleEmailsAllowed": "ℹ️ Sie können eine oder mehrere E-Mail-Adressen eingeben.", + "multipleNumbersAllowed": "ℹ️ Bitte geben Sie eine oder mehrere Nummern ein.", + "multipleSelectionsAllowed": "ℹ️ Treffen Sie Ihre Auswahl, indem Sie die Nummern Ihrer Wahl eingeben. Mehrfachauswahlen sind möglich; bitte senden Sie für jede Auswahl eine separate Nachricht.", + "multipleValuesAllowed": "ℹ️ Sie können mehrere Werte eingeben. Bitte senden Sie jeden Wert in einer separaten Nachricht.", + "onlyOneAddressAllowed": "ℹ️ Bitte geben Sie nur eine Adresse ein.", + "onlyOneEmailAllowed": "ℹ️ Bitte geben Sie nur eine E-Mail-Adresse ein.", + "onlyOneNumberAllowed": "ℹ️ Bitte geben Sie nur eine Nummer ein.", + "onlyOneSelectionAllowed": "ℹ️ Treffen Sie Ihre Auswahl, indem Sie die Nummer Ihrer Wahl eingeben oder den entsprechenden Button drücken. Nur eine Auswahl ist möglich.", + "onlyOneValueAllowed": "ℹ️ Sie können nur einen Wert eingeben.", + "placeholder": "ℹ️ Bitte antworten Sie mit etwas Ähnlichem wie \"{placeholder}\".", + "uploadFileHere": "ℹ️ Bitte laden Sie die Datei hier hoch.", + "uploadFilesHere": "ℹ️ Bitte laden Sie die Dateien hier hoch." + } }, - "start": { - "command": "starten", - "description": "Starte den Bot" + "keyboard": { + "label": { + "cancel": "Abbrechen", + "continue": "Fortfahren", + "no": "Nein", + "yes": "Ja" + }, + "message": { + "selectDetailCallout": "❓ Über welchen Callout möchten Sie mehr Informationen erhalten? Wählen Sie eine Nummer." + } }, - "subscribe": { - "command": "abonnieren", - "description": "Abonniere einen Callout" + "reactions": { + "messages": { + "done": "Fertig", + "falsy": "Nein", + "truthy": "Ja" + } }, - "unsubscribe": { - "command": "abmelden", - "description": "Abmelden von einem Callout" - } - }, - "info": { - "messages": { - "done": "ℹ️ Wenn Sie mit Ihrer Antwort fertig sind, tippen Sie bitte \"{done}\".", - "enter-amount-of-money": "ℹ️ Bitte geben Sie einen Geldbetrag ein.", - "enter-content": "ℹ️ Sie können in mehreren Zeilen antworten. Bitte senden Sie Ihre Antwort erst, nachdem Sie fertig geschrieben haben. Markdown-Formatierung wird unterstützt.", - "enter-date": "ℹ️ Bitte geben Sie ein Datum ein.", - "enter-lots-of-text": "ℹ️ Sie können in mehreren Zeilen antworten. Bitte senden Sie Ihre Antwort erst, nachdem Sie fertig geschrieben haben. Markdown-Formatierung wird unterstützt.", - "enter-password": "ℹ️ Bitte geben Sie ein Passwort ein. Aus Sicherheitsgründen ist es ratsam, Ihre Antwort nach dem Senden zu löschen, damit sie nicht im Chatverlauf bleibt.", - "enter-telephone-number": "ℹ️ Bitte geben Sie eine Telefonnummer ein.", - "enter-text": "ℹ️ Bitte halten Sie Ihre Antwort kurz, idealerweise in einem Satz.", - "enter-time": "ℹ️ Bitte geben Sie eine Uhrzeit ein.", - "enter-url": "ℹ️ Bitte geben Sie eine URL ein.", - "multiple-addresses-allowed": "ℹ️ Sie können eine oder mehrere Adressen eingeben.", - "multiple-emails-allowed": "ℹ️ Sie können eine oder mehrere E-Mail-Adressen eingeben.", - "multiple-numbers-allowed": "ℹ️ Bitte geben Sie eine oder mehrere Nummern ein.", - "multiple-selections-allowed": "ℹ️ Treffen Sie Ihre Auswahl, indem Sie die Nummern Ihrer Wahl eingeben. Mehrfachauswahlen sind möglich; bitte senden Sie für jede Auswahl eine separate Nachricht.", - "multiple-values-allowed": "ℹ️ Sie können mehrere Werte eingeben. Bitte senden Sie jeden Wert in einer separaten Nachricht.", - "only-one-address-allowed": "ℹ️ Bitte geben Sie nur eine Adresse ein.", - "only-one-email-allowed": "ℹ️ Bitte geben Sie nur eine E-Mail-Adresse ein.", - "only-one-number-allowed": "ℹ️ Bitte geben Sie nur eine Nummer ein.", - "only-one-selection-allowed": "ℹ️ Treffen Sie Ihre Auswahl, indem Sie die Nummer Ihrer Wahl eingeben oder den entsprechenden Button drücken. Nur eine Auswahl ist möglich.", - "only-one-value-allowed": "ℹ️ Sie können nur einen Wert eingeben.", - "placeholder": "ℹ️ Bitte antworten Sie mit etwas Ähnlichem wie \"{placeholder}\".", - "upload-file-here": "ℹ️ Bitte laden Sie die Datei hier hoch.", - "upload-files-here": "ℹ️ Bitte laden Sie die Dateien hier hoch." - } - }, - "keyboard": { - "label": { - "cancel": "Abbrechen", - "continue": "Fortfahren", - "no": "Nein", - "yes": "Ja" + "render": { + "callout": { + "list": { + "title": "Liste der aktiven Callouts" + } + } }, - "message": { - "select-detail-callout": "❓ Über welchen Callout möchten Sie mehr Informationen erhalten? Wählen Sie eine Nummer." - } - }, - "reactions": { - "messages": { - "done": "Fertig", - "falsy": "Nein", - "truthy": "Ja" - } - }, - "render": { - "callout": { - "list": { - "title": "Liste der aktiven Callouts" + "response": { + "messages": { + "answerWithTruthyOrFalsy": "🤔 Bitte antworten Sie mit \"{truthy}\" oder \"{falsy}\".", + "calloutNotFound": "😥 Entschuldigung, Callout nicht gefunden.", + "calloutStartResponse": "❓ Möchten Sie den Callout beantworten?", + "componentNotSupported": "😅 Der Komponententyp \"{type}\" ist noch nicht implementiert.", + "componentUnknown": "😅 Unbekannter Komponententyp \"{type}\".", + "noActiveCallouts": "😔 Entschuldigung, derzeit gibt es keine aktiven Callouts.", + "notAFileMessage": "🤔 Bitte senden Sie eine Datei.", + "notASelectionMessage": "🤔 Bitte wählen Sie die Nummer einer der verfügbaren Optionen aus.", + "notATextMessage": "🤔 Bitte senden Sie eine Textnachricht.", + "notTheRightFileType": "🤔 Bitte senden Sie eine Datei des Typs \"{type}\".", + "stop": "☺️ Okay, kein Problem." } + }, + "universal": { + "or": "oder" } - }, - "response": { - "messages": { - "answer-with-truthy-or-falsy": "🤔 Bitte antworten Sie mit \"{truthy}\" oder \"{falsy}\".", - "callout-not-found": "😥 Entschuldigung, Callout nicht gefunden.", - "callout-start-response": "❓ Möchten Sie den Callout beantworten?", - "component-not-supported": "😅 Der Komponententyp \"{type}\" ist noch nicht implementiert.", - "component-unknown": "😅 Unbekannter Komponententyp \"{type}\".", - "no-active-callouts": "😔 Entschuldigung, derzeit gibt es keine aktiven Callouts.", - "not-a-file-message": "🤔 Bitte senden Sie eine Datei.", - "not-a-selection-message": "🤔 Bitte wählen Sie die Nummer einer der verfügbaren Optionen aus.", - "not-a-text-message": "🤔 Bitte senden Sie eine Textnachricht.", - "not-the-right-file-type": "🤔 Bitte senden Sie eine Datei des Typs \"{type}\".", - "stop": "☺️ Okay, kein Problem." - } - }, - "universal": { - "or": "oder" } } diff --git a/telegram-bot/locales/de@informal.json b/telegram-bot/locales/de@informal.json index ee2da1e..093650a 100644 --- a/telegram-bot/locales/de@informal.json +++ b/telegram-bot/locales/de@informal.json @@ -1,94 +1,94 @@ { - "commands": { - "list": { - "command": "liste", - "description": "Liste aktive Callouts" + "bot": { + "commands": { + "list": { + "command": "liste", + "description": "Liste aktive Callouts" + }, + "show": { + "command": "zeigen", + "description": "Zeigt Infos über einen bestimmten Callout" + }, + "start": { + "command": "starten", + "description": "Starte den Bot" + }, + "subscribe": { + "command": "abonnieren", + "description": "Abonniere einen Callout" + }, + "unsubscribe": { + "command": "abmelden", + "description": "Abmelden von einem Callout" + } }, - "show": { - "command": "zeigen", - "description": "Zeigt Infos über einen bestimmten Callout" + "info": { + "messages": { + "done": "ℹ️ Wenn du mit deiner Antwort fertig bist, tippe bitte \"{done}\".", + "enterAmountOfMoney": "ℹ️ Bitte gib einen Geldbetrag ein.", + "enterDate": "ℹ️ Bitte gib ein Datum ein.", + "enterLotsOfText": "ℹ️ Du kannst in mehreren Zeilen antworten. Bitte sende deine Antwort erst, nachdem du fertig geschrieben hast. Markdown-Formatierung wird unterstützt.", + "enterTelephoneNumber": "ℹ️ Bitte gib eine Telefonnummer ein.", + "enterText": "ℹ️ Bitte halte deine Antwort kurz, idealerweise in einem Satz.", + "enterTime": "ℹ️ Bitte gib eine Uhrzeit ein.", + "enterUrl": "ℹ️ Bitte gib eine URL ein.", + "multipleAddressesAllowed": "ℹ️ Du kannst eine oder mehrere Adressen eingeben.", + "multipleEmailsAllowed": "ℹ️ Du kannst eine oder mehrere E-Mail-Adressen eingeben.", + "multipleNumbersAllowed": "ℹ️ Bitte gib eine oder mehrere Nummern ein.", + "multipleSelectionsAllowed": "ℹ️ Treffe deine Auswahl, indem du die Nummer deiner Wahl eingibst. Mehrfachauswahlen sind möglich; bitte sende für jede Auswahl eine separate Nachricht.", + "multipleValuesAllowed": "ℹ️ Du kannst mehrere Werte eingeben. Bitte sende jeden Wert in einer separaten Nachricht.", + "onlyOneAddressAllowed": "ℹ️ Bitte gib nur eine Adresse ein.", + "onlyOneEmailAllowed": "ℹ️ Bitte gib nur eine E-Mail-Adresse ein.", + "onlyOneNumberAllowed": "ℹ️ Bitte gib nur eine Nummer ein.", + "onlyOneSelectionAllowed": "ℹ️ Treffe deine Auswahl, indem du die Nummer deiner Wahl eingibst oder den entsprechenden Button drückst. Nur eine Auswahl ist möglich.", + "onlyOneValueAllowed": "ℹ️ Du kannst nur einen Wert eingeben.", + "placeholder": "ℹ️ Bitte antworte mit etwas Ähnlichem wie \"{placeholder}\".", + "uploadFileHere": "ℹ️ Bitte lade die Datei hier hoch.", + "uploadFilesHere": "ℹ️ Bitte lade die Dateien hier hoch." + } }, - "start": { - "command": "starten", - "description": "Starte den Bot" + "keyboard": { + "label": { + "cancel": "Abbrechen", + "continue": "Weiter", + "no": "Nein", + "yes": "Ja" + }, + "message": { + "selectDetailCallout": "❓ Über welchen Callout möchtest du mehr Informationen erhalten? Wähle eine Nummer." + } }, - "subscribe": { - "command": "abonnieren", - "description": "Abonniere einen Callout" + "reactions": { + "messages": { + "done": "Fertig", + "falsy": "Nein", + "truthy": "Ja" + } }, - "unsubscribe": { - "command": "abmelden", - "description": "Abmelden von einem Callout" - } - }, - "info": { - "messages": { - "done": "ℹ️ Wenn du mit deiner Antwort fertig bist, tippe bitte \"{done}\".", - "enter-amount-of-money": "ℹ️ Bitte gib einen Geldbetrag ein.", - "enter-content": "ℹ️ Du kannst in mehreren Zeilen antworten. Bitte sende deine Antwort erst, nachdem du fertig geschrieben hast. Markdown-Formatierung wird unterstützt.", - "enter-date": "ℹ️ Bitte gib ein Datum ein.", - "enter-lots-of-text": "ℹ️ Du kannst in mehreren Zeilen antworten. Bitte sende deine Antwort erst, nachdem du fertig geschrieben hast. Markdown-Formatierung wird unterstützt.", - "enter-password": "ℹ️ Bitte gebe ein Passwort ein. Aus Sicherheitsgründen ist es ratsam, deine Antwort nach dem Senden zu löschen, damit sie nicht im Chatverlauf bleibt.", - "enter-telephone-number": "ℹ️ Bitte gib eine Telefonnummer ein.", - "enter-text": "ℹ️ Bitte halte deine Antwort kurz, idealerweise in einem Satz.", - "enter-time": "ℹ️ Bitte gib eine Uhrzeit ein.", - "enter-url": "ℹ️ Bitte gib eine URL ein.", - "multiple-addresses-allowed": "ℹ️ Du kannst eine oder mehrere Adressen eingeben.", - "multiple-emails-allowed": "ℹ️ Du kannst eine oder mehrere E-Mail-Adressen eingeben.", - "multiple-numbers-allowed": "ℹ️ Bitte gib eine oder mehrere Nummern ein.", - "multiple-selections-allowed": "ℹ️ Treffe deine Auswahl, indem du die Nummer deiner Wahl eingibst. Mehrfachauswahlen sind möglich; bitte sende für jede Auswahl eine separate Nachricht.", - "multiple-values-allowed": "ℹ️ Du kannst mehrere Werte eingeben. Bitte sende jeden Wert in einer separaten Nachricht.", - "only-one-address-allowed": "ℹ️ Bitte gib nur eine Adresse ein.", - "only-one-email-allowed": "ℹ️ Bitte gib nur eine E-Mail-Adresse ein.", - "only-one-number-allowed": "ℹ️ Bitte gib nur eine Nummer ein.", - "only-one-selection-allowed": "ℹ️ Treffe deine Auswahl, indem du die Nummer deiner Wahl eingibst oder den entsprechenden Button drückst. Nur eine Auswahl ist möglich.", - "only-one-value-allowed": "ℹ️ Du kannst nur einen Wert eingeben.", - "placeholder": "ℹ️ Bitte antworte mit etwas Ähnlichem wie \"{placeholder}\".", - "upload-file-here": "ℹ️ Bitte lade die Datei hier hoch.", - "upload-files-here": "ℹ️ Bitte lade die Dateien hier hoch." - } - }, - "keyboard": { - "label": { - "cancel": "Abbrechen", - "continue": "Weiter", - "no": "Nein", - "yes": "Ja" + "render": { + "callout": { + "list": { + "title": "Liste der aktiven Callouts" + } + } }, - "message": { - "select-detail-callout": "❓ Über welchen Callout möchtest du mehr Informationen erhalten? Wähle eine Nummer." - } - }, - "reactions": { - "messages": { - "done": "Fertig", - "falsy": "Nein", - "truthy": "Ja" - } - }, - "render": { - "callout": { - "list": { - "title": "Liste der aktiven Callouts" + "response": { + "messages": { + "answerWithTruthyOrFalsy": "🤔 Bitte antworte mit \"{truthy}\" oder \"{falsy}\".", + "calloutNotFound": "😥 Entschuldigung, Callout nicht gefunden.", + "calloutStartResponse": "❓ Möchtest du den Callout beantworten?", + "componentNotSupported": "😅 Der Komponententyp \"{type}\" ist noch nicht implementiert.", + "componentUnknown": "😅 Unbekannter Komponententyp \"{type}\".", + "noActiveCallouts": "😔 Entschuldigung, derzeit gibt es keine aktiven Callouts.", + "notAFileMessage": "🤔 Bitte sende eine Datei.", + "notASelectionMessage": "🤔 Bitte wähle die Nummer einer der verfügbaren Optionen aus.", + "notATextMessage": "🤔 Bitte sende eine Textnachricht.", + "notTheRightFileType": "🤔 Bitte sende eine Datei des Typs \"{type}\".", + "stop": "☺️ Okay, kein Problem." } + }, + "universal": { + "or": "oder" } - }, - "response": { - "messages": { - "answer-with-truthy-or-falsy": "🤔 Bitte antworte mit \"{truthy}\" oder \"{falsy}\".", - "callout-not-found": "😥 Entschuldigung, Callout nicht gefunden.", - "callout-start-response": "❓ Möchtest du den Callout beantworten?", - "component-not-supported": "😅 Der Komponententyp \"{type}\" ist noch nicht implementiert.", - "component-unknown": "😅 Unbekannter Komponententyp \"{type}\".", - "no-active-callouts": "😔 Entschuldigung, derzeit gibt es keine aktiven Callouts.", - "not-a-file-message": "🤔 Bitte sende eine Datei.", - "not-a-selection-message": "🤔 Bitte wähle die Nummer einer der verfügbaren Optionen aus.", - "not-a-text-message": "🤔 Bitte sende eine Textnachricht.", - "not-the-right-file-type": "🤔 Bitte sende eine Datei des Typs \"{type}\".", - "stop": "☺️ Okay, kein Problem." - } - }, - "universal": { - "or": "oder" } } diff --git a/telegram-bot/locales/en.json b/telegram-bot/locales/en.json index 3966590..b5b3711 100644 --- a/telegram-bot/locales/en.json +++ b/telegram-bot/locales/en.json @@ -1,94 +1,94 @@ { - "commands": { - "list": { - "command": "list", - "description": "List active callouts" + "bot": { + "commands": { + "list": { + "command": "list", + "description": "List active callouts" + }, + "show": { + "command": "show", + "description": "Show information about a specific callout" + }, + "start": { + "command": "start", + "description": "Start the bot" + }, + "subscribe": { + "command": "subscribe", + "description": "Subscribe to a callout" + }, + "unsubscribe": { + "command": "unsubscribe", + "description": "Unsubscribe from a callout" + } }, - "show": { - "command": "show", - "description": "Show information about a specific callout" + "info": { + "messages": { + "done": "ℹ️ If you have finished your response, please type \"{done}\".", + "enterAmountOfMoney": "ℹ️ Please enter an amount of money.", + "enterDate": "ℹ️ Please enter a date.", + "enterLotsOfText": "ℹ️ You may respond in multiple lines. Please send your response only after you have finished writing.", + "enterTelephoneNumber": "ℹ️ Please enter a telephone number.", + "enterText": "ℹ️ Please keep your response brief, ideally in one sentence.", + "enterTime": "ℹ️ Please enter a time.", + "enterUrl": "ℹ️ Please enter a URL.", + "multipleAddressesAllowed": "ℹ️ You can enter one or more addresses.", + "multipleEmailsAllowed": "ℹ️ You can enter one or more emails.", + "multipleNumbersAllowed": "ℹ️ Please enter one or more numbers.", + "multipleSelectionsAllowed": "ℹ️ Please make your selection by typing the number choices. Multiple selections are allowed, please send a separate message for each of your selection.", + "multipleValuesAllowed": "ℹ️ You can enter multiple values by sending each value separately.", + "onlyOneAddressAllowed": "ℹ️ Please enter an address.", + "onlyOneEmailAllowed": "ℹ️ Please enter an email.", + "onlyOneNumberAllowed": "ℹ️ Please enter a number.", + "onlyOneSelectionAllowed": "ℹ️ Please make your selection by typing the number of your choice or pressing the button of your choice. Only one selection is allowed.", + "onlyOneValueAllowed": "ℹ️ You can only enter one value.", + "placeholder": "ℹ️ Please respond with something like \"{placeholder}\".", + "uploadFileHere": "ℹ️ Please upload the file here.", + "uploadFilesHere": "ℹ️ Please upload the files here." + } }, - "start": { - "command": "start", - "description": "Start the bot" + "keyboard": { + "label": { + "cancel": "Cancel", + "continue": "Continue", + "no": "No", + "yes": "Yes" + }, + "message": { + "selectDetailCallout": "❓ Which Callout would you like to get more information displayed about? Choose a number" + } }, - "subscribe": { - "command": "subscribe", - "description": "Subscribe to a callout" + "reactions": { + "messages": { + "done": "Done", + "falsy": "No", + "truthy": "Yes" + } }, - "unsubscribe": { - "command": "unsubscribe", - "description": "Unsubscribe from a callout" - } - }, - "info": { - "messages": { - "done": "ℹ️ If you have finished your response, please type \"{done}\".", - "enter-amount-of-money": "ℹ️ Please enter an amount of money.", - "enter-content": "ℹ️ You may respond in multiple lines. Please send your response only after you have finished writing. Markdown formatting is supported.", - "enter-date": "ℹ️ Please enter a date.", - "enter-lots-of-text": "ℹ️ You may respond in multiple lines. Please send your response only after you have finished writing.", - "enter-password": "ℹ️ Please enter a password. For security, it's advisable to delete your response after sending to prevent it from remaining in the chat history.", - "enter-telephone-number": "ℹ️ Please enter a telephone number.", - "enter-text": "ℹ️ Please keep your response brief, ideally in one sentence.", - "enter-time": "ℹ️ Please enter a time.", - "enter-url": "ℹ️ Please enter a URL.", - "multiple-addresses-allowed": "ℹ️ You can enter one or more addresses.", - "multiple-emails-allowed": "ℹ️ You can enter one or more emails.", - "multiple-numbers-allowed": "ℹ️ Please enter one or more numbers.", - "multiple-selections-allowed": "ℹ️ Please make your selection by typing the number choices. Multiple selections are allowed, please send a separate message for each of your selection.", - "multiple-values-allowed": "ℹ️ You can enter multiple values by sending each value separately.", - "only-one-address-allowed": "ℹ️ Please enter an address.", - "only-one-email-allowed": "ℹ️ Please enter an email.", - "only-one-number-allowed": "ℹ️ Please enter a number.", - "only-one-selection-allowed": "ℹ️ Please make your selection by typing the number of your choice or pressing the button of your choice. Only one selection is allowed.", - "only-one-value-allowed": "ℹ️ You can only enter one value.", - "placeholder": "ℹ️ Please respond with something like \"{placeholder}\".", - "upload-file-here": "ℹ️ Please upload the file here.", - "upload-files-here": "ℹ️ Please upload the files here." - } - }, - "keyboard": { - "label": { - "cancel": "Cancel", - "continue": "Continue", - "no": "No", - "yes": "Yes" + "render": { + "callout": { + "list": { + "title": "List of active callouts" + } + } }, - "message": { - "select-detail-callout": "❓ Which Callout would you like to get more information displayed about? Choose a number" - } - }, - "reactions": { - "messages": { - "done": "Done", - "falsy": "No", - "truthy": "Yes" - } - }, - "render": { - "callout": { - "list": { - "title": "List of active callouts" + "response": { + "messages": { + "answerWithTruthyOrFalsy": "🤔 Please answer with \"{truthy}\" or \"{falsy}\".", + "calloutNotFound": "😥 Sorry, Callout not found..", + "calloutStartResponse": "❓ Would you like to respond to the callout?", + "componentNotSupported": "😅 \"{type}\" not implemented..", + "componentUnknown": "😅 Unknown component type \"{type}\"..", + "noActiveCallouts": "😔 Sorry, there are currently no active callouts..", + "notAFileMessage": "🤔 Please send a file.", + "notASelectionMessage": "🤔 Please select the number of one of the possible answers.", + "notATextMessage": "🤔 Please send a text message.", + "notTheRightFileType": "🤔 Please send a file of type {type}.", + "stop": "☺️ Ok, no problem." } + }, + "universal": { + "or": "or" } - }, - "response": { - "messages": { - "answer-with-truthy-or-falsy": "🤔 Please answer with \"{truthy}\" or \"{falsy}\".", - "callout-not-found": "😥 Sorry, Callout not found..", - "callout-start-response": "❓ Would you like to respond to the callout?", - "component-not-supported": "😅 \"{type}\" not implemented..", - "component-unknown": "😅 Unknown component type \"{type}\"..", - "no-active-callouts": "😔 Sorry, there are currently no active callouts..", - "not-a-file-message": "🤔 Please send a file.", - "not-a-selection-message": "🤔 Please select the number of one of the possible answers.", - "not-a-text-message": "🤔 Please send a text message.", - "not-the-right-file-type": "🤔 Please send a file of type {type}.", - "stop": "☺️ Ok, no problem." - } - }, - "universal": { - "or": "or" } } diff --git a/telegram-bot/locales/nl.json b/telegram-bot/locales/nl.json index 913108d..fdd5108 100644 --- a/telegram-bot/locales/nl.json +++ b/telegram-bot/locales/nl.json @@ -1,94 +1,94 @@ { - "commands": { - "list": { - "command": "lijst", - "description": "Lijst van actieve oproepen" + "bot": { + "commands": { + "list": { + "command": "lijst", + "description": "Lijst van actieve oproepen" + }, + "show": { + "command": "tonen", + "description": "Toont informatie over een specifieke oproep" + }, + "start": { + "command": "starten", + "description": "Start de bot" + }, + "subscribe": { + "command": "abonneren", + "description": "Abonneren op een oproep" + }, + "unsubscribe": { + "command": "afmelden", + "description": "Afmelden van een oproep" + } }, - "show": { - "command": "tonen", - "description": "Toont informatie over een specifieke oproep" + "info": { + "messages": { + "done": "ℹ️ Als u klaar bent met uw reactie, typ dan \"{done}\".", + "enterAmountOfMoney": "ℹ️ Voer alstublieft een geldbedrag in.", + "enterDate": "ℹ️ Voer alstublieft een datum in.", + "enterLotsOfText": "ℹ️ U mag in meerdere regels reageren. Stuur uw reactie pas nadat u klaar bent met schrijven. Markdown-opmaak wordt ondersteund.", + "enterTelephoneNumber": "ℹ️ Voer alstublieft een telefoonnummer in.", + "enterText": "ℹ️ Houd uw reactie kort, bij voorkeur in één zin.", + "enterTime": "ℹ️ Voer alstublieft een tijd in.", + "enterUrl": "ℹ️ Voer alstublieft een URL in.", + "multipleAddressesAllowed": "ℹ️ U kunt een of meer adressen invoeren.", + "multipleEmailsAllowed": "ℹ️ U kunt een of meer e-mails invoeren.", + "multipleNumbersAllowed": "ℹ️ Voer alstublieft een of meer nummers in.", + "multipleSelectionsAllowed": "ℹ️ Maak uw keuze door de nummerkeuzes in te typen. Meerdere selecties zijn toegestaan, stuur alstublieft een apart bericht voor elke selectie.", + "multipleValuesAllowed": "ℹ️ U kunt meerdere waarden invoeren door elke waarde afzonderlijk te verzenden.", + "onlyOneAddressAllowed": "ℹ️ Voer alstublieft een adres in.", + "onlyOneEmailAllowed": "ℹ️ Voer alstublieft een e-mailadres in.", + "onlyOneNumberAllowed": "ℹ️ Voer alstublieft een nummer in.", + "onlyOneSelectionAllowed": "ℹ️ Maak uw keuze door het nummer van uw keuze in te typen of op de bijbehorende knop te drukken. Slechts één selectie is toegestaan.", + "onlyOneValueAllowed": "ℹ️ U kunt slechts één waarde invoeren.", + "placeholder": "ℹ️ Reageer alstublieft met iets als \"{placeholder}\".", + "uploadFileHere": "ℹ️ Upload het bestand hier alstublieft.", + "uploadFilesHere": "ℹ️ Upload de bestanden hier alstublieft." + } }, - "start": { - "command": "starten", - "description": "Start de bot" + "keyboard": { + "label": { + "cancel": "Annuleren", + "continue": "Doorgaan", + "no": "Nee", + "yes": "Ja" + }, + "message": { + "selectDetailCallout": "❓ Over welke oproep wilt u meer informatie krijgen? Kies een nummer." + } }, - "subscribe": { - "command": "abonneren", - "description": "Abonneren op een oproep" + "reactions": { + "messages": { + "done": "Klaar", + "falsy": "Nee", + "truthy": "Ja" + } }, - "unsubscribe": { - "command": "afmelden", - "description": "Afmelden van een oproep" - } - }, - "info": { - "messages": { - "done": "ℹ️ Als u klaar bent met uw reactie, typ dan \"{done}\".", - "enter-amount-of-money": "ℹ️ Voer alstublieft een geldbedrag in.", - "enter-content": "ℹ️ U mag in meerdere regels reageren. Stuur uw reactie pas nadat u klaar bent met schrijven. Markdown-opmaak wordt ondersteund.", - "enter-date": "ℹ️ Voer alstublieft een datum in.", - "enter-lots-of-text": "ℹ️ U mag in meerdere regels reageren. Stuur uw reactie pas nadat u klaar bent met schrijven. Markdown-opmaak wordt ondersteund.", - "enter-password": "ℹ️ Voer alstublieft een wachtwoord in. Om veiligheidsredenen is het raadzaam uw antwoord te verwijderen na het verzenden, zodat het niet in de chatgeschiedenis blijft.", - "enter-telephone-number": "ℹ️ Voer alstublieft een telefoonnummer in.", - "enter-text": "ℹ️ Houd uw reactie kort, bij voorkeur in één zin.", - "enter-time": "ℹ️ Voer alstublieft een tijd in.", - "enter-url": "ℹ️ Voer alstublieft een URL in.", - "multiple-addresses-allowed": "ℹ️ U kunt een of meer adressen invoeren.", - "multiple-emails-allowed": "ℹ️ U kunt een of meer e-mails invoeren.", - "multiple-numbers-allowed": "ℹ️ Voer alstublieft een of meer nummers in.", - "multiple-selections-allowed": "ℹ️ Maak uw keuze door de nummerkeuzes in te typen. Meerdere selecties zijn toegestaan, stuur alstublieft een apart bericht voor elke selectie.", - "multiple-values-allowed": "ℹ️ U kunt meerdere waarden invoeren door elke waarde afzonderlijk te verzenden.", - "only-one-address-allowed": "ℹ️ Voer alstublieft een adres in.", - "only-one-email-allowed": "ℹ️ Voer alstublieft een e-mailadres in.", - "only-one-number-allowed": "ℹ️ Voer alstublieft een nummer in.", - "only-one-selection-allowed": "ℹ️ Maak uw keuze door het nummer van uw keuze in te typen of op de bijbehorende knop te drukken. Slechts één selectie is toegestaan.", - "only-one-value-allowed": "ℹ️ U kunt slechts één waarde invoeren.", - "placeholder": "ℹ️ Reageer alstublieft met iets als \"{placeholder}\".", - "upload-file-here": "ℹ️ Upload het bestand hier alstublieft.", - "upload-files-here": "ℹ️ Upload de bestanden hier alstublieft." - } - }, - "keyboard": { - "label": { - "cancel": "Annuleren", - "continue": "Doorgaan", - "no": "Nee", - "yes": "Ja" + "render": { + "callout": { + "list": { + "title": "Lijst van actieve oproepen" + } + } }, - "message": { - "select-detail-callout": "❓ Over welke oproep wilt u meer informatie krijgen? Kies een nummer." - } - }, - "reactions": { - "messages": { - "done": "Klaar", - "falsy": "Nee", - "truthy": "Ja" - } - }, - "render": { - "callout": { - "list": { - "title": "Lijst van actieve oproepen" + "response": { + "messages": { + "answerWithTruthyOrFalsy": "🤔 Antwoord alstublieft met \"{truthy}\" of \"{falsy}\".", + "calloutNotFound": "😥 Sorry, oproep niet gevonden..", + "calloutStartResponse": "❓ Wilt u reageren op de oproep?", + "componentNotSupported": "😅 \"{type}\" niet geïmplementeerd..", + "componentUnknown": "😅 Onbekend componenttype \"{type}\"..", + "noActiveCallouts": "😔 Sorry, er zijn momenteel geen actieve oproepen..", + "notAFileMessage": "🤔 Stuur alstublieft een bestand.", + "notASelectionMessage": "🤔 Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", + "notATextMessage": "🤔 Stuur alstublieft een tekstbericht.", + "notTheRightFileType": "🤔 Stuur alstublieft een bestand van het type {type}.", + "stop": "☺️ Oké, geen probleem." } + }, + "universal": { + "or": "of" } - }, - "response": { - "messages": { - "answer-with-truthy-or-falsy": "🤔 Antwoord alstublieft met \"{truthy}\" of \"{falsy}\".", - "callout-not-found": "😥 Sorry, oproep niet gevonden..", - "callout-start-response": "❓ Wilt u reageren op de oproep?", - "component-not-supported": "😅 \"{type}\" niet geïmplementeerd..", - "component-unknown": "😅 Onbekend componenttype \"{type}\"..", - "no-active-callouts": "😔 Sorry, er zijn momenteel geen actieve oproepen..", - "not-a-file-message": "🤔 Stuur alstublieft een bestand.", - "not-a-selection-message": "🤔 Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", - "not-a-text-message": "🤔 Stuur alstublieft een tekstbericht.", - "not-the-right-file-type": "🤔 Stuur alstublieft een bestand van het type {type}.", - "stop": "☺️ Oké, geen probleem." - } - }, - "universal": { - "or": "of" } } diff --git a/telegram-bot/locales/pt.json b/telegram-bot/locales/pt.json index b75a192..319f5fd 100644 --- a/telegram-bot/locales/pt.json +++ b/telegram-bot/locales/pt.json @@ -1,94 +1,94 @@ { - "commands": { - "list": { - "command": "listar", - "description": "Listar chamados ativos" + "bot": { + "commands": { + "list": { + "command": "listar", + "description": "Listar chamados ativos" + }, + "show": { + "command": "mostrar", + "description": "Mostrar informações sobre um chamado específico" + }, + "start": { + "command": "começar", + "description": "Iniciar o bot" + }, + "subscribe": { + "command": "inscrever-se", + "description": "Inscrever-se em um chamado" + }, + "unsubscribe": { + "command": "cancelar inscrição", + "description": "Cancelar inscrição de um chamado" + } }, - "show": { - "command": "mostrar", - "description": "Mostrar informações sobre um chamado específico" + "info": { + "messages": { + "done": "ℹ️ Se você terminou sua resposta, por favor digite \"{done}\".", + "enterAmountOfMoney": "ℹ️ Por favor, insira um valor em dinheiro.", + "enterDate": "ℹ️ Por favor, insira uma data.", + "enterLotsOfText": "ℹ️ Você pode responder em várias linhas. Envie sua resposta somente após terminar de escrever. Formatação Markdown é suportada.", + "enterTelephoneNumber": "ℹ️ Por favor, insira um número de telefone.", + "enterText": "ℹ️ Por favor, mantenha sua resposta breve, idealmente em uma frase.", + "enterTime": "ℹ️ Por favor, insira um horário.", + "enterUrl": "ℹ️ Por favor, insira uma URL.", + "multipleAddressesAllowed": "ℹ️ Você pode inserir um ou mais endereços.", + "multipleEmailsAllowed": "ℹ️ Você pode inserir um ou mais e-mails.", + "multipleNumbersAllowed": "ℹ️ Por favor, insira um ou mais números.", + "multipleSelectionsAllowed": "ℹ️ Faça sua seleção digitando as opções de número. Múltiplas seleções são permitidas, envie uma mensagem separada para cada uma de suas escolhas.", + "multipleValuesAllowed": "ℹ️ Você pode inserir vários valores enviando cada valor separadamente.", + "onlyOneAddressAllowed": "ℹ️ Por favor, insira um endereço.", + "onlyOneEmailAllowed": "ℹ️ Por favor, insira um e-mail.", + "onlyOneNumberAllowed": "ℹ️ Por favor, insira um número.", + "onlyOneSelectionAllowed": "ℹ️ Faça sua seleção digitando o número de sua escolha ou pressionando o botão de sua escolha. Apenas uma seleção é permitida.", + "onlyOneValueAllowed": "ℹ️ Você só pode inserir um valor.", + "placeholder": "ℹ️ Por favor, responda com algo como \"{placeholder}\".", + "uploadFileHere": "ℹ️ Por favor, faça o upload do arquivo aqui.", + "uploadFilesHere": "ℹ️ Por favor, faça o upload dos arquivos aqui." + } }, - "start": { - "command": "começar", - "description": "Iniciar o bot" + "keyboard": { + "label": { + "cancel": "Cancelar", + "continue": "Continuar", + "no": "Não", + "yes": "Sim" + }, + "message": { + "selectDetailCallout": "❓ Sobre qual chamado você gostaria de obter mais informações? Escolha um número." + } }, - "subscribe": { - "command": "inscrever-se", - "description": "Inscrever-se em um chamado" + "reactions": { + "messages": { + "done": "Concluído", + "falsy": "Não", + "truthy": "Sim" + } }, - "unsubscribe": { - "command": "cancelar inscrição", - "description": "Cancelar inscrição de um chamado" - } - }, - "info": { - "messages": { - "done": "ℹ️ Se você terminou sua resposta, por favor digite \"{done}\".", - "enter-amount-of-money": "ℹ️ Por favor, insira um valor em dinheiro.", - "enter-content": "ℹ️ Você pode responder em várias linhas. Envie sua resposta somente após terminar de escrever. Formatação Markdown é suportada.", - "enter-date": "ℹ️ Por favor, insira uma data.", - "enter-lots-of-text": "ℹ️ Você pode responder em várias linhas. Envie sua resposta somente após terminar de escrever. Formatação Markdown é suportada.", - "enter-password": "ℹ️ Por favor, insira uma senha. Por segurança, é aconselhável apagar sua resposta após o envio para evitar que ela permaneça no histórico do chat.", - "enter-telephone-number": "ℹ️ Por favor, insira um número de telefone.", - "enter-text": "ℹ️ Por favor, mantenha sua resposta breve, idealmente em uma frase.", - "enter-time": "ℹ️ Por favor, insira um horário.", - "enter-url": "ℹ️ Por favor, insira uma URL.", - "multiple-addresses-allowed": "ℹ️ Você pode inserir um ou mais endereços.", - "multiple-emails-allowed": "ℹ️ Você pode inserir um ou mais e-mails.", - "multiple-numbers-allowed": "ℹ️ Por favor, insira um ou mais números.", - "multiple-selections-allowed": "ℹ️ Faça sua seleção digitando as opções de número. Múltiplas seleções são permitidas, envie uma mensagem separada para cada uma de suas escolhas.", - "multiple-values-allowed": "ℹ️ Você pode inserir vários valores enviando cada valor separadamente.", - "only-one-address-allowed": "ℹ️ Por favor, insira um endereço.", - "only-one-email-allowed": "ℹ️ Por favor, insira um e-mail.", - "only-one-number-allowed": "ℹ️ Por favor, insira um número.", - "only-one-selection-allowed": "ℹ️ Faça sua seleção digitando o número de sua escolha ou pressionando o botão de sua escolha. Apenas uma seleção é permitida.", - "only-one-value-allowed": "ℹ️ Você só pode inserir um valor.", - "placeholder": "ℹ️ Por favor, responda com algo como \"{placeholder}\".", - "upload-file-here": "ℹ️ Por favor, faça o upload do arquivo aqui.", - "upload-files-here": "ℹ️ Por favor, faça o upload dos arquivos aqui." - } - }, - "keyboard": { - "label": { - "cancel": "Cancelar", - "continue": "Continuar", - "no": "Não", - "yes": "Sim" + "render": { + "callout": { + "list": { + "title": "Lista de chamados ativos" + } + } }, - "message": { - "select-detail-callout": "❓ Sobre qual chamado você gostaria de obter mais informações? Escolha um número." - } - }, - "reactions": { - "messages": { - "done": "Concluído", - "falsy": "Não", - "truthy": "Sim" - } - }, - "render": { - "callout": { - "list": { - "title": "Lista de chamados ativos" + "response": { + "messages": { + "answerWithTruthyOrFalsy": "🤔 Por favor, responda com \"{truthy}\" ou \"{falsy}\".", + "calloutNotFound": "😥 Desculpe, Chamado não encontrado..", + "calloutStartResponse": "❓ Você gostaria de responder ao chamado?", + "componentNotSupported": "😅 O tipo de componente \"{type}\" ainda não foi implementado..", + "componentUnknown": "😅 Tipo de componente desconhecido \"{type}\"..", + "noActiveCallouts": "😔 Desculpe, atualmente não há chamados ativos..", + "notAFileMessage": "🤔 Por favor, envie um arquivo.", + "notASelectionMessage": "🤔 Por favor, selecione o número de uma das possíveis respostas.", + "notATextMessage": "🤔 Por favor, envie uma mensagem de texto.", + "notTheRightFileType": "🤔 Por favor, envie um arquivo do tipo {type}.", + "stop": "☺️ Ok, sem problema." } + }, + "universal": { + "or": "ou" } - }, - "response": { - "messages": { - "answer-with-truthy-or-falsy": "🤔 Por favor, responda com \"{truthy}\" ou \"{falsy}\".", - "callout-not-found": "😥 Desculpe, Chamado não encontrado..", - "callout-start-response": "❓ Você gostaria de responder ao chamado?", - "component-not-supported": "😅 O tipo de componente \"{type}\" ainda não foi implementado..", - "component-unknown": "😅 Tipo de componente desconhecido \"{type}\"..", - "no-active-callouts": "😔 Desculpe, atualmente não há chamados ativos..", - "not-a-file-message": "🤔 Por favor, envie um arquivo.", - "not-a-selection-message": "🤔 Por favor, selecione o número de uma das possíveis respostas.", - "not-a-text-message": "🤔 Por favor, envie uma mensagem de texto.", - "not-the-right-file-type": "🤔 Por favor, envie um arquivo do tipo {type}.", - "stop": "☺️ Ok, sem problema." - } - }, - "universal": { - "or": "ou" } } diff --git a/telegram-bot/locales/ru.json b/telegram-bot/locales/ru.json index f86f931..8feed26 100644 --- a/telegram-bot/locales/ru.json +++ b/telegram-bot/locales/ru.json @@ -1,94 +1,94 @@ { - "commands": { - "list": { - "command": "список", - "description": "Список активных вызовов" + "bot": { + "commands": { + "list": { + "command": "список", + "description": "Список активных вызовов" + }, + "show": { + "command": "показать", + "description": "Показывает информацию о конкретном вызове" + }, + "start": { + "command": "начать", + "description": "Запустить бота" + }, + "subscribe": { + "command": "подписаться", + "description": "Подписаться на вызов" + }, + "unsubscribe": { + "command": "отписаться", + "description": "Отписаться от вызова" + } }, - "show": { - "command": "показать", - "description": "Показывает информацию о конкретном вызове" + "info": { + "messages": { + "done": "ℹ️ Если вы закончили свой ответ, пожалуйста, введите \"{done}\".", + "enterAmountOfMoney": "ℹ️ Пожалуйста, введите сумму денег.", + "enterDate": "ℹ️ Пожалуйста, введите дату.", + "enterLotsOfText": "ℹ️ Вы можете ответить несколькими строками. Пожалуйста, отправьте ваш ответ только после того, как вы закончите писать. Поддерживается форматирование Markdown.", + "enterTelephoneNumber": "ℹ️ Пожалуйста, введите телефонный номер.", + "enterText": "ℹ️ Пожалуйста, держите ваш ответ кратким, желательно в одном предложении.", + "enterTime": "ℹ️ Пожалуйста, введите время.", + "enterUrl": "ℹ️ Пожалуйста, введите URL.", + "multipleAddressesAllowed": "ℹ️ Вы можете ввести один или несколько адресов.", + "multipleEmailsAllowed": "ℹ️ Вы можете ввести один или несколько адресов электронной почты.", + "multipleNumbersAllowed": "ℹ️ Пожалуйста, введите один или несколько номеров.", + "multipleSelectionsAllowed": "ℹ️ Пожалуйста, сделайте свой выбор, введя номера ваших выборов. Разрешено несколько выборов, отправьте отдельное сообщение для каждого вашего выбора.", + "multipleValuesAllowed": "ℹ️ Вы можете ввести несколько значений, отправляя каждое значение отдельно.", + "onlyOneAddressAllowed": "ℹ️ Пожалуйста, введите адрес.", + "onlyOneEmailAllowed": "ℹ️ Пожалуйста, введите адрес электронной почты.", + "onlyOneNumberAllowed": "ℹ️ Пожалуйста, введите номер.", + "onlyOneSelectionAllowed": "ℹ️ Пожалуйста, сделайте свой выбор, введя номер вашего выбора или нажав соответствующую кнопку. Разрешен только один выбор.", + "onlyOneValueAllowed": "ℹ️ Вы можете ввести только одно значение.", + "placeholder": "ℹ️ Пожалуйста, ответьте чем-то вроде \"{placeholder}\".", + "uploadFileHere": "ℹ️ Пожалуйста, загрузите файл здесь.", + "uploadFilesHere": "ℹ️ Пожалуйста, загрузите файлы здесь." + } }, - "start": { - "command": "начать", - "description": "Запустить бота" + "keyboard": { + "label": { + "cancel": "Отмена", + "continue": "Продолжить", + "no": "Нет", + "yes": "Да" + }, + "message": { + "selectDetailCallout": "❓ О каком вызове вы хотели бы получить больше информации? Выберите номер." + } }, - "subscribe": { - "command": "подписаться", - "description": "Подписаться на вызов" + "reactions": { + "messages": { + "done": "Готово", + "falsy": "Нет", + "truthy": "Да" + } }, - "unsubscribe": { - "command": "отписаться", - "description": "Отписаться от вызова" - } - }, - "info": { - "messages": { - "done": "ℹ️ Если вы закончили свой ответ, пожалуйста, введите \"{done}\".", - "enter-amount-of-money": "ℹ️ Пожалуйста, введите сумму денег.", - "enter-content": "ℹ️ Вы можете ответить несколькими строками. Пожалуйста, отправьте ваш ответ только после того, как вы закончите писать. Поддерживается форматирование Markdown.", - "enter-date": "ℹ️ Пожалуйста, введите дату.", - "enter-lots-of-text": "ℹ️ Вы можете ответить несколькими строками. Пожалуйста, отправьте ваш ответ только после того, как вы закончите писать. Поддерживается форматирование Markdown.", - "enter-password": "ℹ️ Пожалуйста, введите пароль. Для безопасности рекомендуется удалить ваш ответ после отправки, чтобы он не оставался в истории чата.", - "enter-telephone-number": "ℹ️ Пожалуйста, введите телефонный номер.", - "enter-text": "ℹ️ Пожалуйста, держите ваш ответ кратким, желательно в одном предложении.", - "enter-time": "ℹ️ Пожалуйста, введите время.", - "enter-url": "ℹ️ Пожалуйста, введите URL.", - "multiple-addresses-allowed": "ℹ️ Вы можете ввести один или несколько адресов.", - "multiple-emails-allowed": "ℹ️ Вы можете ввести один или несколько адресов электронной почты.", - "multiple-numbers-allowed": "ℹ️ Пожалуйста, введите один или несколько номеров.", - "multiple-selections-allowed": "ℹ️ Пожалуйста, сделайте свой выбор, введя номера ваших выборов. Разрешено несколько выборов, отправьте отдельное сообщение для каждого вашего выбора.", - "multiple-values-allowed": "ℹ️ Вы можете ввести несколько значений, отправляя каждое значение отдельно.", - "only-one-address-allowed": "ℹ️ Пожалуйста, введите адрес.", - "only-one-email-allowed": "ℹ️ Пожалуйста, введите адрес электронной почты.", - "only-one-number-allowed": "ℹ️ Пожалуйста, введите номер.", - "only-one-selection-allowed": "ℹ️ Пожалуйста, сделайте свой выбор, введя номер вашего выбора или нажав соответствующую кнопку. Разрешен только один выбор.", - "only-one-value-allowed": "ℹ️ Вы можете ввести только одно значение.", - "placeholder": "ℹ️ Пожалуйста, ответьте чем-то вроде \"{placeholder}\".", - "upload-file-here": "ℹ️ Пожалуйста, загрузите файл здесь.", - "upload-files-here": "ℹ️ Пожалуйста, загрузите файлы здесь." - } - }, - "keyboard": { - "label": { - "cancel": "Отмена", - "continue": "Продолжить", - "no": "Нет", - "yes": "Да" + "render": { + "callout": { + "list": { + "title": "Список активных вызовов" + } + } }, - "message": { - "select-detail-callout": "❓ О каком вызове вы хотели бы получить больше информации? Выберите номер." - } - }, - "reactions": { - "messages": { - "done": "Готово", - "falsy": "Нет", - "truthy": "Да" - } - }, - "render": { - "callout": { - "list": { - "title": "Список активных вызовов" + "response": { + "messages": { + "answerWithTruthyOrFalsy": "🤔 Пожалуйста, ответьте \"{truthy}\" или \"{falsy}\".", + "calloutNotFound": "😥 Извините, вызов не найден..", + "calloutStartResponse": "❓ Вы хотите ответить на вызов?", + "componentNotSupported": "😅 Тип компонента \"{type}\" еще не реализован..", + "componentUnknown": "😅 Неизвестный тип компонента \"{type}\"..", + "noActiveCallouts": "😔 Извините, в настоящее время нет активных вызовов..", + "notAFileMessage": "🤔 Пожалуйста, отправьте файл.", + "notASelectionMessage": "🤔 Пожалуйста, выберите номер одного из возможных ответов.", + "notATextMessage": "🤔 Пожалуйста, отправьте текстовое сообщение.", + "notTheRightFileType": "🤔 Пожалуйста, отправьте файл типа {type}.", + "stop": "☺️ Окей, без проблем." } + }, + "universal": { + "or": "или" } - }, - "response": { - "messages": { - "answer-with-truthy-or-falsy": "🤔 Пожалуйста, ответьте \"{truthy}\" или \"{falsy}\".", - "callout-not-found": "😥 Извините, вызов не найден..", - "callout-start-response": "❓ Вы хотите ответить на вызов?", - "component-not-supported": "😅 Тип компонента \"{type}\" еще не реализован..", - "component-unknown": "😅 Неизвестный тип компонента \"{type}\"..", - "no-active-callouts": "😔 Извините, в настоящее время нет активных вызовов..", - "not-a-file-message": "🤔 Пожалуйста, отправьте файл.", - "not-a-selection-message": "🤔 Пожалуйста, выберите номер одного из возможных ответов.", - "not-a-text-message": "🤔 Пожалуйста, отправьте текстовое сообщение.", - "not-the-right-file-type": "🤔 Пожалуйста, отправьте файл типа {type}.", - "stop": "☺️ Окей, без проблем." - } - }, - "universal": { - "or": "или" } } diff --git a/telegram-bot/renderer/callout-response.renderer.ts b/telegram-bot/renderer/callout-response.renderer.ts index 9c9f25a..13b3e46 100644 --- a/telegram-bot/renderer/callout-response.renderer.ts +++ b/telegram-bot/renderer/callout-response.renderer.ts @@ -119,7 +119,7 @@ export class CalloutResponseRenderer { if (placeholder) { result.markdown = `_${ escapeMd( - this.i18n.t("info.messages.placeholder", { placeholder }), + this.i18n.t("bot.info.messages.placeholder", { placeholder }), ) }_`; } @@ -128,7 +128,7 @@ export class CalloutResponseRenderer { } protected multipleMd(component: CalloutComponentSchema, prefix: string) { - const doneMessage = this.i18n.t("reactions.messages.done"); + const doneMessage = this.i18n.t("bot.reactions.messages.done"); const multiple = this.isMultiple(component); const result: Render = { key: createCalloutGroupKey(component.key, prefix), @@ -140,7 +140,7 @@ export class CalloutResponseRenderer { if (multiple) { result.markdown += `_${ escapeMd( - `${this.i18n.t("info.messages.multiple-values-allowed")}\n\n${ + `${this.i18n.t("bot.info.messages.multiple-values-allowed")}\n\n${ this.messageRenderer.writeDoneMessage(doneMessage).text }`, ) @@ -148,7 +148,7 @@ export class CalloutResponseRenderer { } else { result.markdown += `_${ escapeMd( - `${this.i18n.t("info.messages.only-one-value-allowed")}\n\n${ + `${this.i18n.t("bot.info.messages.only-one-value-allowed")}\n\n${ this.messageRenderer.writeDoneMessage(doneMessage).text }`, ) @@ -174,7 +174,7 @@ export class CalloutResponseRenderer { accepted: this.condition.replayConditionSelection( multiple, this.selectValuesToValueLabelPairs(selectable.values), - multiple ? [this.i18n.t("reactions.messages.done")] : [], + multiple ? [this.i18n.t("bot.reactions.messages.done")] : [], ), markdown: ``, parseType: calloutComponentTypeToParsedResponseType(selectable), @@ -235,7 +235,7 @@ export class CalloutResponseRenderer { accepted: this.condition.replayConditionCalloutConponent( multiple, base, - multiple ? [this.i18n.t("reactions.messages.done")] : [], + multiple ? [this.i18n.t("bot.reactions.messages.done")] : [], ), parseType: ParsedResponseType.CALLOUT_COMPONENT, }; @@ -273,15 +273,15 @@ export class CalloutResponseRenderer { result.markdown += `_${ escapeMd( multiple - ? this.i18n.t("info.messages.upload-files-here") - : this.i18n.t("info.messages.upload-file-here"), + ? this.i18n.t("bot.info.messages.upload-files-here") + : this.i18n.t("bot.info.messages.upload-file-here"), ) }_`; result.accepted = this.condition.replayConditionFilePattern( multiple, file.filePattern || file.type === "signature" ? "image/*" : "", - multiple ? [this.i18n.t("reactions.messages.done")] : [], + multiple ? [this.i18n.t("bot.reactions.messages.done")] : [], ); if (file.placeholder) { @@ -340,13 +340,13 @@ export class CalloutResponseRenderer { result.parseType = ParsedResponseType.BOOLEAN; result.markdown += `\n\n`; - const truthyMessage = this.i18n.t("reactions.messages.truthy"); - const falsyMessage = this.i18n.t("reactions.messages.falsy"); - const doneMessage = this.i18n.t("reactions.messages.done"); + const truthyMessage = this.i18n.t("bot.reactions.messages.truthy"); + const falsyMessage = this.i18n.t("bot.reactions.messages.falsy"); + const doneMessage = this.i18n.t("bot.reactions.messages.done"); result.markdown += `_${ escapeMd( - this.i18n.t("response.messages.answer-with-truthy-or-falsy", { + this.i18n.t("bot.response.messages.answer-with-truthy-or-falsy", { truthy: truthyMessage, falsy: falsyMessage, }), @@ -384,8 +384,8 @@ export class CalloutResponseRenderer { result.markdown += `_${ escapeMd( result.accepted.multiple - ? this.i18n.t("info.messages.multiple-addresses-allowed") - : this.i18n.t("info.messages.only-one-address-allowed"), + ? this.i18n.t("bot.info.messages.multiple-addresses-allowed") + : this.i18n.t("bot.info.messages.only-one-address-allowed"), ) }_`; break; @@ -394,8 +394,8 @@ export class CalloutResponseRenderer { result.markdown += `_${ escapeMd( result.accepted.multiple - ? this.i18n.t("info.messages.multiple-emails-allowed") - : this.i18n.t("info.messages.only-one-email-allowed"), + ? this.i18n.t("bot.info.messages.multiple-emails-allowed") + : this.i18n.t("bot.info.messages.only-one-email-allowed"), ) }_`; @@ -405,8 +405,8 @@ export class CalloutResponseRenderer { result.markdown += `_${ escapeMd( result.accepted.multiple - ? this.i18n.t("info.messages.multiple-numbers-allowed") - : this.i18n.t("info.messages.only-one-number-allowed"), + ? this.i18n.t("bot.info.messages.multiple-numbers-allowed") + : this.i18n.t("bot.info.messages.only-one-number-allowed"), ) }_`; break; @@ -414,7 +414,7 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_TEXT_FIELD: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-text"), + this.i18n.t("bot.info.messages.enter-text"), ) }_`; break; @@ -422,7 +422,7 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_TEXT_AREA: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-lots-of-text"), + this.i18n.t("bot.info.messages.enter-lots-of-text"), ) }_`; break; @@ -430,7 +430,7 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_PHONE_NUMBER: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-telephone-number"), + this.i18n.t("bot.info.messages.enter-telephone-number"), ) }_`; break; @@ -438,7 +438,7 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_CURRENCY: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-amount-of-money"), + this.i18n.t("bot.info.messages.enter-amount-of-money"), ) }_`; break; @@ -446,7 +446,7 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_DATE_TIME: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-date"), + this.i18n.t("bot.info.messages.enter-date"), ) }_`; break; @@ -454,7 +454,7 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_TIME: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-time"), + this.i18n.t("bot.info.messages.enter-time"), ) }_`; break; @@ -462,16 +462,19 @@ export class CalloutResponseRenderer { case CalloutComponentType.INPUT_URL: { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.enter-url"), + this.i18n.t("bot.info.messages.enterUrl"), ) }_`; break; } default: { - result.markdown += this.i18n.t("response.messages.component-unknown", { - type: (input as CalloutComponentSchema).type || "undefined", - }); + result.markdown += this.i18n.t( + "bot.response.messages.componentUnknown", + { + type: (input as CalloutComponentSchema).type || "undefined", + }, + ); break; } } @@ -513,7 +516,7 @@ export class CalloutResponseRenderer { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.only-one-selection-allowed"), + this.i18n.t("bot.info.messages.onlyOneSelectionAllowed"), ) }_`; return result; @@ -537,7 +540,7 @@ export class CalloutResponseRenderer { ...this.condition.replayConditionSelection( multiple, this.selectValuesToValueLabelPairs(selectable.values), - multiple ? [this.i18n.t("reactions.messages.done")] : [], + multiple ? [this.i18n.t("bot.reactions.messages.done")] : [], ), }; @@ -551,7 +554,7 @@ export class CalloutResponseRenderer { case "radio": { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.only-one-selection-allowed"), + this.i18n.t("bot.info.messages.onlyOneSelectionAllowed"), ) }_`; break; @@ -559,10 +562,10 @@ export class CalloutResponseRenderer { case "selectboxes": { result.markdown += `_${ escapeMd( - this.i18n.t("info.messages.multiple-selections-allowed") + + this.i18n.t("bot.info.messages.multipleSelectionsAllowed") + "\n\n" + this.messageRenderer.writeDoneMessage( - this.i18n.t("reactions.messages.done"), + this.i18n.t("bot.reactions.messages.done"), ).text, ) }_`; @@ -672,7 +675,7 @@ export class CalloutResponseRenderer { ), type: RenderType.MARKDOWN, accepted: this.condition.replayConditionAny(multiple), - markdown: this.i18n.t("response.messages.component-unknown", { + markdown: this.i18n.t("bot.response.messages.componentUnknown", { type: (component as CalloutComponentSchema).type || "undefined", }), parseType: calloutComponentTypeToParsedResponseType(component), diff --git a/telegram-bot/renderer/callout.renderer.ts b/telegram-bot/renderer/callout.renderer.ts index d7d7496..2d32d5c 100644 --- a/telegram-bot/renderer/callout.renderer.ts +++ b/telegram-bot/renderer/callout.renderer.ts @@ -37,7 +37,7 @@ export class CalloutRenderer { */ protected startResponseKeyboard(callout: CalloutDataExt) { const keyboardMessageMd = `_${ - escapeMd(this.i18n.t("response.messages.callout-start-response")) + escapeMd(this.i18n.t("bot.response.messages.callout-start-response")) }_`; const yesNoKeyboard = this.keyboard.yesNo( `${BUTTON_CALLBACK_CALLOUT_INTRO}:${callout.shortSlug}`, @@ -90,13 +90,13 @@ export class CalloutRenderer { if (callouts.items.length === 0) { listResult.markdown = escapeMd( - this.i18n.t("response.messages.no-active-callouts"), + this.i18n.t("bot.response.messages.no-active-callouts"), ); return [listResult]; } listResult.markdown = `*${ - escapeMd(this.i18n.t("render.callout.list.title")) + escapeMd(this.i18n.t("bot.render.callout.list.title")) }*\n\n`; let p = 1; for (const callout of callouts.items) { @@ -106,7 +106,7 @@ export class CalloutRenderer { const keyboard = this.keyboard.calloutSelection(callouts.items); const keyboardMessageMd = `_${ - escapeMd(this.i18n.t("keyboard.message.select-detail-callout")) + escapeMd(this.i18n.t("bot.keyboard.message.select-detail-callout")) }_`; const keyboardResult: Render = { diff --git a/telegram-bot/renderer/message.renderer.ts b/telegram-bot/renderer/message.renderer.ts index b29b1e8..5978911 100644 --- a/telegram-bot/renderer/message.renderer.ts +++ b/telegram-bot/renderer/message.renderer.ts @@ -88,11 +88,11 @@ export class MessageRenderer { public notTheRightFileType(mimeTypes: string[]) { const mimeTypesStr = getSimpleMimeTypes(mimeTypes).join(", ").replace( /, ([^,]*)$/, - ` ${this.i18n.t("universal.or")} $1`, + ` ${this.i18n.t("bot.universal.or")} $1`, ); return { type: RenderType.TEXT, - text: this.i18n.t("response.messages.not-the-right-file-type", { + text: this.i18n.t("bot.response.messages.notTheRightFileType", { type: mimeTypesStr, }), } as RenderText; @@ -143,7 +143,7 @@ export class MessageRenderer { public writeDoneMessage(doneText: string) { return { type: RenderType.TEXT, - text: this.i18n.t("info.messages.done", { done: doneText }), + text: this.i18n.t("bot.info.messages.done", { done: doneText }), } as RenderText; } } diff --git a/telegram-bot/scripts/i18n.ts b/telegram-bot/scripts/i18n.ts index a009830..6a199f4 100644 --- a/telegram-bot/scripts/i18n.ts +++ b/telegram-bot/scripts/i18n.ts @@ -1,4 +1,5 @@ // This script is based on https://github.com/beabee-communityrm/beabee-frontend/blob/main/scripts/i18n.js +import { toCamelCase } from "../utils/string.ts"; import { dirname, @@ -97,11 +98,15 @@ async function loadSheet(name: string) { // Construct nested objects from a.b.c key paths for (const row of rows) { const keyParts = row.key.split("."); - const [lastKeyPart, ...keyOpts] = keyParts.pop()?.split(":") ?? []; + const [_lastKeyPart, ...keyOpts] = keyParts.pop()?.split(":") ?? []; + const lastKeyPart = toCamelCase(_lastKeyPart); + console.debug("lastKeyPart", _lastKeyPart, lastKeyPart); for (const locale of locales) { let localeDataPart = localeData[locale] as LocaleEntry; - for (const part of keyParts) { + for (const _part of keyParts) { + const part = toCamelCase(_part); + console.debug("part", _part, part); if (!localeDataPart[part]) { localeDataPart[part] = {}; } diff --git a/telegram-bot/services/i18n.service.ts b/telegram-bot/services/i18n.service.ts index 94eb80c..8b2a4fe 100644 --- a/telegram-bot/services/i18n.service.ts +++ b/telegram-bot/services/i18n.service.ts @@ -1,5 +1,5 @@ import { dirname, fromFileUrl, Singleton } from "../deps.ts"; -import { readJson, readJsonSync } from "../utils/file.ts"; +import { readJson, readJsonSync, toCamelCase } from "../utils/index.ts"; import { EventService } from "./event.service.ts"; import { I18nEvent } from "../enums/i18n-event.ts"; @@ -137,7 +137,8 @@ export class I18nService { } const segments = path.split("."); - const key = segments.shift() ?? ""; + const _key = segments.shift() ?? ""; + const key = toCamelCase(_key); const nextTranslations = translations[key]; if (!nextTranslations) { diff --git a/telegram-bot/services/keyboard.service.ts b/telegram-bot/services/keyboard.service.ts index 322a5bb..75a4c19 100644 --- a/telegram-bot/services/keyboard.service.ts +++ b/telegram-bot/services/keyboard.service.ts @@ -75,11 +75,11 @@ export class KeyboardService { public yesNo(prefix = "") { const inlineKeyboard = new InlineKeyboard(); inlineKeyboard.text( - this.i18n.t("keyboard.label.yes"), + this.i18n.t("bot.keyboard.label.yes"), prefix ? `${prefix}:yes` : `yes`, ); inlineKeyboard.text( - this.i18n.t("keyboard.label.no"), + this.i18n.t("bot.keyboard.label.no"), prefix ? `${prefix}:no` : `no`, ); return inlineKeyboard; @@ -96,11 +96,11 @@ export class KeyboardService { public continueCancel(prefix = "") { const inlineKeyboard = new InlineKeyboard(); inlineKeyboard.text( - this.i18n.t("keyboard.label.continue"), + this.i18n.t("bot.keyboard.label.continue"), prefix ? `${prefix}:continue` : `continue`, ); inlineKeyboard.text( - this.i18n.t("keyboard.label.cancel"), + this.i18n.t("bot.keyboard.label.cancel"), prefix ? `${prefix}:cancel` : `cancel`, ); return inlineKeyboard; diff --git a/telegram-bot/services/transform.service.ts b/telegram-bot/services/transform.service.ts index c09f5cf..aa55bf1 100644 --- a/telegram-bot/services/transform.service.ts +++ b/telegram-bot/services/transform.service.ts @@ -144,9 +144,9 @@ export class TransformService { ): RenderResponseParsedBoolean["data"] { const boolStr = getTextFromMessage(context.message).toLowerCase().trim(); let bool = false; - const truthyStr = this.i18n.t("reactions.messages.truthy").toLowerCase() + const truthyStr = this.i18n.t("bot.reactions.messages.truthy").toLowerCase() .trim(); - const falsyStr = this.i18n.t("reactions.messages.falsy").toLowerCase() + const falsyStr = this.i18n.t("bot.reactions.messages.falsy").toLowerCase() .trim(); if (boolStr === truthyStr) { bool = true; diff --git a/telegram-bot/utils/string.ts b/telegram-bot/utils/string.ts index 4b1dfd7..71e017a 100644 --- a/telegram-bot/utils/string.ts +++ b/telegram-bot/utils/string.ts @@ -37,3 +37,25 @@ export const generateSlug = (input: string): string => { export const truncateSlug = (input: string, maxLength = 32): string => { return truncateString(generateSlug(input), maxLength); }; + +/** + * Converts a string to camelCase format. + * It removes special characters and spaces, then capitalizes the letter after each removed space while the first letter is in lowercase. + * It splits the string by spaces, hyphens (-), and underscores (_). + * + * @param input The string to be converted to camelCase. + * @returns The camelCase formatted string. + */ +export const toCamelCase = (input: string): string => { + return input + // Remove special characters except spaces, hyphens, and underscores + .replace(/[^a-zA-Z0-9\s-_]/g, "") + // Split by spaces, hyphens, and underscores + .split(/[\s-_]+/) + .map((word, index) => + index === 0 + ? word.toLowerCase() + : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() + ) + .join(""); +}; From d98aec6aac47640ee341fa58dd099622803d623f Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 29 Feb 2024 17:18:16 +0100 Subject: [PATCH 08/17] chore(i18n): Updated locales --- telegram-bot/README.md | 2 +- telegram-bot/locales/de.json | 84 +++++++++++++-------------- telegram-bot/locales/de@informal.json | 80 ++++++++++++------------- telegram-bot/locales/en.json | 68 +++++++++++----------- telegram-bot/locales/nl.json | 76 ++++++++++++------------ telegram-bot/locales/pt.json | 70 +++++++++++----------- telegram-bot/locales/ru.json | 70 +++++++++++----------- 7 files changed, 225 insertions(+), 225 deletions(-) diff --git a/telegram-bot/README.md b/telegram-bot/README.md index 58d0b14..db1229a 100644 --- a/telegram-bot/README.md +++ b/telegram-bot/README.md @@ -53,7 +53,7 @@ To update the locale data in the repository you run the following ``` deno task i18n -git add -A locales/ && git commit locales/ -m 'chore: updated locales' +git add -A locales/ && git commit locales/ -m 'chore(i18n): Updated locales' ``` #### Using the localisation strings diff --git a/telegram-bot/locales/de.json b/telegram-bot/locales/de.json index 3faa4c1..881d824 100644 --- a/telegram-bot/locales/de.json +++ b/telegram-bot/locales/de.json @@ -2,65 +2,65 @@ "bot": { "commands": { "list": { - "command": "liste", - "description": "Auflistung aktiver Callouts" + "command": "auflisten", + "description": "Offene Aufrufe anzeigen" }, "show": { "command": "zeigen", - "description": "Zeigt Informationen über einen spezifischen Callout" + "description": "Details über einen spezifischen Aufruf anzeigen" }, "start": { "command": "starten", - "description": "Starte den Bot" + "description": "Den Bot starten" }, "subscribe": { "command": "abonnieren", - "description": "Abonniere einen Callout" + "description": "Einem Aufruf abonnieren" }, "unsubscribe": { - "command": "abmelden", - "description": "Abmelden von einem Callout" + "command": "abbestellen", + "description": "Ein Abonnement für einen Aufruf kündigen" } }, "info": { "messages": { - "done": "ℹ️ Wenn Sie mit Ihrer Antwort fertig sind, tippen Sie bitte \"{done}\".", - "enterAmountOfMoney": "ℹ️ Bitte geben Sie einen Geldbetrag ein.", - "enterDate": "ℹ️ Bitte geben Sie ein Datum ein.", - "enterLotsOfText": "ℹ️ Sie können in mehreren Zeilen antworten. Bitte senden Sie Ihre Antwort erst, nachdem Sie fertig geschrieben haben. Markdown-Formatierung wird unterstützt.", - "enterTelephoneNumber": "ℹ️ Bitte geben Sie eine Telefonnummer ein.", - "enterText": "ℹ️ Bitte halten Sie Ihre Antwort kurz, idealerweise in einem Satz.", - "enterTime": "ℹ️ Bitte geben Sie eine Uhrzeit ein.", - "enterUrl": "ℹ️ Bitte geben Sie eine URL ein.", - "multipleAddressesAllowed": "ℹ️ Sie können eine oder mehrere Adressen eingeben.", - "multipleEmailsAllowed": "ℹ️ Sie können eine oder mehrere E-Mail-Adressen eingeben.", - "multipleNumbersAllowed": "ℹ️ Bitte geben Sie eine oder mehrere Nummern ein.", - "multipleSelectionsAllowed": "ℹ️ Treffen Sie Ihre Auswahl, indem Sie die Nummern Ihrer Wahl eingeben. Mehrfachauswahlen sind möglich; bitte senden Sie für jede Auswahl eine separate Nachricht.", - "multipleValuesAllowed": "ℹ️ Sie können mehrere Werte eingeben. Bitte senden Sie jeden Wert in einer separaten Nachricht.", - "onlyOneAddressAllowed": "ℹ️ Bitte geben Sie nur eine Adresse ein.", - "onlyOneEmailAllowed": "ℹ️ Bitte geben Sie nur eine E-Mail-Adresse ein.", - "onlyOneNumberAllowed": "ℹ️ Bitte geben Sie nur eine Nummer ein.", - "onlyOneSelectionAllowed": "ℹ️ Treffen Sie Ihre Auswahl, indem Sie die Nummer Ihrer Wahl eingeben oder den entsprechenden Button drücken. Nur eine Auswahl ist möglich.", - "onlyOneValueAllowed": "ℹ️ Sie können nur einen Wert eingeben.", - "placeholder": "ℹ️ Bitte antworten Sie mit etwas Ähnlichem wie \"{placeholder}\".", - "uploadFileHere": "ℹ️ Bitte laden Sie die Datei hier hoch.", - "uploadFilesHere": "ℹ️ Bitte laden Sie die Dateien hier hoch." + "done": "Wenn Sie mit Ihrer Antwort fertig sind, tippen Sie einfach \"{done}\".", + "enterAmountOfMoney": "Bitte geben Sie einen Geldbetrag ein.", + "enterDate": "Bitte geben Sie ein Datum ein.", + "enterLotsOfText": "Bitte antworten Sie mit einer einzigen Nachricht. Sie können sie so lang machen, wie Sie möchten.", + "enterTelephoneNumber": "Bitte geben Sie eine Telefonnummer ein.", + "enterText": "Bitte halten Sie Ihre Antwort kurz, idealerweise in einem Satz.", + "enterTime": "Bitte geben Sie eine Zeit ein. (z.B. 20:45)", + "enterUrl": "Bitte geben Sie eine Webadresse ein. (z.B. https://www.beispiel.de)", + "multipleAddressesAllowed": "Geben Sie eine oder mehrere Adressen ein.", + "multipleEmailsAllowed": "Sie können eine oder mehrere E-Mail-Adressen eingeben.", + "multipleNumbersAllowed": "Geben Sie eine oder mehrere Zahlen ein.", + "multipleSelectionsAllowed": "Bitte geben Sie die Antwortnummer ein. Sie können mehrere Auswahlmöglichkeiten treffen, senden Sie einfach für jede eine separate Nachricht.", + "multipleValuesAllowed": "Sie können mehrere Werte eingeben, indem Sie jeden Wert in einer separaten Nachricht senden.", + "onlyOneAddressAllowed": "Bitte geben Sie eine Adresse ein.", + "onlyOneEmailAllowed": "Bitte geben Sie eine E-Mail-Adresse ein.", + "onlyOneNumberAllowed": "Bitte geben Sie eine Zahl ein.", + "onlyOneSelectionAllowed": "Bitte wählen Sie Ihre Wahl, indem Sie deren Nummer eingeben oder deren Knopf drücken (Sie können nur eine auswählen).", + "onlyOneValueAllowed": "Bitte geben Sie einen einzigen Wert ein.", + "placeholder": "Bitte antworten Sie mit etwas wie \"{placeholder}\".", + "uploadFileHere": "Bitte senden Sie mir eine Datei.", + "uploadFilesHere": "Bitte senden Sie mir eine oder mehrere Dateien." } }, "keyboard": { "label": { "cancel": "Abbrechen", - "continue": "Fortfahren", + "continue": "Fortsetzen", "no": "Nein", "yes": "Ja" }, "message": { - "selectDetailCallout": "❓ Über welchen Callout möchten Sie mehr Informationen erhalten? Wählen Sie eine Nummer." + "selectDetailCallout": "❓ Über welchen Aufruf möchten Sie mehr Informationen erhalten? Wählen Sie eine Nummer" } }, "reactions": { "messages": { - "done": "Fertig", + "done": "Erledigt", "falsy": "Nein", "truthy": "Ja" } @@ -68,23 +68,23 @@ "render": { "callout": { "list": { - "title": "Liste der aktiven Callouts" + "title": "Liste aktiver Aufrufe" } } }, "response": { "messages": { "answerWithTruthyOrFalsy": "🤔 Bitte antworten Sie mit \"{truthy}\" oder \"{falsy}\".", - "calloutNotFound": "😥 Entschuldigung, Callout nicht gefunden.", - "calloutStartResponse": "❓ Möchten Sie den Callout beantworten?", - "componentNotSupported": "😅 Der Komponententyp \"{type}\" ist noch nicht implementiert.", - "componentUnknown": "😅 Unbekannter Komponententyp \"{type}\".", - "noActiveCallouts": "😔 Entschuldigung, derzeit gibt es keine aktiven Callouts.", - "notAFileMessage": "🤔 Bitte senden Sie eine Datei.", - "notASelectionMessage": "🤔 Bitte wählen Sie die Nummer einer der verfügbaren Optionen aus.", - "notATextMessage": "🤔 Bitte senden Sie eine Textnachricht.", - "notTheRightFileType": "🤔 Bitte senden Sie eine Datei des Typs \"{type}\".", - "stop": "☺️ Okay, kein Problem." + "calloutNotFound": "Es tut mir leid, ich konnte diesen Aufruf nicht finden.", + "calloutStartResponse": "Möchten Sie auf diesen Aufruf reagieren?", + "componentNotSupported": "\"{type}\" nicht implementiert..", + "componentUnknown": "Unbekannter Komponententyp \"{type}\"..", + "noActiveCallouts": "Derzeit gibt es keine offenen Aufrufe.", + "notAFileMessage": "Bitte senden Sie eine Datei.", + "notASelectionMessage": "Bitte wählen Sie die Nummer einer der möglichen Antworten.", + "notATextMessage": "Bitte senden Sie eine Textnachricht.", + "notTheRightFileType": "Bitte senden Sie eine Datei vom Typ {type}.", + "stop": "Ok, kein Problem." } }, "universal": { diff --git a/telegram-bot/locales/de@informal.json b/telegram-bot/locales/de@informal.json index 093650a..6e178f4 100644 --- a/telegram-bot/locales/de@informal.json +++ b/telegram-bot/locales/de@informal.json @@ -2,12 +2,12 @@ "bot": { "commands": { "list": { - "command": "liste", - "description": "Liste aktive Callouts" + "command": "auflisten", + "description": "Zeige offene Aufrufe" }, "show": { "command": "zeigen", - "description": "Zeigt Infos über einen bestimmten Callout" + "description": "Zeige Details über einen spezifischen Aufruf" }, "start": { "command": "starten", @@ -15,36 +15,36 @@ }, "subscribe": { "command": "abonnieren", - "description": "Abonniere einen Callout" + "description": "Abonniere einen Aufruf" }, "unsubscribe": { - "command": "abmelden", - "description": "Abmelden von einem Callout" + "command": "abbestellen", + "description": "Kündige ein Abonnement für einen Aufruf" } }, "info": { "messages": { - "done": "ℹ️ Wenn du mit deiner Antwort fertig bist, tippe bitte \"{done}\".", - "enterAmountOfMoney": "ℹ️ Bitte gib einen Geldbetrag ein.", - "enterDate": "ℹ️ Bitte gib ein Datum ein.", - "enterLotsOfText": "ℹ️ Du kannst in mehreren Zeilen antworten. Bitte sende deine Antwort erst, nachdem du fertig geschrieben hast. Markdown-Formatierung wird unterstützt.", - "enterTelephoneNumber": "ℹ️ Bitte gib eine Telefonnummer ein.", - "enterText": "ℹ️ Bitte halte deine Antwort kurz, idealerweise in einem Satz.", - "enterTime": "ℹ️ Bitte gib eine Uhrzeit ein.", - "enterUrl": "ℹ️ Bitte gib eine URL ein.", - "multipleAddressesAllowed": "ℹ️ Du kannst eine oder mehrere Adressen eingeben.", - "multipleEmailsAllowed": "ℹ️ Du kannst eine oder mehrere E-Mail-Adressen eingeben.", - "multipleNumbersAllowed": "ℹ️ Bitte gib eine oder mehrere Nummern ein.", - "multipleSelectionsAllowed": "ℹ️ Treffe deine Auswahl, indem du die Nummer deiner Wahl eingibst. Mehrfachauswahlen sind möglich; bitte sende für jede Auswahl eine separate Nachricht.", - "multipleValuesAllowed": "ℹ️ Du kannst mehrere Werte eingeben. Bitte sende jeden Wert in einer separaten Nachricht.", - "onlyOneAddressAllowed": "ℹ️ Bitte gib nur eine Adresse ein.", - "onlyOneEmailAllowed": "ℹ️ Bitte gib nur eine E-Mail-Adresse ein.", - "onlyOneNumberAllowed": "ℹ️ Bitte gib nur eine Nummer ein.", - "onlyOneSelectionAllowed": "ℹ️ Treffe deine Auswahl, indem du die Nummer deiner Wahl eingibst oder den entsprechenden Button drückst. Nur eine Auswahl ist möglich.", - "onlyOneValueAllowed": "ℹ️ Du kannst nur einen Wert eingeben.", - "placeholder": "ℹ️ Bitte antworte mit etwas Ähnlichem wie \"{placeholder}\".", - "uploadFileHere": "ℹ️ Bitte lade die Datei hier hoch.", - "uploadFilesHere": "ℹ️ Bitte lade die Dateien hier hoch." + "done": "Wenn du fertig mit deiner Antwort bist, tippe einfach \"{done}\".", + "enterAmountOfMoney": "Bitte gib einen Geldbetrag ein.", + "enterDate": "Bitte gib ein Datum ein.", + "enterLotsOfText": "Bitte antworte mit einer einzigen Nachricht. Du kannst sie so lang machen, wie du möchtest.", + "enterTelephoneNumber": "Bitte gib eine Telefonnummer ein.", + "enterText": "Bitte halte deine Antwort kurz, idealerweise in einem Satz.", + "enterTime": "Bitte gib eine Zeit ein. (z.B. 20:45)", + "enterUrl": "Bitte gib eine Webadresse ein. (z.B. https://www.beispiel.de)", + "multipleAddressesAllowed": "Gib eine oder mehrere Adressen ein.", + "multipleEmailsAllowed": "Du kannst eine oder mehrere E-Mail-Adressen eingeben.", + "multipleNumbersAllowed": "Gib eine oder mehrere Zahlen ein.", + "multipleSelectionsAllowed": "Bitte gib die Antwortnummer ein. Du kannst mehrere Optionen wählen, sende einfach für jede eine separate Nachricht.", + "multipleValuesAllowed": "Du kannst mehrere Werte eingeben, indem du jeden Wert in einer separaten Nachricht sendest.", + "onlyOneAddressAllowed": "Bitte gib eine Adresse ein.", + "onlyOneEmailAllowed": "Bitte gib eine E-Mail-Adresse ein.", + "onlyOneNumberAllowed": "Bitte gib eine Zahl ein.", + "onlyOneSelectionAllowed": "Bitte triff deine Wahl, indem du deren Nummer eintippst oder den Button drückst (du kannst nur eine auswählen).", + "onlyOneValueAllowed": "Bitte gib nur einen Wert ein.", + "placeholder": "Bitte antworte mit etwas wie \"{placeholder}\".", + "uploadFileHere": "Bitte sende mir eine Datei.", + "uploadFilesHere": "Bitte sende mir eine oder mehrere Dateien." } }, "keyboard": { @@ -55,12 +55,12 @@ "yes": "Ja" }, "message": { - "selectDetailCallout": "❓ Über welchen Callout möchtest du mehr Informationen erhalten? Wähle eine Nummer." + "selectDetailCallout": "❓ Welchen Aufruf möchtest du näher betrachten? Wähle eine Nummer" } }, "reactions": { "messages": { - "done": "Fertig", + "done": "Erledigt", "falsy": "Nein", "truthy": "Ja" } @@ -68,23 +68,23 @@ "render": { "callout": { "list": { - "title": "Liste der aktiven Callouts" + "title": "Liste aktiver Aufrufe" } } }, "response": { "messages": { "answerWithTruthyOrFalsy": "🤔 Bitte antworte mit \"{truthy}\" oder \"{falsy}\".", - "calloutNotFound": "😥 Entschuldigung, Callout nicht gefunden.", - "calloutStartResponse": "❓ Möchtest du den Callout beantworten?", - "componentNotSupported": "😅 Der Komponententyp \"{type}\" ist noch nicht implementiert.", - "componentUnknown": "😅 Unbekannter Komponententyp \"{type}\".", - "noActiveCallouts": "😔 Entschuldigung, derzeit gibt es keine aktiven Callouts.", - "notAFileMessage": "🤔 Bitte sende eine Datei.", - "notASelectionMessage": "🤔 Bitte wähle die Nummer einer der verfügbaren Optionen aus.", - "notATextMessage": "🤔 Bitte sende eine Textnachricht.", - "notTheRightFileType": "🤔 Bitte sende eine Datei des Typs \"{type}\".", - "stop": "☺️ Okay, kein Problem." + "calloutNotFound": "Tut mir leid, ich konnte diesen Aufruf nicht finden.", + "calloutStartResponse": "Möchtest du auf diesen Aufruf reagieren?", + "componentNotSupported": "\"{type}\" ist noch nicht implementiert..", + "componentUnknown": "Unbekannter Komponententyp \"{type}\"..", + "noActiveCallouts": "Aktuell gibt es keine offenen Aufrufe.", + "notAFileMessage": "Bitte sende eine Datei.", + "notASelectionMessage": "Bitte wähle die Nummer einer der möglichen Antworten aus.", + "notATextMessage": "Bitte sende eine Textnachricht.", + "notTheRightFileType": "Bitte sende eine Datei vom Typ {type}.", + "stop": "Ok, kein Problem." } }, "universal": { diff --git a/telegram-bot/locales/en.json b/telegram-bot/locales/en.json index b5b3711..49e7314 100644 --- a/telegram-bot/locales/en.json +++ b/telegram-bot/locales/en.json @@ -3,11 +3,11 @@ "commands": { "list": { "command": "list", - "description": "List active callouts" + "description": "Show open callouts" }, "show": { "command": "show", - "description": "Show information about a specific callout" + "description": "Show details about a specific callout" }, "start": { "command": "start", @@ -24,27 +24,27 @@ }, "info": { "messages": { - "done": "ℹ️ If you have finished your response, please type \"{done}\".", - "enterAmountOfMoney": "ℹ️ Please enter an amount of money.", - "enterDate": "ℹ️ Please enter a date.", - "enterLotsOfText": "ℹ️ You may respond in multiple lines. Please send your response only after you have finished writing.", - "enterTelephoneNumber": "ℹ️ Please enter a telephone number.", - "enterText": "ℹ️ Please keep your response brief, ideally in one sentence.", - "enterTime": "ℹ️ Please enter a time.", - "enterUrl": "ℹ️ Please enter a URL.", - "multipleAddressesAllowed": "ℹ️ You can enter one or more addresses.", - "multipleEmailsAllowed": "ℹ️ You can enter one or more emails.", - "multipleNumbersAllowed": "ℹ️ Please enter one or more numbers.", - "multipleSelectionsAllowed": "ℹ️ Please make your selection by typing the number choices. Multiple selections are allowed, please send a separate message for each of your selection.", - "multipleValuesAllowed": "ℹ️ You can enter multiple values by sending each value separately.", - "onlyOneAddressAllowed": "ℹ️ Please enter an address.", - "onlyOneEmailAllowed": "ℹ️ Please enter an email.", - "onlyOneNumberAllowed": "ℹ️ Please enter a number.", - "onlyOneSelectionAllowed": "ℹ️ Please make your selection by typing the number of your choice or pressing the button of your choice. Only one selection is allowed.", - "onlyOneValueAllowed": "ℹ️ You can only enter one value.", - "placeholder": "ℹ️ Please respond with something like \"{placeholder}\".", - "uploadFileHere": "ℹ️ Please upload the file here.", - "uploadFilesHere": "ℹ️ Please upload the files here." + "done": "When you are finished with your response, just type \"{done}\".", + "enterAmountOfMoney": "Please enter an amount of money.", + "enterDate": "Please enter a date.", + "enterLotsOfText": "Please reply with a single message. You can make it as long as you want.", + "enterTelephoneNumber": "Please enter a telephone number.", + "enterText": "Please keep your response brief, ideally in one sentence.", + "enterTime": "Please enter a time. (e.g. 20:45)", + "enterUrl": "Please enter a web address. (e.g. https://www.beabee.io)", + "multipleAddressesAllowed": "Enter one or more addresses.", + "multipleEmailsAllowed": "You can enter one or more e-mail addresses.", + "multipleNumbersAllowed": "Enter one or more numbers.", + "multipleSelectionsAllowed": "Please type in the answer number. You can pick multiple choices, just send a separate message for each.", + "multipleValuesAllowed": "You can enter multiple values by sending each value in a separate message.", + "onlyOneAddressAllowed": "Please enter an address.", + "onlyOneEmailAllowed": "Please enter an e-mail address.", + "onlyOneNumberAllowed": "Please enter a number.", + "onlyOneSelectionAllowed": "Please pick your choice by typing its number or pressing its button (you can only pick one).", + "onlyOneValueAllowed": "Please enter a single value.", + "placeholder": "Please respond with something like \"{placeholder}\".", + "uploadFileHere": "Please send me a file.", + "uploadFilesHere": "Please send me one or more files." } }, "keyboard": { @@ -55,7 +55,7 @@ "yes": "Yes" }, "message": { - "selectDetailCallout": "❓ Which Callout would you like to get more information displayed about? Choose a number" + "selectDetailCallout": "❓ Which Callout would you like to get more information displayed about? Choose a number." } }, "reactions": { @@ -75,16 +75,16 @@ "response": { "messages": { "answerWithTruthyOrFalsy": "🤔 Please answer with \"{truthy}\" or \"{falsy}\".", - "calloutNotFound": "😥 Sorry, Callout not found..", - "calloutStartResponse": "❓ Would you like to respond to the callout?", - "componentNotSupported": "😅 \"{type}\" not implemented..", - "componentUnknown": "😅 Unknown component type \"{type}\"..", - "noActiveCallouts": "😔 Sorry, there are currently no active callouts..", - "notAFileMessage": "🤔 Please send a file.", - "notASelectionMessage": "🤔 Please select the number of one of the possible answers.", - "notATextMessage": "🤔 Please send a text message.", - "notTheRightFileType": "🤔 Please send a file of type {type}.", - "stop": "☺️ Ok, no problem." + "calloutNotFound": "I'm sorry, I couldn't find this callout.", + "calloutStartResponse": "Would you like to respond to this callout?", + "componentNotSupported": "\"{type}\" not implemented..", + "componentUnknown": "Unknown component type \"{type}\"..", + "noActiveCallouts": "There are currently no open callouts.", + "notAFileMessage": "Please send a file.", + "notASelectionMessage": "Please select the number of one of the possible answers.", + "notATextMessage": "Please send a text message.", + "notTheRightFileType": "Please send a file of type {type}.", + "stop": "Ok, no problem." } }, "universal": { diff --git a/telegram-bot/locales/nl.json b/telegram-bot/locales/nl.json index fdd5108..99a8277 100644 --- a/telegram-bot/locales/nl.json +++ b/telegram-bot/locales/nl.json @@ -3,48 +3,48 @@ "commands": { "list": { "command": "lijst", - "description": "Lijst van actieve oproepen" + "description": "Open oproepen tonen" }, "show": { "command": "tonen", - "description": "Toont informatie over een specifieke oproep" + "description": "Details over een specifieke oproep tonen" }, "start": { "command": "starten", - "description": "Start de bot" + "description": "De bot starten" }, "subscribe": { "command": "abonneren", "description": "Abonneren op een oproep" }, "unsubscribe": { - "command": "afmelden", - "description": "Afmelden van een oproep" + "command": "uitschrijven", + "description": "Uitschrijven van een oproep" } }, "info": { "messages": { - "done": "ℹ️ Als u klaar bent met uw reactie, typ dan \"{done}\".", - "enterAmountOfMoney": "ℹ️ Voer alstublieft een geldbedrag in.", - "enterDate": "ℹ️ Voer alstublieft een datum in.", - "enterLotsOfText": "ℹ️ U mag in meerdere regels reageren. Stuur uw reactie pas nadat u klaar bent met schrijven. Markdown-opmaak wordt ondersteund.", - "enterTelephoneNumber": "ℹ️ Voer alstublieft een telefoonnummer in.", - "enterText": "ℹ️ Houd uw reactie kort, bij voorkeur in één zin.", - "enterTime": "ℹ️ Voer alstublieft een tijd in.", - "enterUrl": "ℹ️ Voer alstublieft een URL in.", - "multipleAddressesAllowed": "ℹ️ U kunt een of meer adressen invoeren.", - "multipleEmailsAllowed": "ℹ️ U kunt een of meer e-mails invoeren.", - "multipleNumbersAllowed": "ℹ️ Voer alstublieft een of meer nummers in.", - "multipleSelectionsAllowed": "ℹ️ Maak uw keuze door de nummerkeuzes in te typen. Meerdere selecties zijn toegestaan, stuur alstublieft een apart bericht voor elke selectie.", - "multipleValuesAllowed": "ℹ️ U kunt meerdere waarden invoeren door elke waarde afzonderlijk te verzenden.", - "onlyOneAddressAllowed": "ℹ️ Voer alstublieft een adres in.", - "onlyOneEmailAllowed": "ℹ️ Voer alstublieft een e-mailadres in.", - "onlyOneNumberAllowed": "ℹ️ Voer alstublieft een nummer in.", - "onlyOneSelectionAllowed": "ℹ️ Maak uw keuze door het nummer van uw keuze in te typen of op de bijbehorende knop te drukken. Slechts één selectie is toegestaan.", - "onlyOneValueAllowed": "ℹ️ U kunt slechts één waarde invoeren.", - "placeholder": "ℹ️ Reageer alstublieft met iets als \"{placeholder}\".", - "uploadFileHere": "ℹ️ Upload het bestand hier alstublieft.", - "uploadFilesHere": "ℹ️ Upload de bestanden hier alstublieft." + "done": "Als u klaar bent met uw antwoord, typ dan gewoon \"{done}\".", + "enterAmountOfMoney": "Voer alstublieft een geldbedrag in.", + "enterDate": "Voer alstublieft een datum in.", + "enterLotsOfText": "Antwoord alstublieft met één bericht. U kunt het zo lang maken als u wilt.", + "enterTelephoneNumber": "Voer alstublieft een telefoonnummer in.", + "enterText": "Houd uw antwoord kort, bij voorkeur in één zin.", + "enterTime": "Voer alstublieft een tijd in. (bijv. 20:45)", + "enterUrl": "Voer alstublieft een webadres in. (bijv. https://www.voorbeeld.nl)", + "multipleAddressesAllowed": "Voer een of meer adressen in.", + "multipleEmailsAllowed": "U kunt een of meer e-mailadressen invoeren.", + "multipleNumbersAllowed": "Voer een of meer nummers in.", + "multipleSelectionsAllowed": "Typ alstublieft het antwoordnummer in. U kunt meerdere keuzes maken, stuur gewoon voor elk een apart bericht.", + "multipleValuesAllowed": "U kunt meerdere waarden invoeren door elke waarde in een apart bericht te sturen.", + "onlyOneAddressAllowed": "Voer alstublieft een adres in.", + "onlyOneEmailAllowed": "Voer alstublieft een e-mailadres in.", + "onlyOneNumberAllowed": "Voer alstublieft een nummer in.", + "onlyOneSelectionAllowed": "Kies uw keuze door het nummer in te typen of op de knop te drukken (u kunt er maar één kiezen).", + "onlyOneValueAllowed": "Voer alstublieft een enkele waarde in.", + "placeholder": "Antwoord alstublieft met iets als \"{placeholder}\".", + "uploadFileHere": "Stuur me alstublieft een bestand.", + "uploadFilesHere": "Stuur me alstublieft een of meer bestanden." } }, "keyboard": { @@ -55,12 +55,12 @@ "yes": "Ja" }, "message": { - "selectDetailCallout": "❓ Over welke oproep wilt u meer informatie krijgen? Kies een nummer." + "selectDetailCallout": "❓ Over welke oproep wilt u meer informatie weergegeven krijgen? Kies een nummer" } }, "reactions": { "messages": { - "done": "Klaar", + "done": "Gedaan", "falsy": "Nee", "truthy": "Ja" } @@ -75,16 +75,16 @@ "response": { "messages": { "answerWithTruthyOrFalsy": "🤔 Antwoord alstublieft met \"{truthy}\" of \"{falsy}\".", - "calloutNotFound": "😥 Sorry, oproep niet gevonden..", - "calloutStartResponse": "❓ Wilt u reageren op de oproep?", - "componentNotSupported": "😅 \"{type}\" niet geïmplementeerd..", - "componentUnknown": "😅 Onbekend componenttype \"{type}\"..", - "noActiveCallouts": "😔 Sorry, er zijn momenteel geen actieve oproepen..", - "notAFileMessage": "🤔 Stuur alstublieft een bestand.", - "notASelectionMessage": "🤔 Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", - "notATextMessage": "🤔 Stuur alstublieft een tekstbericht.", - "notTheRightFileType": "🤔 Stuur alstublieft een bestand van het type {type}.", - "stop": "☺️ Oké, geen probleem." + "calloutNotFound": "Sorry, ik kon deze oproep niet vinden.", + "calloutStartResponse": "Wilt u reageren op deze oproep?", + "componentNotSupported": "\"{type}\" niet geïmplementeerd..", + "componentUnknown": "Onbekend componenttype \"{type}\"..", + "noActiveCallouts": "Er zijn momenteel geen open oproepen.", + "notAFileMessage": "Stuur alstublieft een bestand.", + "notASelectionMessage": "Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", + "notATextMessage": "Stuur alstublieft een tekstbericht.", + "notTheRightFileType": "Stuur alstublieft een bestand van het type {type}.", + "stop": "Ok, geen probleem." } }, "universal": { diff --git a/telegram-bot/locales/pt.json b/telegram-bot/locales/pt.json index 319f5fd..2e234c1 100644 --- a/telegram-bot/locales/pt.json +++ b/telegram-bot/locales/pt.json @@ -3,14 +3,14 @@ "commands": { "list": { "command": "listar", - "description": "Listar chamados ativos" + "description": "Mostrar chamados abertos" }, "show": { "command": "mostrar", - "description": "Mostrar informações sobre um chamado específico" + "description": "Mostrar detalhes sobre um chamado específico" }, "start": { - "command": "começar", + "command": "iniciar", "description": "Iniciar o bot" }, "subscribe": { @@ -24,27 +24,27 @@ }, "info": { "messages": { - "done": "ℹ️ Se você terminou sua resposta, por favor digite \"{done}\".", - "enterAmountOfMoney": "ℹ️ Por favor, insira um valor em dinheiro.", - "enterDate": "ℹ️ Por favor, insira uma data.", - "enterLotsOfText": "ℹ️ Você pode responder em várias linhas. Envie sua resposta somente após terminar de escrever. Formatação Markdown é suportada.", - "enterTelephoneNumber": "ℹ️ Por favor, insira um número de telefone.", - "enterText": "ℹ️ Por favor, mantenha sua resposta breve, idealmente em uma frase.", - "enterTime": "ℹ️ Por favor, insira um horário.", - "enterUrl": "ℹ️ Por favor, insira uma URL.", - "multipleAddressesAllowed": "ℹ️ Você pode inserir um ou mais endereços.", - "multipleEmailsAllowed": "ℹ️ Você pode inserir um ou mais e-mails.", - "multipleNumbersAllowed": "ℹ️ Por favor, insira um ou mais números.", - "multipleSelectionsAllowed": "ℹ️ Faça sua seleção digitando as opções de número. Múltiplas seleções são permitidas, envie uma mensagem separada para cada uma de suas escolhas.", - "multipleValuesAllowed": "ℹ️ Você pode inserir vários valores enviando cada valor separadamente.", - "onlyOneAddressAllowed": "ℹ️ Por favor, insira um endereço.", - "onlyOneEmailAllowed": "ℹ️ Por favor, insira um e-mail.", - "onlyOneNumberAllowed": "ℹ️ Por favor, insira um número.", - "onlyOneSelectionAllowed": "ℹ️ Faça sua seleção digitando o número de sua escolha ou pressionando o botão de sua escolha. Apenas uma seleção é permitida.", - "onlyOneValueAllowed": "ℹ️ Você só pode inserir um valor.", - "placeholder": "ℹ️ Por favor, responda com algo como \"{placeholder}\".", - "uploadFileHere": "ℹ️ Por favor, faça o upload do arquivo aqui.", - "uploadFilesHere": "ℹ️ Por favor, faça o upload dos arquivos aqui." + "done": "Quando terminar sua resposta, basta digitar \"{done}\".", + "enterAmountOfMoney": "Por favor, insira uma quantia em dinheiro.", + "enterDate": "Por favor, insira uma data.", + "enterLotsOfText": "Por favor, responda com uma única mensagem. Você pode fazê-la tão longa quanto desejar.", + "enterTelephoneNumber": "Por favor, insira um número de telefone.", + "enterText": "Por favor, mantenha sua resposta breve, idealmente em uma frase.", + "enterTime": "Por favor, insira um horário. (ex: 20:45)", + "enterUrl": "Por favor, insira um endereço web. (ex: https://www.exemplo.com)", + "multipleAddressesAllowed": "Insira um ou mais endereços.", + "multipleEmailsAllowed": "Você pode inserir um ou mais endereços de e-mail.", + "multipleNumbersAllowed": "Insira um ou mais números.", + "multipleSelectionsAllowed": "Por favor, digite o número da resposta. Você pode escolher várias opções, basta enviar uma mensagem separada para cada uma.", + "multipleValuesAllowed": "Você pode inserir vários valores enviando cada valor em uma mensagem separada.", + "onlyOneAddressAllowed": "Por favor, insira um endereço.", + "onlyOneEmailAllowed": "Por favor, insira um endereço de e-mail.", + "onlyOneNumberAllowed": "Por favor, insira um número.", + "onlyOneSelectionAllowed": "Por favor, faça sua escolha digitando seu número ou pressionando seu botão (você só pode escolher um).", + "onlyOneValueAllowed": "Por favor, insira um único valor.", + "placeholder": "Por favor, responda com algo como \"{placeholder}\".", + "uploadFileHere": "Por favor, envie-me um arquivo.", + "uploadFilesHere": "Por favor, envie-me um ou mais arquivos." } }, "keyboard": { @@ -55,7 +55,7 @@ "yes": "Sim" }, "message": { - "selectDetailCallout": "❓ Sobre qual chamado você gostaria de obter mais informações? Escolha um número." + "selectDetailCallout": "❓ Sobre qual chamado você gostaria de obter mais informações? Escolha um número" } }, "reactions": { @@ -75,16 +75,16 @@ "response": { "messages": { "answerWithTruthyOrFalsy": "🤔 Por favor, responda com \"{truthy}\" ou \"{falsy}\".", - "calloutNotFound": "😥 Desculpe, Chamado não encontrado..", - "calloutStartResponse": "❓ Você gostaria de responder ao chamado?", - "componentNotSupported": "😅 O tipo de componente \"{type}\" ainda não foi implementado..", - "componentUnknown": "😅 Tipo de componente desconhecido \"{type}\"..", - "noActiveCallouts": "😔 Desculpe, atualmente não há chamados ativos..", - "notAFileMessage": "🤔 Por favor, envie um arquivo.", - "notASelectionMessage": "🤔 Por favor, selecione o número de uma das possíveis respostas.", - "notATextMessage": "🤔 Por favor, envie uma mensagem de texto.", - "notTheRightFileType": "🤔 Por favor, envie um arquivo do tipo {type}.", - "stop": "☺️ Ok, sem problema." + "calloutNotFound": "Desculpe, não consegui encontrar este chamado.", + "calloutStartResponse": "Você gostaria de responder a este chamado?", + "componentNotSupported": "\"{type}\" não implementado..", + "componentUnknown": "Tipo de componente desconhecido \"{type}\"..", + "noActiveCallouts": "Atualmente, não há chamados abertos.", + "notAFileMessage": "Por favor, envie um arquivo.", + "notASelectionMessage": "Por favor, selecione o número de uma das possíveis respostas.", + "notATextMessage": "Por favor, envie uma mensagem de texto.", + "notTheRightFileType": "Por favor, envie um arquivo do tipo {type}.", + "stop": "Ok, sem problema." } }, "universal": { diff --git a/telegram-bot/locales/ru.json b/telegram-bot/locales/ru.json index 8feed26..0a36bbb 100644 --- a/telegram-bot/locales/ru.json +++ b/telegram-bot/locales/ru.json @@ -3,11 +3,11 @@ "commands": { "list": { "command": "список", - "description": "Список активных вызовов" + "description": "Показать открытые вызовы" }, "show": { "command": "показать", - "description": "Показывает информацию о конкретном вызове" + "description": "Показать детали конкретного вызова" }, "start": { "command": "начать", @@ -24,27 +24,27 @@ }, "info": { "messages": { - "done": "ℹ️ Если вы закончили свой ответ, пожалуйста, введите \"{done}\".", - "enterAmountOfMoney": "ℹ️ Пожалуйста, введите сумму денег.", - "enterDate": "ℹ️ Пожалуйста, введите дату.", - "enterLotsOfText": "ℹ️ Вы можете ответить несколькими строками. Пожалуйста, отправьте ваш ответ только после того, как вы закончите писать. Поддерживается форматирование Markdown.", - "enterTelephoneNumber": "ℹ️ Пожалуйста, введите телефонный номер.", - "enterText": "ℹ️ Пожалуйста, держите ваш ответ кратким, желательно в одном предложении.", - "enterTime": "ℹ️ Пожалуйста, введите время.", - "enterUrl": "ℹ️ Пожалуйста, введите URL.", - "multipleAddressesAllowed": "ℹ️ Вы можете ввести один или несколько адресов.", - "multipleEmailsAllowed": "ℹ️ Вы можете ввести один или несколько адресов электронной почты.", - "multipleNumbersAllowed": "ℹ️ Пожалуйста, введите один или несколько номеров.", - "multipleSelectionsAllowed": "ℹ️ Пожалуйста, сделайте свой выбор, введя номера ваших выборов. Разрешено несколько выборов, отправьте отдельное сообщение для каждого вашего выбора.", - "multipleValuesAllowed": "ℹ️ Вы можете ввести несколько значений, отправляя каждое значение отдельно.", - "onlyOneAddressAllowed": "ℹ️ Пожалуйста, введите адрес.", - "onlyOneEmailAllowed": "ℹ️ Пожалуйста, введите адрес электронной почты.", - "onlyOneNumberAllowed": "ℹ️ Пожалуйста, введите номер.", - "onlyOneSelectionAllowed": "ℹ️ Пожалуйста, сделайте свой выбор, введя номер вашего выбора или нажав соответствующую кнопку. Разрешен только один выбор.", - "onlyOneValueAllowed": "ℹ️ Вы можете ввести только одно значение.", - "placeholder": "ℹ️ Пожалуйста, ответьте чем-то вроде \"{placeholder}\".", - "uploadFileHere": "ℹ️ Пожалуйста, загрузите файл здесь.", - "uploadFilesHere": "ℹ️ Пожалуйста, загрузите файлы здесь." + "done": "Когда вы закончите с ответом, просто введите \"{done}\".", + "enterAmountOfMoney": "Пожалуйста, введите сумму денег.", + "enterDate": "Пожалуйста, введите дату.", + "enterLotsOfText": "Пожалуйста, ответьте одним сообщением. Вы можете сделать его таким длинным, как захотите.", + "enterTelephoneNumber": "Пожалуйста, введите телефонный номер.", + "enterText": "Пожалуйста, сделайте ваш ответ кратким, идеально в одном предложении.", + "enterTime": "Пожалуйста, введите время. (например, 20:45)", + "enterUrl": "Пожалуйста, введите веб-адрес. (например, https://www.example.ru)", + "multipleAddressesAllowed": "Введите один или несколько адресов.", + "multipleEmailsAllowed": "Вы можете ввести один или несколько адресов электронной почты.", + "multipleNumbersAllowed": "Введите один или несколько номеров.", + "multipleSelectionsAllowed": "Пожалуйста, введите номер ответа. Вы можете выбрать несколько вариантов, просто отправьте отдельное сообщение для каждого.", + "multipleValuesAllowed": "Вы можете ввести несколько значений, отправляя каждое значение в отдельном сообщении.", + "onlyOneAddressAllowed": "Пожалуйста, введите адрес.", + "onlyOneEmailAllowed": "Пожалуйста, введите адрес электронной почты.", + "onlyOneNumberAllowed": "Пожалуйста, введите номер.", + "onlyOneSelectionAllowed": "Пожалуйста, сделайте ваш выбор, введя его номер или нажав на его кнопку (можно выбрать только один).", + "onlyOneValueAllowed": "Пожалуйста, введите одно значение.", + "placeholder": "Пожалуйста, ответьте чем-то вроде \"{placeholder}\".", + "uploadFileHere": "Пожалуйста, отправьте мне файл.", + "uploadFilesHere": "Пожалуйста, отправьте мне один или несколько файлов." } }, "keyboard": { @@ -55,12 +55,12 @@ "yes": "Да" }, "message": { - "selectDetailCallout": "❓ О каком вызове вы хотели бы получить больше информации? Выберите номер." + "selectDetailCallout": "❓ О каком вызове вы хотели бы получить больше информации? Выберите номер" } }, "reactions": { "messages": { - "done": "Готово", + "done": "Сделано", "falsy": "Нет", "truthy": "Да" } @@ -75,16 +75,16 @@ "response": { "messages": { "answerWithTruthyOrFalsy": "🤔 Пожалуйста, ответьте \"{truthy}\" или \"{falsy}\".", - "calloutNotFound": "😥 Извините, вызов не найден..", - "calloutStartResponse": "❓ Вы хотите ответить на вызов?", - "componentNotSupported": "😅 Тип компонента \"{type}\" еще не реализован..", - "componentUnknown": "😅 Неизвестный тип компонента \"{type}\"..", - "noActiveCallouts": "😔 Извините, в настоящее время нет активных вызовов..", - "notAFileMessage": "🤔 Пожалуйста, отправьте файл.", - "notASelectionMessage": "🤔 Пожалуйста, выберите номер одного из возможных ответов.", - "notATextMessage": "🤔 Пожалуйста, отправьте текстовое сообщение.", - "notTheRightFileType": "🤔 Пожалуйста, отправьте файл типа {type}.", - "stop": "☺️ Окей, без проблем." + "calloutNotFound": "К сожалению, я не смог найти этот вызов.", + "calloutStartResponse": "Хотите ответить на этот вызов?", + "componentNotSupported": "\"{type}\" не реализован..", + "componentUnknown": "Неизвестный тип компонента \"{type}\"..", + "noActiveCallouts": "В настоящее время нет открытых вызовов.", + "notAFileMessage": "Пожалуйста, отправьте файл.", + "notASelectionMessage": "Пожалуйста, выберите номер одного из возможных ответов.", + "notATextMessage": "Пожалуйста, отправьте текстовое сообщение.", + "notTheRightFileType": "Пожалуйста, отправьте файл типа {type}.", + "stop": "Хорошо, без проблем." } }, "universal": { From 1a4aa41c2075575fb5be3610315c90d1ed232f26 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 29 Feb 2024 17:26:55 +0100 Subject: [PATCH 09/17] fix(i18n): Fix translation not found --- telegram-bot/services/i18n.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram-bot/services/i18n.service.ts b/telegram-bot/services/i18n.service.ts index 8b2a4fe..108d19f 100644 --- a/telegram-bot/services/i18n.service.ts +++ b/telegram-bot/services/i18n.service.ts @@ -139,7 +139,7 @@ export class I18nService { const segments = path.split("."); const _key = segments.shift() ?? ""; const key = toCamelCase(_key); - const nextTranslations = translations[key]; + const nextTranslations = translations[key] || translations[_key]; if (!nextTranslations) { return `Error: Translation not found for '${path}' in language '${lang}'`; From 281a06e21dc0ef81f613181758ea1bd828c3cc98 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 09:41:11 +0100 Subject: [PATCH 10/17] chore(i18n): Update link to Google Sheet to point to the telegram-bot tablesheet / tab --- telegram-bot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram-bot/README.md b/telegram-bot/README.md index db1229a..932ee9f 100644 --- a/telegram-bot/README.md +++ b/telegram-bot/README.md @@ -42,7 +42,7 @@ Telegram API. ### Internationalisation Our locale data is stored in -[this Google Sheet](https://docs.google.com/spreadsheets/d/1l35DW5OMi-xM8HXek5Q1jOxsXScINqqpEvPWDlpBPX8/edit#gid=0.). +[this Google Sheet](https://docs.google.com/spreadsheets/d/1l35DW5OMi-xM8HXek5Q1jOxsXScINqqpEvPWDlpBPX8/edit#gid=383159437). We use the Google Sheets APIs to pull this directly into the repository. You should ask another developers for their `.credentials.json` file so you can use the process below. From 53a11406c3323d3297e7afbefc750acb0e2ad7e5 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 11:19:43 +0100 Subject: [PATCH 11/17] chore(i18n): Ad translations for invalid callout answers --- telegram-bot/locales/de.json | 18 ++++++++++++++++++ telegram-bot/locales/de@informal.json | 18 ++++++++++++++++++ telegram-bot/locales/en.json | 18 ++++++++++++++++++ telegram-bot/locales/nl.json | 18 ++++++++++++++++++ telegram-bot/locales/pt.json | 18 ++++++++++++++++++ telegram-bot/locales/ru.json | 18 ++++++++++++++++++ telegram-bot/renderer/message.renderer.ts | 2 +- telegram-bot/scripts/i18n.ts | 2 -- 8 files changed, 109 insertions(+), 3 deletions(-) diff --git a/telegram-bot/locales/de.json b/telegram-bot/locales/de.json index 881d824..b1fa85e 100644 --- a/telegram-bot/locales/de.json +++ b/telegram-bot/locales/de.json @@ -84,6 +84,24 @@ "notASelectionMessage": "Bitte wählen Sie die Nummer einer der möglichen Antworten.", "notATextMessage": "Bitte senden Sie eine Textnachricht.", "notTheRightFileType": "Bitte senden Sie eine Datei vom Typ {type}.", + "notacalloutcomponent": { + "address": "Ihre Antwort ist keine valide Adresse. Bitte versuchen Sie es erneut.", + "checkbox": "🤔 Bitte antworten Sie mit \"{truthy}\" oder \"{falsy}\".", + "currency": "Ihre Antwort ist kein valider Geldbetrag. Bitte versuchen Sie es erneut.", + "datetime": "Ihre Antwort scheint kein valides Datum zu sein. Bitte versuchen Sie es erneut.", + "email": "Ihre Antwort scheint keine valide E-Mail Adresse zu sein. Bitte versuchen Sie es erneut.", + "file": "Ihre Antwort scheint keine valide Datei zu sein. Bitte versuchen Sie es erneut.", + "number": "Ihre Antwort scheint keine valide Nummer zu sein. Bitte versuchen Sie es erneut.", + "phonenumber": "Ihre Antwort scheint keine valide Telefonnummer zu sein. Bitte versuchen Sie es erneut.", + "radio": "Ihre Antwort scheint keine valide Auswahl zu sein. Bitte versuchen Sie es erneut.", + "select": "Ihre Antwort scheint keine valide Auswahl zu sein. Bitte versuchen Sie es erneut.", + "selectboxes": "Ihre Antwort scheint keine valide Auswahl zu sein. Bitte versuchen Sie es erneut.", + "signature": "Ihre Antwort scheint keine valide Bilddatei zu sein. Bitte versuchen Sie es erneut.", + "textarea": "Ihre Antwort scheint nicht die Regeln für erlaubte Textantworten einzuhalten. Bitte versuchen Sie es erneut.", + "textfield": "Ihre Antwort scheint nicht die Regeln für erlaubte Textantworten einzuhalten. Bitte versuchen Sie es erneut.", + "time": "Ihre Antwort scheint keine valide Uhrzeit zu sein. Bitte versuchen Sie es erneut.", + "url": "Ihre Antwort scheint keine valide URL zu sein. Bitte versuchen Sie es erneut." + }, "stop": "Ok, kein Problem." } }, diff --git a/telegram-bot/locales/de@informal.json b/telegram-bot/locales/de@informal.json index 6e178f4..e6381f6 100644 --- a/telegram-bot/locales/de@informal.json +++ b/telegram-bot/locales/de@informal.json @@ -84,6 +84,24 @@ "notASelectionMessage": "Bitte wähle die Nummer einer der möglichen Antworten aus.", "notATextMessage": "Bitte sende eine Textnachricht.", "notTheRightFileType": "Bitte sende eine Datei vom Typ {type}.", + "notacalloutcomponent": { + "address": "Dies ist keine valide Adresse. Bitte versuche es noch einmal.", + "checkbox": "🤔 Bitte antworte mit \"{truthy}\" oder \"{falsy}\".", + "currency": "Dies ist kein valider Geldbetrag. Bitte versuche es noch einmal.", + "datetime": "Dies ist kein valides Datum. Bitte versuche es noch einmal.", + "email": "Deine Antwort scheint keine gültige E-Mail-Adresse zu sein. Bitte versuche es erneut.", + "file": "Deine Antwort scheint keine gültige Datei zu sein. Bitte versuche es erneut.", + "number": "Deine Antwort scheint keine gültige Nummer zu sein. Bitte versuche es erneut.", + "phonenumber": "Deine Antwort scheint keine gültige Telefonnummer zu sein. Bitte versuche es erneut.", + "radio": "Deine Antwort scheint keine gültige Auswahl zu sein. Bitte versuche es erneut.", + "select": "Deine Antwort scheint keine gültige Auswahl zu sein. Bitte versuche es erneut.", + "selectboxes": "Deine Antwort scheint keine gültige Auswahl zu sein. Bitte versuche es erneut.", + "signature": "Deine Antwort scheint keine gültige Bilddatei zu sein. Bitte versuche es erneut.", + "textarea": "Deine Antwort scheint nicht den Regeln für erlaubte Textantworten zu folgen. Bitte versuche es erneut.", + "textfield": "Deine Antwort scheint nicht den Regeln für erlaubte Textantworten zu folgen. Bitte versuche es erneut.", + "time": "Deine Antwort scheint keine gültige Uhrzeit zu sein. Bitte versuche es erneut.", + "url": "Deine Antwort scheint keine gültige URL zu sein. Bitte versuche es erneut." + }, "stop": "Ok, kein Problem." } }, diff --git a/telegram-bot/locales/en.json b/telegram-bot/locales/en.json index 49e7314..789f862 100644 --- a/telegram-bot/locales/en.json +++ b/telegram-bot/locales/en.json @@ -84,6 +84,24 @@ "notASelectionMessage": "Please select the number of one of the possible answers.", "notATextMessage": "Please send a text message.", "notTheRightFileType": "Please send a file of type {type}.", + "notacalloutcomponent": { + "address": "Your answer is not a valid address. Please try again.", + "checkbox": "🤔 Please answer with \"{truthy}\" or \"{falsy}\".", + "currency": "Your answer is not a valid amount of money. Please try again.", + "datetime": "Your answer doesn't seem to be a valid date. Please try again.", + "email": "Your reply does not appear to be a valid email address. Please try again.", + "file": "Your answer doesn't seem to be a valid file. Please try again.", + "number": "Your answer doesn't seem to be a valid number. Please try again.", + "phonenumber": "Your answer doesn't appear to be a valid phone number. Please try again.", + "radio": "Your answer doesn't seem like a valid choice. Please try again.", + "select": "Your answer doesn't seem like a valid choice. Please try again.", + "selectboxes": "Your answer doesn't seem like a valid choice. Please try again.", + "signature": "Your answer doesn't appear to be a valid image file. Please try again.", + "textarea": "Your answer doesn't seem to follow the rules for allowed text answers. Please try again.", + "textfield": "Your answer doesn't seem to follow the rules for allowed text answers. Please try again.", + "time": "Your answer doesn't seem to be a valid time. Please try again.", + "url": "Your answer doesn't seem to be a valid URL. Please try again." + }, "stop": "Ok, no problem." } }, diff --git a/telegram-bot/locales/nl.json b/telegram-bot/locales/nl.json index 99a8277..76db67b 100644 --- a/telegram-bot/locales/nl.json +++ b/telegram-bot/locales/nl.json @@ -84,6 +84,24 @@ "notASelectionMessage": "Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", "notATextMessage": "Stuur alstublieft een tekstbericht.", "notTheRightFileType": "Stuur alstublieft een bestand van het type {type}.", + "notacalloutcomponent": { + "address": "Uw antwoord is geen geldig adres. Probeer het opnieuw.", + "checkbox": "🤔 Antwoord alstublieft met \"{truthy}\" of \"{falsy}\".", + "currency": "Uw antwoord is geen geldig geldbedrag. Probeer het opnieuw.", + "datetime": "Uw antwoord lijkt geen geldige datum te zijn. Probeer het opnieuw.", + "email": "Uw antwoord lijkt geen geldig e-mailadres te zijn. Probeer het opnieuw.", + "file": "Uw antwoord lijkt geen geldig bestand te zijn. Probeer het opnieuw.", + "number": "Uw antwoord lijkt geen geldig nummer te zijn. Probeer het opnieuw.", + "phonenumber": "Uw antwoord lijkt geen geldig telefoonnummer te zijn. Probeer het opnieuw.", + "radio": "Uw antwoord lijkt geen geldige keuze te zijn. Probeer het opnieuw.", + "select": "Uw antwoord lijkt geen geldige keuze te zijn. Probeer het opnieuw.", + "selectboxes": "Uw antwoord lijkt geen geldige keuze te zijn. Probeer het opnieuw.", + "signature": "Uw antwoord lijkt geen geldig afbeeldingsbestand te zijn. Probeer het opnieuw.", + "textarea": "Uw antwoord lijkt niet te voldoen aan de regels voor toegestane tekstreacties. Probeer het opnieuw.", + "textfield": "Uw antwoord lijkt niet te voldoen aan de regels voor toegestane tekstreacties. Probeer het opnieuw.", + "time": "Uw antwoord lijkt geen geldige tijd te zijn. Probeer het opnieuw.", + "url": "Uw antwoord lijkt geen geldige URL te zijn. Probeer het opnieuw." + }, "stop": "Ok, geen probleem." } }, diff --git a/telegram-bot/locales/pt.json b/telegram-bot/locales/pt.json index 2e234c1..4c759e0 100644 --- a/telegram-bot/locales/pt.json +++ b/telegram-bot/locales/pt.json @@ -84,6 +84,24 @@ "notASelectionMessage": "Por favor, selecione o número de uma das possíveis respostas.", "notATextMessage": "Por favor, envie uma mensagem de texto.", "notTheRightFileType": "Por favor, envie um arquivo do tipo {type}.", + "notacalloutcomponent": { + "address": "Sua resposta não é um endereço válido. Por favor, tente novamente.", + "checkbox": "🤔 Por favor, responda com \"{truthy}\" ou \"{falsy}\".", + "currency": "Sua resposta não é uma quantia de dinheiro válida. Por favor, tente novamente.", + "datetime": "Sua resposta não parece ser uma data válida. Por favor, tente novamente.", + "email": "Sua resposta não parece ser um endereço de e-mail válido. Por favor, tente novamente.", + "file": "Sua resposta não parece ser um arquivo válido. Por favor, tente novamente.", + "number": "Sua resposta não parece ser um número válido. Por favor, tente novamente.", + "phonenumber": "Sua resposta não parece ser um número de telefone válido. Por favor, tente novamente.", + "radio": "Sua resposta não parece ser uma escolha válida. Por favor, tente novamente.", + "select": "Sua resposta não parece ser uma escolha válida. Por favor, tente novamente.", + "selectboxes": "Sua resposta não parece ser uma escolha válida. Por favor, tente novamente.", + "signature": "Sua resposta não parece ser um arquivo de imagem válido. Por favor, tente novamente.", + "textarea": "Sua resposta não parece seguir as regras para respostas de texto permitidas. Por favor, tente novamente.", + "textfield": "Sua resposta não parece seguir as regras para respostas de texto permitidas. Por favor, tente novamente.", + "time": "Sua resposta não parece ser um horário válido. Por favor, tente novamente.", + "url": "Sua resposta não parece ser um URL válido. Por favor, tente novamente." + }, "stop": "Ok, sem problema." } }, diff --git a/telegram-bot/locales/ru.json b/telegram-bot/locales/ru.json index 0a36bbb..7570c08 100644 --- a/telegram-bot/locales/ru.json +++ b/telegram-bot/locales/ru.json @@ -84,6 +84,24 @@ "notASelectionMessage": "Пожалуйста, выберите номер одного из возможных ответов.", "notATextMessage": "Пожалуйста, отправьте текстовое сообщение.", "notTheRightFileType": "Пожалуйста, отправьте файл типа {type}.", + "notacalloutcomponent": { + "address": "Ваш ответ не является допустимым адресом. Пожалуйста, попробуйте снова.", + "checkbox": "🤔 Пожалуйста, ответьте \"{truthy}\" или \"{falsy}\".", + "currency": "Ваш ответ не является допустимой суммой денег. Пожалуйста, попробуйте снова.", + "datetime": "Ваш ответ кажется недопустимой датой. Пожалуйста, попробуйте снова.", + "email": "Ваш ответ не похож на действительный адрес электронной почты. Пожалуйста, попробуйте снова.", + "file": "Ваш ответ не похож на допустимый файл. Пожалуйста, попробуйте снова.", + "number": "Ваш ответ не похож на допустимое число. Пожалуйста, попробуйте снова.", + "phonenumber": "Ваш ответ не похож на действительный телефонный номер. Пожалуйста, попробуйте снова.", + "radio": "Ваш ответ не похож на допустимый выбор. Пожалуйста, попробуйте снова.", + "select": "Ваш ответ не похож на допустимый выбор. Пожалуйста, попробуйте снова.", + "selectboxes": "Ваш ответ не похож на допустимый выбор. Пожалуйста, попробуйте снова.", + "signature": "Ваш ответ не похож на допустимый файл изображения. Пожалуйста, попробуйте снова.", + "textarea": "Ваш ответ кажется не соответствует правилам для разрешенных текстовых ответов. Пожалуйста, попробуйте снова.", + "textfield": "Ваш ответ кажется не соответствует правилам для разрешенных текстовых ответов. Пожалуйста, попробуйте снова.", + "time": "Ваш ответ не похож на допустимое время. Пожалуйста, попробуйте снова.", + "url": "Ваш ответ не похож на допустимый URL. Пожалуйста, попробуйте снова." + }, "stop": "Хорошо, без проблем." } }, diff --git a/telegram-bot/renderer/message.renderer.ts b/telegram-bot/renderer/message.renderer.ts index 5487e54..ba9e903 100644 --- a/telegram-bot/renderer/message.renderer.ts +++ b/telegram-bot/renderer/message.renderer.ts @@ -100,7 +100,7 @@ export class MessageRenderer { public notACalloutComponentMessage(schema: CalloutComponentSchema) { const tKey = - `response.messages.not-a-callout-${schema.type}-component-message`; + `bot.response.messages.notACalloutComponent.${schema.type}`; return { type: RenderType.TEXT, text: this.i18n.t(tKey, { type: schema.type }), diff --git a/telegram-bot/scripts/i18n.ts b/telegram-bot/scripts/i18n.ts index 6a199f4..3010dc0 100644 --- a/telegram-bot/scripts/i18n.ts +++ b/telegram-bot/scripts/i18n.ts @@ -100,13 +100,11 @@ async function loadSheet(name: string) { const keyParts = row.key.split("."); const [_lastKeyPart, ...keyOpts] = keyParts.pop()?.split(":") ?? []; const lastKeyPart = toCamelCase(_lastKeyPart); - console.debug("lastKeyPart", _lastKeyPart, lastKeyPart); for (const locale of locales) { let localeDataPart = localeData[locale] as LocaleEntry; for (const _part of keyParts) { const part = toCamelCase(_part); - console.debug("part", _part, part); if (!localeDataPart[part]) { localeDataPart[part] = {}; } From 9198d97b0b756c23e44ea2b9d8d97be8c928a3ac Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 12:26:57 +0100 Subject: [PATCH 12/17] chore(validation): Rename CalloutResponseAnswersNestable -> CalloutResponseAnswers --- beabee-client/src/deps.ts | 2 +- beabee-client/src/types/get-callout-response-data-with.ts | 4 ++-- beabee-client/src/types/get-callout-response-map-data.ts | 4 ++-- beabee-common | 2 +- telegram-bot/renderer/message.renderer.ts | 3 +-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/beabee-client/src/deps.ts b/beabee-client/src/deps.ts index 5b51885..87a38bd 100644 --- a/beabee-client/src/deps.ts +++ b/beabee-client/src/deps.ts @@ -3,7 +3,7 @@ export type { CalloutResponseAnswer, CalloutResponseAnswerAddress, CalloutResponseAnswerFileUpload, - CalloutResponseAnswersNestable, + CalloutResponseAnswers, CalloutResponseAnswersSlide, ContributionPeriod, ContributionType, diff --git a/beabee-client/src/types/get-callout-response-data-with.ts b/beabee-client/src/types/get-callout-response-data-with.ts index d983ba3..5ef065e 100644 --- a/beabee-client/src/types/get-callout-response-data-with.ts +++ b/beabee-client/src/types/get-callout-response-data-with.ts @@ -1,4 +1,4 @@ -import type { CalloutResponseAnswersNestable } from "../deps.ts"; +import type { CalloutResponseAnswers } from "../deps.ts"; import type { GetCalloutData, @@ -11,7 +11,7 @@ import type { export type GetCalloutResponseDataWith = & GetCalloutResponseData - & ("answers" extends With ? { answers: CalloutResponseAnswersNestable } + & ("answers" extends With ? { answers: CalloutResponseAnswers } : Noop) & ("assignee" extends With ? { assignee: GetContactData | null } : Noop) & ("callout" extends With ? { callout: GetCalloutData } : Noop) diff --git a/beabee-client/src/types/get-callout-response-map-data.ts b/beabee-client/src/types/get-callout-response-map-data.ts index f45c489..6ee2b71 100644 --- a/beabee-client/src/types/get-callout-response-map-data.ts +++ b/beabee-client/src/types/get-callout-response-map-data.ts @@ -1,12 +1,12 @@ import type { CalloutResponseAnswerAddress, CalloutResponseAnswerFileUpload, - CalloutResponseAnswersNestable, + CalloutResponseAnswers, } from "../deps.ts"; export interface GetCalloutResponseMapData { number: number; - answers: CalloutResponseAnswersNestable; + answers: CalloutResponseAnswers; title: string; photos: CalloutResponseAnswerFileUpload[]; address?: CalloutResponseAnswerAddress; diff --git a/beabee-common b/beabee-common index 42a6e0c..25a9e94 160000 --- a/beabee-common +++ b/beabee-common @@ -1 +1 @@ -Subproject commit 42a6e0cfdb06e2d0d2fd3f976ed5e839767fae76 +Subproject commit 25a9e94703d1436a0fb455aa8d0430f90ae11557 diff --git a/telegram-bot/renderer/message.renderer.ts b/telegram-bot/renderer/message.renderer.ts index ba9e903..7613ee6 100644 --- a/telegram-bot/renderer/message.renderer.ts +++ b/telegram-bot/renderer/message.renderer.ts @@ -99,8 +99,7 @@ export class MessageRenderer { } public notACalloutComponentMessage(schema: CalloutComponentSchema) { - const tKey = - `bot.response.messages.notACalloutComponent.${schema.type}`; + const tKey = `bot.response.messages.notACalloutComponent.${schema.type}`; return { type: RenderType.TEXT, text: this.i18n.t(tKey, { type: schema.type }), From b0411fd07cde1e9564e6aa09a313db9862b3e920 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 12:45:20 +0100 Subject: [PATCH 13/17] chore(validation): Changed branch of beabee-common to main --- .gitmodules | 2 +- beabee-common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 1a2c42d..18a44c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "beabee-common"] path = beabee-common url = git@github.com:beabee-communityrm/beabee-common.git - branch = callout-validation + branch = main diff --git a/beabee-common b/beabee-common index 25a9e94..ed32014 160000 --- a/beabee-common +++ b/beabee-common @@ -1 +1 @@ -Subproject commit 25a9e94703d1436a0fb455aa8d0430f90ae11557 +Subproject commit ed32014ce3418347cc2500b8f8cdecd0555f631d From 7b9b0069f2aec3936fc1d04b285edb66a5e039d4 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 15:06:11 +0100 Subject: [PATCH 14/17] chore(validation): Allow urls not starting with a protocol --- telegram-bot/renderer/message.renderer.ts | 8 ++++---- telegram-bot/services/transform.service.ts | 13 +++++++++++++ telegram-bot/services/validation.service.ts | 8 ++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/telegram-bot/renderer/message.renderer.ts b/telegram-bot/renderer/message.renderer.ts index 7613ee6..04a4240 100644 --- a/telegram-bot/renderer/message.renderer.ts +++ b/telegram-bot/renderer/message.renderer.ts @@ -52,11 +52,11 @@ export class MessageRenderer { return result; } - public notATextMessage(): RenderText { - const tKey = "bot.response.messages.notATextMessage"; + public notATextMessage(texts: string[] = []): RenderText { + const tKey = texts.length ? "bot.response.messages.notATextMessageWithAllowed" : "bot.response.messages.notATextMessage"; return { type: RenderType.TEXT, - text: this.i18n.t(tKey), + text: this.i18n.t(tKey, { allowed: texts.join(", ") }), key: tKey, accepted: this.condition.replayConditionNone(), parseType: ParsedResponseType.NONE, @@ -118,7 +118,7 @@ export class MessageRenderer { } if (condition.type === ReplayType.TEXT) { - return this.notATextMessage(); + return this.notATextMessage(condition.texts); } if (condition.type === ReplayType.SELECTION) { diff --git a/telegram-bot/services/transform.service.ts b/telegram-bot/services/transform.service.ts index aa55bf1..415a212 100644 --- a/telegram-bot/services/transform.service.ts +++ b/telegram-bot/services/transform.service.ts @@ -26,6 +26,7 @@ import type { } from "../types/index.ts"; import type { +CalloutComponentInputUrlSchema, CalloutResponseAnswer, CalloutResponseAnswersSlide, } from "../deps.ts"; @@ -187,6 +188,7 @@ export class TransformService { return texts; } + // TODO: Use CalloutComponentInputAdressSchema as return type public parseResponseCalloutComponentAddress( context: Context, ): CalloutResponseAnswerAddress { @@ -206,6 +208,7 @@ export class TransformService { return address; } + // TODO: Use CalloutComponentInputAdressSchema as return type public parseResponsesCalloutComponentAddress( contexts: Context[], ): CalloutResponseAnswerAddress[] { @@ -217,6 +220,16 @@ export class TransformService { return addresses; } + public parseResponseCalloutComponentInputUrl( + context: Context, + ): string { + let text = this.parseResponseText(context); + if(!text.startsWith("http")) { + text = `https://${text}`; + } + return text; + } + public parseResponseAny( context: Context, ): RenderResponseParsedAny["data"] { diff --git a/telegram-bot/services/validation.service.ts b/telegram-bot/services/validation.service.ts index ba6c213..13ca24c 100644 --- a/telegram-bot/services/validation.service.ts +++ b/telegram-bot/services/validation.service.ts @@ -353,11 +353,15 @@ export class ValidationService { case CalloutComponentType.INPUT_PHONE_NUMBER: case CalloutComponentType.INPUT_TEXT_AREA: case CalloutComponentType.INPUT_TEXT_FIELD: - case CalloutComponentType.INPUT_TIME: - case CalloutComponentType.INPUT_URL: { + case CalloutComponentType.INPUT_TIME: { result.answer = this.transform.parseResponseText(context); break; } + case CalloutComponentType.INPUT_URL: { + result.answer = this.transform.parseResponseCalloutComponentInputUrl(context); + break; + } + case CalloutComponentType.INPUT_NUMBER: { result.answer = this.transform.parseResponseNumber(context); break; From df8bbbb650e9006d483ddbb32c3d827bf0da9eac Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 15:07:30 +0100 Subject: [PATCH 15/17] fix(i18n): Fix keys by fixing toCamelCase method --- telegram-bot/locales/de.json | 13 +++++++------ telegram-bot/locales/de@informal.json | 13 +++++++------ telegram-bot/locales/en.json | 13 +++++++------ telegram-bot/locales/nl.json | 13 +++++++------ telegram-bot/locales/pt.json | 13 +++++++------ telegram-bot/locales/ru.json | 13 +++++++------ telegram-bot/utils/string.ts | 5 +++++ 7 files changed, 47 insertions(+), 36 deletions(-) diff --git a/telegram-bot/locales/de.json b/telegram-bot/locales/de.json index b1fa85e..728f2ef 100644 --- a/telegram-bot/locales/de.json +++ b/telegram-bot/locales/de.json @@ -80,11 +80,7 @@ "componentNotSupported": "\"{type}\" nicht implementiert..", "componentUnknown": "Unbekannter Komponententyp \"{type}\"..", "noActiveCallouts": "Derzeit gibt es keine offenen Aufrufe.", - "notAFileMessage": "Bitte senden Sie eine Datei.", - "notASelectionMessage": "Bitte wählen Sie die Nummer einer der möglichen Antworten.", - "notATextMessage": "Bitte senden Sie eine Textnachricht.", - "notTheRightFileType": "Bitte senden Sie eine Datei vom Typ {type}.", - "notacalloutcomponent": { + "notACalloutComponent": { "address": "Ihre Antwort ist keine valide Adresse. Bitte versuchen Sie es erneut.", "checkbox": "🤔 Bitte antworten Sie mit \"{truthy}\" oder \"{falsy}\".", "currency": "Ihre Antwort ist kein valider Geldbetrag. Bitte versuchen Sie es erneut.", @@ -92,7 +88,7 @@ "email": "Ihre Antwort scheint keine valide E-Mail Adresse zu sein. Bitte versuchen Sie es erneut.", "file": "Ihre Antwort scheint keine valide Datei zu sein. Bitte versuchen Sie es erneut.", "number": "Ihre Antwort scheint keine valide Nummer zu sein. Bitte versuchen Sie es erneut.", - "phonenumber": "Ihre Antwort scheint keine valide Telefonnummer zu sein. Bitte versuchen Sie es erneut.", + "phoneNumber": "Ihre Antwort scheint keine valide Telefonnummer zu sein. Bitte versuchen Sie es erneut.", "radio": "Ihre Antwort scheint keine valide Auswahl zu sein. Bitte versuchen Sie es erneut.", "select": "Ihre Antwort scheint keine valide Auswahl zu sein. Bitte versuchen Sie es erneut.", "selectboxes": "Ihre Antwort scheint keine valide Auswahl zu sein. Bitte versuchen Sie es erneut.", @@ -102,6 +98,11 @@ "time": "Ihre Antwort scheint keine valide Uhrzeit zu sein. Bitte versuchen Sie es erneut.", "url": "Ihre Antwort scheint keine valide URL zu sein. Bitte versuchen Sie es erneut." }, + "notAFileMessage": "Bitte senden Sie eine Datei.", + "notASelectionMessage": "Bitte wählen Sie die Nummer einer der möglichen Antworten.", + "notATextMessage": "Bitte senden Sie eine Textnachricht.", + "notATextMessageWithAllowed": "Bitte senden Sie eine Textnachricht mit einem der folgenden Inhalte: {allowed}.", + "notTheRightFileType": "Bitte senden Sie eine Datei vom Typ {type}.", "stop": "Ok, kein Problem." } }, diff --git a/telegram-bot/locales/de@informal.json b/telegram-bot/locales/de@informal.json index e6381f6..f09b22d 100644 --- a/telegram-bot/locales/de@informal.json +++ b/telegram-bot/locales/de@informal.json @@ -80,11 +80,7 @@ "componentNotSupported": "\"{type}\" ist noch nicht implementiert..", "componentUnknown": "Unbekannter Komponententyp \"{type}\"..", "noActiveCallouts": "Aktuell gibt es keine offenen Aufrufe.", - "notAFileMessage": "Bitte sende eine Datei.", - "notASelectionMessage": "Bitte wähle die Nummer einer der möglichen Antworten aus.", - "notATextMessage": "Bitte sende eine Textnachricht.", - "notTheRightFileType": "Bitte sende eine Datei vom Typ {type}.", - "notacalloutcomponent": { + "notACalloutComponent": { "address": "Dies ist keine valide Adresse. Bitte versuche es noch einmal.", "checkbox": "🤔 Bitte antworte mit \"{truthy}\" oder \"{falsy}\".", "currency": "Dies ist kein valider Geldbetrag. Bitte versuche es noch einmal.", @@ -92,7 +88,7 @@ "email": "Deine Antwort scheint keine gültige E-Mail-Adresse zu sein. Bitte versuche es erneut.", "file": "Deine Antwort scheint keine gültige Datei zu sein. Bitte versuche es erneut.", "number": "Deine Antwort scheint keine gültige Nummer zu sein. Bitte versuche es erneut.", - "phonenumber": "Deine Antwort scheint keine gültige Telefonnummer zu sein. Bitte versuche es erneut.", + "phoneNumber": "Deine Antwort scheint keine gültige Telefonnummer zu sein. Bitte versuche es erneut.", "radio": "Deine Antwort scheint keine gültige Auswahl zu sein. Bitte versuche es erneut.", "select": "Deine Antwort scheint keine gültige Auswahl zu sein. Bitte versuche es erneut.", "selectboxes": "Deine Antwort scheint keine gültige Auswahl zu sein. Bitte versuche es erneut.", @@ -102,6 +98,11 @@ "time": "Deine Antwort scheint keine gültige Uhrzeit zu sein. Bitte versuche es erneut.", "url": "Deine Antwort scheint keine gültige URL zu sein. Bitte versuche es erneut." }, + "notAFileMessage": "Bitte sende eine Datei.", + "notASelectionMessage": "Bitte wähle die Nummer einer der möglichen Antworten aus.", + "notATextMessage": "Bitte sende eine Textnachricht.", + "notATextMessageWithAllowed": "Bitte sende eine Textnachricht mit einem der folgenden Inhalte: {allowed}.", + "notTheRightFileType": "Bitte sende eine Datei vom Typ {type}.", "stop": "Ok, kein Problem." } }, diff --git a/telegram-bot/locales/en.json b/telegram-bot/locales/en.json index 789f862..67d1990 100644 --- a/telegram-bot/locales/en.json +++ b/telegram-bot/locales/en.json @@ -80,11 +80,7 @@ "componentNotSupported": "\"{type}\" not implemented..", "componentUnknown": "Unknown component type \"{type}\"..", "noActiveCallouts": "There are currently no open callouts.", - "notAFileMessage": "Please send a file.", - "notASelectionMessage": "Please select the number of one of the possible answers.", - "notATextMessage": "Please send a text message.", - "notTheRightFileType": "Please send a file of type {type}.", - "notacalloutcomponent": { + "notACalloutComponent": { "address": "Your answer is not a valid address. Please try again.", "checkbox": "🤔 Please answer with \"{truthy}\" or \"{falsy}\".", "currency": "Your answer is not a valid amount of money. Please try again.", @@ -92,7 +88,7 @@ "email": "Your reply does not appear to be a valid email address. Please try again.", "file": "Your answer doesn't seem to be a valid file. Please try again.", "number": "Your answer doesn't seem to be a valid number. Please try again.", - "phonenumber": "Your answer doesn't appear to be a valid phone number. Please try again.", + "phoneNumber": "Your answer doesn't appear to be a valid phone number. Please try again.", "radio": "Your answer doesn't seem like a valid choice. Please try again.", "select": "Your answer doesn't seem like a valid choice. Please try again.", "selectboxes": "Your answer doesn't seem like a valid choice. Please try again.", @@ -102,6 +98,11 @@ "time": "Your answer doesn't seem to be a valid time. Please try again.", "url": "Your answer doesn't seem to be a valid URL. Please try again." }, + "notAFileMessage": "Please send a file.", + "notASelectionMessage": "Please select the number of one of the possible answers.", + "notATextMessage": "Please send a text message.", + "notATextMessageWithAllowed": "Please send a text message containing one of the following: {allowed}.", + "notTheRightFileType": "Please send a file of type {type}.", "stop": "Ok, no problem." } }, diff --git a/telegram-bot/locales/nl.json b/telegram-bot/locales/nl.json index 76db67b..50e5101 100644 --- a/telegram-bot/locales/nl.json +++ b/telegram-bot/locales/nl.json @@ -80,11 +80,7 @@ "componentNotSupported": "\"{type}\" niet geïmplementeerd..", "componentUnknown": "Onbekend componenttype \"{type}\"..", "noActiveCallouts": "Er zijn momenteel geen open oproepen.", - "notAFileMessage": "Stuur alstublieft een bestand.", - "notASelectionMessage": "Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", - "notATextMessage": "Stuur alstublieft een tekstbericht.", - "notTheRightFileType": "Stuur alstublieft een bestand van het type {type}.", - "notacalloutcomponent": { + "notACalloutComponent": { "address": "Uw antwoord is geen geldig adres. Probeer het opnieuw.", "checkbox": "🤔 Antwoord alstublieft met \"{truthy}\" of \"{falsy}\".", "currency": "Uw antwoord is geen geldig geldbedrag. Probeer het opnieuw.", @@ -92,7 +88,7 @@ "email": "Uw antwoord lijkt geen geldig e-mailadres te zijn. Probeer het opnieuw.", "file": "Uw antwoord lijkt geen geldig bestand te zijn. Probeer het opnieuw.", "number": "Uw antwoord lijkt geen geldig nummer te zijn. Probeer het opnieuw.", - "phonenumber": "Uw antwoord lijkt geen geldig telefoonnummer te zijn. Probeer het opnieuw.", + "phoneNumber": "Uw antwoord lijkt geen geldig telefoonnummer te zijn. Probeer het opnieuw.", "radio": "Uw antwoord lijkt geen geldige keuze te zijn. Probeer het opnieuw.", "select": "Uw antwoord lijkt geen geldige keuze te zijn. Probeer het opnieuw.", "selectboxes": "Uw antwoord lijkt geen geldige keuze te zijn. Probeer het opnieuw.", @@ -102,6 +98,11 @@ "time": "Uw antwoord lijkt geen geldige tijd te zijn. Probeer het opnieuw.", "url": "Uw antwoord lijkt geen geldige URL te zijn. Probeer het opnieuw." }, + "notAFileMessage": "Stuur alstublieft een bestand.", + "notASelectionMessage": "Selecteer alstublieft het nummer van een van de mogelijke antwoorden.", + "notATextMessage": "Stuur alstublieft een tekstbericht.", + "notATextMessageWithAllowed": "Stuur een sms-bericht met een van de volgende gegevens: {allowed}.", + "notTheRightFileType": "Stuur alstublieft een bestand van het type {type}.", "stop": "Ok, geen probleem." } }, diff --git a/telegram-bot/locales/pt.json b/telegram-bot/locales/pt.json index 4c759e0..c644668 100644 --- a/telegram-bot/locales/pt.json +++ b/telegram-bot/locales/pt.json @@ -80,11 +80,7 @@ "componentNotSupported": "\"{type}\" não implementado..", "componentUnknown": "Tipo de componente desconhecido \"{type}\"..", "noActiveCallouts": "Atualmente, não há chamados abertos.", - "notAFileMessage": "Por favor, envie um arquivo.", - "notASelectionMessage": "Por favor, selecione o número de uma das possíveis respostas.", - "notATextMessage": "Por favor, envie uma mensagem de texto.", - "notTheRightFileType": "Por favor, envie um arquivo do tipo {type}.", - "notacalloutcomponent": { + "notACalloutComponent": { "address": "Sua resposta não é um endereço válido. Por favor, tente novamente.", "checkbox": "🤔 Por favor, responda com \"{truthy}\" ou \"{falsy}\".", "currency": "Sua resposta não é uma quantia de dinheiro válida. Por favor, tente novamente.", @@ -92,7 +88,7 @@ "email": "Sua resposta não parece ser um endereço de e-mail válido. Por favor, tente novamente.", "file": "Sua resposta não parece ser um arquivo válido. Por favor, tente novamente.", "number": "Sua resposta não parece ser um número válido. Por favor, tente novamente.", - "phonenumber": "Sua resposta não parece ser um número de telefone válido. Por favor, tente novamente.", + "phoneNumber": "Sua resposta não parece ser um número de telefone válido. Por favor, tente novamente.", "radio": "Sua resposta não parece ser uma escolha válida. Por favor, tente novamente.", "select": "Sua resposta não parece ser uma escolha válida. Por favor, tente novamente.", "selectboxes": "Sua resposta não parece ser uma escolha válida. Por favor, tente novamente.", @@ -102,6 +98,11 @@ "time": "Sua resposta não parece ser um horário válido. Por favor, tente novamente.", "url": "Sua resposta não parece ser um URL válido. Por favor, tente novamente." }, + "notAFileMessage": "Por favor, envie um arquivo.", + "notASelectionMessage": "Por favor, selecione o número de uma das possíveis respostas.", + "notATextMessage": "Por favor, envie uma mensagem de texto.", + "notATextMessageWithAllowed": "Envie uma mensagem de texto contendo um dos seguintes itens: {allowed}.", + "notTheRightFileType": "Por favor, envie um arquivo do tipo {type}.", "stop": "Ok, sem problema." } }, diff --git a/telegram-bot/locales/ru.json b/telegram-bot/locales/ru.json index 7570c08..59e6bbe 100644 --- a/telegram-bot/locales/ru.json +++ b/telegram-bot/locales/ru.json @@ -80,11 +80,7 @@ "componentNotSupported": "\"{type}\" не реализован..", "componentUnknown": "Неизвестный тип компонента \"{type}\"..", "noActiveCallouts": "В настоящее время нет открытых вызовов.", - "notAFileMessage": "Пожалуйста, отправьте файл.", - "notASelectionMessage": "Пожалуйста, выберите номер одного из возможных ответов.", - "notATextMessage": "Пожалуйста, отправьте текстовое сообщение.", - "notTheRightFileType": "Пожалуйста, отправьте файл типа {type}.", - "notacalloutcomponent": { + "notACalloutComponent": { "address": "Ваш ответ не является допустимым адресом. Пожалуйста, попробуйте снова.", "checkbox": "🤔 Пожалуйста, ответьте \"{truthy}\" или \"{falsy}\".", "currency": "Ваш ответ не является допустимой суммой денег. Пожалуйста, попробуйте снова.", @@ -92,7 +88,7 @@ "email": "Ваш ответ не похож на действительный адрес электронной почты. Пожалуйста, попробуйте снова.", "file": "Ваш ответ не похож на допустимый файл. Пожалуйста, попробуйте снова.", "number": "Ваш ответ не похож на допустимое число. Пожалуйста, попробуйте снова.", - "phonenumber": "Ваш ответ не похож на действительный телефонный номер. Пожалуйста, попробуйте снова.", + "phoneNumber": "Ваш ответ не похож на действительный телефонный номер. Пожалуйста, попробуйте снова.", "radio": "Ваш ответ не похож на допустимый выбор. Пожалуйста, попробуйте снова.", "select": "Ваш ответ не похож на допустимый выбор. Пожалуйста, попробуйте снова.", "selectboxes": "Ваш ответ не похож на допустимый выбор. Пожалуйста, попробуйте снова.", @@ -102,6 +98,11 @@ "time": "Ваш ответ не похож на допустимое время. Пожалуйста, попробуйте снова.", "url": "Ваш ответ не похож на допустимый URL. Пожалуйста, попробуйте снова." }, + "notAFileMessage": "Пожалуйста, отправьте файл.", + "notASelectionMessage": "Пожалуйста, выберите номер одного из возможных ответов.", + "notATextMessage": "Пожалуйста, отправьте текстовое сообщение.", + "notATextMessageWithAllowed": "Отправьте текстовое сообщение, содержащее одно из следующего: {allowed}.", + "notTheRightFileType": "Пожалуйста, отправьте файл типа {type}.", "stop": "Хорошо, без проблем." } }, diff --git a/telegram-bot/utils/string.ts b/telegram-bot/utils/string.ts index 71e017a..cd69fec 100644 --- a/telegram-bot/utils/string.ts +++ b/telegram-bot/utils/string.ts @@ -47,6 +47,11 @@ export const truncateSlug = (input: string, maxLength = 32): string => { * @returns The camelCase formatted string. */ export const toCamelCase = (input: string): string => { + // Check if no need to convert + if (!/[\s-_]/.test(input)) { + return input; + } + return input // Remove special characters except spaces, hyphens, and underscores .replace(/[^a-zA-Z0-9\s-_]/g, "") From 8aa808433496c5a8f1858513f07d28e01c26df3d Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 15:09:14 +0100 Subject: [PATCH 16/17] chore: Formatting --- telegram-bot/renderer/message.renderer.ts | 4 +++- telegram-bot/services/transform.service.ts | 4 ++-- telegram-bot/services/validation.service.ts | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/telegram-bot/renderer/message.renderer.ts b/telegram-bot/renderer/message.renderer.ts index 04a4240..e0d10e9 100644 --- a/telegram-bot/renderer/message.renderer.ts +++ b/telegram-bot/renderer/message.renderer.ts @@ -53,7 +53,9 @@ export class MessageRenderer { } public notATextMessage(texts: string[] = []): RenderText { - const tKey = texts.length ? "bot.response.messages.notATextMessageWithAllowed" : "bot.response.messages.notATextMessage"; + const tKey = texts.length + ? "bot.response.messages.notATextMessageWithAllowed" + : "bot.response.messages.notATextMessage"; return { type: RenderType.TEXT, text: this.i18n.t(tKey, { allowed: texts.join(", ") }), diff --git a/telegram-bot/services/transform.service.ts b/telegram-bot/services/transform.service.ts index 415a212..eaca3dd 100644 --- a/telegram-bot/services/transform.service.ts +++ b/telegram-bot/services/transform.service.ts @@ -26,7 +26,7 @@ import type { } from "../types/index.ts"; import type { -CalloutComponentInputUrlSchema, + CalloutComponentInputUrlSchema, CalloutResponseAnswer, CalloutResponseAnswersSlide, } from "../deps.ts"; @@ -224,7 +224,7 @@ export class TransformService { context: Context, ): string { let text = this.parseResponseText(context); - if(!text.startsWith("http")) { + if (!text.startsWith("http")) { text = `https://${text}`; } return text; diff --git a/telegram-bot/services/validation.service.ts b/telegram-bot/services/validation.service.ts index 13ca24c..7e4691e 100644 --- a/telegram-bot/services/validation.service.ts +++ b/telegram-bot/services/validation.service.ts @@ -358,10 +358,12 @@ export class ValidationService { break; } case CalloutComponentType.INPUT_URL: { - result.answer = this.transform.parseResponseCalloutComponentInputUrl(context); + result.answer = this.transform.parseResponseCalloutComponentInputUrl( + context, + ); break; } - + case CalloutComponentType.INPUT_NUMBER: { result.answer = this.transform.parseResponseNumber(context); break; From c5411baba012fce5949f9f9624ad34c77efe3fb4 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 1 Mar 2024 15:12:14 +0100 Subject: [PATCH 17/17] chore: Linter --- telegram-bot/services/transform.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/telegram-bot/services/transform.service.ts b/telegram-bot/services/transform.service.ts index eaca3dd..542f230 100644 --- a/telegram-bot/services/transform.service.ts +++ b/telegram-bot/services/transform.service.ts @@ -26,7 +26,6 @@ import type { } from "../types/index.ts"; import type { - CalloutComponentInputUrlSchema, CalloutResponseAnswer, CalloutResponseAnswersSlide, } from "../deps.ts";