const getRegex = /^[a-zA-Zа-яА-Я0-9_-]+$/g;
/**
* Getter Commands
* @namespace get
* @memberof Commands
*/
/**
* Get data from Redis
* @param {import('grammy').Context} ctx
* @param {string} name
* @returns {Promise<string>}
* @memberof Commands.get
*/
async function redisGet(ctx, name) {
const redis = require('../../services/redis').getRedis();
if (!redis) {
throw new Error('Storage is offline');
}
let key = `${ctx.chat?.id || ctx.from.id}:get:${name}`;
let result = await redis.hgetall(key);
return result.data ? JSON.parse(result.data) : result; // legacy support for not stringified get-s
}
/**
* Set data in Redis
* @param {import('grammy').Context} ctx
* @param {string} name
* @param {{[string]: string}} data
* @returns {Promise}
* @memberof Commands.get
*/
async function redisSet(ctx, name, data) {
const redis = require('../../services/redis').getRedis();
if (!redis) {
throw new Error('Storage is offline');
}
let key = `${ctx.chat.id || ctx.from.id}:get:${name}`;
for (let i in data) {
if (!data[i]) {
delete data[i];
}
}
if (!Object.keys(data).length) {
throw new Error('Cannot save empty data');
}
let owner;
if (ctx.chat.id !== ctx.from.id) {
owner = ctx.from.id;
}
if (await redis.exists(key)) {
throw new Error('This key already exists');
}
return redis.hset(key, { data: JSON.stringify(data), owner });
}
/**
* Get list of names from Redis
* @param {import('grammy').Context} ctx
* @returns {Promise<string[]>}
* @memberof Commands.get
*/
async function redisGetList(ctx) {
const redis = require('../../services/redis').getRedis();
if (!redis) {
throw new Error('Storage is offline');
}
let key = `${ctx.chat?.id || ctx.from.id}:get:*`;
let r_keys = await redis.keys(key);
let keys = [];
for (let r_key of r_keys) {
keys.push(r_key.split(':').slice(-1)[0]);
}
return keys;
}
/**
* Delete data from Redis
* @param {import('grammy').Context} ctx
* @param {string} name
* @returns {Promise}
* @memberof Commands.get
*/
async function redisDel(ctx, name) {
const redis = require('../../services/redis').getRedis();
if (!redis) {
throw new Error('Storage is offline');
}
let key = `${ctx.chat?.id || ctx.from.id}:get:${name}`;
if (!await redis.exists(key)) {
throw new Error('No such key');
}
let owner_id = await redis.hget(key, 'owner');
if (owner_id && `${owner_id}` !== `${ctx.from.id}`) {
throw new Error('Can be deleted only by the owner');
}
await redis.del(key);
}
/**
* `/get` command handler
* @param {GrammyTypes.Context | Object} ctx
* @memberof Commands.get
*/
async function get(ctx) {
let name = require('./utils').parseArgs(ctx, 1)[1];
if (!name) {
return ['Ты забыл указать название гета'];
}
if (!name.match(getRegex)) {
return ['Название гета может состоять только из букв латинского, русского алфавитов, цифр и символов -, _'];
}
let result = null;
try {
result = await redisGet(ctx, name);
}
catch (err) {
interaction.logger.error(`Error while getting content from redis`, { error: err.stack || err, args: [name] });
return [`Что-то случилось во время получения гета:\n<code>${err}</code>`];
}
if (!result || !Object.keys(result).length) {
return [`Такого гета нет, можешь быть первым кто его сделает`];
}
return [null, result];
}
/**
* `/set` command handler
* @param {import('grammy').Context} ctx
* @param {import('../telegram-client').TelegramInteraction} interaction
* @memberof Commands.get
*/
async function set(ctx, interaction) {
let name = require('./utils').parseArgs(ctx, 1)[1];
if (!name) {
return ['Ты забыл указать название гета'];
}
if (!name.match(getRegex)) {
return ['Название гета может состоять только из букв латинского, русского алфавитов, цифр и символов -, _'];
}
if (!ctx.message.reply_to_message) {
return ['Чтобы сохранить гет, ответьте на какое-нибудь сообщение с помощью <code>/set {название гета}</code>'];
}
const parsed_data = interaction._parseMessageMedia(ctx.message.reply_to_message);
if (!parsed_data.type) {
return [`Такое сохранить не получится, сейчас поддерживаются только следующие форматы:
Простой текст, изображение, гифки, аудио, видео, документы, стикеры, голосовые и видео сообщения`];
}
try {
await redisSet(ctx, name, parsed_data);
}
catch (err) {
interaction.logger.error(`Error while saving content to redis`,{ error: err.stack || err, args: [name], parsed_data });
return [`Что-то случилось во время сохранения гета:\n<code>${err}</code>`];
}
return [null, `Гет был сохранён, теперь его можно вызвать командой:\n<code>/get ${name}</code>${
ctx.chat.id === ctx.from.id ? `\nТак же можешь вызвать этот гет написав <code>@${ctx.me.username} /get ${name}</code> в поле ввода сообщения` : ''}`];
}
/**
* `/get_list` command handler
* @param {import('grammy').Context} ctx
* @memberof Commands.get
*/
async function getList(ctx, interaction) {
let gets;
try {
gets = await redisGetList(ctx);
}
catch (err) {
interaction.logger.error(`Error while getting list from redis`,{ error: err.stack || err });
return [`Что-то случилось во время получения списка гетов:\n<code>${err}</code>`];
}
if (!gets?.length) {
return [`В этом чате ещё нет ни одного гета`];
}
return [null, `Геты доступные в этом чате:\n\n${gets.join(', ')}`, `${gets.join(', ')}`];
}
/**
* `/del` command handler
* @param {import('grammy').Context} ctx
* @memberof Commands.get
*/
async function del(ctx, interaction) {
let name = require('./utils').parseArgs(ctx, 1)[1];
if (!name) {
return ['Ты забыл указать название гета'];
}
try {
await redisDel(ctx, name);
}
catch (err) {
interaction.logger.error(`Error while deleting data from redis`,{ error: err.stack || err, args: [name] });
return [`Что-то случилось во время удаления гета:\n<code>${err}</code>`];
}
return [null, `Гет <b>${name}</b> успешно удалён`];
}
module.exports = {
get, set, getList, del
}