bots/telegram.ts (view raw)
1import { Telegram, getUpdates } from "@gramio/wrappergram";
2import { findStops } 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, location } = update.message;
39
40 logger.info(
41 `New message from ${getUsername(from)}: ${
42 text || JSON.stringify(location)
43 }`
44 );
45
46 telegram.api.setMyCommands({
47 commands: [
48 { command: "aide", description: "Explique comment utiliser le bot" },
49 {
50 command: "favoris",
51 description: "Affiche la liste de vos arrêts favoris",
52 },
53 ],
54 });
55
56 if (text === "/aide" || text === "/start") {
57 sendMessage({
58 chat_id: from.id,
59 text: `Hey ! Je suis un bot qui te permet d'obtenir rapidement des informations sur les transports en commun dans toute la Suisse.
60Entre le nom d'un arrêt et laisse-toi guider !
61
62Ce bot est en cours de développement actif. Les fonctionnalités sont pour le moment limitées.
63
64/aide - Affiche ce message
65/favoris - Affiche vos favoris`,
66 });
67 } else if (text === "/favoris") {
68 const userId = `telegram:${from.id}`;
69 const favorites = kvdb.getUserFavorites(userId);
70
71 let inlineKeyboard = new InlineKeyboard();
72 for await (const favorite of favorites)
73 inlineKeyboard = inlineKeyboard
74 .text(favorite.value.name, {
75 cmd: "nextDepartures",
76 stopRef: favorite.value.stopRef,
77 })
78 .row();
79
80 await sendMessage({
81 chat_id: from.id,
82 text: "Voici vos favoris",
83 reply_markup: inlineKeyboard,
84 });
85 } else {
86 const searchInput = location ? location : text;
87 const stopLists = await findStops(searchInput);
88 if (!stopLists?.length) {
89 await sendMessage({
90 chat_id: from.id,
91 text: "Désolé, je n'ai pas réussi à récupérer une liste d'arrêts correspondants.",
92 });
93 } else {
94 kvdb.saveStops(stopLists);
95 let keyboard = new InlineKeyboard();
96 for (const stop of stopLists)
97 keyboard = keyboard
98 .text(stop.name, { cmd: "nextDepartures", stopRef: stop.stopRef })
99 .row();
100
101 await sendMessage({
102 chat_id: from.id,
103 text: "Quel arrêt correspond ?",
104 reply_markup: keyboard,
105 });
106 }
107 }
108 }
109
110 // On keyboard event (callback query)
111 else if (update.callback_query) {
112 const { from, data, message } = update.callback_query;
113 const userId = `telegram:${from.id}`;
114 const payload = JSON.parse(data);
115
116 logger.info(`New request from ${getUsername(from)}: ${data}`);
117
118 if (payload?.cmd === "nextDepartures") {
119 const nextDepartures = await getNextDepartures(payload.stopRef);
120 const stop = await kvdb.getStopByRef(payload.stopRef);
121 const text = formatContent(`Prochains départs depuis *${
122 stop.value?.name || "n.c."
123 }*:\n
124${nextDepartures
125 .map(
126 item =>
127 `*${item.serviceName}* ${item.serviceTypeIcon} *${item.departure}* _${item.departureIn} min_ \n${item.to}\n`
128 )
129 .join("\n")}`);
130
131 let keyboard = new InlineKeyboard().text("Rafraîchir", {
132 cmd: "nextDepartures",
133 stopRef: payload.stopRef,
134 refresh: message.message_id,
135 });
136 const existingFavorite = await kvdb.getUserFavorite(
137 userId,
138 payload.stopRef
139 );
140 if (!existingFavorite?.value)
141 keyboard = keyboard.text("Enregistrer comme favoris", {
142 cmd: "saveFavorite",
143 stopRef: payload.stopRef,
144 });
145
146 await sendMessage({
147 chat_id: from.id,
148 parse_mode: "MarkdownV2",
149 text,
150 reply_markup: keyboard,
151 });
152 } else if (payload?.cmd === "saveFavorite") {
153 const stop = await kvdb.getStopByRef(payload.stopRef);
154 if (stop?.value) await kvdb.saveUserFavorite(userId, stop.value);
155 await sendMessage({
156 chat_id: from.id,
157 parse_mode: "MarkdownV2",
158 text: `L'arrêt *${formatContent(
159 stop.value?.name
160 )}* a été enregistré dans vos /favoris`,
161 });
162 }
163 }
164}