better logging, stats tracking command, clean up url warning, user permissions on settings
This commit is contained in:
parent
23ebd021f3
commit
a952ddfc67
7 changed files with 340 additions and 251 deletions
|
@ -20,3 +20,4 @@ maxRetries = 9999
|
||||||
botAdmins = 00000000, 00000000, 00000000
|
botAdmins = 00000000, 00000000, 00000000
|
||||||
lastKey = "InsertYourLastFmApiKeyHere"
|
lastKey = "InsertYourLastFmApiKeyHere"
|
||||||
weatherKey = "InsertYourWeatherDotComApiKeyHere"
|
weatherKey = "InsertYourWeatherDotComApiKeyHere"
|
||||||
|
longerLogs = true
|
|
@ -122,6 +122,7 @@ If you prefer to use Docker directly, you can use these instructions instead.
|
||||||
- **botAdmins**: Put the ID of the people responsible for managing the bot. They can use some administrative + exclusive commands on any group.
|
- **botAdmins**: Put the ID of the people responsible for managing the bot. They can use some administrative + exclusive commands on any group.
|
||||||
- **lastKey**: Last.fm API key, for use on `lastfm.js` functions, like see who is listening to what song and etc.
|
- **lastKey**: Last.fm API key, for use on `lastfm.js` functions, like see who is listening to what song and etc.
|
||||||
- **weatherKey**: Weather.com API key, used for the `/weather` command.
|
- **weatherKey**: Weather.com API key, used for the `/weather` command.
|
||||||
|
- **longerLogs**: Set to `true` to enable verbose logging whenever possible.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import { logger } from "../utils/log"
|
||||||
import { ensureUserInDb } from "../utils/ensure-user"
|
import { ensureUserInDb } from "../utils/ensure-user"
|
||||||
import * as schema from '../db/schema'
|
import * as schema from '../db/schema'
|
||||||
import type { NodePgDatabase } from "drizzle-orm/node-postgres"
|
import type { NodePgDatabase } from "drizzle-orm/node-postgres"
|
||||||
|
import { eq, sql } from 'drizzle-orm'
|
||||||
|
|
||||||
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch)
|
const spamwatchMiddleware = spamwatchMiddlewareModule(isOnSpamWatch)
|
||||||
export const flash_model = process.env.flashModel || "gemma3:4b"
|
export const flash_model = process.env.flashModel || "gemma3:4b"
|
||||||
|
@ -270,7 +271,7 @@ function containsUrls(text: string): boolean {
|
||||||
return text.includes('http://') || text.includes('https://');
|
return text.includes('http://') || text.includes('https://');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Message, model: string, aiTemperature: number, originalMessage: string): Promise<{ success: boolean; response?: string; error?: string }> {
|
async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Message, model: string, aiTemperature: number, originalMessage: string, db: NodePgDatabase<typeof schema>, userId: string): Promise<{ success: boolean; response?: string; error?: string }> {
|
||||||
const Strings = getStrings(languageCode(ctx));
|
const Strings = getStrings(languageCode(ctx));
|
||||||
if (!ctx.chat) {
|
if (!ctx.chat) {
|
||||||
return {
|
return {
|
||||||
|
@ -283,7 +284,11 @@ async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Me
|
||||||
.replace("{model}", model)
|
.replace("{model}", model)
|
||||||
.replace("{temperature}", aiTemperature)
|
.replace("{temperature}", aiTemperature)
|
||||||
.replace("{status}", status) + "\n\n";
|
.replace("{status}", status) + "\n\n";
|
||||||
const urlWarning = containsUrls(originalMessage) ? Strings.ai.urlWarning : '';
|
|
||||||
|
const promptCharCount = originalMessage.length;
|
||||||
|
await db.update(schema.usersTable)
|
||||||
|
.set({ aiCharacters: sql`${schema.usersTable.aiCharacters} + ${promptCharCount}` })
|
||||||
|
.where(eq(schema.usersTable.telegramId, userId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const aiResponse = await axios.post<unknown>(
|
const aiResponse = await axios.post<unknown>(
|
||||||
|
@ -314,7 +319,7 @@ async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Me
|
||||||
try {
|
try {
|
||||||
ln = JSON.parse(line);
|
ln = JSON.parse(line);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[✨ AI | !] Error parsing chunk:", e);
|
console.error("[✨ AI | !] Error parsing chunk");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (model === thinking_model && ln.response) {
|
if (model === thinking_model && ln.response) {
|
||||||
|
@ -351,7 +356,7 @@ async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Me
|
||||||
ctx,
|
ctx,
|
||||||
ctx.chat.id,
|
ctx.chat.id,
|
||||||
replyGenerating.message_id,
|
replyGenerating.message_id,
|
||||||
modelHeader + urlWarning + escapeMarkdown(fullResponse),
|
modelHeader + escapeMarkdown(fullResponse),
|
||||||
{ parse_mode: 'Markdown' }
|
{ parse_mode: 'Markdown' }
|
||||||
);
|
);
|
||||||
lastUpdateCharCount = fullResponse.length;
|
lastUpdateCharCount = fullResponse.length;
|
||||||
|
@ -365,7 +370,7 @@ async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Me
|
||||||
ctx,
|
ctx,
|
||||||
ctx.chat.id,
|
ctx.chat.id,
|
||||||
replyGenerating.message_id,
|
replyGenerating.message_id,
|
||||||
modelHeader + urlWarning + escapeMarkdown(fullResponse),
|
modelHeader + escapeMarkdown(fullResponse),
|
||||||
{ parse_mode: 'Markdown' }
|
{ parse_mode: 'Markdown' }
|
||||||
);
|
);
|
||||||
lastUpdateCharCount = fullResponse.length;
|
lastUpdateCharCount = fullResponse.length;
|
||||||
|
@ -383,9 +388,16 @@ async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Me
|
||||||
ctx,
|
ctx,
|
||||||
ctx.chat.id,
|
ctx.chat.id,
|
||||||
replyGenerating.message_id,
|
replyGenerating.message_id,
|
||||||
modelHeader + urlWarning + escapeMarkdown(fullResponse),
|
modelHeader + escapeMarkdown(fullResponse),
|
||||||
{ parse_mode: 'Markdown' }
|
{ parse_mode: 'Markdown' }
|
||||||
);
|
);
|
||||||
|
const responseCharCount = fullResponse.length;
|
||||||
|
await db.update(schema.usersTable)
|
||||||
|
.set({
|
||||||
|
aiCharacters: sql`${schema.usersTable.aiCharacters} + ${responseCharCount}`,
|
||||||
|
aiRequests: sql`${schema.usersTable.aiRequests} + 1`
|
||||||
|
})
|
||||||
|
.where(eq(schema.usersTable.telegramId, userId));
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
response: fullResponse,
|
response: fullResponse,
|
||||||
|
@ -437,9 +449,9 @@ async function getResponse(prompt: string, ctx: TextContext, replyGenerating: Me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleAiReply(ctx: TextContext, model: string, prompt: string, replyGenerating: Message, aiTemperature: number, originalMessage: string) {
|
async function handleAiReply(ctx: TextContext, model: string, prompt: string, replyGenerating: Message, aiTemperature: number, originalMessage: string, db: NodePgDatabase<typeof schema>, userId: string) {
|
||||||
const Strings = getStrings(languageCode(ctx));
|
const Strings = getStrings(languageCode(ctx));
|
||||||
const aiResponse = await getResponse(prompt, ctx, replyGenerating, model, aiTemperature, originalMessage);
|
const aiResponse = await getResponse(prompt, ctx, replyGenerating, model, aiTemperature, originalMessage, db, userId);
|
||||||
if (!aiResponse) return;
|
if (!aiResponse) return;
|
||||||
if (!ctx.chat) return;
|
if (!ctx.chat) return;
|
||||||
if (aiResponse.success && aiResponse.response) {
|
if (aiResponse.success && aiResponse.response) {
|
||||||
|
@ -453,7 +465,7 @@ async function handleAiReply(ctx: TextContext, model: string, prompt: string, re
|
||||||
ctx,
|
ctx,
|
||||||
ctx.chat.id,
|
ctx.chat.id,
|
||||||
replyGenerating.message_id,
|
replyGenerating.message_id,
|
||||||
modelHeader + urlWarning + sanitizeMarkdownForTelegram(aiResponse.response),
|
modelHeader + sanitizeMarkdownForTelegram(aiResponse.response) + urlWarning,
|
||||||
{ parse_mode: 'Markdown' }
|
{ parse_mode: 'Markdown' }
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -545,11 +557,11 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
if (command === 'ai') {
|
if (command === 'ai') {
|
||||||
model = customAiModel || flash_model;
|
model = customAiModel || flash_model;
|
||||||
fixedMsg = message.replace(/^\/ai(@\w+)?\s*/, "").trim();
|
fixedMsg = message.replace(/^\/ai(@\w+)?\s*/, "").trim();
|
||||||
logger.logCmdStart(author, "ask");
|
logger.logCmdStart(author, command, model);
|
||||||
} else {
|
} else {
|
||||||
model = command === 'ask' ? flash_model : thinking_model;
|
model = command === 'ask' ? flash_model : thinking_model;
|
||||||
fixedMsg = message.replace(/^\/(ask|think)(@\w+)?\s*/, "").trim();
|
fixedMsg = message.replace(/^\/(ask|think)(@\w+)?\s*/, "").trim();
|
||||||
logger.logCmdStart(author, command);
|
logger.logCmdStart(author, command, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process.env.ollamaApi) {
|
if (!process.env.ollamaApi) {
|
||||||
|
@ -573,9 +585,8 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
parse_mode: 'Markdown',
|
parse_mode: 'Markdown',
|
||||||
...(reply_to_message_id && { reply_parameters: { message_id: reply_to_message_id } })
|
...(reply_to_message_id && { reply_parameters: { message_id: reply_to_message_id } })
|
||||||
});
|
});
|
||||||
logger.logPrompt(fixedMsg);
|
|
||||||
const prompt = sanitizeForJson(await usingSystemPrompt(ctx, db, botName, fixedMsg));
|
const prompt = sanitizeForJson(await usingSystemPrompt(ctx, db, botName, fixedMsg));
|
||||||
await handleAiReply(ctx, model, prompt, replyGenerating, aiTemperature, fixedMsg);
|
await handleAiReply(ctx, model, prompt, replyGenerating, aiTemperature, fixedMsg, db, user.telegramId);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isProcessing) {
|
if (isProcessing) {
|
||||||
|
@ -601,4 +612,16 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
if (!ctx.message || !('text' in ctx.message)) return;
|
if (!ctx.message || !('text' in ctx.message)) return;
|
||||||
await aiCommandHandler(ctx as TextContext, 'ai');
|
await aiCommandHandler(ctx as TextContext, 'ai');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bot.command(["aistats"], spamwatchMiddleware, async (ctx) => {
|
||||||
|
const { user, Strings } = await getUserWithStringsAndModel(ctx, db);
|
||||||
|
if (!user) {
|
||||||
|
await ctx.reply(Strings.userNotFound || "User not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bookCount = Math.max(1, Math.round(user.aiCharacters / 500000));
|
||||||
|
const bookWord = bookCount === 1 ? 'book' : 'books';
|
||||||
|
const msg = `${Strings.aiStats.header}\n\n${Strings.aiStats.requests.replace('{aiRequests}', user.aiRequests)}\n${Strings.aiStats.characters.replace('{aiCharacters}', user.aiCharacters).replace('{bookCount}', bookCount).replace('books', bookWord)}`;
|
||||||
|
await ctx.reply(msg, { parse_mode: 'Markdown' });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,23 +48,41 @@ type SettingsMenu = { text: string, reply_markup: any };
|
||||||
function getSettingsMenu(user: UserRow, Strings: any): SettingsMenu {
|
function getSettingsMenu(user: UserRow, Strings: any): SettingsMenu {
|
||||||
const langObj = langs.find(l => l.code === user.languageCode);
|
const langObj = langs.find(l => l.code === user.languageCode);
|
||||||
const langLabel = langObj ? langObj.label : user.languageCode;
|
const langLabel = langObj ? langObj.label : user.languageCode;
|
||||||
|
const userId = user.telegramId;
|
||||||
return {
|
return {
|
||||||
text: Strings.settings.selectSetting,
|
text: Strings.settings.selectSetting,
|
||||||
reply_markup: {
|
reply_markup: {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
[
|
[
|
||||||
{ text: `✨ ${Strings.settings.ai.aiEnabled}: ${user.aiEnabled ? Strings.settings.enabled : Strings.settings.disabled}`, callback_data: 'settings_aiEnabled' },
|
{ 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' }
|
{ text: `🧠 ${Strings.settings.ai.aiModel}: ${getModelLabelByName(user.customAiModel)}`, callback_data: `settings_aiModel_${userId}` }
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ text: `🌡️ ${Strings.settings.ai.aiTemperature}: ${user.aiTemperature}`, callback_data: 'settings_aiTemperature' },
|
{ text: `🌡️ ${Strings.settings.ai.aiTemperature}: ${user.aiTemperature}`, callback_data: `settings_aiTemperature_${userId}` },
|
||||||
{ text: `🌐 ${langLabel}`, callback_data: 'settings_language' }
|
{ text: `🌐 ${langLabel}`, callback_data: `settings_language_${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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
bot.start(spamwatchMiddleware, async (ctx: Context) => {
|
bot.start(spamwatchMiddleware, async (ctx: Context) => {
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
|
@ -118,8 +136,15 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
await ctx.editMessageReplyMarkup(menu.reply_markup);
|
await ctx.editMessageReplyMarkup(menu.reply_markup);
|
||||||
};
|
};
|
||||||
|
|
||||||
bot.action('settings_aiEnabled', async (ctx) => {
|
bot.action(/^settings_aiEnabled_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
@ -128,13 +153,17 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
.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 updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||||
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling settings_aiEnabled callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action('settings_aiModel', async (ctx) => {
|
bot.action(/^settings_aiModel_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
@ -144,9 +173,9 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
{
|
{
|
||||||
reply_markup: {
|
reply_markup: {
|
||||||
inline_keyboard: models.map(series => [
|
inline_keyboard: models.map(series => [
|
||||||
{ text: series.label, callback_data: `selectseries_${series.name}` }
|
{ text: series.label, callback_data: `selectseries_${series.name}_${user.telegramId}` }
|
||||||
]).concat([[
|
]).concat([[
|
||||||
{ text: `${Strings.varStrings.varBack}`, callback_data: 'settings_back' }
|
{ text: `${Strings.varStrings.varBack}`, callback_data: `settings_back_${user.telegramId}` }
|
||||||
]])
|
]])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,18 +191,21 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
)
|
)
|
||||||
console.error('Unexpected Telegram error:', err);
|
console.error('Unexpected Telegram error:', err);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling settings_aiModel callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action(/^selectseries_.+$/, async (ctx) => {
|
bot.action(/^selectseries_.+_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
const data = (ctx.callbackQuery as any).data;
|
const seriesName = data.replace(/^selectseries_/, '').replace(/_\d+$/, '');
|
||||||
const seriesName = data.replace('selectseries_', '');
|
|
||||||
const series = models.find(s => s.name === seriesName);
|
const series = models.find(s => s.name === seriesName);
|
||||||
if (!series) return;
|
if (!series) return;
|
||||||
const desc = user.languageCode === 'pt' ? series.descriptionPt : series.descriptionEn;
|
const desc = user.languageCode === 'pt' ? series.descriptionPt : series.descriptionEn;
|
||||||
|
@ -183,9 +215,9 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
{
|
{
|
||||||
reply_markup: {
|
reply_markup: {
|
||||||
inline_keyboard: series.models.map(m => [
|
inline_keyboard: series.models.map(m => [
|
||||||
{ text: `${m.label} (${m.parameterSize})`, callback_data: `setmodel_${series.name}_${m.name}` }
|
{ text: `${m.label} (${m.parameterSize})`, callback_data: `setmodel_${series.name}_${m.name}_${user.telegramId}` }
|
||||||
]).concat([[
|
]).concat([[
|
||||||
{ text: `${Strings.varStrings.varBack}`, callback_data: 'settings_aiModel' }
|
{ text: `${Strings.varStrings.varBack}`, callback_data: `settings_aiModel_${user.telegramId}` }
|
||||||
]])
|
]])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,20 +233,23 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
)
|
)
|
||||||
console.error('Unexpected Telegram error:', err);
|
console.error('Unexpected Telegram error:', err);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling selectseries callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action(/^setmodel_.+$/, async (ctx) => {
|
bot.action(/^setmodel_.+_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
const data = (ctx.callbackQuery as any).data;
|
|
||||||
const parts = data.split('_');
|
const parts = data.split('_');
|
||||||
const seriesName = parts[1];
|
const seriesName = parts[1];
|
||||||
const modelName = parts.slice(2).join('_');
|
const modelName = parts.slice(2, -1).join('_');
|
||||||
const series = models.find(s => s.name === seriesName);
|
const series = models.find(s => s.name === seriesName);
|
||||||
const model = series?.models.find(m => m.name === modelName);
|
const model = series?.models.find(m => m.name === modelName);
|
||||||
if (!series || !model) return;
|
if (!series || !model) return;
|
||||||
|
@ -249,20 +284,24 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
)
|
)
|
||||||
console.error('[Settings] Unexpected Telegram error:', err);
|
console.error('[Settings] Unexpected Telegram error:', err);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling setmodel callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action('settings_aiTemperature', async (ctx) => {
|
bot.action(/^settings_aiTemperature_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
const temps = [0.2, 0.5, 0.7, 0.9, 1.2];
|
const temps = [0.2, 0.5, 0.7, 0.9, 1.2];
|
||||||
try {
|
try {
|
||||||
await ctx.editMessageReplyMarkup({
|
await ctx.editMessageReplyMarkup({
|
||||||
inline_keyboard: temps.map(t => [{ text: t.toString(), callback_data: `settemp_${t}` }]).concat([[{ text: `${Strings.varStrings.varBack}`, callback_data: 'settings_back' }]])
|
inline_keyboard: temps.map(t => [{ text: t.toString(), callback_data: `settemp_${t}_${user.telegramId}` }]).concat([[{ text: `${Strings.varStrings.varBack}`, callback_data: `settings_back_${user.telegramId}` }]])
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
|
@ -275,36 +314,43 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
)
|
)
|
||||||
console.error('Unexpected Telegram error:', err);
|
console.error('Unexpected Telegram error:', err);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling settings_aiTemperature callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action(/^settemp_.+$/, async (ctx) => {
|
bot.action(/^settemp_.+_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
const data = (ctx.callbackQuery as any).data;
|
const temp = parseFloat(data.replace(/^settemp_/, '').replace(/_\d+$/, ''));
|
||||||
const temp = parseFloat(data.replace('settemp_', ''));
|
|
||||||
await db.update(schema.usersTable)
|
await db.update(schema.usersTable)
|
||||||
.set({ aiTemperature: temp })
|
.set({ aiTemperature: temp })
|
||||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
.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 updatedUser = (await db.query.usersTable.findMany({ where: (fields, { eq }) => eq(fields.telegramId, String(user.telegramId)), limit: 1 }))[0];
|
||||||
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
await updateSettingsKeyboard(ctx, updatedUser, Strings);
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling settemp callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action('settings_language', async (ctx) => {
|
bot.action(/^settings_language_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
try {
|
try {
|
||||||
await ctx.editMessageReplyMarkup({
|
await ctx.editMessageReplyMarkup({
|
||||||
inline_keyboard: langs.map(l => [{ text: l.label, callback_data: `setlang_${l.code}` }]).concat([[{ text: `${Strings.varStrings.varBack}`, callback_data: 'settings_back' }]])
|
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) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
|
@ -317,42 +363,39 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
)
|
)
|
||||||
console.error('Unexpected Telegram error:', err);
|
console.error('Unexpected Telegram error:', err);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling settings_language callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.action('settings_back', async (ctx) => {
|
bot.action(/^settings_back_\d+$/, async (ctx) => {
|
||||||
try {
|
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();
|
await ctx.answerCbQuery();
|
||||||
const { user, Strings } = await getUserAndStrings(ctx, db);
|
const { user, Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
await updateSettingsKeyboard(ctx, user, Strings);
|
await updateSettingsKeyboard(ctx, user, Strings);
|
||||||
} catch (err) {
|
|
||||||
console.error('Error handling settings_back callback:', err);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.command('privacy', spamwatchMiddleware, async (ctx: Context) => {
|
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);
|
const { Strings } = await getUserAndStrings(ctx, db);
|
||||||
if (!ctx.from || !ctx.message) return;
|
return ctx.answerCbQuery(getNotAllowedMessage(Strings), { show_alert: true });
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
bot.action(/^setlang_.+$/, async (ctx) => {
|
|
||||||
try {
|
|
||||||
await ctx.answerCbQuery();
|
await ctx.answerCbQuery();
|
||||||
const { user } = await getUserAndStrings(ctx, db);
|
const { user } = await getUserAndStrings(ctx, db);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
console.log('[Settings] No user found');
|
console.log('[Settings] No user found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = (ctx.callbackQuery as any).data;
|
const lang = data.replace(/^setlang_/, '').replace(/_\d+$/, '');
|
||||||
const lang = data.replace('setlang_', '');
|
|
||||||
await db.update(schema.usersTable)
|
await db.update(schema.usersTable)
|
||||||
.set({ languageCode: lang })
|
.set({ languageCode: lang })
|
||||||
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
.where(eq(schema.usersTable.telegramId, String(user.telegramId)));
|
||||||
|
@ -385,8 +428,15 @@ export default (bot: Telegraf<Context>, db: NodePgDatabase<typeof schema>) => {
|
||||||
)
|
)
|
||||||
console.error('[Settings] Unexpected Telegram error:', err);
|
console.error('[Settings] Unexpected Telegram error:', err);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
});
|
||||||
console.error('[Settings] Error handling setlang callback:', err);
|
|
||||||
}
|
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);
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -74,7 +74,7 @@
|
||||||
"languageCode": "Language",
|
"languageCode": "Language",
|
||||||
"thinking": "Thinking...",
|
"thinking": "Thinking...",
|
||||||
"finishedThinking": "Done.",
|
"finishedThinking": "Done.",
|
||||||
"urlWarning": "\n\n⚠️ The user provided one or more URLs in their message. Please do not visit any suspicious URLs.",
|
"urlWarning": "\n\n⚠️ Note: The model cannot access or visit links!",
|
||||||
"inQueue": "ℹ️ You are {position} in the queue.",
|
"inQueue": "ℹ️ You are {position} in the queue.",
|
||||||
"startingProcessing": "✨ Starting to process your request...",
|
"startingProcessing": "✨ Starting to process your request...",
|
||||||
"systemPrompt": "You are a friendly assistant called {botName}, capable of Telegram MarkdownV2.\nYou are currently in a chat with a user, who has sent a message to you.\nCurrent Date/Time (UTC): {date}\n\n---\n\nRespond to the user's message:\n{message}",
|
"systemPrompt": "You are a friendly assistant called {botName}, capable of Telegram MarkdownV2.\nYou are currently in a chat with a user, who has sent a message to you.\nCurrent Date/Time (UTC): {date}\n\n---\n\nRespond to the user's message:\n{message}",
|
||||||
|
@ -182,5 +182,10 @@
|
||||||
"pong": "Pong in {ms}ms.",
|
"pong": "Pong in {ms}ms.",
|
||||||
"botInfo": "Kowalski is a multipurpose bot with a variety of features, including AI, moderation, and more.",
|
"botInfo": "Kowalski is a multipurpose bot with a variety of features, including AI, moderation, and more.",
|
||||||
"credits": "Kowalski was created by ihatenodejs/Aidan, with contributions from the open-source community. It is licensed under the Unlicense license."
|
"credits": "Kowalski was created by ihatenodejs/Aidan, with contributions from the open-source community. It is licensed under the Unlicense license."
|
||||||
|
},
|
||||||
|
"aiStats": {
|
||||||
|
"header": "✨ *Your AI Usage Stats*",
|
||||||
|
"requests": "*Total AI Requests:* {aiRequests}",
|
||||||
|
"characters": "*Total AI Characters:* {aiCharacters}\n_That's around {bookCount} books of text!_"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
"askNoMessage": "Você precisa fazer uma pergunta\\.",
|
"askNoMessage": "Você precisa fazer uma pergunta\\.",
|
||||||
"thinking": "Pensando\\.\\.\\.",
|
"thinking": "Pensando\\.\\.\\.",
|
||||||
"finishedThinking": "Pronto\\.",
|
"finishedThinking": "Pronto\\.",
|
||||||
"urlWarning": "\n\n⚠️ O usuário forneceu um ou mais URLs na sua mensagem\\. Por favor, não visite URLs suspeitos\\.",
|
"urlWarning": "\n\n⚠️ Nota: O modelo de IA não pode acessar ou visitar links!",
|
||||||
"inQueue": "ℹ️ Você é o {position} na fila.",
|
"inQueue": "ℹ️ Você é o {position} na fila.",
|
||||||
"startingProcessing": "✨ Começando a processar o seu pedido\\.\\.\\.",
|
"startingProcessing": "✨ Começando a processar o seu pedido\\.\\.\\.",
|
||||||
"aiEnabled": "IA",
|
"aiEnabled": "IA",
|
||||||
|
@ -179,5 +179,10 @@
|
||||||
"gsmarenaNotAllowed": "você não tem permissão para interagir com isso.",
|
"gsmarenaNotAllowed": "você não tem permissão para interagir com isso.",
|
||||||
"gsmarenaInvalidOrExpired": "Ops! Opção inválida ou expirada. Por favor, tente novamente.",
|
"gsmarenaInvalidOrExpired": "Ops! Opção inválida ou expirada. Por favor, tente novamente.",
|
||||||
"gsmarenaDeviceDetails": "estes são os detalhes do seu dispositivo:",
|
"gsmarenaDeviceDetails": "estes são os detalhes do seu dispositivo:",
|
||||||
"gsmarenaErrorFetchingDetails": "Erro ao buscar detalhes do celular."
|
"gsmarenaErrorFetchingDetails": "Erro ao buscar detalhes do celular.",
|
||||||
|
"aiStats": {
|
||||||
|
"header": "✨ *Suas estatísticas de uso de IA*",
|
||||||
|
"requests": "*Total de requisições de IA:* {aiRequests}",
|
||||||
|
"characters": "*Total de caracteres de IA:* {aiCharacters}\n_Isso é cerca de {bookCount} livros de texto!_"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,8 @@ class Logger {
|
||||||
return Logger.instance
|
return Logger.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
logCmdStart(user: string, type: "ask" | "think"): void {
|
logCmdStart(user: string, command: string, model: string): void {
|
||||||
console.log(`\n[✨ AI | START] Received /${type} for model ${type === "ask" ? flash_model : thinking_model}`)
|
console.log(`\n[✨ AI | START] Received /${command} for model ${model} (from ${user})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
logThinking(chatId: number, messageId: number, thinking: boolean): void {
|
logThinking(chatId: number, messageId: number, thinking: boolean): void {
|
||||||
|
@ -55,12 +55,16 @@ class Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
logChunk(chatId: number, messageId: number, text: string, isOverflow: boolean = false): void {
|
logChunk(chatId: number, messageId: number, text: string, isOverflow: boolean = false): void {
|
||||||
|
if (process.env.longerLogs === 'true') {
|
||||||
const prefix = isOverflow ? "[✨ AI | OVERFLOW]" : "[✨ AI | CHUNK]"
|
const prefix = isOverflow ? "[✨ AI | OVERFLOW]" : "[✨ AI | CHUNK]"
|
||||||
console.log(`${prefix} [${chatId}:${messageId}] ${text.length} chars pushed to Telegram`)
|
console.log(`${prefix} [${chatId}:${messageId}] ${text.length} chars pushed to Telegram`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logPrompt(prompt: string): void {
|
logPrompt(prompt: string): void {
|
||||||
console.log(`[✨ AI | PROMPT] ${prompt.length} chars input`)
|
if (process.env.longerLogs === 'true') {
|
||||||
|
console.log(`[✨ AI | PROMPT] ${prompt}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logError(error: unknown): void {
|
logError(error: unknown): void {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue