From c709bbf94cda3d22e21bbdb53f49c5a3a2cbc9c8 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 28 May 2021 15:04:39 +0700 Subject: [PATCH] added pop action for telegram --- config.example.yml | 1 + src/api/telegram/index.ts | 29 ++++++++++++++++++++- src/index.ts | 2 +- src/service/db/postgres/entities/Request.ts | 19 ++++++++++++++ src/service/db/postgres/index.ts | 19 ++++++++++++++ src/service/telegram/index.ts | 9 +++++++ src/service/telegram/types.ts | 1 + src/service/telegram/validation.ts | 12 ++++++--- src/service/vk/index.ts | 6 +++-- 9 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 src/service/db/postgres/entities/Request.ts diff --git a/config.example.yml b/config.example.yml index ecfc5bc..acfa9d8 100644 --- a/config.example.yml +++ b/config.example.yml @@ -2,6 +2,7 @@ http: port: 80 telegram: # Get it from bot father + owners: [] key: '' webhook: url: https://something.org/webhook diff --git a/src/api/telegram/index.ts b/src/api/telegram/index.ts index c79bba7..c09257f 100644 --- a/src/api/telegram/index.ts +++ b/src/api/telegram/index.ts @@ -1,12 +1,15 @@ import { TelegramService } from "../../service/telegram"; import axios from "axios"; import logger from "../../service/logger"; +import { PostgresDB } from "../../service/db/postgres"; +import { Readable } from "stream"; export class TelegramApi { - constructor(private telegram: TelegramService) {} + constructor(private telegram: TelegramService, private db: PostgresDB) {} public listen() { this.telegram.bot.command("ping", TelegramApi.ping); + this.telegram.bot.command("pop", this.pop); return; } @@ -19,6 +22,30 @@ export class TelegramApi { ); } + /** + * Pops last recorded request from vk + */ + private pop = async (ctx, next) => { + const username = ctx?.update?.message?.from?.username; + + if (!username || !this.telegram.isOwner(`@${username}`)) { + return; + } + + const { body, createdAt } = await this.db.popRequest(); + const source = JSON.stringify(body, null, 2); + + await ctx.replyWithDocument( + { + source: Readable.from(source), + filename: `debug-${createdAt.toISOString()}.txt`, + }, + { caption: `recorded at: ${createdAt.toLocaleString()}` } + ); + + return next(); + }; + /** * Probes webhook url and falls back to polling mode on error */ diff --git a/src/index.ts b/src/index.ts index 1186f69..aabf478 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,7 +16,7 @@ async function main() { const telegram = new TelegramService(config.telegram); const vkService = new VkService(config.vk, telegram, config.templates, db); - const telegramApi = new TelegramApi(telegram); + const telegramApi = new TelegramApi(telegram, db); telegramApi.listen(); await telegram.start(); diff --git a/src/service/db/postgres/entities/Request.ts b/src/service/db/postgres/entities/Request.ts new file mode 100644 index 0000000..cc5e4ae --- /dev/null +++ b/src/service/db/postgres/entities/Request.ts @@ -0,0 +1,19 @@ +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity() +export class Request { + @PrimaryGeneratedColumn() + id!: number; + @Column("simple-json", { default: {}, nullable: false }) + body!: Record; + @CreateDateColumn() + createdAt!: Date; + @UpdateDateColumn() + updatedAt!: Date; +} diff --git a/src/service/db/postgres/index.ts b/src/service/db/postgres/index.ts index 303835b..23181d6 100644 --- a/src/service/db/postgres/index.ts +++ b/src/service/db/postgres/index.ts @@ -8,6 +8,7 @@ import { Like } from "./entities/Like"; import { Event } from "./entities/Event"; import { Post } from "./entities/Post"; import { LoggerConfig } from "../../logger/types"; +import { Request } from "./entities/Request"; const entities = [path.join(__dirname, "./entities/*")]; @@ -16,6 +17,7 @@ export class PostgresDB implements Storage { private events!: Repository; private likes!: Repository; private posts!: Repository; + private requests!: Repository; constructor( private config: PostgresConfig, @@ -38,6 +40,7 @@ export class PostgresDB implements Storage { this.events = this.connection.getRepository(Event); this.likes = this.connection.getRepository(Like); this.posts = this.connection.getRepository(Post); + this.requests = this.connection.getRepository(Request); logger.info(`db connected to ${this.config.uri}`); }; @@ -132,6 +135,22 @@ export class PostgresDB implements Storage { return this.posts.save({ eventId, text, vkPostId }); }; + insertRequest = async (body: Record) => { + return this.requests.save({ body }); + }; + + /** + * Returns last request with shift + */ + popRequest = async (skip: number = 0) => { + const requests = await this.requests.find({ + order: { createdAt: "DESC" }, + take: 1, + skip, + }); + return requests[0]; + }; + healthcheck = async () => { try { await this.connection.query("SELECT 1"); diff --git a/src/service/telegram/index.ts b/src/service/telegram/index.ts index c951fff..2f6e0e2 100644 --- a/src/service/telegram/index.ts +++ b/src/service/telegram/index.ts @@ -149,4 +149,13 @@ export class TelegramService { throw e; } }; + + /** + * Checks if user is owner + */ + public isOwner = (username: string) => { + return ( + !!username && !!this.props.owners && this.props.owners.includes(username) + ); + }; } diff --git a/src/service/telegram/types.ts b/src/service/telegram/types.ts index adb7bf9..fc22c1e 100644 --- a/src/service/telegram/types.ts +++ b/src/service/telegram/types.ts @@ -5,5 +5,6 @@ export interface WebhookConfig { export interface TelegramConfig { key: string; + owners?: string[]; webhook: WebhookConfig; } diff --git a/src/service/telegram/validation.ts b/src/service/telegram/validation.ts index 2f1f9d6..f4b5469 100644 --- a/src/service/telegram/validation.ts +++ b/src/service/telegram/validation.ts @@ -6,7 +6,11 @@ const webhookValidationSchema = object().optional().shape({ enabled: boolean(), }); -export const telegramConfigSchema = yup.object().required().shape({ - key: yup.string().required(), - webhook: webhookValidationSchema, -}); +export const telegramConfigSchema = yup + .object() + .required() + .shape({ + key: yup.string().required(), + owners: yup.array().of(yup.string().required()), + webhook: webhookValidationSchema, + }); diff --git a/src/service/vk/index.ts b/src/service/vk/index.ts index 5d86e51..fa7ea74 100644 --- a/src/service/vk/index.ts +++ b/src/service/vk/index.ts @@ -9,7 +9,7 @@ import { vkEventToHandler } from "./handlers"; import { TelegramService } from "../telegram"; import { Template } from "../template"; import { TemplateConfig } from "../../config/types"; -import { Storage } from "../db"; +import { PostgresDB } from "../db/postgres"; /** * Service to handle VK to Telegram interactions @@ -26,7 +26,7 @@ export class VkService { private config: VkConfig, private telegram: TelegramService, private templates: TemplateConfig, - private db: Storage + private db: PostgresDB ) { if (!config.groups.length) { throw new Error("No vk groups to handle. Specify them in config"); @@ -63,6 +63,8 @@ export class VkService { const eventId = body?.event_id; const type = body?.type; + await this.db.insertRequest(body); + if (!groupId || !has(groupId, groups) || !has(groupId, this.instances)) { logger.warn(`vk received unknown call`, { body }); res.sendStatus(200);