mirror of
https://github.com/muerwre/vk-tg-bot.git
synced 2025-04-24 22:46:41 +07:00
168 lines
4.2 KiB
TypeScript
168 lines
4.2 KiB
TypeScript
import { ConfigGroup, GroupInstance, VkConfig, VkEvent } from "./types";
|
|
import { API, Updates, Upload } from "vk-io";
|
|
import logger from "../logger";
|
|
import { Request, Response } from "express";
|
|
import { flatten, has, keys, prop } from "ramda";
|
|
import { NextFunction } from "connect";
|
|
import { VkEventHandler } from "./handlers/VkEventHandler";
|
|
import { vkEventToHandler } from "./handlers";
|
|
import { TelegramService } from "../telegram";
|
|
import { Template } from "../template";
|
|
import { TemplateConfig } from "../../config/types";
|
|
import { Storage } from "../db";
|
|
|
|
/**
|
|
* Service to handle VK to Telegram interactions
|
|
*/
|
|
export class VkService {
|
|
public endpoint: string = "/";
|
|
private readonly instances: Record<string, GroupInstance>;
|
|
private readonly groups: Record<number, ConfigGroup>;
|
|
|
|
// Used to register all incoming events
|
|
private incomingEvents: string[] = [];
|
|
|
|
constructor(
|
|
private config: VkConfig,
|
|
private telegram: TelegramService,
|
|
private templates: TemplateConfig,
|
|
private db: Storage
|
|
) {
|
|
if (!config.groups.length) {
|
|
throw new Error("No vk groups to handle. Specify them in config");
|
|
}
|
|
|
|
this.endpoint = config.endpoint || "/";
|
|
|
|
this.groups = config.groups.reduce(
|
|
(acc, group) => ({
|
|
...acc,
|
|
[group.id]: group,
|
|
}),
|
|
{}
|
|
);
|
|
|
|
this.instances = config.groups.reduce(
|
|
(acc, group) => ({
|
|
...acc,
|
|
[group.id]: this.createGroupInstance(group),
|
|
}),
|
|
{}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handles incoming VK events
|
|
*/
|
|
public handle = async (req: Request, res: Response, next: NextFunction) => {
|
|
try {
|
|
const { body } = req;
|
|
const { groups } = this;
|
|
const groupId = body?.group_id;
|
|
const group = this.groups[groupId];
|
|
const eventId = body?.event_id;
|
|
const type = body?.type;
|
|
|
|
if (!groupId || !has(groupId, groups) || !has(groupId, this.instances)) {
|
|
logger.warn(`vk received unknown call`, { body });
|
|
res.sendStatus(200);
|
|
return;
|
|
}
|
|
|
|
if (eventId) {
|
|
if (this.incomingEvents.includes(eventId)) {
|
|
logger.warn(
|
|
`received duplicate event "${type} (${eventId})" for group "${group.name} (skipping)"`
|
|
);
|
|
return res.send("OK");
|
|
}
|
|
|
|
this.incomingEvents.push(eventId);
|
|
}
|
|
|
|
logger.debug(`received vk event`, { body });
|
|
|
|
const inst = this.instances[groupId] as GroupInstance;
|
|
|
|
await inst.updates.getWebhookCallback(this.config.endpoint)(
|
|
req,
|
|
res,
|
|
next
|
|
);
|
|
} catch (e) {
|
|
next(e);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates vk bot instance for each group with api, uploader and updates handler
|
|
*/
|
|
private createGroupInstance = (group: ConfigGroup): GroupInstance => {
|
|
const api = new API({
|
|
token: group.apiKey,
|
|
});
|
|
const upload = new Upload({ api });
|
|
const updates = new Updates({
|
|
api,
|
|
upload,
|
|
webhookConfirmation: group.testResponse,
|
|
webhookSecret: group.secretKey,
|
|
});
|
|
|
|
const instance = {
|
|
api,
|
|
upload,
|
|
updates,
|
|
};
|
|
|
|
const handlers = this.setupHandlers(group, instance);
|
|
|
|
handlers.forEach((channel) => {
|
|
keys(channel).forEach((event) => {
|
|
logger.info(` - ${group.name} listens for ${String(event)}`);
|
|
updates.on(event as any, channel[event].execute);
|
|
});
|
|
});
|
|
|
|
return instance;
|
|
};
|
|
|
|
/**
|
|
* Setups handlers
|
|
*/
|
|
private setupHandlers(
|
|
group: ConfigGroup,
|
|
instance: GroupInstance
|
|
): Record<VkEvent, VkEventHandler>[] {
|
|
return flatten(
|
|
group.channels.map((chan) =>
|
|
chan.events.reduce((acc, event) => {
|
|
const template = new Template(
|
|
prop(event, chan?.templates) ||
|
|
prop(event, group?.templates) ||
|
|
prop(event, this.templates)
|
|
);
|
|
|
|
const handler = new vkEventToHandler[event](
|
|
event,
|
|
group,
|
|
chan,
|
|
instance,
|
|
this,
|
|
this.telegram,
|
|
template,
|
|
this.db
|
|
);
|
|
return { ...acc, [event]: handler };
|
|
}, {} as Record<VkEvent, VkEventHandler>[])
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Performs healthcheck for telegram
|
|
*/
|
|
public healthcheck = async () => {
|
|
await this.db.healthcheck();
|
|
};
|
|
}
|