From cd6a9c242435114158f8af0eb4302b5fdb8c0642 Mon Sep 17 00:00:00 2001 From: Eric Matala de Mazza Date: Mon, 1 May 2023 22:27:27 +0200 Subject: [PATCH] feat: add webhook support for express server --- .env.example | 3 ++- package.json | 3 ++- src/app.ts | 67 +++++++++++++++++++++++------------------------ src/handlers.ts | 40 +++++++++++++++++----------- src/messages.ts | 15 +++++++++-- src/middleware.ts | 5 ---- 6 files changed, 74 insertions(+), 59 deletions(-) diff --git a/.env.example b/.env.example index 331db29..bff5cf3 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ TELEGRAM_BOT_TOKEN="YOUR_TELEGRAM_BOT_TOKEN" DATABASE_URL="YOUR_DATABASE_URL" PASSWORD="YOUR_PASSWORD" -APP_URL="YOUR_APP_URL" \ No newline at end of file +APP_URL="YOUR_APP_URL" +WEBHOOK_SECRET="YOUR_WEBHOOK_SECRET" \ No newline at end of file diff --git a/package.json b/package.json index 4748b08..9c69d16 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "A telegram bot for automating cleaning duties", "scripts": { "dev": "ts-node src/app.ts", - "build": "tsc", + "postinstall": "prisma generate", + "build": "tsc && prisma generate", "start": "node build/app.js", "deploy": "gcloud app deploy" }, diff --git a/src/app.ts b/src/app.ts index dc2a4ef..3018a4f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,21 +1,27 @@ -import { PrismaClient } from "@prisma/client"; import "dotenv/config"; +import { PrismaClient } from "@prisma/client"; import express from "express"; import { Telegraf } from "telegraf"; -import { message } from "telegraf/filters"; import { handleGetAllDuties, handleGetDuty, - handleMessage, + handleHelp, + handleRegister, handleRemind, handleRoomieIsDone, handleRotate, handleWelcome, } from "./handlers"; -import { auth, log } from "./middleware"; - -["DATABASE_URL", "PASSWORD", "TELEGRAM_BOT_TOKEN", "APP_URL"].forEach((key) => { +import { auth } from "./middleware"; + +[ + "DATABASE_URL", + "PASSWORD", + "TELEGRAM_BOT_TOKEN", + "APP_URL", + "WEBHOOK_SECRET", +].forEach((key) => { if (!process.env[key]) throw new Error(`${key} must be provided`); }); @@ -25,47 +31,40 @@ const bot = new Telegraf(token); const prisma = new PrismaClient(); // middleware -bot.use(log); +bot.use(Telegraf.log()); bot.use(auth(prisma)); // commands bot.command("start", handleWelcome); +bot.command("register", handleRegister(prisma)); bot.command("get", handleGetDuty(prisma)); bot.command("getall", handleGetAllDuties(prisma)); bot.command("done", handleRoomieIsDone(prisma, bot)); bot.command("remind", handleRemind(prisma, bot)); bot.command("rotate", handleRotate(prisma, bot)); -bot.on(message("text"), handleMessage(prisma)); - -// rotate duties every monday at 10am -// schedule("0 10 * * mon", rotate); - -// send a reminder every sunday at 6pm -// schedule("0 18 * * sun", remind); - -// send out trash reminders every tuesday at 6pm -// schedule("0 18 * * tue", remindTrash); +bot.command("help", handleHelp); -// launch bot -bot.launch(); +const launch = async (bot: Telegraf) => { + if (process.env.NODE_ENV === "development") return bot.launch(); + const port = process.env.PORT ?? 8080; + const app = express(); -// enable graceful stop -process.once("SIGINT", () => bot.stop("SIGINT")); -process.once("SIGTERM", () => bot.stop("SIGTERM")); + app.use(express.json()); -// const port = process.env.PORT ?? 8080; -// const app = express(); + app.use( + await bot.createWebhook({ + domain: process.env.APP_URL!, + allowed_updates: ["message"], + path: "/telegraf", + secret_token: process.env.WEBHOOK_SECRET, + }) + ); -// app.use(express.json()); + app.post("/", async (req, res) => res.status(200).send("OK")); -// // app.use(async () => await bot.createWebhook({ domain: process.env.APP_URL! })); -// app.post("/", (req, res) => { -// console.log("request body", req.body); -// res.status(200).send("OK"); -// }); + return app.listen(port, () => console.log("app listening on port", port)); +}; -// const server = app.listen(port, () => -// console.log("app listening on port", port) -// ); +const server = launch(bot); -// export default server; +export default server; diff --git a/src/handlers.ts b/src/handlers.ts index 7588772..4970d22 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -4,6 +4,7 @@ import { dutyIsDoneMessage, getAllDutiesMessage, getOwnDutyMessage, + helpMessage, noDutiesMessage, reminderIsSentMessage, roomieHasNoDutyMessage, @@ -96,23 +97,30 @@ export const handleRotate = rotate(prisma, bot); }; -export const handleMessage = (prisma: PrismaClient) => async (ctx: Context) => { - try { - if ( - ctx.chat?.type === "private" && - ctx.has(message("text")) && - ctx.message.text === process.env.PASSWORD - ) { - await prisma.roomie.upsert({ - where: { id: ctx.chat.id }, - update: {}, - create: { - id: ctx.chat.id, - name: ctx.chat.first_name, - }, - }); - ctx.reply(roomieIsOnboardedMessage(ctx.chat.first_name)); +export const handleRegister = + (prisma: PrismaClient) => async (ctx: Context) => { + try { + if (!ctx.has(message("text"))) return; + const password = ctx.message.text.split(" ").at(-1); + if (ctx.chat?.type === "private" && password === process.env.PASSWORD) { + await prisma.roomie.upsert({ + where: { id: ctx.chat.id }, + update: {}, + create: { + id: ctx.chat.id, + name: ctx.chat.first_name, + }, + }); + ctx.reply(roomieIsOnboardedMessage(ctx.chat.first_name)); + } + } catch (error) { + console.error(error); } + }; + +export const handleHelp = (ctx: Context) => { + try { + ctx.reply(helpMessage); } catch (error) { console.error(error); } diff --git a/src/messages.ts b/src/messages.ts index 2053ab2..5ff9ba8 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -1,10 +1,10 @@ import { getTrashItems } from "./utils"; +// static messages export const welcomeMessage = - "Hi πŸ‘‹πŸ½ Bitte gib das Passwort ein, dann fΓΌge ich dich hinzu."; + "Hi πŸ‘‹πŸ½ Bitte registriere dich mit /register "; export const roomieIsOnboardedMessage = (name: string) => `Moin ${name}, du kannst jetzt loslegen πŸš€`; -export const roomieIsNotRegisteredMessage = "Du bist noch nicht registriert πŸ€·πŸ½β€β™‚οΈ"; export const roomieIsDoneMessage = "Du bist fΓΌr diese Woche fertig πŸ‘πŸ½"; export const roomieIsAlreadyDoneMessage = "Du bist schon fertig πŸ₯³"; export const dutyIsDoneMessage = @@ -13,6 +13,17 @@ export const roomieHasNoDutyMessage = "Du hast noch keinen Dienst πŸ€”"; export const noDutiesMessage = "Es gibt noch keine Dienste"; export const reminderIsSentMessage = "Ich habe die anderen daran erinnert ihre Dienste zu machen."; +export const helpMessage = [ + "/register - Registrieren (Passwort erforderlich)", + "/get - Den eigenen Dienst anzeigen", + "/getall - Alle Dienste anzeigen", + "/remind - Die anderen an ihre Dienste erinnern", + "/done - Den eigenen Dienst als erledigt markieren", + "/rotate - Dienste rotieren", + "/help - Hilfe anzeigen", +].join("\n"); + +// dynamic messages export const getTrashReminderMessage = async () => ["Hey, du musst den MΓΌll rausbringen πŸš› \n", ...(await getTrashItems())].join( "\n" diff --git a/src/middleware.ts b/src/middleware.ts index 8fb719e..bf09b5c 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -16,8 +16,3 @@ export const auth = console.error(error); } }; - -export const log = (ctx: Context, next: () => Promise) => { - console.log(new Date(), ctx.update); - return next(); -};