diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2026-05-06 21:46:21 +0300 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2026-05-06 21:46:21 +0300 |
| commit | 713ced9c68cd70a8e56e8ce5ed27cb9bbe3e55c4 (patch) | |
| tree | 29f9997386b9f46db9f263ef63a5e01ca19db393 /src/tg.ts | |
| parent | cba8c3d49b53702a488eaff16109e29e49d94b6d (diff) | |
Add shopping list features, rework
Diffstat (limited to 'src/tg.ts')
| -rw-r--r-- | src/tg.ts | 119 |
1 files changed, 110 insertions, 9 deletions
@@ -1,11 +1,14 @@ -import { Telegraf } from "telegraf"; +import { Markup, Telegraf } from "telegraf"; import config from "./config"; -import { addActiveChat, getActiveChats, removeActiveChat } from "./db"; +import { + addActiveChat, getActiveChats, removeActiveChat, + addShoppingItem, getShoppingList, removeShoppingItem, + addRecurringTask, getRecurringTasks, updateRecurringTask, markTaskDone, removeRecurringTask, +} from "./db"; import { tgAuth } from "./middleware"; +import { getNextDate, formatDate, parseInterval, today } from "./tasks"; -console.log(`Setting up Telegram bot with token ${config.tgBotToken}`); const bot = new Telegraf(config.tgBotToken); - bot.use(tgAuth); bot.command("/start", async (ctx) => { @@ -18,13 +21,111 @@ bot.command("/stop", async (ctx) => { await ctx.reply("HommaBot pysäytetty!"); }); +// --- Shopping list --- + +bot.command("add", async (ctx) => { + const item = ctx.message.text.replace(/^\/add\s*/, "").trim(); + if (!item) { await ctx.reply("Käyttö: /add <tuote>"); return; } + addShoppingItem(item, ctx.message.from.id); + await ctx.reply(`Lisätty: ${item}`); +}); + +bot.command("list", async (ctx) => { + const items = getShoppingList(); + if (items.length === 0) { await ctx.reply("Ostoslista on tyhjä."); return; } + const text = items.map((i, idx) => `${idx + 1}. ${i.item}`).join("\n"); + const buttons = items.map((i) => [Markup.button.callback(`❌ ${i.item}`, `del:${i.id}`)]); + await ctx.reply(`🛒 Ostoslista:\n${text}`, Markup.inlineKeyboard(buttons)); +}); + +bot.action(/^del:(\d+)$/, async (ctx) => { + const id = Number(ctx.match[1]); + removeShoppingItem(id); + await ctx.answerCbQuery("Poistettu!"); + const items = getShoppingList(); + if (items.length === 0) { await ctx.editMessageText("Ostoslista on tyhjä."); return; } + const text = items.map((i, idx) => `${idx + 1}. ${i.item}`).join("\n"); + const buttons = items.map((i) => [Markup.button.callback(`❌ ${i.item}`, `del:${i.id}`)]); + await ctx.editMessageText(`🛒 Ostoslista:\n${text}`, Markup.inlineKeyboard(buttons)); +}); + +// --- Recurring tasks --- + +bot.command("newtask", async (ctx) => { + const args = ctx.message.text.replace(/^\/newtask\s*/, "").trim(); + const match = /^(\d+(?:pv|vk|kk|v))\s+(.+)$/.exec(args); + if (!match) { + await ctx.reply("Käyttö: /newtask <intervalli> <nimi>\nEsim: /newtask 2vk Imurointi\n\nIntervallit: pv, vk, kk, v"); + return; + } + try { + parseInterval(match[1]); + } catch { + await ctx.reply("Virheellinen intervalli. Käytä: pv, vk, kk, v (esim. 2vk, 1kk)"); + return; + } + addRecurringTask(match[2], match[1]); + await ctx.reply(`✅ Uusi tehtävä: ${match[2]} (joka ${match[1]})`); +}); + +bot.command("tasks", async (ctx) => { + const tasks = getRecurringTasks(); + if (tasks.length === 0) { await ctx.reply("Ei tehtäviä. Lisää: /newtask"); return; } + const lines = tasks.map((t) => { + const next = formatDate(getNextDate(t)); + const done = t.last_done || "—"; + return `*${t.id}.* ${t.name}\n ↻ ${t.interval} | edellinen: ${done} | seuraava: ${next}`; + }); + const buttons = tasks.map((t) => [ + Markup.button.callback(`✅ ${t.name}`, `taskdone:${t.id}`), + Markup.button.callback(`🗑️`, `taskdel:${t.id}`), + ]); + await ctx.reply(`📋 Toistuvat tehtävät:\n\n${lines.join("\n\n")}`, { + parse_mode: "Markdown", + ...Markup.inlineKeyboard(buttons), + }); +}); + +bot.command("edittask", async (ctx) => { + const args = ctx.message.text.replace(/^\/edittask\s*/, "").trim(); + const match = /^(\d+)\s+(\d+(?:pv|vk|kk|v))\s+(.+)$/.exec(args); + if (!match) { + await ctx.reply("Käyttö: /edittask <id> <intervalli> <nimi>\nEsim: /edittask 3 1kk Ikkunoiden pesu"); + return; + } + try { + parseInterval(match[2]); + } catch { + await ctx.reply("Virheellinen intervalli."); + return; + } + updateRecurringTask(Number(match[1]), match[3], match[2]); + await ctx.reply(`✏️ Päivitetty: ${match[3]} (${match[2]})`); +}); + +bot.command("done", async (ctx) => { + const idStr = ctx.message.text.replace(/^\/done\s*/, "").trim(); + const id = Number(idStr); + if (!id) { await ctx.reply("Käyttö: /done <id>"); return; } + markTaskDone(id, today()); + await ctx.reply("✅ Merkitty tehdyksi!"); +}); + +bot.action(/^taskdone:(\d+)$/, async (ctx) => { + const id = Number(ctx.match[1]); + markTaskDone(id, today()); + await ctx.answerCbQuery("Merkitty tehdyksi!"); +}); + +bot.action(/^taskdel:(\d+)$/, async (ctx) => { + const id = Number(ctx.match[1]); + removeRecurringTask(id); + await ctx.answerCbQuery("Poistettu!"); +}); + const broadcastMessage = async (msg: string): Promise<void> => { const activeChats = await getActiveChats(); - const chatIds = activeChats.map(c => c.id); - - await Promise.all(chatIds.map(async chatId => - bot.telegram.sendMessage(chatId, msg), - )); + await Promise.all(activeChats.map((c) => bot.telegram.sendMessage(c.id, msg))); }; export { bot, broadcastMessage }; |
