-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathmenu-with-conversation.ts
105 lines (86 loc) · 3.36 KB
/
menu-with-conversation.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
import { Bot, Context, session, type SessionFlavor } from "grammy";
import { Menu } from "@grammyjs/menu";
import { hydrate, type HydrateFlavor } from "@grammyjs/hydrate";
import {
type Conversation,
type ConversationFlavor,
conversations,
createConversation,
} from "@grammyjs/conversations";
interface SessionData {
name?: string;
email?: string;
}
type MyContext = ConversationFlavor<Context & SessionFlavor<SessionData>>;
type NameContext = Context;
type NameConversation = Conversation<MyContext, NameContext>;
type EmailContext = HydrateFlavor<Context>;
type EmailConversation = Conversation<MyContext, EmailContext>;
const bot = new Bot<MyContext>("");
bot.use(session({ initial: () => ({}) }));
bot.use(conversations());
const menu = new Menu<MyContext>("root")
.submenu("Settings", "settings")
.submenu("About", "about");
const settings = new Menu<MyContext>("settings")
.text("Set name", (ctx) => ctx.conversation.enter("name"))
.text("Set email", (ctx) => ctx.conversation.enter("email"))
.back("Back");
menu.register(settings);
const about = new Menu<MyContext>("about")
.back("Back");
menu.register(about);
async function name(conversation: NameConversation, ctx: NameContext) {
// Define the structure that the ouside menu expects.
const settingsClone = conversation.menu("settings")
.text("Set name")
.text("Set email")
.back("Back");
// Override the outside menu when the conversation is entered.
const nameMenu = conversation.menu().text("Cancel", async (ctx) => {
await ctx.menu.nav("settings", { immediate: true });
await conversation.halt();
});
await ctx.editMessageReplyMarkup({ reply_markup: nameMenu });
await ctx.reply("What's your name?");
const name = await conversation.form.text();
await conversation.external((ctx) => ctx.session.name = name);
await ctx.reply("Name set!");
await ctx.editMessageReplyMarkup({ reply_markup: settingsClone });
}
async function email(conversation: EmailConversation, ctx: EmailContext) {
// Define the structure that the ouside menu expects.
const settingsClone = conversation.menu("settings")
.text("Set name")
.text("Set email")
.back("Back");
// Override the outside menu when the conversation is entered.
const emailMenu = conversation.menu().text("Cancel", async (ctx) => {
await ctx.menu.nav("settings", { immediate: true });
await conversation.halt();
});
await ctx.editMessageReplyMarkup({ reply_markup: emailMenu });
const currentName = await conversation.external((ctx) => ctx.session.name);
const question = await ctx.reply(
currentName
? `What's your email, ${currentName}?`
: "What's your email?",
);
const email = await conversation.form.text({
action: (ctx) => ctx.deleteMessage(),
});
await conversation.external((ctx: MyContext) => ctx.session.email = email);
await Promise.all([
question.delete(),
ctx.reply("Email set!"),
ctx.editMessageReplyMarkup({ reply_markup: settingsClone }),
]);
}
bot.use(createConversation(name));
bot.use(createConversation(email, { plugins: [hydrate()] }));
bot.use(menu);
bot.command("start", async (ctx) => {
await ctx.reply("Menu", { reply_markup: menu });
});
bot.use((ctx) => ctx.reply("Send /start"));
bot.start();