forked from grammyjs/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
menu.ts
115 lines (104 loc) · 3.29 KB
/
menu.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { Bot, Context, session, SessionFlavor } from "grammy";
import { Menu, MenuRange } from "@grammyjs/menu";
/** This is how the dishes look that this bot is managing */
interface Dish {
id: string;
name: string;
}
interface SessionData {
favoriteIds: string[];
}
type MyContext = Context & SessionFlavor<SessionData>;
/**
* All known dishes. Users can rate them to store which ones are their favorite
* dishes.
*
* They can also decide to delete them. If a user decides to delete a dish, it
* will be gone for everyone.
*/
const dishDatabase: Dish[] = [
{ id: "pasta", name: "Pasta" },
{ id: "pizza", name: "Pizza" },
{ id: "sushi", name: "Sushi" },
{ id: "entrct", name: "Entrecôte" },
];
/** Define initial session data */
function initial(): SessionData {
return { favoriteIds: [] };
}
const bot = new Bot<MyContext>("");
bot.use(session({ initial }));
// Create a dynamic menu that lists all dishes in the dishDatabase,
// one button each
const mainText = "Pick a dish to rate it!";
const mainMenu = new Menu<MyContext>("food");
mainMenu.dynamic(() =>
dishDatabase.reduce(addDishToDynamicRange, new MenuRange<MyContext>())
);
function addDishToDynamicRange(range: MenuRange<MyContext>, dish: Dish) {
return range
.submenu(
{ text: dish.name, payload: dish.id }, // label and payload
"dish", // navigation target menu
(ctx) => ctx.editMessageText(dishText(dish.name), { parse_mode: "HTML" }), // handler
)
.row();
}
// Create the sub-menu that is used for rendering dishes
const dishText = (dish: string) => `<b>${dish}</b>\n\nYour rating:`;
const dishMenu = new Menu<MyContext>("dish");
dishMenu.dynamic((ctx) => {
const dish = ctx.match;
if (typeof dish !== "string") throw new Error("No dish chosen!");
return createDishMenu(dish);
});
/** Creates a menu that can render any given dish */
function createDishMenu(dish: string) {
return new MenuRange<MyContext>()
.text({
text: (ctx) => ctx.session.favoriteIds.includes(dish) ? "Yummy!" : "Meh.",
payload: dish,
}, (ctx) => {
const set = new Set(ctx.session.favoriteIds);
if (!set.delete(dish)) set.add(dish);
ctx.session.favoriteIds = Array.from(set.values());
ctx.menu.update();
})
.row()
.back({ text: "X Delete", payload: dish }, async (ctx) => {
const index = dishDatabase.findIndex((d) => d.id === dish);
dishDatabase.splice(index, 1);
await ctx.editMessageText("Pick a dish to rate it!");
})
.row()
.back({ text: "Back", payload: dish });
}
mainMenu.register(dishMenu);
bot.use(mainMenu);
bot.command(
"start",
(ctx) => ctx.reply(mainText, { reply_markup: mainMenu }),
);
bot.command(
"help",
async (ctx) => {
const text =
"Send /start to see and rate dishes. Send /fav to list your favorites!";
await ctx.reply(text);
},
);
bot.command("fav", async (ctx) => {
const favs = ctx.session.favoriteIds;
if (favs.length === 0) {
await ctx.reply("You do not have any favorites yet!");
return;
}
const names = favs
.map((id) => dishDatabase.find((dish) => dish.id === id))
.filter((dish): dish is Dish => dish !== undefined)
.map((dish) => dish.name)
.join("\n");
await ctx.reply(`Those are your favorite dishes:\n\n${names}`);
});
bot.catch(console.error.bind(console));
bot.start();