aboutsummaryrefslogtreecommitdiffstats
path: root/src/tg.ts
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2026-05-06 21:46:21 +0300
committerJan Tuomi <jan@jantuomi.fi>2026-05-06 21:46:21 +0300
commit713ced9c68cd70a8e56e8ce5ed27cb9bbe3e55c4 (patch)
tree29f9997386b9f46db9f263ef63a5e01ca19db393 /src/tg.ts
parentcba8c3d49b53702a488eaff16109e29e49d94b6d (diff)
Add shopping list features, rework
Diffstat (limited to 'src/tg.ts')
-rw-r--r--src/tg.ts119
1 files changed, 110 insertions, 9 deletions
diff --git a/src/tg.ts b/src/tg.ts
index 4850c88..2e5e49d 100644
--- a/src/tg.ts
+++ b/src/tg.ts
@@ -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 };