add initial complete webui, more ai commands for moderation, add api
This commit is contained in:
parent
19e794e34c
commit
173d4e7a52
112 changed files with 8176 additions and 780 deletions
1291
telegram/commands/ai.ts
Executable file
1291
telegram/commands/ai.ts
Executable file
File diff suppressed because it is too large
Load diff
159
telegram/commands/animal.ts
Executable file
159
telegram/commands/animal.ts
Executable file
|
@ -0,0 +1,159 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import axios from 'axios';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import { replyToMessageId } from '../utils/reply-to-message-id';
|
||||
import { languageCode } from '../utils/language-code';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
export const duckHandler = async (ctx: Context & { message: { text: string } }) => {
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
try {
|
||||
const response = await axios(Resources.duckApi);
|
||||
ctx.replyWithPhoto(response.data.url, {
|
||||
caption: "🦆",
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const message = Strings.duckApiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const foxHandler = async (ctx: Context & { message: { text: string } }) => {
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
try {
|
||||
const response = await axios(Resources.foxApi);
|
||||
ctx.replyWithPhoto(response.data.image, {
|
||||
caption: "🦊",
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
const message = Strings.foxApiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const dogHandler = async (ctx: Context & { message: { text: string } }) => {
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
try {
|
||||
const response = await axios(Resources.dogApi);
|
||||
ctx.replyWithPhoto(response.data.message, {
|
||||
caption: "🐶",
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
const message = Strings.dogApiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const catHandler = async (ctx: Context & { message: { text: string } }) => {
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const apiUrl = `${Resources.catApi}?json=true`;
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
try {
|
||||
const response = await axios.get(apiUrl);
|
||||
const data = response.data;
|
||||
const imageUrl = `${data.url}`;
|
||||
await ctx.replyWithPhoto(imageUrl, {
|
||||
caption: `🐱`,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
const message = Strings.catImgErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const soggyHandler = async (ctx: Context & { message: { text: string } }) => {
|
||||
const userInput = ctx.message.text.split(' ')[1];
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
|
||||
switch (true) {
|
||||
case (userInput === "2" || userInput === "thumb"):
|
||||
ctx.replyWithPhoto(
|
||||
Resources.soggyCat2, {
|
||||
caption: Resources.soggyCat2,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
break;
|
||||
|
||||
case (userInput === "3" || userInput === "sticker"):
|
||||
ctx.replyWithSticker(
|
||||
Resources.soggyCatSticker,
|
||||
reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : undefined
|
||||
);
|
||||
break;
|
||||
|
||||
case (userInput === "4" || userInput === "alt"):
|
||||
ctx.replyWithPhoto(
|
||||
Resources.soggyCatAlt, {
|
||||
caption: Resources.soggyCatAlt,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
ctx.replyWithPhoto(
|
||||
Resources.soggyCat, {
|
||||
caption: Resources.soggyCat,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
export default (bot: Telegraf<Context>, db: any) => {
|
||||
bot.command("duck", spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'animals-basic')) return;
|
||||
await duckHandler(ctx);
|
||||
});
|
||||
|
||||
bot.command("fox", spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'animals-basic')) return;
|
||||
await foxHandler(ctx);
|
||||
});
|
||||
|
||||
bot.command("dog", spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'animals-basic')) return;
|
||||
await dogHandler(ctx);
|
||||
});
|
||||
|
||||
bot.command("cat", spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'animals-basic')) return;
|
||||
await catHandler(ctx);
|
||||
});
|
||||
|
||||
bot.command(['soggy', 'soggycat'], spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'soggy-cat')) return;
|
||||
await soggyHandler(ctx);
|
||||
});
|
||||
}
|
88
telegram/commands/codename.ts
Executable file
88
telegram/commands/codename.ts
Executable file
|
@ -0,0 +1,88 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import axios from 'axios';
|
||||
import verifyInput from '../plugins/verifyInput';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import { replyToMessageId } from '../utils/reply-to-message-id';
|
||||
import * as schema from '../../database/schema';
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
interface Device {
|
||||
brand: string;
|
||||
codename: string;
|
||||
model: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export async function getDeviceByCodename(codename: string): Promise<Device | null> {
|
||||
try {
|
||||
const response = await axios.get(Resources.codenameApi);
|
||||
const jsonRes = response.data;
|
||||
const deviceDetails = jsonRes[codename];
|
||||
if (!deviceDetails) return null;
|
||||
return deviceDetails.find((item: Device) => item.brand) || deviceDetails[0];
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db?: NodePgDatabase<typeof schema>): Promise<{ Strings: any, languageCode: string }> {
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
const from = ctx.from;
|
||||
if (db && from.id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(from.id)), limit: 1 });
|
||||
if (dbUser.length > 0) {
|
||||
languageCode = dbUser[0].languageCode;
|
||||
}
|
||||
}
|
||||
if (from.language_code && languageCode === 'en') {
|
||||
languageCode = from.language_code;
|
||||
console.warn('[WARN !] Falling back to Telegram language_code for user', from.id);
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command(['codename', 'whatis'], spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'codename-lookup')) return;
|
||||
|
||||
const userInput = ctx.message.text.split(" ").slice(1).join(" ");
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const { noCodename } = Strings.codenameCheck;
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
|
||||
if (verifyInput(ctx, userInput, noCodename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const device = await getDeviceByCodename(userInput);
|
||||
|
||||
if (!device) {
|
||||
return ctx.reply(Strings.codenameCheck.notFound, {
|
||||
parse_mode: "Markdown",
|
||||
...({ reply_to_message_id })
|
||||
});
|
||||
}
|
||||
|
||||
const message = Strings.codenameCheck.resultMsg
|
||||
.replace('{brand}', device.brand)
|
||||
.replace('{codename}', userInput)
|
||||
.replace('{model}', device.model)
|
||||
.replace('{name}', device.name);
|
||||
|
||||
return ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...({ reply_to_message_id })
|
||||
});
|
||||
})
|
||||
}
|
271
telegram/commands/crew.ts
Executable file
271
telegram/commands/crew.ts
Executable file
|
@ -0,0 +1,271 @@
|
|||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import os from 'os';
|
||||
import { exec } from 'child_process';
|
||||
import { error } from 'console';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import * as schema from '../../database/schema';
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db?: NodePgDatabase<typeof schema>): Promise<{ Strings: any, languageCode: string }> {
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
const from = ctx.from;
|
||||
if (db && from.id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(from.id)), limit: 1 });
|
||||
if (dbUser.length > 0) {
|
||||
languageCode = dbUser[0].languageCode;
|
||||
}
|
||||
}
|
||||
if (from.language_code && languageCode === 'en') {
|
||||
languageCode = from.language_code;
|
||||
console.warn('[WARN !] Falling back to Telegram language_code for user', from.id);
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
|
||||
function getGitCommitHash() {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec('git rev-parse --short HEAD', (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(`Error: ${stderr}`);
|
||||
} else {
|
||||
resolve(stdout.trim());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateBot() {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec('git pull && echo "A" >> restart.txt', (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(`Error: ${stderr}`);
|
||||
} else {
|
||||
resolve(stdout.trim());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function formatUptime(uptime: number) {
|
||||
const hours = Math.floor(uptime / 3600);
|
||||
const minutes = Math.floor((uptime % 3600) / 60);
|
||||
const seconds = Math.floor(uptime % 60);
|
||||
return `${hours}h ${minutes}m ${seconds}s`;
|
||||
}
|
||||
|
||||
function getSystemInfo() {
|
||||
const { platform, release, arch, cpus, totalmem, freemem, loadavg, uptime } = os;
|
||||
const [cpu] = cpus();
|
||||
return `*Server Stats*\n\n` +
|
||||
`*OS:* \`${platform()} ${release()}\`\n` +
|
||||
`*Arch:* \`${arch()}\`\n` +
|
||||
`*Node.js Version:* \`${process.version}\`\n` +
|
||||
`*CPU:* \`${cpu.model}\`\n` +
|
||||
`*CPU Cores:* \`${cpus().length} cores\`\n` +
|
||||
`*RAM:* \`${(freemem() / (1024 ** 3)).toFixed(2)} GB / ${(totalmem() / (1024 ** 3)).toFixed(2)} GB\`\n` +
|
||||
`*Load Average:* \`${loadavg().map(avg => avg.toFixed(2)).join(', ')}\`\n` +
|
||||
`*Uptime:* \`${formatUptime(uptime())}\`\n\n`;
|
||||
}
|
||||
|
||||
async function handleAdminCommand(ctx: Context & { message: { text: string } }, action: () => Promise<void>, successMessage: string, errorMessage: string) {
|
||||
const { Strings } = await getUserAndStrings(ctx);
|
||||
const userId = ctx.from?.id;
|
||||
const adminArray = process.env.botAdmins ? process.env.botAdmins.split(',').map(id => parseInt(id.trim())) : [];
|
||||
if (userId && adminArray.includes(userId)) {
|
||||
try {
|
||||
await action();
|
||||
if (successMessage) {
|
||||
ctx.reply(successMessage, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ctx.reply(errorMessage.replace(/{error}/g, error.message), {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ctx.reply(Strings.noPermission, {
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command('getbotstats', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
handleAdminCommand(ctx, async () => {
|
||||
const stats = getSystemInfo();
|
||||
await ctx.reply(stats, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}, '', Strings.errorRetrievingStats);
|
||||
});
|
||||
|
||||
bot.command('getbotcommit', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
handleAdminCommand(ctx, async () => {
|
||||
try {
|
||||
const commitHash = await getGitCommitHash();
|
||||
await ctx.reply(Strings.gitCurrentCommit.replace(/{commitHash}/g, commitHash), {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
ctx.reply(Strings.gitErrRetrievingCommit.replace(/{error}/g, error), {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
}, '', Strings.gitErrRetrievingCommit);
|
||||
});
|
||||
|
||||
bot.command('updatebot', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
handleAdminCommand(ctx, async () => {
|
||||
try {
|
||||
const result = await updateBot();
|
||||
await ctx.reply(Strings.botUpdated.replace(/{result}/g, result), {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
ctx.reply(Strings.errorUpdatingBot.replace(/{error}/g, error), {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
}, '', Strings.errorUpdatingBot);
|
||||
});
|
||||
|
||||
bot.command('setbotname', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const botName = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
handleAdminCommand(ctx, async () => {
|
||||
await ctx.telegram.setMyName(botName);
|
||||
}, Strings.botNameChanged.replace(/{botName}/g, botName), Strings.botNameErr.replace(/{error}/g, error));
|
||||
});
|
||||
|
||||
bot.command('setbotdesc', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const botDesc = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
handleAdminCommand(ctx, async () => {
|
||||
await ctx.telegram.setMyDescription(botDesc);
|
||||
}, Strings.botDescChanged.replace(/{botDesc}/g, botDesc), Strings.botDescErr.replace(/{error}/g, error));
|
||||
});
|
||||
|
||||
bot.command('botkickme', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
handleAdminCommand(ctx, async () => {
|
||||
if (!ctx.chat) {
|
||||
ctx.reply(Strings.chatNotFound, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
ctx.reply(Strings.kickingMyself, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
await ctx.telegram.leaveChat(ctx.chat.id);
|
||||
}, '', Strings.kickingMyselfErr);
|
||||
});
|
||||
|
||||
bot.command('getfile', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const botFile = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
|
||||
if (!botFile) {
|
||||
ctx.reply(Strings.noFileProvided, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
handleAdminCommand(ctx, async () => {
|
||||
try {
|
||||
await ctx.replyWithDocument({
|
||||
// @ts-ignore
|
||||
source: botFile,
|
||||
caption: botFile
|
||||
}, {
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
ctx.reply(Strings.unexpectedErr.replace(/{error}/g, error.message), {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
}, '', Strings.unexpectedErr);
|
||||
});
|
||||
|
||||
bot.command('run', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const command = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
handleAdminCommand(ctx, async () => {
|
||||
if (!command) {
|
||||
ctx.reply('Por favor, forneça um comando para executar.');
|
||||
return;
|
||||
}
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
return ctx.reply(`\`${error.message}\``, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
if (stderr) {
|
||||
return ctx.reply(`\`${stderr}\``, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
ctx.reply(`\`${stdout}\``, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
});
|
||||
}, '', "Nope!");
|
||||
});
|
||||
|
||||
bot.command('eval', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
const code = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
if (!code) {
|
||||
return ctx.reply('Por favor, forneça um código para avaliar.');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = eval(code);
|
||||
ctx.reply(`Result: ${result}`, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
ctx.reply(`Error: ${error.message}`, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
bot.command('crash', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
handleAdminCommand(ctx, async () => {
|
||||
ctx.reply('Crashed!');
|
||||
}, '', "Nope!");
|
||||
});
|
||||
};
|
140
telegram/commands/fun.ts
Executable file
140
telegram/commands/fun.ts
Executable file
|
@ -0,0 +1,140 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import * as schema from '../../database/schema';
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db?: NodePgDatabase<typeof schema>): Promise<{ Strings: any, languageCode: string }> {
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
const from = ctx.from;
|
||||
if (db && from.id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(from.id)), limit: 1 });
|
||||
if (dbUser.length > 0) {
|
||||
languageCode = dbUser[0].languageCode;
|
||||
}
|
||||
}
|
||||
if (from.language_code && languageCode === 'en') {
|
||||
languageCode = from.language_code;
|
||||
console.warn('[WARN !] Falling back to Telegram language_code for user', from.id);
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
|
||||
function sendRandomReply(ctx: Context & { message: { text: string } }, gifUrl: string, textKey: string, db: any) {
|
||||
getUserAndStrings(ctx, db).then(({ Strings }) => {
|
||||
const randomNumber = Math.floor(Math.random() * 100);
|
||||
const shouldSendGif = randomNumber > 50;
|
||||
const caption = Strings[textKey].replace('{randomNum}', randomNumber);
|
||||
if (shouldSendGif) {
|
||||
ctx.replyWithAnimation(gifUrl, {
|
||||
caption,
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
}).catch(err => {
|
||||
const gifErr = Strings.gifErr.replace('{err}', err);
|
||||
ctx.reply(gifErr, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ctx.reply(caption, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDiceCommand(ctx: Context & { message: { text: string } }, emoji: string, delay: number, db: any) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
|
||||
// @ts-ignore
|
||||
const result = await ctx.sendDice({ emoji, ...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {}) });
|
||||
const botResponse = Strings.funEmojiResult
|
||||
.replace('{emoji}', result.dice.emoji)
|
||||
.replace('{value}', result.dice.value);
|
||||
|
||||
setTimeout(() => {
|
||||
ctx.reply(botResponse, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function getRandomInt(max: number) {
|
||||
return Math.floor(Math.random() * (max + 1));
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command('random', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'fun-random')) return;
|
||||
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const randomValue = getRandomInt(10);
|
||||
const randomVStr = Strings.randomNum.replace('{number}', randomValue);
|
||||
|
||||
ctx.reply(
|
||||
randomVStr, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: maybe send custom stickers to match result of the roll? i think there are pre-existing ones
|
||||
bot.command('dice', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'games-dice')) return;
|
||||
await handleDiceCommand(ctx, '🎲', 4000, db);
|
||||
});
|
||||
|
||||
bot.command('slot', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'games-dice')) return;
|
||||
await handleDiceCommand(ctx, '🎰', 3000, db);
|
||||
});
|
||||
|
||||
bot.command('ball', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'games-dice')) return;
|
||||
await handleDiceCommand(ctx, '⚽', 3000, db);
|
||||
});
|
||||
|
||||
bot.command('dart', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'games-dice')) return;
|
||||
await handleDiceCommand(ctx, '🎯', 3000, db);
|
||||
});
|
||||
|
||||
bot.command('bowling', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'games-dice')) return;
|
||||
await handleDiceCommand(ctx, '🎳', 3000, db);
|
||||
});
|
||||
|
||||
bot.command('idice', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'infinite-dice')) return;
|
||||
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
ctx.replyWithSticker(
|
||||
Resources.infiniteDice, {
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
});
|
||||
|
||||
bot.command('furry', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'fun-random')) return;
|
||||
sendRandomReply(ctx, Resources.furryGif, 'furryAmount', db);
|
||||
});
|
||||
|
||||
bot.command('gay', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'fun-random')) return;
|
||||
sendRandomReply(ctx, Resources.gayFlag, 'gayAmount', db);
|
||||
});
|
||||
};
|
341
telegram/commands/gsmarena.ts
Executable file
341
telegram/commands/gsmarena.ts
Executable file
|
@ -0,0 +1,341 @@
|
|||
// Ported and improved from Hitalo's PyKorone bot
|
||||
// Copyright (c) 2024 Hitalo M. (https://github.com/HitaloM)
|
||||
// Original code license: BSD-3-Clause
|
||||
// With some help from GPT (I don't really like AI but whatever)
|
||||
// If this were a kang, I would not be giving credits to him!
|
||||
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import axios from 'axios';
|
||||
import { parse } from 'node-html-parser';
|
||||
import { getDeviceByCodename } from './codename';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { languageCode } from '../utils/language-code';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
interface PhoneSearchResult {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface PhoneDetails {
|
||||
specs: Record<string, Record<string, string>>;
|
||||
name?: string;
|
||||
url?: string;
|
||||
picture?: string;
|
||||
}
|
||||
|
||||
const HEADERS = {
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
|
||||
};
|
||||
|
||||
function getDataFromSpecs(specsData, category, attributes) {
|
||||
const details = specsData?.specs?.[category] || {};
|
||||
|
||||
return attributes
|
||||
.map(attr => details[attr] || null)
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function parseSpecs(specsData: PhoneDetails): PhoneDetails {
|
||||
const categories = {
|
||||
"status": ["Launch", ["Status"]],
|
||||
"network": ["Network", ["Technology"]],
|
||||
"system": ["Platform", ["OS"]],
|
||||
"models": ["Misc", ["Models"]],
|
||||
"weight": ["Body", ["Weight"]],
|
||||
"jack": ["Sound", ["3.5mm jack"]],
|
||||
"usb": ["Comms", ["USB"]],
|
||||
"sensors": ["Features", ["Sensors"]],
|
||||
"battery": ["Battery", ["Type"]],
|
||||
"charging": ["Battery", ["Charging"]],
|
||||
"display_type": ["Display", ["Type"]],
|
||||
"display_size": ["Display", ["Size"]],
|
||||
"display_resolution": ["Display", ["Resolution"]],
|
||||
"platform_chipset": ["Platform", ["Chipset"]],
|
||||
"platform_cpu": ["Platform", ["CPU"]],
|
||||
"platform_gpu": ["Platform", ["GPU"]],
|
||||
"memory": ["Memory", ["Internal"]],
|
||||
"main_camera_single": ["Main Camera", ["Single"]],
|
||||
"main_camera_dual": ["Main Camera", ["Dual"]],
|
||||
"main_camera_triple": ["Main Camera", ["Triple"]],
|
||||
"main_camera_quad": ["Main Camera", ["Quad"]],
|
||||
"main_camera_features": ["Main Camera", ["Features"]],
|
||||
"main_camera_video": ["Main Camera", ["Video"]],
|
||||
"selfie_camera_single": ["Selfie Camera", ["Single"]],
|
||||
"selfie_camera_dual": ["Selfie Camera", ["Dual"]],
|
||||
"selfie_camera_triple": ["Selfie Camera", ["Triple"]],
|
||||
"selfie_camera_quad": ["Selfie Camera", ["Quad"]],
|
||||
"selfie_camera_features": ["Selfie Camera", ["Features"]],
|
||||
"selfie_camera_video": ["Selfie Camera", ["Video"]]
|
||||
};
|
||||
|
||||
const parsedData = Object.keys(categories).reduce((acc, key) => {
|
||||
const [cat, attrs] = categories[key];
|
||||
acc[key] = getDataFromSpecs(specsData, cat, attrs) || "";
|
||||
return acc;
|
||||
}, { specs: {} } as PhoneDetails);
|
||||
|
||||
parsedData["name"] = specsData.name || "";
|
||||
parsedData["url"] = specsData.url || "";
|
||||
|
||||
return parsedData;
|
||||
}
|
||||
|
||||
function formatPhone(phone: PhoneDetails) {
|
||||
const formattedPhone = parseSpecs(phone);
|
||||
const attributesDict = {
|
||||
"Status": "status",
|
||||
"Network": "network",
|
||||
"OS": "system",
|
||||
"Models": "models",
|
||||
"Weight": "weight",
|
||||
"3.5mm jack": "jack",
|
||||
"USB": "usb",
|
||||
"Sensors": "sensors",
|
||||
"Battery": "battery",
|
||||
"Charging": "charging",
|
||||
"Display Type": "display_type",
|
||||
"Display Size": "display_size",
|
||||
"Display Resolution": "display_resolution",
|
||||
"Chipset": "platform_chipset",
|
||||
"CPU": "platform_cpu",
|
||||
"GPU": "platform_gpu",
|
||||
"Memory": "memory",
|
||||
"Rear Camera (Single)": "main_camera_single",
|
||||
"Rear Camera (Dual)": "main_camera_dual",
|
||||
"Rear Camera (Triple)": "main_camera_triple",
|
||||
"Rear Camera (Quad)": "main_camera_quad",
|
||||
"Rear Camera (Features)": "main_camera_features",
|
||||
"Rear Camera (Video)": "main_camera_video",
|
||||
"Front Camera (Single)": "selfie_camera_single",
|
||||
"Front Camera (Dual)": "selfie_camera_dual",
|
||||
"Front Camera (Triple)": "selfie_camera_triple",
|
||||
"Front Camera (Quad)": "selfie_camera_quad",
|
||||
"Front Camera (Features)": "selfie_camera_features",
|
||||
"Front Camera (Video)": "selfie_camera_video"
|
||||
};
|
||||
|
||||
const attributes = Object.entries(attributesDict)
|
||||
.filter(([_, key]) => formattedPhone[key])
|
||||
.map(([label, key]) => `<b>${label}:</b> <code>${formattedPhone[key]}</code>`)
|
||||
.join("\n\n");
|
||||
|
||||
const deviceUrl = `<b>GSMArena page:</b> ${formattedPhone.url}`;
|
||||
const deviceImage = phone.picture ? `<b>Device Image</b>: ${phone.picture}` : '';
|
||||
|
||||
return `<b>\n\nName: </b><code>${formattedPhone.name}</code>\n\n${attributes}\n\n${deviceImage}\n\n${deviceUrl}`;
|
||||
}
|
||||
|
||||
async function fetchHtml(url: string) {
|
||||
try {
|
||||
const response = await axios.get(url, { headers: HEADERS });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching HTML:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function searchPhone(phone: string): Promise<PhoneSearchResult[]> {
|
||||
try {
|
||||
const searchUrl = `https://m.gsmarena.com/results.php3?sQuickSearch=yes&sName=${encodeURIComponent(phone)}`;
|
||||
const htmlContent = await fetchHtml(searchUrl);
|
||||
const root = parse(htmlContent);
|
||||
const foundPhones = root.querySelectorAll('.general-menu.material-card ul li');
|
||||
|
||||
return foundPhones.map((phoneTag) => {
|
||||
const name = phoneTag.querySelector('img')?.getAttribute('title') || "";
|
||||
const url = phoneTag.querySelector('a')?.getAttribute('href') || "";
|
||||
return { name, url };
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error searching for phone:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function checkPhoneDetails(url) {
|
||||
try {
|
||||
const htmlContent = await fetchHtml(`https://www.gsmarena.com/${url}`);
|
||||
const root = parse(htmlContent);
|
||||
const specsTables = root.querySelectorAll('table[cellspacing="0"]');
|
||||
const specsData = extractSpecs(specsTables);
|
||||
const metaScripts = root.querySelectorAll('script[language="javascript"]');
|
||||
const meta = metaScripts.length ? metaScripts[0].text.split("\n") : [];
|
||||
const name = extractMetaData(meta, "ITEM_NAME");
|
||||
const picture = extractMetaData(meta, "ITEM_IMAGE");
|
||||
|
||||
return { ...specsData, name, picture, url: `https://www.gsmarena.com/${url}` };
|
||||
} catch (error) {
|
||||
console.error("Error fetching phone details:", error);
|
||||
return { specs: {}, name: "", url: "", picture: "" };
|
||||
}
|
||||
}
|
||||
|
||||
function extractSpecs(specsTables) {
|
||||
return {
|
||||
specs: specsTables.reduce((acc, table) => {
|
||||
const feature = table.querySelector('th')?.text.trim() || "";
|
||||
table.querySelectorAll('tr').forEach((tr) => {
|
||||
const header = tr.querySelector('.ttl')?.text.trim() || "info";
|
||||
let detail = tr.querySelector('.nfo')?.text.trim() || "";
|
||||
detail = detail.replace(/\s*\n\s*/g, " / ").trim();
|
||||
if (!acc[feature]) {
|
||||
acc[feature] = {};
|
||||
}
|
||||
acc[feature][header] = acc[feature][header]
|
||||
? `${acc[feature][header]} / ${detail}`
|
||||
: detail;
|
||||
});
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
}
|
||||
|
||||
function extractMetaData(meta, key) {
|
||||
const line = meta.find((line) => line.includes(key));
|
||||
return line ? line.split('"')[1] : "";
|
||||
}
|
||||
|
||||
function getUsername(ctx){
|
||||
let userName = String(ctx.from.first_name);
|
||||
if(userName.includes("<") && userName.includes(">")) {
|
||||
userName = userName.replaceAll("<", "").replaceAll(">", "");
|
||||
}
|
||||
return userName;
|
||||
}
|
||||
|
||||
const deviceSelectionCache: Record<number, { results: PhoneSearchResult[], timeout: NodeJS.Timeout }> = {};
|
||||
const lastSelectionMessageId: Record<number, number> = {};
|
||||
|
||||
export default (bot, db) => {
|
||||
bot.command(['d', 'device'], spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'device-specs')) return;
|
||||
|
||||
const userId = ctx.from.id;
|
||||
const userName = getUsername(ctx);
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
|
||||
const phone = ctx.message.text.split(" ").slice(1).join(" ");
|
||||
if (!phone) {
|
||||
return ctx.reply(Strings.gsmarenaProvidePhoneName || "[TODO: Add gsmarenaProvidePhoneName to locales] Please provide the phone name.", { ...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {}) });
|
||||
}
|
||||
|
||||
console.log("[GSMArena] Searching for", phone);
|
||||
const statusMsg = await ctx.reply((Strings.gsmarenaSearchingFor || "[TODO: Add gsmarenaSearchingFor to locales] Searching for {phone}...").replace('{phone}', phone), { ...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {}), parse_mode: 'Markdown' });
|
||||
|
||||
let results = await searchPhone(phone);
|
||||
if (results.length === 0) {
|
||||
const codenameResults = await getDeviceByCodename(phone.split(" ")[0]);
|
||||
if (!codenameResults) {
|
||||
await ctx.telegram.editMessageText(ctx.chat.id, statusMsg.message_id, undefined, (Strings.gsmarenaNoPhonesFound || "[TODO: Add gsmarenaNoPhonesFound to locales] No phones found for {phone}.").replace('{phone}', phone), { parse_mode: 'Markdown' });
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.telegram.editMessageText(ctx.chat.id, statusMsg.message_id, undefined, (Strings.gsmarenaSearchingFor || "[TODO: Add gsmarenaSearchingFor to locales] Searching for {phone}...").replace('{phone}', codenameResults.name), { parse_mode: 'Markdown' });
|
||||
const nameResults = await searchPhone(codenameResults.name);
|
||||
if (nameResults.length === 0) {
|
||||
await ctx.telegram.editMessageText(ctx.chat.id, statusMsg.message_id, undefined, (Strings.gsmarenaNoPhonesFoundBoth || "[TODO: Add gsmarenaNoPhonesFoundBoth to locales] No phones found for {name} and {phone}.").replace('{name}', codenameResults.name).replace('{phone}', phone), { parse_mode: 'Markdown' });
|
||||
return;
|
||||
}
|
||||
results = nameResults;
|
||||
}
|
||||
|
||||
if (deviceSelectionCache[userId]?.timeout) {
|
||||
clearTimeout(deviceSelectionCache[userId].timeout);
|
||||
}
|
||||
deviceSelectionCache[userId] = {
|
||||
results,
|
||||
timeout: setTimeout(() => { delete deviceSelectionCache[userId]; }, 5 * 60 * 1000)
|
||||
};
|
||||
|
||||
if (lastSelectionMessageId[userId]) {
|
||||
try {
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
lastSelectionMessageId[userId],
|
||||
undefined,
|
||||
Strings.gsmarenaSelectDevice || "[TODO: Add gsmarenaSelectDevice to locales] Please select your device:",
|
||||
{
|
||||
parse_mode: 'HTML',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: {
|
||||
inline_keyboard: results.map((result, idx) => {
|
||||
const callbackData = `gsmadetails:${idx}:${ctx.from.id}`;
|
||||
return [{ text: result.name, callback_data: callbackData }];
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
const testUser = `<a href=\"tg://user?id=${userId}\">${userName}</a>, ${Strings.gsmarenaSelectDevice || "[TODO: Add gsmarenaSelectDevice to locales] please select your device:"}`;
|
||||
const options = {
|
||||
parse_mode: 'HTML',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: {
|
||||
inline_keyboard: results.map((result, idx) => {
|
||||
const callbackData = `gsmadetails:${idx}:${ctx.from.id}`;
|
||||
return [{ text: result.name, callback_data: callbackData }];
|
||||
})
|
||||
}
|
||||
};
|
||||
const selectionMsg = await ctx.reply(testUser, options);
|
||||
lastSelectionMessageId[userId] = selectionMsg.message_id;
|
||||
}
|
||||
} else {
|
||||
const testUser = `<a href=\"tg://user?id=${userId}\">${userName}</a>, ${Strings.gsmarenaSelectDevice || "[TODO: Add gsmarenaSelectDevice to locales] please select your device:"}`;
|
||||
const inlineKeyboard = results.map((result, idx) => {
|
||||
const callbackData = `gsmadetails:${idx}:${ctx.from.id}`;
|
||||
return [{ text: result.name, callback_data: callbackData }];
|
||||
});
|
||||
const options = {
|
||||
parse_mode: 'HTML',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: {
|
||||
inline_keyboard: inlineKeyboard
|
||||
}
|
||||
};
|
||||
const selectionMsg = await ctx.reply(testUser, options);
|
||||
lastSelectionMessageId[userId] = selectionMsg.message_id;
|
||||
}
|
||||
await ctx.telegram.deleteMessage(ctx.chat.id, statusMsg.message_id).catch(() => {});
|
||||
});
|
||||
|
||||
bot.action(/gsmadetails:(\d+):(\d+)/, async (ctx) => {
|
||||
const idx = parseInt(ctx.match[1]);
|
||||
const userId = parseInt(ctx.match[2]);
|
||||
const userName = getUsername(ctx);
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
|
||||
const callbackQueryUserId = ctx.update.callback_query.from.id;
|
||||
|
||||
if (userId !== callbackQueryUserId) {
|
||||
return ctx.answerCbQuery(`${userName}, ${Strings.gsmarenaNotAllowed || "[TODO: Add gsmarenaNotAllowed to locales] you are not allowed to interact with this."}`);
|
||||
}
|
||||
|
||||
ctx.answerCbQuery();
|
||||
|
||||
const cache = deviceSelectionCache[userId];
|
||||
if (!cache || !cache.results[idx]) {
|
||||
return ctx.reply(Strings.gsmarenaInvalidOrExpired || "[TODO: Add gsmarenaInvalidOrExpired to locales] Whoops, invalid or expired option. Please try again.", { ...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {}) });
|
||||
}
|
||||
const url = cache.results[idx].url;
|
||||
|
||||
const phoneDetails = await checkPhoneDetails(url);
|
||||
|
||||
if (phoneDetails.name) {
|
||||
const message = formatPhone(phoneDetails);
|
||||
ctx.editMessageText(`<b><a href=\"tg://user?id=${userId}\">${userName}</a>, ${Strings.gsmarenaDeviceDetails || "[TODO: Add gsmarenaDeviceDetails to locales] these are the details of your device:"}</b>` + message, { parse_mode: 'HTML', disable_web_page_preview: false });
|
||||
} else {
|
||||
ctx.reply(Strings.gsmarenaErrorFetchingDetails || "[TODO: Add gsmarenaErrorFetchingDetails to locales] Error fetching phone details.", { ...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {}) });
|
||||
}
|
||||
});
|
||||
};
|
154
telegram/commands/help.ts
Executable file
154
telegram/commands/help.ts
Executable file
|
@ -0,0 +1,154 @@
|
|||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import type { Context } from 'telegraf';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db?: any): Promise<{ Strings: any, languageCode: string }> {
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
const from = ctx.from;
|
||||
if (db && from.id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(from.id)), limit: 1 });
|
||||
if (dbUser.length > 0) {
|
||||
languageCode = dbUser[0].languageCode;
|
||||
}
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
|
||||
function isAdmin(ctx: Context): boolean {
|
||||
const userId = ctx.from?.id;
|
||||
if (!userId) return false;
|
||||
const adminArray = process.env.botAdmins ? process.env.botAdmins.split(',').map(id => parseInt(id.trim())) : [];
|
||||
return adminArray.includes(userId);
|
||||
}
|
||||
|
||||
interface MessageOptions {
|
||||
parse_mode: string;
|
||||
disable_web_page_preview: boolean;
|
||||
reply_markup: {
|
||||
inline_keyboard: { text: string; callback_data: string; }[][];
|
||||
};
|
||||
reply_to_message_id?: number;
|
||||
}
|
||||
|
||||
async function sendHelpMessage(ctx, isEditing, db) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const botInfo = await ctx.telegram.getMe();
|
||||
const helpText = Strings.botHelp
|
||||
.replace(/{botName}/g, botInfo.first_name)
|
||||
.replace(/{sourceLink}/g, process.env.botSource);
|
||||
function getMessageId(ctx) {
|
||||
return ctx.message?.message_id || ctx.callbackQuery?.message?.message_id;
|
||||
};
|
||||
const createOptions = (ctx, includeReplyTo = false): MessageOptions => {
|
||||
const options: MessageOptions = {
|
||||
parse_mode: 'Markdown',
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[{ text: Strings.mainCommands, callback_data: 'helpMain' }, { text: Strings.usefulCommands, callback_data: 'helpUseful' }],
|
||||
[{ text: Strings.interactiveEmojis, callback_data: 'helpInteractive' }, { text: Strings.funnyCommands, callback_data: 'helpFunny' }],
|
||||
[{ text: Strings.lastFm.helpEntry, callback_data: 'helpLast' }, { text: Strings.animalCommands, callback_data: 'helpAnimals' }],
|
||||
[{ text: Strings.ytDownload.helpEntry, callback_data: 'helpYouTube' }, { text: Strings.ponyApi.helpEntry, callback_data: 'helpMLP' }],
|
||||
[{ text: Strings.ai.helpEntry, callback_data: 'helpAi' }]
|
||||
]
|
||||
}
|
||||
};
|
||||
if (includeReplyTo) {
|
||||
const messageId = getMessageId(ctx);
|
||||
if (messageId) {
|
||||
(options as any).reply_parameters = { message_id: messageId };
|
||||
};
|
||||
};
|
||||
return options;
|
||||
};
|
||||
if (isEditing) {
|
||||
await ctx.editMessageText(helpText, createOptions(ctx));
|
||||
} else {
|
||||
await ctx.reply(helpText, createOptions(ctx, true));
|
||||
};
|
||||
}
|
||||
|
||||
export default (bot, db) => {
|
||||
bot.help(spamwatchMiddleware, async (ctx) => {
|
||||
await sendHelpMessage(ctx, false, db);
|
||||
});
|
||||
|
||||
bot.command("about", spamwatchMiddleware, async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const aboutMsg = Strings.botAbout.replace(/{sourceLink}/g, `${process.env.botSource}`);
|
||||
ctx.reply(aboutMsg, {
|
||||
parse_mode: 'Markdown',
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
});
|
||||
|
||||
const options = (Strings) => ({
|
||||
parse_mode: 'Markdown',
|
||||
disable_web_page_preview: true,
|
||||
reply_markup: JSON.stringify({
|
||||
inline_keyboard: [
|
||||
[{ text: Strings.varStrings.varBack, callback_data: 'helpBack' }],
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
bot.action('helpMain', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.mainCommandsDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpUseful', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.usefulCommandsDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpInteractive', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.interactiveEmojisDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpFunny', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.funnyCommandsDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpLast', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.lastFm.helpDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpYouTube', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.ytDownload.helpDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpAnimals', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.animalCommandsDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpMLP', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
await ctx.editMessageText(Strings.ponyApi.helpDesc, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpAi', async (ctx) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const helpText = isAdmin(ctx) ? Strings.ai.helpDescAdmin : Strings.ai.helpDesc;
|
||||
await ctx.editMessageText(helpText, options(Strings));
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
bot.action('helpBack', async (ctx) => {
|
||||
await sendHelpMessage(ctx, true, db);
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
}
|
114
telegram/commands/http.ts
Executable file
114
telegram/commands/http.ts
Executable file
|
@ -0,0 +1,114 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import axios from 'axios';
|
||||
import verifyInput from '../plugins/verifyInput';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import * as schema from '../../database/schema';
|
||||
import { languageCode } from '../utils/language-code';
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db?: NodePgDatabase<typeof schema>): Promise<{ Strings: any, languageCode: string }> {
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
const from = ctx.from;
|
||||
if (db && from.id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(from.id)), limit: 1 });
|
||||
if (dbUser.length > 0) {
|
||||
languageCode = dbUser[0].languageCode;
|
||||
}
|
||||
}
|
||||
if (from.language_code && languageCode === 'en') {
|
||||
languageCode = from.language_code;
|
||||
console.warn('[WARN !] Falling back to Telegram language_code for user', from.id);
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command("http", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'http-status')) return;
|
||||
|
||||
const reply_to_message_id = ctx.message.message_id;
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
const userInput = ctx.message.text.split(' ')[1];
|
||||
const apiUrl = Resources.httpApi;
|
||||
const { invalidCode } = Strings.httpCodes
|
||||
|
||||
if (verifyInput(ctx, userInput, invalidCode, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get(apiUrl);
|
||||
const data = response.data;
|
||||
const codesArray = Array.isArray(data) ? data : Object.values(data);
|
||||
const codeInfo = codesArray.find(item => item.code === parseInt(userInput));
|
||||
|
||||
if (codeInfo) {
|
||||
const message = Strings.httpCodes.resultMsg
|
||||
.replace("{code}", codeInfo.code)
|
||||
.replace("{message}", codeInfo.message)
|
||||
.replace("{description}", codeInfo.description);
|
||||
await ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} else {
|
||||
await ctx.reply(Strings.httpCodes.notFound, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
} catch (error) {
|
||||
const message = Strings.httpCodes.fetchErr.replace('{error}', error);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
bot.command("httpcat", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'animals-basic')) return;
|
||||
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const reply_to_message_id = ctx.message.message_id;
|
||||
const userInput = ctx.message.text.split(' ').slice(1).join(' ').replace(/\s+/g, '');
|
||||
const { invalidCode } = Strings.httpCodes
|
||||
|
||||
if (verifyInput(ctx, userInput, invalidCode, true)) {
|
||||
return;
|
||||
}
|
||||
if (userInput.length !== 3) {
|
||||
ctx.reply(Strings.httpCodes.invalidCode, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const apiUrl = `${Resources.httpCatApi}${userInput}`;
|
||||
|
||||
try {
|
||||
await ctx.replyWithPhoto(apiUrl, {
|
||||
caption: `🐱 ${apiUrl}`,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
ctx.reply(Strings.catImgErr, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
88
telegram/commands/info.ts
Executable file
88
telegram/commands/info.ts
Executable file
|
@ -0,0 +1,88 @@
|
|||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import * as schema from '../../database/schema';
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db?: NodePgDatabase<typeof schema>): Promise<{ Strings: any, languageCode: string }> {
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
const from = ctx.from;
|
||||
if (db && from.id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(from.id)), limit: 1 });
|
||||
if (dbUser.length > 0) {
|
||||
languageCode = dbUser[0].languageCode;
|
||||
}
|
||||
}
|
||||
if (from.language_code && languageCode === 'en') {
|
||||
languageCode = from.language_code;
|
||||
console.warn('[WARN !] Falling back to Telegram language_code for user', from.id);
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { Strings, languageCode };
|
||||
}
|
||||
|
||||
async function getUserInfo(ctx: Context & { message: { text: string } }, db: any) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
let lastName = ctx.from?.last_name;
|
||||
if (lastName === undefined) {
|
||||
lastName = " ";
|
||||
}
|
||||
const userInfo = Strings.userInfo
|
||||
.replace('{userName}', `${ctx.from?.first_name} ${lastName}` || Strings.varStrings.varUnknown)
|
||||
.replace('{userId}', ctx.from?.id || Strings.varStrings.varUnknown)
|
||||
.replace('{userHandle}', ctx.from?.username ? `@${ctx.from?.username}` : Strings.varStrings.varNone)
|
||||
.replace('{userPremium}', ctx.from?.is_premium ? Strings.varStrings.varYes : Strings.varStrings.varNo)
|
||||
.replace('{userLang}', ctx.from?.language_code || Strings.varStrings.varUnknown);
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
async function getChatInfo(ctx: Context & { message: { text: string } }, db: any) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
if ((ctx.chat?.type === 'group' || ctx.chat?.type === 'supergroup')) {
|
||||
const chat = ctx.chat as (typeof ctx.chat & { username?: string; is_forum?: boolean });
|
||||
const chatInfo = Strings.chatInfo
|
||||
.replace('{chatId}', chat?.id || Strings.varStrings.varUnknown)
|
||||
.replace('{chatName}', chat?.title || Strings.varStrings.varUnknown)
|
||||
.replace('{chatHandle}', chat?.username ? `@${chat.username}` : Strings.varStrings.varNone)
|
||||
.replace('{chatMembersCount}', await ctx.getChatMembersCount())
|
||||
.replace('{chatType}', chat?.type || Strings.varStrings.varUnknown)
|
||||
.replace('{isForum}', chat?.is_forum ? Strings.varStrings.varYes : Strings.varStrings.varNo);
|
||||
return chatInfo;
|
||||
} else {
|
||||
return Strings.groupOnly;
|
||||
}
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command('chatinfo', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'info-commands')) return;
|
||||
|
||||
const chatInfo = await getChatInfo(ctx, db);
|
||||
ctx.reply(
|
||||
chatInfo, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
bot.command('userinfo', spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'info-commands')) return;
|
||||
|
||||
const userInfo = await getUserInfo(ctx, db);
|
||||
ctx.reply(
|
||||
userInfo, {
|
||||
parse_mode: 'Markdown',
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
229
telegram/commands/lastfm.ts
Executable file
229
telegram/commands/lastfm.ts
Executable file
|
@ -0,0 +1,229 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import fs from 'fs';
|
||||
import axios from 'axios';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
const scrobbler_url = Resources.lastFmApi;
|
||||
const api_key = process.env.lastKey;
|
||||
|
||||
const dbFile = 'telegram/props/lastfm.json';
|
||||
let users = {};
|
||||
|
||||
function loadUsers() {
|
||||
if (!fs.existsSync(dbFile)) {
|
||||
console.log(`WARN: Last.fm user database ${dbFile} not found. Creating a new one.`);
|
||||
saveUsers();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = fs.readFileSync(dbFile, 'utf-8');
|
||||
users = JSON.parse(data);
|
||||
} catch (err) {
|
||||
console.log("WARN: Error loading the Last.fm user database:", err);
|
||||
users = {};
|
||||
}
|
||||
}
|
||||
|
||||
function saveUsers() {
|
||||
try {
|
||||
fs.writeFileSync(dbFile, JSON.stringify(users, null, 2), 'utf-8');
|
||||
} catch (err) {
|
||||
console.error("WARN: Error saving Last.fm users:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function getFromMusicBrainz(mbid: string) {
|
||||
try {
|
||||
const response = await axios.get(`${Resources.musicBrainzApi}${mbid}`);
|
||||
const imgObjLarge = response.data.images[0]?.thumbnails?.['1200'];
|
||||
const imgObjMid = response.data.images[0]?.thumbnails?.large;
|
||||
const imageUrl = imgObjLarge || imgObjMid || '';
|
||||
return imageUrl;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getFromLast(track) {
|
||||
if (!track || !track.image) return '';
|
||||
|
||||
const imageExtralarge = track.image.find(img => img.size === 'extralarge');
|
||||
const imageMega = track.image.find(img => img.size === 'mega');
|
||||
const imageUrl = (imageExtralarge?.['#text']) || (imageMega?.['#text']) || '';
|
||||
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
export default (bot, db) => {
|
||||
loadUsers();
|
||||
|
||||
bot.command('setuser', async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'lastfm')) return;
|
||||
|
||||
const userId = ctx.from.id;
|
||||
const Strings = getStrings(ctx.from.language_code);
|
||||
const lastUser = ctx.message.text.split(' ')[1];
|
||||
|
||||
if (!lastUser) {
|
||||
return ctx.reply(Strings.lastFm.noUser, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
};
|
||||
|
||||
users[userId] = lastUser;
|
||||
saveUsers();
|
||||
|
||||
const message = Strings.lastFm.userHasBeenSet.replace('{lastUser}', lastUser);
|
||||
|
||||
ctx.reply(message, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
});
|
||||
|
||||
bot.command(['lt', 'lmu', 'last', 'lfm'], spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'lastfm')) return;
|
||||
|
||||
const userId = ctx.from.id;
|
||||
const Strings = getStrings(ctx.from.language_code);
|
||||
const lastfmUser = users[userId];
|
||||
const genericImg = Resources.lastFmGenericImg;
|
||||
const botInfo = await ctx.telegram.getMe();
|
||||
|
||||
if (!lastfmUser) {
|
||||
return ctx.reply(Strings.lastFm.noUserSet, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.get(scrobbler_url, {
|
||||
params: {
|
||||
method: 'user.getRecentTracks',
|
||||
user: lastfmUser,
|
||||
api_key,
|
||||
format: 'json',
|
||||
limit: 1
|
||||
},
|
||||
headers: {
|
||||
'User-Agent': `@${botInfo.username}-node-telegram-bot`
|
||||
}
|
||||
});
|
||||
|
||||
const track = response.data.recenttracks.track[0];
|
||||
|
||||
if (!track) {
|
||||
const noRecent = Strings.lastFm.noRecentTracks.replace('{lastfmUser}', lastfmUser);
|
||||
return ctx.reply(noRecent, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
};
|
||||
|
||||
const trackName = track.name;
|
||||
const artistName = track.artist['#text'];
|
||||
const nowPlaying = track['@attr'] && track['@attr'].nowplaying ? Strings.varStrings.varIs : Strings.varStrings.varWas;
|
||||
const albumMbid = track.album.mbid;
|
||||
|
||||
let imageUrl = "";
|
||||
|
||||
if (albumMbid) {
|
||||
imageUrl = await getFromMusicBrainz(albumMbid);
|
||||
}
|
||||
|
||||
if (!imageUrl) {
|
||||
imageUrl = getFromLast(track);
|
||||
}
|
||||
|
||||
if (imageUrl == genericImg) {
|
||||
imageUrl = "";
|
||||
}
|
||||
|
||||
const trackUrl = `https://www.last.fm/music/${encodeURIComponent(artistName)}/_/${encodeURIComponent(trackName)}`;
|
||||
const artistUrl = `https://www.last.fm/music/${encodeURIComponent(artistName)}`;
|
||||
const userUrl = `https://www.last.fm/user/${encodeURIComponent(lastfmUser)}`;
|
||||
|
||||
let num_plays = 0;
|
||||
try {
|
||||
const response_plays = await axios.get(scrobbler_url, {
|
||||
params: {
|
||||
method: 'track.getInfo',
|
||||
api_key,
|
||||
track: trackName,
|
||||
artist: artistName,
|
||||
username: lastfmUser,
|
||||
format: 'json',
|
||||
},
|
||||
headers: {
|
||||
'User-Agent': `@${botInfo.username}-node-telegram-bot`
|
||||
}
|
||||
});
|
||||
|
||||
num_plays = response_plays.data.track.userplaycount;
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
const message = Strings.lastFm.apiErr
|
||||
.replace("{lastfmUser}", `[${lastfmUser}](${userUrl})`)
|
||||
.replace("{err}", err);
|
||||
ctx.reply(message, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
};
|
||||
|
||||
let message = Strings.lastFm.listeningTo
|
||||
.replace("{lastfmUser}", `[${lastfmUser}](${userUrl})`)
|
||||
.replace("{nowPlaying}", nowPlaying)
|
||||
.replace("{trackName}", `[${trackName}](${trackUrl})`)
|
||||
.replace("{artistName}", `[${artistName}](${artistUrl})`)
|
||||
|
||||
if (`${num_plays}` !== "0" && `${num_plays}` !== "1" && `${num_plays}` !== "2" && `${num_plays}` !== "3") {
|
||||
message = message
|
||||
.replace("{playCount}", Strings.lastFm.playCount)
|
||||
.replace("{plays}", `${num_plays}`);
|
||||
} else {
|
||||
message = message
|
||||
.replace("{playCount}", Strings.varStrings.varTo);
|
||||
};
|
||||
|
||||
if (imageUrl) {
|
||||
ctx.replyWithPhoto(imageUrl, {
|
||||
caption: message,
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
} else {
|
||||
ctx.reply(message, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
};
|
||||
} catch (err) {
|
||||
const userUrl = `https://www.last.fm/user/${encodeURIComponent(lastfmUser)}`;
|
||||
const message = Strings.lastFm.apiErr
|
||||
.replace("{lastfmUser}", `[${lastfmUser}](${userUrl})`)
|
||||
.replace("{err}", err);
|
||||
ctx.reply(message, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
...(ctx.message?.message_id ? { reply_parameters: { message_id: ctx.message.message_id } } : {})
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
556
telegram/commands/main.ts
Executable file
556
telegram/commands/main.ts
Executable file
|
@ -0,0 +1,556 @@
|
|||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import { replyToMessageId } from '../utils/reply-to-message-id';
|
||||
import * as schema from '../../database/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { ensureUserInDb } from '../utils/ensure-user';
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||
import { getModelLabelByName } from './ai';
|
||||
import { models } from '../../config/ai';
|
||||
import { langs } from '../locales/config';
|
||||
import { modelPageSize, seriesPageSize } from '../../config/settings';
|
||||
|
||||
type UserRow = typeof schema.usersTable.$inferSelect;
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
async function getUserAndStrings(ctx: Context, db: NodePgDatabase<typeof schema>): Promise<{ user: UserRow | null, Strings: any, languageCode: string }> {
|
||||
let user: UserRow | null = null;
|
||||
let languageCode = 'en';
|
||||
if (!ctx.from) {
|
||||
const Strings = getStrings(languageCode);
|
||||
return { user, Strings, languageCode };
|
||||
}
|
||||
const { id, language_code } = ctx.from;
|
||||
if (id) {
|
||||
const dbUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(id)), limit: 1 });
|
||||
if (dbUser.length === 0) {
|
||||
await ensureUserInDb(ctx, db);
|
||||
const newUser = await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(id)), limit: 1 });
|
||||
if (newUser.length > 0) {
|
||||
user = newUser[0];
|
||||
languageCode = user.languageCode;
|
||||
}
|
||||
} else {
|
||||
user = dbUser[0];
|
||||
languageCode = user.languageCode;
|
||||
}
|
||||
}
|
||||
if (!user && language_code) {
|
||||
languageCode = language_code;
|
||||
console.warn('[WARN !] Falling back to Telegram language_code for user', id);
|
||||
}
|
||||
const Strings = getStrings(languageCode);
|
||||
return { user, Strings, languageCode };
|
||||
}
|
||||
|
||||
type SettingsMenu = { text: string, reply_markup: any };
|
||||
function getSettingsMenu(user: UserRow, Strings: any): SettingsMenu {
|
||||
const langObj = langs.find(l => l.code === user.languageCode);
|
||||
const langLabel = langObj ? langObj.label : user.languageCode;
|
||||
const userId = user.telegramId;
|
||||
return {
|
||||
text: `*${Strings.settings.selectSetting}*`,
|
||||
reply_markup: {
|
||||
inline_keyboard: [
|
||||
[
|
||||
{ text: `✨ ${Strings.settings.ai.aiEnabled}: ${user.aiEnabled ? Strings.settings.enabled : Strings.settings.disabled}`, callback_data: `settings_aiEnabled_${userId}` },
|
||||
{ text: `🧠 ${Strings.settings.ai.aiModel}: ${getModelLabelByName(user.customAiModel)}`, callback_data: `settings_aiModel_0_${userId}` }
|
||||
],
|
||||
[
|
||||
{ text: `🌡️ ${Strings.settings.ai.aiTemperature}: ${user.aiTemperature}`, callback_data: `settings_aiTemperature_${userId}` },
|
||||
{ text: `🌐 ${langLabel}`, callback_data: `settings_language_${userId}` }
|
||||
],
|
||||
[
|
||||
{ text: `🧠 ${Strings.settings.ai.showThinking}: ${user.showThinking ? Strings.settings.enabled : Strings.settings.disabled}`, callback_data: `settings_showThinking_${userId}` }
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function extractUserIdFromCallback(data: string): string | null {
|
||||
const match = data.match(/_(\d+)$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function getNotAllowedMessage(Strings: any) {
|
||||
return Strings.gsmarenaNotAllowed;
|
||||
}
|
||||
|
||||
function logSettingsAccess(action: string, ctx: Context, allowed: boolean, expectedUserId: string | null) {
|
||||
if (process.env.longerLogs === 'true') {
|
||||
const actualUserId = ctx.from?.id;
|
||||
const username = ctx.from?.username || ctx.from?.first_name || 'unknown';
|
||||
console.log(`[Settings] Action: ${action}, Callback from: ${username} (${actualUserId}), Expected: ${expectedUserId}, Allowed: ${allowed}`);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTelegramError(err: any, context: string) {
|
||||
const description = err?.response?.description || '';
|
||||
const ignoredErrors = [
|
||||
'query is too old',
|
||||
'query ID is invalid',
|
||||
'message is not modified',
|
||||
'message to edit not found',
|
||||
];
|
||||
|
||||
const isIgnored = ignoredErrors.some(errorString => description.includes(errorString));
|
||||
|
||||
if (!isIgnored) {
|
||||
console.error(`[${context}] Unexpected Telegram error:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||
bot.start(spamwatchMiddleware, async (ctx: Context) => {
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
const botInfo = await ctx.telegram.getMe();
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
const startMsg = Strings.botWelcome.replace(/{botName}/g, botInfo.first_name);
|
||||
if (!user) return;
|
||||
ctx.reply(
|
||||
startMsg.replace(
|
||||
/{aiEnabled}/g,
|
||||
user.aiEnabled ? Strings.settings.enabled : Strings.settings.disabled
|
||||
).replace(
|
||||
/{aiModel}/g,
|
||||
getModelLabelByName(user.customAiModel)
|
||||
).replace(
|
||||
/{aiTemperature}/g,
|
||||
user.aiTemperature.toString()
|
||||
).replace(
|
||||
/{aiRequests}/g,
|
||||
user.aiRequests.toString()
|
||||
).replace(
|
||||
/{aiCharacters}/g,
|
||||
user.aiCharacters.toString()
|
||||
).replace(
|
||||
/{languageCode}/g,
|
||||
user.languageCode
|
||||
), {
|
||||
parse_mode: 'Markdown',
|
||||
...({ reply_to_message_id })
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
bot.command(["settings"], spamwatchMiddleware, async (ctx: Context) => {
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const menu = getSettingsMenu(user, Strings);
|
||||
await ctx.reply(
|
||||
menu.text,
|
||||
{
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown',
|
||||
...({ reply_to_message_id })
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const updateSettingsKeyboard = async (ctx: Context, user: UserRow, Strings: any) => {
|
||||
const menu = getSettingsMenu(user, Strings);
|
||||
await ctx.editMessageReplyMarkup(menu.reply_markup);
|
||||
};
|
||||
|
||||
bot.action(/^settings_aiEnabled_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settings_aiEnabled', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
await db.update(schema.usersTable)
|
||||
.set({ aiEnabled: !user.aiEnabled })
|
||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
||||
const updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
||||
});
|
||||
|
||||
bot.action(/^settings_showThinking_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settings_showThinking', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
await db.update(schema.usersTable)
|
||||
.set({ showThinking: !user.showThinking })
|
||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
||||
const updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
||||
});
|
||||
|
||||
bot.action(/^settings_aiModel_(\d+)_(\d+)$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settings_aiModel', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
|
||||
const match = data.match(/^settings_aiModel_(\d+)_/);
|
||||
if (!match) return;
|
||||
|
||||
const page = parseInt(match[1], 10);
|
||||
const pageSize = 4;
|
||||
const start = page * pageSize;
|
||||
const end = start + pageSize;
|
||||
|
||||
const paginatedModels = models.slice(start, end);
|
||||
|
||||
const buttons = paginatedModels.map((series, idx) => {
|
||||
const originalIndex = start + idx;
|
||||
const isSelected = series.models.some(m => m.name === user.customAiModel);
|
||||
const label = isSelected ? `✅ ${series.label}` : series.label;
|
||||
return { text: label, callback_data: `selectseries_${originalIndex}_0_${user.telegramId}` };
|
||||
});
|
||||
|
||||
const navigationButtons: any[] = [];
|
||||
if (page > 0) {
|
||||
navigationButtons.push({ text: Strings.varStrings.varLess, callback_data: `settings_aiModel_${page - 1}_${user.telegramId}` });
|
||||
}
|
||||
if (end < models.length) {
|
||||
navigationButtons.push({ text: Strings.varStrings.varMore, callback_data: `settings_aiModel_${page + 1}_${user.telegramId}` });
|
||||
}
|
||||
|
||||
const keyboard: any[][] = [];
|
||||
for (const button of buttons) {
|
||||
keyboard.push([button]);
|
||||
}
|
||||
|
||||
if (navigationButtons.length > 0) {
|
||||
keyboard.push(navigationButtons);
|
||||
}
|
||||
keyboard.push([{ text: `${Strings.varStrings.varBack}`, callback_data: `settings_back_${user.telegramId}` }]);
|
||||
|
||||
try {
|
||||
await ctx.editMessageText(
|
||||
`${Strings.settings.ai.selectSeries}`,
|
||||
{
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: keyboard
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'settings_aiModel');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^selectseries_\d+_\d+_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('selectseries', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const match = data.match(/^selectseries_(\d+)_(\d+)_(\d+)$/);
|
||||
if (!match) return;
|
||||
const seriesIdx = parseInt(match[1], 10);
|
||||
const modelPage = parseInt(match[2], 10);
|
||||
const series = models[seriesIdx];
|
||||
if (!series) return;
|
||||
|
||||
const seriesPage = Math.floor(seriesIdx / seriesPageSize);
|
||||
|
||||
const start = modelPage * modelPageSize;
|
||||
const end = start + modelPageSize;
|
||||
const paginatedSeriesModels = series.models.slice(start, end);
|
||||
|
||||
const modelButtons = paginatedSeriesModels.map((m, idx) => {
|
||||
const originalModelIndex = start + idx;
|
||||
const isSelected = m.name === user.customAiModel;
|
||||
const label = isSelected ? `✅ ${m.label}` : m.label;
|
||||
return [{ text: `${label} (${m.parameterSize})`, callback_data: `setmodel_${seriesIdx}_${originalModelIndex}_${user.telegramId}` }];
|
||||
});
|
||||
|
||||
const navigationButtons: any[] = [];
|
||||
if (modelPage > 0) {
|
||||
navigationButtons.push({ text: Strings.varStrings.varLess, callback_data: `selectseries_${seriesIdx}_${modelPage - 1}_${user.telegramId}` });
|
||||
}
|
||||
if (end < series.models.length) {
|
||||
navigationButtons.push({ text: Strings.varStrings.varMore, callback_data: `selectseries_${seriesIdx}_${modelPage + 1}_${user.telegramId}` });
|
||||
}
|
||||
|
||||
const keyboard: any[][] = [...modelButtons];
|
||||
if (navigationButtons.length > 0) {
|
||||
keyboard.push(navigationButtons);
|
||||
}
|
||||
keyboard.push([{ text: `${Strings.varStrings.varBack}`, callback_data: `settings_aiModel_${seriesPage}_${user.telegramId}` }]);
|
||||
const desc = user.languageCode === 'pt' ? series.descriptionPt : series.descriptionEn;
|
||||
try {
|
||||
await ctx.editMessageText(
|
||||
`${Strings.settings.ai.seriesDescription.replace('{seriesDescription}', desc)}\n\n${Strings.settings.ai.selectParameterSize.replace('{seriesLabel}', series.label).replace(' [ & Uncensored ]', '')}\n\n${Strings.settings.ai.parameterSizeExplanation}`,
|
||||
{
|
||||
reply_markup: {
|
||||
inline_keyboard: keyboard
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'selectseries');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^setmodel_\d+_\d+_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('setmodel', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const match = data.match(/^setmodel_(\d+)_(\d+)_\d+$/);
|
||||
if (!match) return;
|
||||
const seriesIdx = parseInt(match[1], 10);
|
||||
const modelIdx = parseInt(match[2], 10);
|
||||
const series = models[seriesIdx];
|
||||
const model = series?.models[modelIdx];
|
||||
if (!series || !model) return;
|
||||
await db.update(schema.usersTable)
|
||||
.set({ customAiModel: model.name })
|
||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
||||
const updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||
const menu = getSettingsMenu(updatedUser, Strings);
|
||||
try {
|
||||
if (ctx.callbackQuery.message) {
|
||||
await ctx.editMessageText(
|
||||
menu.text,
|
||||
{
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown'
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await ctx.reply(menu.text, {
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'setmodel');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^settings_aiTemperature_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settings_aiTemperature', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const temps = [0.2, 0.5, 0.7, 0.9, 1.2];
|
||||
try {
|
||||
await ctx.editMessageText(
|
||||
`${Strings.settings.ai.temperatureExplanation}\n\n${Strings.settings.ai.selectTemperature}`,
|
||||
{
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: temps.map(t => [{ text: t.toString(), callback_data: `settemp_${t}_${user.telegramId}` }])
|
||||
.concat([
|
||||
[{ text: Strings.varStrings.varMore, callback_data: `show_more_temps_${user.telegramId}` }],
|
||||
[
|
||||
{ text: Strings.varStrings.varBack, callback_data: `settings_back_${user.telegramId}` }
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'settings_aiTemperature');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^show_more_temps_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('show_more_temps', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const moreTemps = [1.4, 1.6, 1.8, 2.0];
|
||||
try {
|
||||
await ctx.editMessageReplyMarkup({
|
||||
inline_keyboard: moreTemps.map(t => [{ text: `🔥 ${t}`, callback_data: `settemp_${t}_${user.telegramId}` }])
|
||||
.concat([
|
||||
[{ text: Strings.varStrings.varLess, callback_data: `settings_aiTemperature_${user.telegramId}` }],
|
||||
[{ text: Strings.varStrings.varBack, callback_data: `settings_back_${user.telegramId}` }]
|
||||
])
|
||||
});
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'show_more_temps');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^settemp_.+_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settemp', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const temp = parseFloat(data.replace(/^settemp_/, '').replace(/_\d+$/, ''));
|
||||
await db.update(schema.usersTable)
|
||||
.set({ aiTemperature: temp })
|
||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
||||
const updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
||||
});
|
||||
|
||||
bot.action(/^settings_language_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settings_language', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
try {
|
||||
await ctx.editMessageText(
|
||||
Strings.settings.selectLanguage,
|
||||
{
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: {
|
||||
inline_keyboard: langs.map(l => [{ text: l.label, callback_data: `setlang_${l.code}_${user.telegramId}` }]).concat([[{ text: `${Strings.varStrings.varBack}`, callback_data: `settings_back_${user.telegramId}` }]])
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'settings_language');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^settings_back_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('settings_back', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!user) return;
|
||||
const menu = getSettingsMenu(user, Strings);
|
||||
try {
|
||||
if (ctx.callbackQuery.message) {
|
||||
await ctx.editMessageText(
|
||||
menu.text,
|
||||
{
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown'
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await ctx.reply(menu.text, {
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'settings_back');
|
||||
}
|
||||
});
|
||||
|
||||
bot.action(/^setlang_.+_\d+$/, async (ctx) => {
|
||||
const data = (ctx.callbackQuery as any).data;
|
||||
const userId = extractUserIdFromCallback(data);
|
||||
const allowed = !!userId && String(ctx.from.id) === userId;
|
||||
logSettingsAccess('setlang', ctx, allowed, userId);
|
||||
if (!allowed) {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
const { user } = await getUserAndStrings(ctx, db);
|
||||
if (!user) {
|
||||
console.log('[Settings] No user found');
|
||||
return;
|
||||
}
|
||||
const lang = data.replace(/^setlang_/, '').replace(/_\d+$/, '');
|
||||
await db.update(schema.usersTable)
|
||||
.set({ languageCode: lang })
|
||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
||||
const updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||
const updatedStrings = getStrings(updatedUser.languageCode);
|
||||
const menu = getSettingsMenu(updatedUser, updatedStrings);
|
||||
try {
|
||||
if (ctx.callbackQuery.message) {
|
||||
await ctx.editMessageText(
|
||||
menu.text,
|
||||
{
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown'
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await ctx.reply(menu.text, {
|
||||
reply_markup: menu.reply_markup,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
handleTelegramError(err, 'setlang');
|
||||
}
|
||||
});
|
||||
|
||||
bot.command('privacy', spamwatchMiddleware, async (ctx: Context) => {
|
||||
const { Strings } = await getUserAndStrings(ctx, db);
|
||||
if (!ctx.from || !ctx.message) return;
|
||||
const message = Strings.botPrivacy.replace("{botPrivacy}", process.env.botPrivacy ?? "");
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id
|
||||
} as any);
|
||||
});
|
||||
};
|
88
telegram/commands/modarchive.ts
Executable file
88
telegram/commands/modarchive.ts
Executable file
|
@ -0,0 +1,88 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import axios from 'axios';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import { languageCode } from '../utils/language-code';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import { replyToMessageId } from '../utils/reply-to-message-id';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
interface ModuleResult {
|
||||
filePath: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
async function downloadModule(moduleId: string): Promise<ModuleResult | null> {
|
||||
try {
|
||||
const downloadUrl = `${Resources.modArchiveApi}${moduleId}`;
|
||||
const response = await axios({
|
||||
url: downloadUrl,
|
||||
method: 'GET',
|
||||
responseType: 'stream',
|
||||
});
|
||||
const disposition = response.headers['content-disposition'];
|
||||
let fileName = moduleId;
|
||||
if (disposition && disposition.includes('filename=')) {
|
||||
fileName = disposition
|
||||
.split('filename=')[1]
|
||||
.split(';')[0]
|
||||
.replace(/['"]/g, '');
|
||||
}
|
||||
const filePath = path.join(__dirname, fileName);
|
||||
const writer = fs.createWriteStream(filePath);
|
||||
response.data.pipe(writer);
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on('finish', () => resolve({ filePath, fileName }));
|
||||
writer.on('error', reject);
|
||||
});
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const modarchiveHandler = async (ctx: Context) => {
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
const moduleId = ctx.message && 'text' in ctx.message && typeof ctx.message.text === 'string'
|
||||
? ctx.message.text.split(' ')[1]?.trim()
|
||||
: undefined;
|
||||
if (!moduleId || !/^\d+$/.test(moduleId)) {
|
||||
return ctx.reply(Strings.maInvalidModule, {
|
||||
parse_mode: "Markdown",
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
}
|
||||
const result = await downloadModule(moduleId);
|
||||
if (result) {
|
||||
const { filePath, fileName } = result;
|
||||
const regexExtension = /\.\w+$/i;
|
||||
const hasExtension = regexExtension.test(fileName);
|
||||
if (hasExtension) {
|
||||
try {
|
||||
await ctx.replyWithDocument({ source: filePath }, {
|
||||
caption: fileName,
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} finally {
|
||||
try { fs.unlinkSync(filePath); } catch (e) { /* ignore */ }
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return ctx.reply(Strings.maInvalidModule, {
|
||||
parse_mode: "Markdown",
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command(['modarchive', 'tma'], spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'modarchive')) return;
|
||||
await modarchiveHandler(ctx);
|
||||
});
|
||||
};
|
286
telegram/commands/ponyapi.ts
Executable file
286
telegram/commands/ponyapi.ts
Executable file
|
@ -0,0 +1,286 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import axios from 'axios';
|
||||
import verifyInput from '../plugins/verifyInput';
|
||||
import { Telegraf, Context } from 'telegraf';
|
||||
import { languageCode } from '../utils/language-code';
|
||||
import { replyToMessageId } from '../utils/reply-to-message-id';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
interface Character {
|
||||
id: string;
|
||||
name: string;
|
||||
alias: string;
|
||||
url: string;
|
||||
sex: string;
|
||||
residence: string;
|
||||
occupation: string;
|
||||
kind: string;
|
||||
image: string[];
|
||||
}
|
||||
|
||||
interface Episode {
|
||||
id: string;
|
||||
name: string;
|
||||
image: string;
|
||||
url: string;
|
||||
season: string;
|
||||
episode: string;
|
||||
overall: string;
|
||||
airdate: string;
|
||||
storyby: string;
|
||||
writtenby: string;
|
||||
storyboard: string;
|
||||
}
|
||||
|
||||
interface Comic {
|
||||
id: string;
|
||||
name: string;
|
||||
series: string;
|
||||
image: string;
|
||||
url: string;
|
||||
writer: string;
|
||||
artist: string;
|
||||
colorist: string;
|
||||
letterer: string;
|
||||
editor: string;
|
||||
}
|
||||
|
||||
function capitalizeFirstLetter(letter: string) {
|
||||
return letter.charAt(0).toUpperCase() + letter.slice(1);
|
||||
}
|
||||
|
||||
function sendReply(ctx: Context, text: string, reply_to_message_id?: number) {
|
||||
return ctx.reply(text, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
}
|
||||
|
||||
function sendPhoto(ctx: Context, photo: string, caption: string, reply_to_message_id?: number) {
|
||||
return ctx.replyWithPhoto(photo, {
|
||||
caption,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command("mlp", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'mlp-content')) return;
|
||||
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
sendReply(ctx, Strings.ponyApi.helpDesc, reply_to_message_id);
|
||||
});
|
||||
|
||||
bot.command("mlpchar", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'mlp-content')) return;
|
||||
|
||||
const { message } = ctx;
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
const Strings = getStrings(languageCode(ctx) || 'en');
|
||||
const userInput = message.text.split(' ').slice(1).join(' ').trim().replace(/\s+/g, '+');
|
||||
const { noCharName } = Strings.ponyApi;
|
||||
|
||||
if (verifyInput(ctx, userInput, noCharName)) return;
|
||||
if (!userInput || /[^a-zA-Z\s]/.test(userInput) || userInput.length > 30) {
|
||||
return sendReply(ctx, Strings.mlpInvalidCharacter, reply_to_message_id);
|
||||
}
|
||||
|
||||
const capitalizedInput = capitalizeFirstLetter(userInput);
|
||||
const apiUrl = `${Resources.ponyApi}/character/${capitalizedInput}`;
|
||||
|
||||
try {
|
||||
const response = await axios(apiUrl);
|
||||
const data = response.data.data;
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const character = data[0];
|
||||
const aliases = Array.isArray(character.alias)
|
||||
? character.alias.join(', ')
|
||||
: character.alias || Strings.varStrings.varNone;
|
||||
const result = Strings.ponyApi.charRes
|
||||
.replace("{id}", character.id)
|
||||
.replace("{name}", character.name)
|
||||
.replace("{alias}", aliases)
|
||||
.replace("{url}", character.url)
|
||||
.replace("{sex}", character.sex)
|
||||
.replace("{residence}", character.residence ? character.residence.replace(/\n/g, ' / ') : Strings.varStrings.varNone)
|
||||
.replace("{occupation}", character.occupation ? character.occupation.replace(/\n/g, ' / ') : Strings.varStrings.varNone)
|
||||
.replace("{kind}", Array.isArray(character.kind) ? character.kind.join(', ') : Strings.varStrings.varNone);
|
||||
sendPhoto(ctx, character.image[0], result, reply_to_message_id);
|
||||
} else {
|
||||
sendReply(ctx, Strings.ponyApi.noCharFound, reply_to_message_id);
|
||||
}
|
||||
} catch (error: any) {
|
||||
const message = Strings.ponyApi.apiErr.replace('{error}', error.message || 'Unknown error');
|
||||
sendReply(ctx, message, reply_to_message_id);
|
||||
}
|
||||
});
|
||||
|
||||
bot.command("mlpep", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'mlp-content')) return;
|
||||
|
||||
const Strings = getStrings(languageCode(ctx) || 'en');
|
||||
const userInput = ctx.message.text.split(' ').slice(1).join(' ').replace(" ", "+");
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
|
||||
const { noEpisodeNum } = Strings.ponyApi
|
||||
|
||||
if (verifyInput(ctx, userInput, noEpisodeNum, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number(userInput) > 10000) {
|
||||
ctx.reply(Strings.mlpInvalidEpisode, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = `${Resources.ponyApi}/episode/by-overall/${userInput}`;
|
||||
|
||||
try {
|
||||
const response = await axios(apiUrl);
|
||||
const episodeArray: Episode[] = [];
|
||||
|
||||
if (Array.isArray(response.data.data)) {
|
||||
response.data.data.forEach((episode: Episode) => {
|
||||
episodeArray.push({
|
||||
id: episode.id,
|
||||
name: episode.name,
|
||||
image: episode.image,
|
||||
url: episode.url,
|
||||
season: episode.season,
|
||||
episode: episode.episode,
|
||||
overall: episode.overall,
|
||||
airdate: episode.airdate,
|
||||
storyby: episode.storyby ? episode.storyby.replace(/\n/g, ' / ') : Strings.varStrings.varNone,
|
||||
writtenby: episode.writtenby ? episode.writtenby.replace(/\n/g, ' / ') : Strings.varStrings.varNone,
|
||||
storyboard: episode.storyboard ? episode.storyboard.replace(/\n/g, ' / ') : Strings.varStrings.varNone,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (episodeArray.length > 0) {
|
||||
const result = Strings.ponyApi.epRes
|
||||
.replace("{id}", episodeArray[0].id)
|
||||
.replace("{name}", episodeArray[0].name)
|
||||
.replace("{url}", episodeArray[0].url)
|
||||
.replace("{season}", episodeArray[0].season)
|
||||
.replace("{episode}", episodeArray[0].episode)
|
||||
.replace("{overall}", episodeArray[0].overall)
|
||||
.replace("{airdate}", episodeArray[0].airdate)
|
||||
.replace("{storyby}", episodeArray[0].storyby)
|
||||
.replace("{writtenby}", episodeArray[0].writtenby)
|
||||
.replace("{storyboard}", episodeArray[0].storyboard);
|
||||
|
||||
ctx.replyWithPhoto(episodeArray[0].image, {
|
||||
caption: `${result}`,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} else {
|
||||
ctx.reply(Strings.ponyApi.noEpisodeFound, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
} catch (error) {
|
||||
const message = Strings.ponyApi.apiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
bot.command("mlpcomic", spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'mlp-content')) return;
|
||||
|
||||
const Strings = getStrings(languageCode(ctx) || 'en');
|
||||
const userInput = ctx.message.text.split(' ').slice(1).join(' ').replace(" ", "+");
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
|
||||
const { noComicName } = Strings.ponyApi
|
||||
|
||||
if (verifyInput(ctx, userInput, noComicName)) {
|
||||
return;
|
||||
};
|
||||
|
||||
// if special characters or numbers (max 30 characters)
|
||||
if (/[^a-zA-Z\s]/.test(userInput) || userInput.length > 30) {
|
||||
ctx.reply(Strings.mlpInvalidCharacter, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = `${Resources.ponyApi}/comics-story/${userInput}`;
|
||||
|
||||
try {
|
||||
const response = await axios(apiUrl);
|
||||
const comicArray: Comic[] = [];
|
||||
if (Array.isArray(response.data.data)) {
|
||||
response.data.data.forEach(comic => {
|
||||
let letterers: string[] = [];
|
||||
if (comic.letterer) {
|
||||
if (typeof comic.letterer === 'string') {
|
||||
letterers.push(comic.letterer);
|
||||
} else if (Array.isArray(comic.letterer)) {
|
||||
letterers = letterers.concat(comic.letterer);
|
||||
}
|
||||
}
|
||||
comicArray.push({
|
||||
id: comic.id,
|
||||
name: comic.name,
|
||||
series: comic.series,
|
||||
image: comic.image,
|
||||
url: comic.url,
|
||||
writer: comic.writer ? comic.writer.replace(/\n/g, ' / ') : Strings.varStrings.varNone,
|
||||
artist: comic.artist ? comic.artist.replace(/\n/g, ' / ') : Strings.varStrings.varNone,
|
||||
colorist: comic.colorist ? comic.colorist.replace(/\n/g, ' / ') : Strings.varStrings.varNone,
|
||||
letterer: letterers.length > 0 ? letterers.join(', ') : Strings.varStrings.varNone,
|
||||
editor: comic.editor
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (comicArray.length > 0) {
|
||||
const result = Strings.ponyApi.comicRes
|
||||
.replace("{id}", comicArray[0].id)
|
||||
.replace("{name}", comicArray[0].name)
|
||||
.replace("{series}", comicArray[0].series)
|
||||
.replace("{url}", comicArray[0].url)
|
||||
.replace("{writer}", comicArray[0].writer)
|
||||
.replace("{artist}", comicArray[0].artist)
|
||||
.replace("{colorist}", comicArray[0].colorist)
|
||||
.replace("{letterer}", comicArray[0].letterer)
|
||||
.replace("{editor}", comicArray[0].editor);
|
||||
|
||||
ctx.replyWithPhoto(comicArray[0].image, {
|
||||
caption: `${result}`,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} else {
|
||||
ctx.reply(Strings.ponyApi.noComicFound, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
} catch (error) {
|
||||
const message = Strings.ponyApi.apiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
32
telegram/commands/quotes.ts
Executable file
32
telegram/commands/quotes.ts
Executable file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import escape from 'markdown-escape';
|
||||
import axios from 'axios';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
export default (bot) => {
|
||||
bot.command("quote", spamwatchMiddleware, async (ctx) => {
|
||||
const Strings = getStrings(ctx.from.language_code);
|
||||
|
||||
try {
|
||||
const response = await axios.get(Resources.quoteApi);
|
||||
const data = response.data;
|
||||
|
||||
ctx.reply(escape(`${escape(Strings.quoteResult)}\n> *${escape(data.quote)}*\n_${escape(data.author)}_`), {
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
parse_mode: 'Markdown'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ctx.reply(Strings.quoteErr, {
|
||||
reply_to_message_id: ctx.message.id,
|
||||
parse_mode: 'MarkdownV2'
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
*/
|
52
telegram/commands/randompony.ts
Executable file
52
telegram/commands/randompony.ts
Executable file
|
@ -0,0 +1,52 @@
|
|||
import Resources from '../props/resources.json';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import axios from 'axios';
|
||||
import { Telegraf, Context } from 'telegraf';
|
||||
import { languageCode } from '../utils/language-code';
|
||||
import { replyToMessageId } from '../utils/reply-to-message-id';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
export const randomponyHandler = async (ctx: Context & { message: { text: string } }) => {
|
||||
const Strings = getStrings(languageCode(ctx));
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
ctx.reply(Strings.ponyApi.searching, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
try {
|
||||
const response = await axios(Resources.randomPonyApi);
|
||||
let tags: string[] = [];
|
||||
|
||||
if (response.data.pony.tags) {
|
||||
if (typeof response.data.pony.tags === 'string') {
|
||||
tags.push(response.data.pony.tags);
|
||||
} else if (Array.isArray(response.data.pony.tags)) {
|
||||
tags = tags.concat(response.data.pony.tags);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.replyWithPhoto(response.data.pony.representations.full, {
|
||||
caption: `${response.data.pony.sourceURL}\n\n${tags.length > 0 ? tags.join(', ') : ''}`,
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
} catch (error) {
|
||||
const message = Strings.ponyApi.apiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: 'Markdown',
|
||||
...(reply_to_message_id ? { reply_parameters: { message_id: reply_to_message_id } } : {})
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export default (bot: Telegraf<Context>, db) => {
|
||||
bot.command(["rpony", "randompony", "mlpart"], spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'random-pony')) return;
|
||||
await randomponyHandler(ctx);
|
||||
});
|
||||
}
|
124
telegram/commands/weather.ts
Executable file
124
telegram/commands/weather.ts
Executable file
|
@ -0,0 +1,124 @@
|
|||
// Ported and improved from BubbalooTeam's PyCoala bot
|
||||
// Copyright (c) 2024 BubbalooTeam. (https://github.com/BubbalooTeam)
|
||||
// Minor code changes by lucmsilva (https://github.com/lucmsilva651)
|
||||
|
||||
import Resources from '../props/resources.json';
|
||||
import axios from 'axios';
|
||||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import verifyInput from '../plugins/verifyInput';
|
||||
import { Context, Telegraf } from 'telegraf';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
const statusEmojis = {
|
||||
0: '⛈', 1: '⛈', 2: '⛈', 3: '⛈', 4: '⛈', 5: '🌨', 6: '🌨', 7: '🌨',
|
||||
8: '🌨', 9: '🌨', 10: '🌨', 11: '🌧', 12: '🌧', 13: '🌨', 14: '🌨',
|
||||
15: '🌨', 16: '🌨', 17: '⛈', 18: '🌧', 19: '🌫', 20: '🌫', 21: '🌫',
|
||||
22: '🌫', 23: '🌬', 24: '🌬', 25: '🌨', 26: '☁️', 27: '🌥', 28: '🌥',
|
||||
29: '⛅️', 30: '⛅️', 31: '🌙', 32: '☀️', 33: '🌤', 34: '🌤', 35: '⛈',
|
||||
36: '🔥', 37: '🌩', 38: '🌩', 39: '🌧', 40: '🌧', 41: '❄️', 42: '❄️',
|
||||
43: '❄️', 44: 'n/a', 45: '🌧', 46: '🌨', 47: '🌩'
|
||||
};
|
||||
|
||||
const getStatusEmoji = (statusCode: number) => statusEmojis[statusCode] || 'n/a';
|
||||
|
||||
function getLocaleUnit(countryCode: string) {
|
||||
const fahrenheitCountries: string[] = ['US', 'BS', 'BZ', 'KY', 'LR'];
|
||||
|
||||
if (fahrenheitCountries.includes(countryCode)) {
|
||||
return { temperatureUnit: 'F', speedUnit: 'mph', apiUnit: 'e' };
|
||||
} else {
|
||||
return { temperatureUnit: 'C', speedUnit: 'km/h', apiUnit: 'm' };
|
||||
}
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>, db: any) => {
|
||||
bot.command(['weather', 'clima'], spamwatchMiddleware, async (ctx: Context & { message: { text: string } }) => {
|
||||
if (await isCommandDisabled(ctx, db, 'weather')) return;
|
||||
|
||||
const reply_to_message_id = ctx.message.message_id;
|
||||
const userLang = ctx.from?.language_code || "en-US";
|
||||
const Strings = getStrings(userLang);
|
||||
const userInput = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
const { provideLocation } = Strings.weatherStatus
|
||||
|
||||
if (verifyInput(ctx, userInput, provideLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const location: string = userInput;
|
||||
const apiKey: string = process.env.weatherKey || '';
|
||||
|
||||
if (!apiKey || apiKey === "InsertYourWeatherDotComApiKeyHere") {
|
||||
return ctx.reply(Strings.weatherStatus.apiKeyErr, {
|
||||
parse_mode: "Markdown",
|
||||
...({ reply_to_message_id })
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: this also needs to be sanitized and validated
|
||||
const locationResponse = await axios.get(`${Resources.weatherApi}/location/search`, {
|
||||
params: {
|
||||
apiKey: apiKey,
|
||||
format: 'json',
|
||||
language: userLang,
|
||||
query: location,
|
||||
},
|
||||
});
|
||||
|
||||
const locationData = locationResponse.data.location;
|
||||
if (!locationData || !locationData.address) {
|
||||
return ctx.reply(Strings.weatherStatus.invalidLocation, {
|
||||
parse_mode: "Markdown",
|
||||
...({ reply_to_message_id })
|
||||
});
|
||||
}
|
||||
|
||||
const addressFirst = locationData.address[0];
|
||||
const latFirst = locationData.latitude[0];
|
||||
const lonFirst = locationData.longitude[0];
|
||||
const countryCode = locationData.countryCode[0];
|
||||
const { temperatureUnit, speedUnit, apiUnit } = getLocaleUnit(countryCode);
|
||||
|
||||
const weatherResponse = await axios.get(`${Resources.weatherApi}/aggcommon/v3-wx-observations-current`, {
|
||||
params: {
|
||||
apiKey: apiKey,
|
||||
format: 'json',
|
||||
language: userLang,
|
||||
geocode: `${latFirst},${lonFirst}`,
|
||||
units: apiUnit,
|
||||
},
|
||||
});
|
||||
|
||||
const weatherData = weatherResponse.data['v3-wx-observations-current'];
|
||||
const { temperature, temperatureFeelsLike, relativeHumidity, windSpeed, iconCode, wxPhraseLong } = weatherData;
|
||||
|
||||
const weatherMessage = Strings.weatherStatus.resultMsg
|
||||
.replace('{addressFirst}', addressFirst)
|
||||
.replace('{getStatusEmoji(iconCode)}', getStatusEmoji(iconCode))
|
||||
.replace('{wxPhraseLong}', wxPhraseLong)
|
||||
.replace('{temperature}', temperature)
|
||||
.replace('{temperatureFeelsLike}', temperatureFeelsLike)
|
||||
.replace('{temperatureUnit}', temperatureUnit)
|
||||
.replace('{temperatureUnit2}', temperatureUnit)
|
||||
.replace('{relativeHumidity}', relativeHumidity)
|
||||
.replace('{windSpeed}', windSpeed)
|
||||
.replace('{speedUnit}', speedUnit);
|
||||
|
||||
ctx.reply(weatherMessage, {
|
||||
parse_mode: "Markdown",
|
||||
...({ reply_to_message_id })
|
||||
});
|
||||
} catch (error) {
|
||||
const message = Strings.weatherStatus.apiErr.replace('{error}', error.message);
|
||||
ctx.reply(message, {
|
||||
parse_mode: "Markdown",
|
||||
...({ reply_to_message_id })
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
41
telegram/commands/wiki.ts
Executable file
41
telegram/commands/wiki.ts
Executable file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
import axios from "axios";
|
||||
import { Context, Telegraf } from "telegraf";
|
||||
import { replyToMessageId } from "../utils/reply-to-message-id";
|
||||
|
||||
function capitalizeFirstLetter(string: string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
function mediaWikiToMarkdown(input: string) {
|
||||
input = input.replace(/===(.*?)===/g, '*$1*');
|
||||
input = input.replace(/==(.*?)==/g, '*$1*');
|
||||
input = input.replace(/=(.*?)=/g, '*$1*');
|
||||
input = input.replace(/'''(.*?)'''/g, '**$1**');
|
||||
input = input.replace(/''(.*?)''/g, '_$1_');
|
||||
input = input.replace(/^\*\s/gm, '- ');
|
||||
input = input.replace(/^\#\s/gm, '1. ');
|
||||
input = input.replace(/{{Quote(.*?)}}/g, "```\n$1```\n");
|
||||
input = input.replace(/\[\[(.*?)\|?(.*?)\]\]/g, (_, link, text) => {
|
||||
const sanitizedLink = link.replace(/ /g, '_');
|
||||
return text ? `[${text}](${sanitizedLink})` : `[${sanitizedLink}](${sanitizedLink})`;
|
||||
});
|
||||
input = input.replace(/\[\[File:(.*?)\|.*?\]\]/g, '');
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
export default (bot: Telegraf<Context>) => {
|
||||
bot.command("wiki", async (ctx) => {
|
||||
const userInput = capitalizeFirstLetter(ctx.message.text.split(' ')[1]);
|
||||
const apiUrl = `https://en.wikipedia.org/w/index.php?title=${userInput}&action=raw`;
|
||||
const response = await axios(apiUrl, { headers: { 'Accept': "text/plain" } });
|
||||
const convertedResponse = response.data.replace(/<\/?div>/g, "").replace(/{{Infobox.*?}}/s, "");
|
||||
|
||||
const result = mediaWikiToMarkdown(convertedResponse).slice(0, 2048);
|
||||
const reply_to_message_id = replyToMessageId(ctx);
|
||||
|
||||
ctx.reply(result, { parse_mode: 'Markdown', ...({ reply_to_message_id, disable_web_page_preview: true }) });
|
||||
});
|
||||
};
|
||||
*/
|
256
telegram/commands/youtube.ts
Executable file
256
telegram/commands/youtube.ts
Executable file
|
@ -0,0 +1,256 @@
|
|||
import { getStrings } from '../plugins/checklang';
|
||||
import { isOnSpamWatch } from '../spamwatch/spamwatch';
|
||||
import spamwatchMiddlewareModule from '../spamwatch/Middleware';
|
||||
import { execFile } from 'child_process';
|
||||
import { isCommandDisabled } from '../utils/check-command-disabled';
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as ytUrl from 'youtube-url';
|
||||
|
||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch);
|
||||
|
||||
const ytDlpPaths = {
|
||||
linux: path.resolve(__dirname, '../plugins/yt-dlp/yt-dlp'),
|
||||
win32: path.resolve(__dirname, '../plugins/yt-dlp/yt-dlp.exe'),
|
||||
darwin: path.resolve(__dirname, '../plugins/yt-dlp/yt-dlp_macos'),
|
||||
};
|
||||
|
||||
const getYtDlpPath = () => {
|
||||
const platform = os.platform();
|
||||
return ytDlpPaths[platform] || ytDlpPaths.linux;
|
||||
};
|
||||
|
||||
|
||||
const ffmpegPaths = {
|
||||
linux: '/usr/bin/ffmpeg',
|
||||
win32: path.resolve(__dirname, '../plugins/ffmpeg/bin/ffmpeg.exe'),
|
||||
};
|
||||
|
||||
const getFfmpegPath = () => {
|
||||
const platform = os.platform();
|
||||
return ffmpegPaths[platform] || ffmpegPaths.linux;
|
||||
};
|
||||
|
||||
const downloadFromYoutube = async (command: string, args: string[]): Promise<{ stdout: string; stderr: string }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(command, args, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject({ error, stdout, stderr });
|
||||
} else {
|
||||
resolve({ stdout, stderr });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getApproxSize = async (command: string, videoUrl: string): Promise<number> => {
|
||||
let args: string[] = [];
|
||||
if (fs.existsSync(path.resolve(__dirname, "../props/cookies.txt"))) {
|
||||
args = [videoUrl, '--compat-opt', 'manifest-filesize-approx', '-O', 'filesize_approx', '--cookies', path.resolve(__dirname, "../props/cookies.txt")];
|
||||
} else {
|
||||
args = [videoUrl, '--compat-opt', 'manifest-filesize-approx', '-O', 'filesize_approx'];
|
||||
}
|
||||
try {
|
||||
const { stdout } = await downloadFromYoutube(command, args);
|
||||
const sizeInBytes = parseInt(stdout.trim(), 10);
|
||||
if (!isNaN(sizeInBytes)) {
|
||||
return sizeInBytes / (1024 * 1024);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const isValidUrl = (url: string): boolean => {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default (bot, db) => {
|
||||
bot.command(['yt', 'ytdl', 'sdl', 'video', 'dl'], spamwatchMiddleware, async (ctx) => {
|
||||
if (await isCommandDisabled(ctx, db, 'youtube-download')) return;
|
||||
|
||||
const Strings = getStrings(ctx.from.language_code);
|
||||
const ytDlpPath = getYtDlpPath();
|
||||
const userId: number = ctx.from.id;
|
||||
const videoUrl: string = ctx.message.text.split(' ').slice(1).join(' ');
|
||||
const videoIsYoutube: boolean = ytUrl.valid(videoUrl);
|
||||
const randId: string = Math.random().toString(36).substring(2, 15);
|
||||
const mp4File: string = `tmp/${userId}-${randId}.mp4`;
|
||||
const tempMp4File: string = `tmp/${userId}-${randId}.f137.mp4`;
|
||||
const tempWebmFile: string = `tmp/${userId}-${randId}.f251.webm`;
|
||||
let cmdArgs: string = "";
|
||||
const dlpCommand: string = ytDlpPath;
|
||||
const ffmpegPath: string = getFfmpegPath();
|
||||
const ffmpegArgs: string[] = ['-i', tempMp4File, '-i', tempWebmFile, '-c:v copy -c:a copy -strict -2', mp4File];
|
||||
|
||||
/*
|
||||
for now, no checking is done for the video url
|
||||
yt-dlp should handle the validation, though it supports too many sites to hard-code
|
||||
*/
|
||||
if (!videoUrl) {
|
||||
return ctx.reply(Strings.ytDownload.noLink, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
reply_to_message_id: ctx.message.message_id
|
||||
});
|
||||
}
|
||||
|
||||
// make sure its a valid url
|
||||
if (!isValidUrl(videoUrl)) {
|
||||
console.log("[!] Invalid URL:", videoUrl)
|
||||
return ctx.reply(Strings.ytDownload.noLink, {
|
||||
parse_mode: "Markdown",
|
||||
disable_web_page_preview: true,
|
||||
reply_to_message_id: ctx.message.message_id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`\nDownload Request:\nURL: ${videoUrl}\nYOUTUBE: ${videoIsYoutube}\n`)
|
||||
|
||||
if (fs.existsSync(path.resolve(__dirname, "../props/cookies.txt"))) {
|
||||
cmdArgs = "--max-filesize 2G --no-playlist --cookies telegram/props/cookies.txt --merge-output-format mp4 -o";
|
||||
} else {
|
||||
cmdArgs = `--max-filesize 2G --no-playlist --merge-output-format mp4 -o`;
|
||||
}
|
||||
|
||||
try {
|
||||
const downloadingMessage = await ctx.reply(Strings.ytDownload.checkingSize, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
});
|
||||
|
||||
if (fs.existsSync(ytDlpPath)) {
|
||||
const approxSizeInMB = await Promise.race([
|
||||
getApproxSize(ytDlpPath, videoUrl),
|
||||
]);
|
||||
|
||||
if (approxSizeInMB > 50) {
|
||||
console.log("[!] Video size exceeds 50MB:", approxSizeInMB)
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
downloadingMessage.message_id,
|
||||
null,
|
||||
Strings.ytDownload.uploadLimit, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
},
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[i] Downloading video...")
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
downloadingMessage.message_id,
|
||||
null,
|
||||
Strings.ytDownload.downloadingVid, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
},
|
||||
);
|
||||
|
||||
const dlpArgs = [videoUrl, ...cmdArgs.split(' '), mp4File];
|
||||
await downloadFromYoutube(dlpCommand, dlpArgs);
|
||||
|
||||
console.log("[i] Uploading video...")
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
downloadingMessage.message_id,
|
||||
null,
|
||||
Strings.ytDownload.uploadingVid, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
},
|
||||
);
|
||||
|
||||
if (fs.existsSync(tempMp4File)) {
|
||||
await downloadFromYoutube(ffmpegPath, ffmpegArgs);
|
||||
}
|
||||
|
||||
if (fs.existsSync(mp4File)) {
|
||||
const message = Strings.ytDownload.msgDesc.replace("{userMention}", `[${ctx.from.first_name}](tg://user?id=${userId})`)
|
||||
|
||||
try {
|
||||
await ctx.replyWithVideo({
|
||||
source: mp4File
|
||||
}, {
|
||||
caption: message,
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
});
|
||||
|
||||
fs.unlinkSync(mp4File);
|
||||
} catch (error) {
|
||||
if (error.response.description.includes("Request Entity Too Large")) {
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
downloadingMessage.message_id,
|
||||
null,
|
||||
Strings.ytDownload.uploadLimit, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const errMsg = Strings.ytDownload.uploadErr.replace("{error}", error)
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
downloadingMessage.message_id,
|
||||
null,
|
||||
errMsg, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
fs.unlinkSync(mp4File);
|
||||
}
|
||||
} else {
|
||||
await ctx.reply(mp4File, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await ctx.telegram.editMessageText(
|
||||
ctx.chat.id,
|
||||
downloadingMessage.message_id,
|
||||
null,
|
||||
Strings.ytDownload.libNotFound, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
console.log("[i] Request completed\n")
|
||||
} catch (error) {
|
||||
let errMsg = Strings.ytDownload.uploadErr
|
||||
|
||||
if (error.stderr.includes("--cookies-from-browser")) {
|
||||
console.log("[!] Ratelimited by video provider:", error.stderr)
|
||||
errMsg = Strings.ytDownload.botDetection
|
||||
if (error.stderr.includes("youtube")) {
|
||||
errMsg = Strings.ytDownload.botDetection.replace("video provider", "YouTube")
|
||||
}
|
||||
} else {
|
||||
console.log("[!]", error.stderr)
|
||||
}
|
||||
|
||||
// will no longer edit the message as the message context is not outside the try block
|
||||
await ctx.reply(errMsg, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_to_message_id: ctx.message.message_id,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue