mirror of
https://github.com/muerwre/vk-tg-bot.git
synced 2025-04-24 22:46:41 +07:00
added plaintext fallback for markdown
This commit is contained in:
parent
1b0a1f20d5
commit
ef4794a33f
8 changed files with 161 additions and 68 deletions
|
@ -73,10 +73,14 @@ export class TelegramService {
|
||||||
public sendMessageToChan = async (
|
public sendMessageToChan = async (
|
||||||
channel: string,
|
channel: string,
|
||||||
message: string,
|
message: string,
|
||||||
|
markdown?: boolean,
|
||||||
extra?: ExtraReplyMessage
|
extra?: ExtraReplyMessage
|
||||||
) => {
|
) => {
|
||||||
logger.debug(`sending message "${message}" to chan "${channel}"`);
|
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,
|
channel: string,
|
||||||
caption: string,
|
caption: string,
|
||||||
src: string,
|
src: string,
|
||||||
|
markdown?: boolean,
|
||||||
extra?: ExtraReplyMessage
|
extra?: ExtraReplyMessage
|
||||||
) => {
|
) => {
|
||||||
logger.debug(`sending photo message "${caption}" to chan "${channel}"`);
|
logger.debug(`sending photo message "${caption}" to chan "${channel}"`);
|
||||||
return await this.bot.telegram.sendPhoto(channel, src, {
|
return await this.bot.telegram.sendPhoto(channel, src, {
|
||||||
...extra,
|
...extra,
|
||||||
caption,
|
caption,
|
||||||
|
...(markdown ? { parse_mode: "Markdown" } : {}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import toVFile from "to-vfile";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import hb from "handlebars";
|
import hb from "handlebars";
|
||||||
import strip from "strip-markdown";
|
import strip from "strip-markdown";
|
||||||
|
import { VFileCompatible } from "vfile";
|
||||||
|
|
||||||
const removeFrontmatter = () => (tree) => {
|
const removeFrontmatter = () => (tree) => {
|
||||||
tree.children = tree.children.filter((item) => item.type !== "yaml");
|
tree.children = tree.children.filter((item) => item.type !== "yaml");
|
||||||
|
@ -20,25 +21,26 @@ export class Template<
|
||||||
public fields: F = {} as F;
|
public fields: F = {} as F;
|
||||||
public template: string = "";
|
public template: string = "";
|
||||||
|
|
||||||
|
private readonly file: VFileCompatible = "";
|
||||||
|
|
||||||
constructor(filename: string) {
|
constructor(filename: string) {
|
||||||
try {
|
try {
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read file and fields from it to this.fields
|
||||||
|
|
||||||
const processor = unified()
|
const processor = unified()
|
||||||
.use(stringify)
|
.use(stringify)
|
||||||
.use(frontmatter)
|
.use(frontmatter)
|
||||||
.use(extract, { yaml: parse })
|
.use(extract, { yaml: parse })
|
||||||
.use(removeFrontmatter)
|
.use(removeFrontmatter)
|
||||||
.use(parser)
|
.use(parser);
|
||||||
.use(strip);
|
|
||||||
|
|
||||||
const file = toVFile.readSync(path.join(__dirname, "../../", filename));
|
|
||||||
const result = processor.processSync(file);
|
|
||||||
|
|
||||||
|
this.file = toVFile.readSync(path.join(__dirname, "../../", filename));
|
||||||
|
const result = processor.processSync(this.file);
|
||||||
this.fields = result.data as F;
|
this.fields = result.data as F;
|
||||||
this.template = result.contents.toString().trim();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Template: ${e.toString()}`);
|
throw new Error(`Template: ${e.toString()}`);
|
||||||
}
|
}
|
||||||
|
@ -47,8 +49,23 @@ export class Template<
|
||||||
/**
|
/**
|
||||||
* Themes the template with values
|
* Themes the template with values
|
||||||
*/
|
*/
|
||||||
public theme = (values: V) => {
|
public theme = (values: V, markdown?: boolean) => {
|
||||||
return hb.compile(this.template)(values).replace(/\n+/g, "\n\n");
|
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) {
|
public static cleanText(text: string) {
|
||||||
const processor = unified()
|
return unified()
|
||||||
.use(stringify)
|
.use(stringify)
|
||||||
.use(frontmatter)
|
|
||||||
.use(extract, { yaml: parse })
|
|
||||||
.use(removeFrontmatter)
|
|
||||||
.use(parser)
|
.use(parser)
|
||||||
.use(strip);
|
.use(strip)
|
||||||
|
.processSync(text)
|
||||||
return processor.processSync(text).contents.toString();
|
.contents.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,20 +29,27 @@ export class JoinLeaveHandler extends VkEventHandler<Fields, Values> {
|
||||||
`vk, group ${this.group.name}: ${user.first_name} ${user.last_name} ${dir} the group`
|
`vk, group ${this.group.name}: ${user.first_name} ${user.last_name} ${dir} the group`
|
||||||
);
|
);
|
||||||
|
|
||||||
const parsed = this.template.theme({
|
const parsed = this.template.theme(
|
||||||
|
{
|
||||||
user,
|
user,
|
||||||
group: this.group,
|
group: this.group,
|
||||||
isJoined: context.isJoin,
|
isJoined: context.isJoin,
|
||||||
isLeave: context.isLeave,
|
isLeave: context.isLeave,
|
||||||
count,
|
count,
|
||||||
});
|
},
|
||||||
|
!!this.channel.markdown
|
||||||
|
);
|
||||||
|
|
||||||
const extras: ExtraReplyMessage = {
|
const extras: ExtraReplyMessage = {
|
||||||
parse_mode: "Markdown",
|
|
||||||
disable_web_page_preview: true,
|
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();
|
await next();
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,20 +34,27 @@ export class MessageNewHandler extends VkEventHandler<Fields, Values> {
|
||||||
`vk, group ${this.group.name} received message from ${user.first_name} ${user.last_name}: "${context.text}"`
|
`vk, group ${this.group.name} received message from ${user.first_name} ${user.last_name}: "${context.text}"`
|
||||||
);
|
);
|
||||||
|
|
||||||
const parsed = this.template.theme({
|
const parsed = this.template.theme(
|
||||||
|
{
|
||||||
user,
|
user,
|
||||||
group: this.group,
|
group: this.group,
|
||||||
text: context?.text || "",
|
text: context?.text || "",
|
||||||
});
|
},
|
||||||
|
!!this.channel.markdown
|
||||||
|
);
|
||||||
|
|
||||||
const extras: ExtraReplyMessage = {
|
const extras: ExtraReplyMessage = {
|
||||||
parse_mode: "Markdown",
|
|
||||||
disable_web_page_preview: true,
|
disable_web_page_preview: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.appendButtons(extras, user.id);
|
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();
|
await next();
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,33 +100,20 @@ export class PostNewHandler extends VkEventHandler<Fields, Values> {
|
||||||
reply_markup: await this.createKeyboard(text, undefined, context.wall.id),
|
reply_markup: await this.createKeyboard(text, undefined, context.wall.id),
|
||||||
};
|
};
|
||||||
|
|
||||||
let msg: Message | PhotoMessage;
|
|
||||||
|
|
||||||
const images = context.wall.getAttachments("photo");
|
const images = context.wall.getAttachments("photo");
|
||||||
const thumbs = images
|
const thumbs = images
|
||||||
.map(getAttachment)
|
.map(getAttachment)
|
||||||
.filter((el) => el)
|
.filter((el) => el)
|
||||||
.slice(0, this.template.fields.images_limit) as string[];
|
.slice(0, this.template.fields.images_limit) as string[];
|
||||||
|
|
||||||
const hasThumb =
|
const msg = await this.sendMessage(
|
||||||
!!this.template.fields.image &&
|
text,
|
||||||
!!this.template.fields.images_limit &&
|
thumbs,
|
||||||
thumbs.length > 0;
|
extras,
|
||||||
|
postType,
|
||||||
if (hasThumb) {
|
user,
|
||||||
msg = await this.telegram.sendPhotoToChan(
|
this.channel.markdown
|
||||||
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 event = await this.createEvent(
|
const event = await this.createEvent(
|
||||||
id,
|
id,
|
||||||
|
@ -380,14 +367,18 @@ export class PostNewHandler extends VkEventHandler<Fields, Values> {
|
||||||
private themeText = (
|
private themeText = (
|
||||||
text: string,
|
text: string,
|
||||||
type?: string,
|
type?: string,
|
||||||
user?: UsersUserFull
|
user?: UsersUserFull,
|
||||||
|
markdown?: boolean
|
||||||
): string => {
|
): string => {
|
||||||
return this.template.theme({
|
return this.template.theme(
|
||||||
|
{
|
||||||
user,
|
user,
|
||||||
group: this.group,
|
group: this.group,
|
||||||
type,
|
type,
|
||||||
text: Template.cleanText(text),
|
text: Template.cleanText(text),
|
||||||
});
|
},
|
||||||
|
markdown
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,9 +388,10 @@ export class PostNewHandler extends VkEventHandler<Fields, Values> {
|
||||||
text: string,
|
text: string,
|
||||||
maxChars: number,
|
maxChars: number,
|
||||||
type?: string,
|
type?: string,
|
||||||
user?: UsersUserFull
|
user?: UsersUserFull,
|
||||||
|
markdown?: boolean
|
||||||
): string => {
|
): string => {
|
||||||
const withText = this.themeText(text, type, user);
|
const withText = this.themeText(text, type, user, markdown);
|
||||||
const limit = this.template.fields.char_limit
|
const limit = this.template.fields.char_limit
|
||||||
? Math.min(this.template.fields.char_limit, maxChars)
|
? Math.min(this.template.fields.char_limit, maxChars)
|
||||||
: maxChars;
|
: maxChars;
|
||||||
|
@ -408,13 +400,11 @@ export class PostNewHandler extends VkEventHandler<Fields, Values> {
|
||||||
return withText;
|
return withText;
|
||||||
}
|
}
|
||||||
|
|
||||||
const withoutText = this.themeText("", type, user);
|
const withoutText = this.themeText("", type, user, markdown);
|
||||||
const suffix = "...";
|
const suffix = "...";
|
||||||
const trimmed = text.slice(0, limit - withoutText.length - suffix.length);
|
const trimmed = text.slice(0, limit - withoutText.length - suffix.length);
|
||||||
|
|
||||||
const txt = this.themeText(`${trimmed}${suffix}`, type, user);
|
return this.themeText(`${trimmed}${suffix}`, type, user, markdown);
|
||||||
|
|
||||||
return txt;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -431,4 +421,69 @@ export class PostNewHandler extends VkEventHandler<Fields, Values> {
|
||||||
.filter((el) => el)
|
.filter((el) => el)
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim() || "someone";
|
.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);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ export interface GroupChannel {
|
||||||
events: VkEvent[];
|
events: VkEvent[];
|
||||||
post_types: WallPostType[];
|
post_types: WallPostType[];
|
||||||
templates: Partial<TemplateConfig>;
|
templates: Partial<TemplateConfig>;
|
||||||
|
markdown?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum VkEvent {
|
export enum VkEvent {
|
||||||
|
|
|
@ -14,6 +14,7 @@ const vkChannelSchema = yup
|
||||||
.matches(/^@/, ({ path }) => `${path} should start with "@"`),
|
.matches(/^@/, ({ path }) => `${path} should start with "@"`),
|
||||||
events: yup.array().of(vkChannelEventSchema),
|
events: yup.array().of(vkChannelEventSchema),
|
||||||
templates: templateOptionalSchema,
|
templates: templateOptionalSchema,
|
||||||
|
markdown: yup.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const vkConfigSchema = yup
|
export const vkConfigSchema = yup
|
||||||
|
|
|
@ -19,5 +19,7 @@
|
||||||
|
|
||||||
--}}
|
--}}
|
||||||
{{#ifEq type 'suggest'}}Предложка:{{/ifEq}}
|
{{#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}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue