1
0
Fork 0
mirror of https://github.com/muerwre/vk-tg-bot.git synced 2025-04-25 06:56:40 +07:00

added vk events handler stub

This commit is contained in:
Fedor Katurov 2021-04-28 09:55:02 +07:00
parent dfae42c197
commit 3f908da91e
11 changed files with 125 additions and 67 deletions

View file

@ -1,13 +1,17 @@
http: http:
port: 3002 port: 3002
telegram: telegram:
# Get it from bot father
key: '' key: ''
webhook: webhook:
url: https://something.org:65534/webhook url: https://something.org:3002/webhook
enabled: false enabled: false
logger: logger:
level: info level: info
#vk: vk:
# Default path for POST requests from VK api
endpoint: /
groups: []
# groups: # groups:
# - id: 0 # - id: 0
# name: 'Group name' # name: 'Group name'

View file

@ -6,19 +6,21 @@ import loggerHttpMiddleware from "../../service/logger/http";
import logger from "../../service/logger"; import logger from "../../service/logger";
import { TelegramService } from "../../service/telegram"; import { TelegramService } from "../../service/telegram";
import http from "http"; import http from "http";
import { WebhookConfig } from "../../config/types";
import { URL } from "url"; import { URL } from "url";
import { corsMiddleware, errorMiddleware } from "./middleware"; import { corsMiddleware, errorMiddleware } from "./middleware";
import { WebhookConfig } from "../../service/telegram/types";
export class HttpApi { export class HttpApi {
app: Express; app: Express;
webhook: WebhookConfig;
constructor( constructor(
private props: HttpConfig, private props: HttpConfig,
private telegram: TelegramService, private telegram: TelegramService,
private vk: VkService, private vk: VkService
private webhook?: WebhookConfig
) { ) {
this.webhook = this.telegram.webhook;
this.app = express(); this.app = express();
this.app.use(corsMiddleware); this.app.use(corsMiddleware);
this.app.use(express.json()); this.app.use(express.json());
@ -28,11 +30,7 @@ export class HttpApi {
this.app.use(bodyParser.json()); this.app.use(bodyParser.json());
this.app.use(express.json()); this.app.use(express.json());
if (this?.webhook?.enabled && this?.webhook?.url) { this.setupHandlers();
const url = new URL(this.webhook.url);
logger.info(`using webhook at ${url.pathname}`);
this.app.post(url.pathname, this.handleWebhook);
}
this.app.use(errorMiddleware); this.app.use(errorMiddleware);
} }
@ -46,6 +44,21 @@ export class HttpApi {
logger.info(`http api listening at ${this.props.port}`); logger.info(`http api listening at ${this.props.port}`);
} }
/**
* Adds webhandlers
*/
private setupHandlers() {
// Webhooks (if available)
if (this?.webhook?.enabled && this?.webhook?.url) {
const url = new URL(this.webhook.url);
logger.info(`using webhook at ${url.pathname}`);
this.app.post(url.pathname, this.handleWebhook);
this.app.get(url.pathname, this.testWebhook);
}
this.app.post(this.vk.endpoint, this.handleVkEvent);
}
/** /**
* Handles telegram webhooks * Handles telegram webhooks
*/ */
@ -53,4 +66,19 @@ export class HttpApi {
logger.debug("got message via webhook", req.body); logger.debug("got message via webhook", req.body);
await this.telegram.handleUpdate(req.body, res); await this.telegram.handleUpdate(req.body, res);
}; };
/**
* Just returns 200
*/
private testWebhook = async (req: Request, res: Response) => {
res.sendStatus(200);
};
/**
* Handles VK events
*/
private handleVkEvent = async (req: Request, res: Response) => {
await this.vk.handle(req.body);
res.sendStatus(200);
};
} }

View file

@ -3,14 +3,9 @@ import { VkConfig } from "../service/vk/types";
import { HttpConfig } from "../api/http/types"; import { HttpConfig } from "../api/http/types";
import { LoggerConfig } from "../service/logger/types"; import { LoggerConfig } from "../service/logger/types";
export interface WebhookConfig {
url?: string;
enabled?: boolean;
}
export interface Config extends Record<string, any> { export interface Config extends Record<string, any> {
http: HttpConfig; http: HttpConfig;
telegram: TelegramConfig; telegram: TelegramConfig;
vk: VkConfig; vk: VkConfig;
logger?: LoggerConfig; logger?: LoggerConfig;
webhook?: WebhookConfig;
} }

View file

@ -5,17 +5,11 @@ import { vkConfigSchema } from "../service/vk/validation";
import { telegramConfigSchema } from "../service/telegram/validation"; import { telegramConfigSchema } from "../service/telegram/validation";
import { loggerConfigSchema } from "../service/logger/config"; import { loggerConfigSchema } from "../service/logger/config";
const webhookValidationSchema = object().optional().shape({
url: string(),
enabled: boolean(),
});
const configSchema = object<Config>().required().shape({ const configSchema = object<Config>().required().shape({
http: httpConfigSchema, http: httpConfigSchema,
vk: vkConfigSchema, vk: vkConfigSchema,
telegram: telegramConfigSchema, telegram: telegramConfigSchema,
logger: loggerConfigSchema, logger: loggerConfigSchema,
webhook: webhookValidationSchema,
}); });
export const validateConfig = (config: Config) => export const validateConfig = (config: Config) =>

View file

@ -8,18 +8,13 @@ import { HttpApi } from "./api/http";
async function main() { async function main() {
try { try {
const config = prepareConfig(); const config = prepareConfig();
const telegram = new TelegramService(config.telegram, config.webhook); const telegram = new TelegramService(config.telegram);
const vkService = new VkService(config.vk); const vkService = new VkService(config.vk);
const telegramApi = new TelegramApi(telegram).listen(); const telegramApi = new TelegramApi(telegram).listen();
await telegram.start(); await telegram.start();
const httpApi = new HttpApi( const httpApi = new HttpApi(config.http, telegram, vkService).listen();
config.http,
telegram,
vkService,
config.webhook
).listen();
} catch (e) { } catch (e) {
logger.error(e.message); logger.error(e.message);
} }

View file

@ -1,17 +1,17 @@
import { TelegramConfig } from "./types"; import { TelegramConfig, WebhookConfig } from "./types";
import { Telegraf } from "telegraf"; import { Telegraf } from "telegraf";
import logger from "../logger"; import logger from "../logger";
import { Response } from "express"; import { Response } from "express";
import { Update } from "typegram"; import { Update } from "typegram";
import loggerTgMiddleware from "../logger/tg"; import loggerTgMiddleware from "../logger/tg";
import { WebhookConfig } from "../../config/types";
// import SocksProxyAgent from 'socks-proxy-agent'; // import SocksProxyAgent from 'socks-proxy-agent';
export class TelegramService { export class TelegramService {
public readonly bot: Telegraf; public readonly bot: Telegraf;
public readonly webhook: WebhookConfig = {};
constructor(private props: TelegramConfig, private webhook: WebhookConfig) { constructor(private props: TelegramConfig) {
// const agent = (CONFIG.PROXY && new SocksProxyAgent(CONFIG.PROXY)) || null; // const agent = (CONFIG.PROXY && new SocksProxyAgent(CONFIG.PROXY)) || null;
const options: Partial<Telegraf.Options<any>> = { const options: Partial<Telegraf.Options<any>> = {
telegram: { telegram: {
@ -21,6 +21,8 @@ export class TelegramService {
}, },
}; };
this.webhook = props.webhook;
this.bot = new Telegraf(props.key, options); this.bot = new Telegraf(props.key, options);
this.bot.use(loggerTgMiddleware); this.bot.use(loggerTgMiddleware);

View file

@ -1,3 +1,9 @@
export interface WebhookConfig {
url?: string;
enabled?: boolean;
}
export interface TelegramConfig { export interface TelegramConfig {
key: string; key: string;
webhook: WebhookConfig;
} }

View file

@ -1,5 +1,12 @@
import * as yup from "yup"; import * as yup from "yup";
import { boolean, object, string } from "yup";
const webhookValidationSchema = object().optional().shape({
url: string(),
enabled: boolean(),
});
export const telegramConfigSchema = yup.object().required().shape({ export const telegramConfigSchema = yup.object().required().shape({
key: yup.string().required(), key: yup.string().required(),
webhook: webhookValidationSchema,
}); });

View file

@ -1,9 +1,20 @@
import { VkConfig } from './types'; import { VkConfig } from "./types";
export class VkService { export class VkService {
public endpoint: string = "/";
constructor(private config: VkConfig) { constructor(private config: VkConfig) {
if (!config.groups.length) { if (!config.groups.length) {
throw new Error('No vk groups to handle. Specify them in config') throw new Error("No vk groups to handle. Specify them in config");
} }
this.endpoint = config.endpoint;
} }
/**
* Handles incoming VK events
*/
public handle = async (event: any) => {
// TODO: handle events
};
} }

View file

@ -1,26 +1,27 @@
export interface VkConfig extends Record<string, any> { export interface VkConfig extends Record<string, any> {
groups: ConfigGroup[] groups: ConfigGroup[];
endpoint?: string;
} }
interface ConfigGroup { interface ConfigGroup {
id: number id: number;
name: string name: string;
testResponse: string testResponse: string;
secretKey: string secretKey: string;
apiKey: string apiKey: string;
channels: GroupChannel[] channels: GroupChannel[];
} }
interface GroupChannel { interface GroupChannel {
id: string, id: string;
events: VkEvent[] events: VkEvent[];
} }
export enum VkEvent { export enum VkEvent {
Confirmation = 'confirmation', Confirmation = "confirmation",
WallPostNew = 'wall_post_new', WallPostNew = "wall_post_new",
PostSuggestion = 'post_suggestion', PostSuggestion = "post_suggestion",
GroupJoin = 'group_join', GroupJoin = "group_join",
GroupLeave = 'group_leave', GroupLeave = "group_leave",
MessageNew = 'message_new', MessageNew = "message_new",
} }

View file

@ -1,20 +1,35 @@
import * as yup from 'yup' import * as yup from "yup";
import { VkConfig, VkEvent } from './types'; import { VkConfig, VkEvent } from "./types";
const vkChannelEventSchema = yup.string().oneOf(Object.values(VkEvent)) const vkChannelEventSchema = yup.string().oneOf(Object.values(VkEvent));
const vkChannelSchema = yup.object().required().shape({ const vkChannelSchema = yup
id: yup.string().required().matches(/^@/, ({ path }) => `${path} should start with "@"`), .object()
events: yup.array().of(vkChannelEventSchema) .required()
}) .shape({
id: yup
.string()
.required()
.matches(/^@/, ({ path }) => `${path} should start with "@"`),
events: yup.array().of(vkChannelEventSchema),
});
export const vkConfigSchema = yup.object<VkConfig>().required().shape({ export const vkConfigSchema = yup
groups: yup.array().required().of(yup.object().shape({ .object<VkConfig>()
.required()
.shape({
endpoint: yup.string().optional(),
groups: yup
.array()
.required()
.of(
yup.object().shape({
id: yup.number().positive(), id: yup.number().positive(),
name: yup.string().required(), name: yup.string().required(),
testResponse: yup.string().required(), testResponse: yup.string().required(),
secretKey: yup.string().required(), secretKey: yup.string().required(),
apiKey: yup.string().required(), apiKey: yup.string().required(),
channels: yup.array().of(vkChannelSchema), channels: yup.array().of(vkChannelSchema),
}))
}) })
),
});