all repos — kokyo @ 6236a1282014a4097ecb54d5a9e9ed6bd7ae796d

Chatbot and CLI tool for Swiss public transports

bots/telegram.ts (view raw)

  1import { Telegram, getUpdates } from "@gramio/wrappergram";
  2import { findStopByName } from "@api/locationInformationRequest.ts";
  3import { InlineKeyboard } from "@gramio/keyboards";
  4import { getNextDepartures } from "@api/stopEvent.ts";
  5import logger from "@lib/logger.ts";
  6import * as kvdb from "../lib/kvdb.ts";
  7
  8const telegram = new Telegram(Deno.env.get("TELEGRAM_TOKEN") as string);
  9
 10const formatContent = (content?: string) =>
 11  content
 12    ?.replaceAll("-", "\\-")
 13    .replaceAll(".", "\\.")
 14    .replaceAll("(", "\\(")
 15    .replaceAll(")", "\\)");
 16
 17const sendMessage = async (message: any) => {
 18  await telegram.api.sendMessage(message);
 19};
 20
 21const getUsername = (from: From) => {
 22  let username = `id:${from.id}`;
 23  if (from.username) username = `user:${from.username}`;
 24  if (from.first_name && from.last_name)
 25    return `${from.first_name} ${from.last_name} (${username})`;
 26  else return username;
 27};
 28
 29for await (const update of getUpdates(telegram)) {
 30  logger.debug(JSON.stringify(update, null, 4));
 31  // On new message
 32  if (update.message) {
 33    if (!update.message?.from) {
 34      console.error("No 'from' in message");
 35      continue;
 36    }
 37
 38    const { from, text } = update.message;
 39
 40    logger.info(`New message from ${getUsername(from)}: ${text}`);
 41
 42    telegram.api.setMyCommands({
 43      commands: [
 44        { command: "aide", description: "Explique comment utiliser le bot" },
 45        {
 46          command: "favoris",
 47          description: "Affiche la liste de vos arrêts favoris",
 48        },
 49      ],
 50    });
 51
 52    if (text === "/aide" || text === "/start") {
 53      sendMessage({
 54        chat_id: from.id,
 55        text: `Hey ! Je suis un bot qui te permet d'obtenir rapidement des informations sur les transports en commun dans toute la Suisse.
 56Entre le nom d'un arrêt et laisse-toi guider !
 57
 58Ce bot est en cours de développement actif. Les fonctionnalités sont pour le moment limitées.
 59
 60/aide - Affiche ce message
 61/favoris - Affiche vos favoris`,
 62      });
 63    } else if (text === "/favoris") {
 64      const userId = `telegram:${from.id}`;
 65      const favorites = kvdb.getUserFavorites(userId);
 66
 67      let inlineKeyboard = new InlineKeyboard();
 68      for await (const favorite of favorites)
 69        inlineKeyboard = inlineKeyboard
 70          .text(favorite.value.name, {
 71            cmd: "nextDepartures",
 72            stopRef: favorite.value.stopRef,
 73          })
 74          .row();
 75
 76      await sendMessage({
 77        chat_id: from.id,
 78        text: "Voici vos favoris",
 79        reply_markup: inlineKeyboard,
 80      });
 81    } else {
 82      const stopLists = await findStopByName(text);
 83      kvdb.saveStops(stopLists);
 84      let keyboard = new InlineKeyboard();
 85      for (const stop of stopLists)
 86        keyboard = keyboard
 87          .text(stop.name, { cmd: "nextDepartures", stopRef: stop.stopRef })
 88          .row();
 89
 90      await sendMessage({
 91        chat_id: from.id,
 92        text: "Quel arrêt correspond ?",
 93        reply_markup: keyboard,
 94      });
 95    }
 96  }
 97
 98  // On keyboard event (callback query)
 99  else if (update.callback_query) {
100    const { from, data, message } = update.callback_query;
101    const userId = `telegram:${from.id}`;
102    const payload = JSON.parse(data);
103
104    logger.info(`New request from ${getUsername(from)}: ${data}`);
105
106    if (payload?.cmd === "nextDepartures") {
107      const nextDepartures = await getNextDepartures(payload.stopRef);
108      const stop = await kvdb.getStopByRef(payload.stopRef);
109      const text = formatContent(`Prochains départs depuis *${
110        stop.value?.name || "n.c."
111      }*:\n
112${nextDepartures
113  .map(
114    item =>
115      `*${item.serviceName}*  ${item.serviceTypeIcon}    *${item.departure}*    _${item.departureIn} min_     \n${item.to}\n`
116  )
117  .join("\n")}`);
118
119      let keyboard = new InlineKeyboard().text("Rafraîchir", {
120        cmd: "nextDepartures",
121        stopRef: payload.stopRef,
122        refresh: message.message_id,
123      });
124      const existingFavorite = await kvdb.getUserFavorite(
125        userId,
126        payload.stopRef
127      );
128      if (!existingFavorite?.value)
129        keyboard = keyboard.text("Enregistrer comme favoris", {
130          cmd: "saveFavorite",
131          stopRef: payload.stopRef,
132        });
133
134      await sendMessage({
135        chat_id: from.id,
136        parse_mode: "MarkdownV2",
137        text,
138        reply_markup: keyboard,
139      });
140    } else if (payload?.cmd === "saveFavorite") {
141      const stop = await kvdb.getStopByRef(payload.stopRef);
142      if (stop?.value) await kvdb.saveUserFavorite(userId, stop.value);
143      await sendMessage({
144        chat_id: from.id,
145        parse_mode: "MarkdownV2",
146        text: `L'arrêt *${formatContent(
147          stop.value?.name
148        )}* a été enregistré dans vos /favoris`,
149      });
150    }
151  }
152}