This commit is contained in:
2023-07-16 05:21:26 +03:00
commit b5295dcda8
12 changed files with 1424 additions and 0 deletions

56
src/bot.ts Normal file
View File

@ -0,0 +1,56 @@
import { PhotoAttachment, VK } from "vk-io";
import { env } from "../env";
import { MEDIUM_SIZES, getAttachmentBySizes } from "./utils";
import { makeLiquidRescale } from "./queues/liquid.queue";
export const bot = new VK({ token: env.VK_BOT_TOKEN });
bot.updates.on('message_new', async (context, next) => {
if (context.isChat) return; // ignore chats for now
if (!context.hasAllAttachments('photo')) return;
const processingMessage = await context.reply('Обрабатываю...');
const results = [];
for (let attachment of context.attachments) {
if (attachment.type === 'photo') {
const [size] = getAttachmentBySizes(attachment as PhotoAttachment, MEDIUM_SIZES);
const imageUrl = size?.url;
if (imageUrl) {
const liquidRescaleJob = await makeLiquidRescale({ imageUrl: imageUrl });
let result;
try {
result = await liquidRescaleJob.finished();
} catch (err) {
return context.reply('Упс... что-то пошло не так...');
}
results.push(Buffer.from(result.data));
}
}
}
const attachments: PhotoAttachment[] = [];
for (let image of results) {
const attachment = await bot.upload.messagePhoto({
peer_id: context.senderId,
source: {
values: {
value: image,
contentType: 'image/jpeg'
}
}
});
attachments.push(attachment);
}
await processingMessage.editMessage({ attachment: attachments });
return next();
});

19
src/logger.ts Normal file
View File

@ -0,0 +1,19 @@
import pino, { LoggerOptions } from "pino";
import PinoPretty, { PrettyOptions } from "pino-pretty";
import { env } from "../env";
const options: LoggerOptions = {
level: env.LOG_LEVEL
};
const prettyOptions: PrettyOptions = {
ignore: 'pid,hostname',
colorize: env.isDev ? true : false,
translateTime: 'SYS:dd.mm.yyyy, HH:MM:ss'
};
export let logger = pino(options);
if (env.isDev) {
logger = pino(options, PinoPretty(prettyOptions));
}

View File

@ -0,0 +1,27 @@
import { Job } from "bull";
import { logger } from "../logger";
import { env } from "../../env";
import { fetchImage } from "../utils";
export const liquidRescaleProcess = async (job: Job) => {
logger.info({ id: job.id, data: job.data }, 'Processing liquid rescale job');
const { imageUrl } = job.data;
const image = await fetchImage(imageUrl);
const formData = new FormData();
formData.append('file', new Blob([image]));
let result;
try {
result = await fetchImage(env.LIQUID_RESCALE_API_URL, { method: 'POST', body: formData });
} catch (err) {
logger.error(err);
return Promise.reject(err);
}
// return Promise.resolve({ data: result.toString('base64') });
return Promise.resolve(result);
};

View File

@ -0,0 +1,18 @@
import Bull from "bull";
import { env } from "../../env";
import { liquidRescaleProcess } from "../processes/liquid.process";
export const liquidRescaleQueue = new Bull('liquid-rescale', {
redis: {
host: env.REDIS_HOST,
port: env.REDIS_PORT
}
});
liquidRescaleQueue.process(env.BULL_CONCURRENCY, liquidRescaleProcess);
const makeLiquidRescale = async (data: { imageUrl: string; }) => {
return liquidRescaleQueue.add({ ...data }, { attempts: 3 });
};
export { makeLiquidRescale };

35
src/utils.ts Normal file
View File

@ -0,0 +1,35 @@
import { PhotoAttachment } from "vk-io";
export const SMALL_SIZES = ['m', 's'];
export const MEDIUM_SIZES = ['y', 'r', 'q', 'p', ...SMALL_SIZES];
export const LARGE_SIZES = ['w', 'z', ...MEDIUM_SIZES];
export async function fetchImage(url: string, params?: RequestInit) {
const image = await fetch(url, params)
.then(r => r.arrayBuffer());
const imageBuffer = Buffer.from(image);
return imageBuffer;
}
/**
* Example:
* ```js
* const SMALL_SIZES = ['m', 's'];
* const MEDIUM_SIZES = ['y', 'r', 'q', 'p', ...SMALL_SIZES];
* const LARGE_SIZES = ['w', 'z', ...MEDIUM_SIZES];
*
* const [size] = getAttachmentBySizes(attachment, LARGE_SIZES);
* console.log(size)
* ```
*/
export function getAttachmentBySizes(photoAttachment: PhotoAttachment, sizeTypes: string[] = []) {
const { sizes } = photoAttachment;
if (!sizes) return [];
return sizeTypes
.map((sizeType) => (
sizes.find((size) => size.type === sizeType)
))
.filter(Boolean);
}