From 621b03fb70888778381005feb4185fe578bf73cb Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Sat, 30 Dec 2023 18:09:02 +0700 Subject: [PATCH] added help command --- config.example.yml | 2 ++ src/api/telegram/index.ts | 18 ++++++++++++++++++ src/commands/roll.ts | 4 ++-- src/config/default.ts | 2 ++ src/config/index.ts | 8 +++++++- src/config/types.ts | 3 ++- src/config/validate.ts | 2 ++ src/service/telegram/index.ts | 36 +++++++++++++++++++++++++++++++++++ src/service/telegram/types.ts | 4 ++++ src/service/template/index.ts | 10 +++++----- templates/help.md | 4 ++++ templates/help_admin.md | 7 +++++++ 12 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 templates/help.md create mode 100644 templates/help_admin.md diff --git a/config.example.yml b/config.example.yml index d77c7f2..1cf3c89 100644 --- a/config.example.yml +++ b/config.example.yml @@ -16,6 +16,8 @@ vk: endpoint: / groups: [] templates: + help: templates/help.md + help_admin: templates/help_admin.md message_new: templates/message_new.md wall_post_new: templates/post_new.md group_join: templates/group_join.md diff --git a/src/api/telegram/index.ts b/src/api/telegram/index.ts index 5c19fba..aafecbd 100644 --- a/src/api/telegram/index.ts +++ b/src/api/telegram/index.ts @@ -16,6 +16,8 @@ export class TelegramApi { this.telegram.bot.command("config", this.dumpConfig); this.telegram.bot.command("pop", this.pop); this.telegram.bot.command("wtf", this.wtf); + this.telegram.bot.command("help", this.help); + this.telegram.bot.command("start", this.help); return; } @@ -104,6 +106,22 @@ export class TelegramApi { return next(); }; + /** + * Sends recent logs + */ + private help = async (ctx, next) => { + const username = ctx?.update?.message?.from?.username; + const isOwner = !!username && this.telegram.isOwner(`@${username}`); + + const message = this.telegram.getHelpMessage(isOwner); + if (!message) { + console.warn("No templates for help found, skipping"); + } + await ctx.reply(message); + + return next(); + }; + /** * Probes webhook url and falls back to polling mode on error */ diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 90fd372..ffd0add 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -33,7 +33,7 @@ const parseVal = (val?: string) => { return parsed; }; -const escape = (val?: string) => val && val.replace(/([-.\[\]])/g, "\\$1"); +const escape = (val?: string) => val && val.replace(/([-.\[\]\(\)])/g, "\\$1"); const deviate = (max: number, factor: number) => [ Math.round(max * (1 - factor)), Math.round(max * (1 + factor)), @@ -86,7 +86,7 @@ const getRoute = async ( export const roll = async (text: string) => { try { - const parts = text.match(/^\/roll\s?(\d+)?[-\s]?(\d+)?$/); + const parts = text.match(/^\/roll\s?(\d+)?[-\s,]?(\d+)?$/); const result = await getRoute(parseVal(parts?.[1]), parseVal(parts?.[2])); if (!result || !result?.id) { diff --git a/src/config/default.ts b/src/config/default.ts index ca1e532..9fccad1 100644 --- a/src/config/default.ts +++ b/src/config/default.ts @@ -16,6 +16,8 @@ export const defaultConfig: Config = { groups: [], }, templates: { + help: "templates/help.md", + help_admin: "templates/help_admin.md", message_new: "templates/message_new.md", wall_post_new: "templates/post_new.md", group_join: "templates/group_join.md", diff --git a/src/config/index.ts b/src/config/index.ts index b5902e4..0de8576 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -13,12 +13,18 @@ const data = fs.readFileSync( "utf8" ); -const userConfig = yaml.load(data) as Config; +const userConfig = yaml.load(data); const config = (userConfig && merge(defaultConfig, userConfig)) || defaultConfig; export default function prepareConfig() { validateConfig(config); + + config.telegram.templates = { + help: config.templates.help, + help_admin: config.templates.help_admin, + }; + return config; } diff --git a/src/config/types.ts b/src/config/types.ts index d64ade2..bb8ca73 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -4,7 +4,8 @@ import { HttpConfig } from "../api/http/types"; import { LoggerConfig } from "../service/logger/types"; import { PostgresConfig } from "../service/db/postgres/types"; -export type TemplateConfig = Record; +export type TemplateConfig = Record & + Partial>; export interface Config extends Record { http: HttpConfig; diff --git a/src/config/validate.ts b/src/config/validate.ts index 429ccc3..fd5cf7a 100644 --- a/src/config/validate.ts +++ b/src/config/validate.ts @@ -11,6 +11,8 @@ export const templateConfigSchema = object().required().shape({ wall_post_new: string().required(), group_join: string().required(), group_leave: string().required(), + help: string().optional(), + help_admin: string().optional(), }); export const templateOptionalSchema = object().shape({ diff --git a/src/service/telegram/index.ts b/src/service/telegram/index.ts index f38eb27..c83ba7c 100644 --- a/src/service/telegram/index.ts +++ b/src/service/telegram/index.ts @@ -5,6 +5,7 @@ import { Response } from "express"; import { InputMediaPhoto, Update } from "typegram"; import loggerTgMiddleware from "../logger/tg"; import { ExtraReplyMessage } from "telegraf/typings/telegram-types"; +import { Template } from "../template"; // import SocksProxyAgent from 'socks-proxy-agent'; @@ -14,6 +15,9 @@ export class TelegramService { public readonly bot: Telegraf; public readonly webhook: WebhookConfig = {}; + protected helpTemplate: Template<{}, {}> | undefined; + protected adminHelpTemplate: Template<{}, {}> | undefined; + constructor(private props: TelegramConfig) { // const agent = (CONFIG.PROXY && new SocksProxyAgent(CONFIG.PROXY)) || null; const options: Partial> = { @@ -29,6 +33,14 @@ export class TelegramService { this.bot = new Telegraf(props.key, options); this.bot.use(loggerTgMiddleware); + this.helpTemplate = props.templates?.help + ? new Template(props.templates?.help) + : undefined; + + this.adminHelpTemplate = props.templates?.help_admin + ? new Template(props.templates?.help_admin) + : undefined; + process.once("SIGINT", () => this.stop("SIGINT")); process.once("SIGTERM", () => this.stop("SIGTERM")); } @@ -40,6 +52,18 @@ export class TelegramService { * Connects to telegram */ public async start() { + if (!this.helpTemplate) { + console.warn( + "No help template specified, check templates.help in config" + ); + } + + if (!this.adminHelpTemplate) { + console.warn( + "No admin help template specified, check templates.help_admin in config" + ); + } + if (this.isWebhookEnabled) { await this.bot.telegram .deleteWebhook() @@ -196,4 +220,16 @@ export class TelegramService { ); } }); + + public getHelpMessage = (forOwner?: boolean) => { + const template = forOwner + ? this.adminHelpTemplate ?? this.helpTemplate + : this.helpTemplate; + + if (!template) { + return; + } + + return template?.theme({}); + }; } diff --git a/src/service/telegram/types.ts b/src/service/telegram/types.ts index fc22c1e..ab60f48 100644 --- a/src/service/telegram/types.ts +++ b/src/service/telegram/types.ts @@ -7,4 +7,8 @@ export interface TelegramConfig { key: string; owners?: string[]; webhook: WebhookConfig; + templates?: { + help?: string; + help_admin?: string; + }; } diff --git a/src/service/template/index.ts b/src/service/template/index.ts index c1e5e46..4771bf8 100644 --- a/src/service/template/index.ts +++ b/src/service/template/index.ts @@ -16,10 +16,10 @@ const removeFrontmatter = () => (tree) => { }; export class Template< - F extends Record, - V extends Record + Fields extends Record, + Values extends Record > { - public fields: F = {} as F; + public fields: Fields = {} as Fields; public template: string = ""; private readonly file: VFileCompatible = ""; @@ -44,7 +44,7 @@ export class Template< this.file = toVFile.readSync(path.join(__dirname, dir, filename)); const result = processor.processSync(this.file); - this.fields = result.data as F; + this.fields = result.data as Fields; } catch (e) { throw new Error(`Template: ${e?.toString()}`); } @@ -54,7 +54,7 @@ export class Template< * Themes the template with values, removes markdown from template. * NOTE: text, that we'll insert into template, won't be used here */ - public theme = (values: V, markdown?: boolean) => { + public theme = (values: Values, markdown?: boolean) => { const processor = unified() .use(stringify) .use(frontmatter) diff --git a/templates/help.md b/templates/help.md new file mode 100644 index 0000000..b109d6a --- /dev/null +++ b/templates/help.md @@ -0,0 +1,4 @@ +Привет, вот, что я умею: +/roll - вернёт случайный маршрут +/roll 100 - вернёт маршрут около 100км +/roll 50-100 - вернёт маршрут от 50 до 100км diff --git a/templates/help_admin.md b/templates/help_admin.md new file mode 100644 index 0000000..67ce74f --- /dev/null +++ b/templates/help_admin.md @@ -0,0 +1,7 @@ +Привет, вот, что я умею: +/roll - вернёт случайный маршрут +/roll 100 - вернёт маршрут около 100км +/roll 50-100 - вернёт маршрут от 50 до 100км +/wtf - вышлет последние логи +/pop - вернёт последнее сообщение, принятое из ВК +/config - вернёт дамп конфига