From ef4794a33f38d66fdb48f466f3cbfaf114dd42b2 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Thu, 27 May 2021 12:27:13 +0700 Subject: [PATCH] added plaintext fallback for markdown --- src/service/telegram/index.ts | 8 +- src/service/template/index.ts | 44 ++++--- src/service/vk/handlers/JoinLeaveHandler.ts | 25 ++-- src/service/vk/handlers/MessageNewHandler.ts | 21 ++-- src/service/vk/handlers/PostNewHandler.ts | 123 ++++++++++++++----- src/service/vk/types.ts | 1 + src/service/vk/validation.ts | 1 + templates/post_new.md | 6 +- 8 files changed, 161 insertions(+), 68 deletions(-) diff --git a/src/service/telegram/index.ts b/src/service/telegram/index.ts index 24af558..c951fff 100644 --- a/src/service/telegram/index.ts +++ b/src/service/telegram/index.ts @@ -73,10 +73,14 @@ export class TelegramService { public sendMessageToChan = async ( channel: string, message: string, + markdown?: boolean, extra?: ExtraReplyMessage ) => { logger.debug(`sending message "${message}" to chan "${channel}"`); - return await this.bot.telegram.sendMessage(channel, message, extra); + return await this.bot.telegram.sendMessage(channel, message, { + ...extra, + ...(markdown ? { parse_mode: "Markdown" } : {}), + }); }; /** @@ -86,12 +90,14 @@ export class TelegramService { channel: string, caption: string, src: string, + markdown?: boolean, extra?: ExtraReplyMessage ) => { logger.debug(`sending photo message "${caption}" to chan "${channel}"`); return await this.bot.telegram.sendPhoto(channel, src, { ...extra, caption, + ...(markdown ? { parse_mode: "Markdown" } : {}), }); }; diff --git a/src/service/template/index.ts b/src/service/template/index.ts index 3aaeed5..79a1580 100644 --- a/src/service/template/index.ts +++ b/src/service/template/index.ts @@ -8,6 +8,7 @@ import toVFile from "to-vfile"; import path from "path"; import hb from "handlebars"; import strip from "strip-markdown"; +import { VFileCompatible } from "vfile"; const removeFrontmatter = () => (tree) => { tree.children = tree.children.filter((item) => item.type !== "yaml"); @@ -20,25 +21,26 @@ export class Template< public fields: F = {} as F; public template: string = ""; + private readonly file: VFileCompatible = ""; + constructor(filename: string) { try { if (!filename) { return; } + // read file and fields from it to this.fields + const processor = unified() .use(stringify) .use(frontmatter) .use(extract, { yaml: parse }) .use(removeFrontmatter) - .use(parser) - .use(strip); - - const file = toVFile.readSync(path.join(__dirname, "../../", filename)); - const result = processor.processSync(file); + .use(parser); + this.file = toVFile.readSync(path.join(__dirname, "../../", filename)); + const result = processor.processSync(this.file); this.fields = result.data as F; - this.template = result.contents.toString().trim(); } catch (e) { throw new Error(`Template: ${e.toString()}`); } @@ -47,8 +49,23 @@ export class Template< /** * Themes the template with values */ - public theme = (values: V) => { - return hb.compile(this.template)(values).replace(/\n+/g, "\n\n"); + public theme = (values: V, markdown?: boolean) => { + const processor = unified() + .use(stringify) + .use(frontmatter) + .use(removeFrontmatter) + .use(parser); + + if (!markdown) { + processor.use(strip); + } + + const result = processor.processSync(this.file); + const template = result.contents.toString().trim(); + + return hb + .compile(template)(values) + .replace(/\n{2,}/g, "\n\n"); }; /** @@ -62,15 +79,12 @@ export class Template< } public static cleanText(text: string) { - const processor = unified() + return unified() .use(stringify) - .use(frontmatter) - .use(extract, { yaml: parse }) - .use(removeFrontmatter) .use(parser) - .use(strip); - - return processor.processSync(text).contents.toString(); + .use(strip) + .processSync(text) + .contents.toString(); } } diff --git a/src/service/vk/handlers/JoinLeaveHandler.ts b/src/service/vk/handlers/JoinLeaveHandler.ts index b5963fe..1bee76c 100644 --- a/src/service/vk/handlers/JoinLeaveHandler.ts +++ b/src/service/vk/handlers/JoinLeaveHandler.ts @@ -29,20 +29,27 @@ export class JoinLeaveHandler extends VkEventHandler { `vk, group ${this.group.name}: ${user.first_name} ${user.last_name} ${dir} the group` ); - const parsed = this.template.theme({ - user, - group: this.group, - isJoined: context.isJoin, - isLeave: context.isLeave, - count, - }); + const parsed = this.template.theme( + { + user, + group: this.group, + isJoined: context.isJoin, + isLeave: context.isLeave, + count, + }, + !!this.channel.markdown + ); const extras: ExtraReplyMessage = { - parse_mode: "Markdown", disable_web_page_preview: true, }; - await this.telegram.sendMessageToChan(this.channel.id, parsed, extras); + await this.telegram.sendMessageToChan( + this.channel.id, + parsed, + !!this.channel.markdown, + extras + ); await next(); }; diff --git a/src/service/vk/handlers/MessageNewHandler.ts b/src/service/vk/handlers/MessageNewHandler.ts index 7887f69..69eb7bd 100644 --- a/src/service/vk/handlers/MessageNewHandler.ts +++ b/src/service/vk/handlers/MessageNewHandler.ts @@ -34,20 +34,27 @@ export class MessageNewHandler extends VkEventHandler { `vk, group ${this.group.name} received message from ${user.first_name} ${user.last_name}: "${context.text}"` ); - const parsed = this.template.theme({ - user, - group: this.group, - text: context?.text || "", - }); + const parsed = this.template.theme( + { + user, + group: this.group, + text: context?.text || "", + }, + !!this.channel.markdown + ); const extras: ExtraReplyMessage = { - parse_mode: "Markdown", disable_web_page_preview: true, }; this.appendButtons(extras, user.id); - await this.telegram.sendMessageToChan(this.channel.id, parsed, extras); + await this.telegram.sendMessageToChan( + this.channel.id, + parsed, + !!this.channel.markdown, + extras + ); await next(); }; diff --git a/src/service/vk/handlers/PostNewHandler.ts b/src/service/vk/handlers/PostNewHandler.ts index d55d1c7..66ab5e6 100644 --- a/src/service/vk/handlers/PostNewHandler.ts +++ b/src/service/vk/handlers/PostNewHandler.ts @@ -100,33 +100,20 @@ export class PostNewHandler extends VkEventHandler { reply_markup: await this.createKeyboard(text, undefined, context.wall.id), }; - let msg: Message | PhotoMessage; - const images = context.wall.getAttachments("photo"); const thumbs = images .map(getAttachment) .filter((el) => el) .slice(0, this.template.fields.images_limit) as string[]; - const hasThumb = - !!this.template.fields.image && - !!this.template.fields.images_limit && - thumbs.length > 0; - - if (hasThumb) { - msg = await this.telegram.sendPhotoToChan( - this.channel.id, - this.trimTextForPhoto(text, PHOTO_CAPTION_LIMIT, postType, user), - thumbs[0]!, - extras - ); - } else { - msg = await this.telegram.sendMessageToChan( - this.channel.id, - this.trimTextForPhoto(text, POST_TEXT_LIMIT, postType, user), - extras - ); - } + const msg = await this.sendMessage( + text, + thumbs, + extras, + postType, + user, + this.channel.markdown + ); const event = await this.createEvent( id, @@ -380,14 +367,18 @@ export class PostNewHandler extends VkEventHandler { private themeText = ( text: string, type?: string, - user?: UsersUserFull + user?: UsersUserFull, + markdown?: boolean ): string => { - return this.template.theme({ - user, - group: this.group, - type, - text: Template.cleanText(text), - }); + return this.template.theme( + { + user, + group: this.group, + type, + text: Template.cleanText(text), + }, + markdown + ); }; /** @@ -397,9 +388,10 @@ export class PostNewHandler extends VkEventHandler { text: string, maxChars: number, type?: string, - user?: UsersUserFull + user?: UsersUserFull, + markdown?: boolean ): string => { - const withText = this.themeText(text, type, user); + const withText = this.themeText(text, type, user, markdown); const limit = this.template.fields.char_limit ? Math.min(this.template.fields.char_limit, maxChars) : maxChars; @@ -408,13 +400,11 @@ export class PostNewHandler extends VkEventHandler { return withText; } - const withoutText = this.themeText("", type, user); + const withoutText = this.themeText("", type, user, markdown); const suffix = "..."; const trimmed = text.slice(0, limit - withoutText.length - suffix.length); - const txt = this.themeText(`${trimmed}${suffix}`, type, user); - - return txt; + return this.themeText(`${trimmed}${suffix}`, type, user, markdown); }; /** @@ -431,4 +421,69 @@ export class PostNewHandler extends VkEventHandler { .filter((el) => el) .join(" ") .trim() || "someone"; + + /** + * Sends message + */ + sendMessage = async ( + text: string, + thumbs: string[], + extras?: ExtraReplyMessage, + postType?: string, + user?: UsersUserFull, + markdown?: boolean + ) => { + try { + const hasThumb = + !!this.template.fields.image && + !!this.template.fields.images_limit && + thumbs.length > 0; + + if (hasThumb) { + const trimmed = this.trimTextForPhoto( + text, + PHOTO_CAPTION_LIMIT, + postType, + user, + markdown + ); + + return await this.telegram.sendPhotoToChan( + this.channel.id, + trimmed, + thumbs[0]!, + markdown, + extras + ); + } else { + const trimmed = this.trimTextForPhoto( + text, + POST_TEXT_LIMIT, + postType, + user, + markdown + ); + + return await this.telegram.sendMessageToChan( + this.channel.id, + trimmed, + markdown, + extras + ); + } + } catch (e) { + if (!markdown) { + throw e; + } + + // Try to send as plain text + + logger.warn( + `telegram: failed to send markdown, falling back to plaintext: ${e}`, + e + ); + + return this.sendMessage(text, thumbs, extras, postType, user, false); + } + }; } diff --git a/src/service/vk/types.ts b/src/service/vk/types.ts index ab4e6e8..c73bc43 100644 --- a/src/service/vk/types.ts +++ b/src/service/vk/types.ts @@ -22,6 +22,7 @@ export interface GroupChannel { events: VkEvent[]; post_types: WallPostType[]; templates: Partial; + markdown?: boolean; } export enum VkEvent { diff --git a/src/service/vk/validation.ts b/src/service/vk/validation.ts index 4c10e0c..f277b73 100644 --- a/src/service/vk/validation.ts +++ b/src/service/vk/validation.ts @@ -14,6 +14,7 @@ const vkChannelSchema = yup .matches(/^@/, ({ path }) => `${path} should start with "@"`), events: yup.array().of(vkChannelEventSchema), templates: templateOptionalSchema, + markdown: yup.boolean().optional(), }); export const vkConfigSchema = yup diff --git a/templates/post_new.md b/templates/post_new.md index 4a94e46..419f296 100644 --- a/templates/post_new.md +++ b/templates/post_new.md @@ -19,5 +19,7 @@ --}} {{#ifEq type 'suggest'}}Предложка:{{/ifEq}} -{{text}} -{{#if user}}[{{user.first_name}} {{user.last_name}}](https://vk.com/id{{user.id}}){{/if}} + +{{{text}}} +{{#if user}} +— [{{user.first_name}} {{user.last_name}}](https://vk.com/id{{user.id}}){{/if}}