Enrutador (router
)
La clase Router
(Referencia API) proporciona una forma de estructurar tu bot enrutando objetos de contexto a diferentes partes de tu código. Es una versión más sofisticada de bot
en Composer
(grammY API Reference).
Ejemplo
Aquí hay un ejemplo de uso de un enrutador que habla por sí mismo.
const router = new Router((ctx) => {
// Determine la ruta a elegir aquí.
return "key";
});
router.route("key", async (ctx) => {/* ... */});
router.route("other-key", async (ctx) => {/* ... */});
router.otherwise((ctx) => {/* ... */}); // se llama si no hay ninguna ruta que coincida
bot.use(router);
2
3
4
5
6
7
8
9
10
Integración con Middleware
Naturalmente, el plugin del enrutador se integra perfectamente con las jerarquías de middleware de grammY. Por ejemplo, puedes filtrar más las actualizaciones después de enrutarlas.
router.route("key").on("message:text", async (ctx) => {/* ... */});
const other = router.otherwise();
other.on(":text", async (ctx) => {/* ... */});
other.use((ctx) => {/* ... */});
2
3
4
También puedes revisar esta sección sobre la combinación de manejadores de middleware.
Combinación de enrutadores con sesiones
Los enrutadores funcionan bien junto con las sesiones. A modo de ejemplo, la combinación de ambos conceptos permite recrear formularios en la interfaz del chat.
Tenga en cuenta que una solución mucho mejor es utilizar el plugin de conversaciones. El resto de esta página está obsoleta desde que se creó ese plugin. Mantendremos esta página como referencia para aquellos que usaban el enrutador para los formularios.
Digamos que quieres construir un bot que le diga a los usuarios cuántos días faltan para su cumpleaños. Para calcular el número de días, el bot tiene que saber el mes (por ejemplo, junio) y el día del mes (por ejemplo, el 15) del cumpleaños.
Por lo tanto, el bot tiene que hacer dos preguntas
- ¿En qué mes nació el usuario?
- ¿Qué día del mes ha nacido el usuario?
Sólo si se conocen ambos valores, el bot puede decir al usuario cuántos días le quedan.
Así es como se podría implementar un bot de este tipo:
import { Bot, Context, Keyboard, session, SessionFlavor } from "grammy";
import { Router } from "@grammyjs/router";
interface SessionData {
step: "idle" | "day" | "month"; // en qué paso del formulario estamos
dayOfMonth?: number; // día de cumpleaños
month?: number; // mes de cumpleaños
}
type MyContext = Context & SessionFlavor<SessionData>;
const bot = new Bot<MyContext>("");
// Usar sesiones
bot.use(session({ initial: (): SessionData => ({ step: "idle" }) }));
// Definir algunos comandos.
bot.command("start", async (ctx) => {
await ctx.reply(`¡Bienvenido!
¡Puedo decirte en cuántos días es tu cumpleaños!
Envía /birthday para empezar`);
});
bot.command("birthday", async (ctx) => {
const day = ctx.session.dayOfMonth;
const month = ctx.session.month;
if (day !== undefined && month !== undefined) {
// ¡Información ya proporcionada!
await ctx.reply(`¡Tu cumpleaños es en ${getDays(month, day)} días!`);
} else {
// Falta información, introduzca el formulario basado en el router
ctx.session.step = "day";
await ctx.reply(
"¡Por favor, envíame el día del mes \
de tu cumpleaños como un número!",
);
}
});
// Utilizar el router.
const router = new Router<MyContext>((ctx) => ctx.session.step);
// Definir el paso que maneja el día.
const day = router.route("day");
day.on("message:text", async (ctx) => {
const day = parseInt(ctx.msg.text, 10);
if (isNaN(day) || day < 1 || 31 < day) {
await ctx.reply("¡Ese no es un día válido, inténtelo de nuevo!");
return;
}
ctx.session.dayOfMonth = day;
// Formulario de avance al paso del mes
ctx.session.step = "month";
await ctx.reply("¡Lo tengo! Ahora, ¡envíame el mes!", {
reply_markup: {
one_time_keyboard: true,
keyboard: new Keyboard()
.text("Jan").text("Feb").text("Mar").row()
.text("Apr").text("May").text("Jun").row()
.text("Jul").text("Aug").text("Sep").row()
.text("Oct").text("Nov").text("Dec").build(),
},
});
});
day.use((ctx) =>
ctx.reply("¡Por favor, envíenme el día en forma de mensaje de texto!")
);
// Definir el paso que maneja el mes.
const month = router.route("month");
month.on("message:text", async (ctx) => {
// No debería ocurrir, a menos que los datos de la sesión estén corruptos.
const day = ctx.session.dayOfMonth;
if (day === undefined) {
await ctx.reply("¡Necesito tu día del mes!");
ctx.session.step = "day";
return;
}
const month = months.indexOf(ctx.msg.text);
if (month === -1) {
await ctx.reply(
"¡Ese no es un mes válido, \
por favor, utilice uno de los botones",
);
return;
}
ctx.session.month = month;
const diff = getDays(month, day);
await ctx.reply(
`Su cumpleaños es en ${months[month]} ${day}.
¡Eso es en ${diff} días!`,
{ reply_markup: { remove_keyboard: true } },
);
ctx.session.step = "idle";
});
month.use((ctx) => ctx.reply("¡Por favor, pulse uno de los botones.!"));
// Definir el paso que maneja todos los demás casos.
router.otherwise(async (ctx) => { // idle
await ctx.reply(
"Envía /cumpleaños para saber cuánto tiempo tienes que esperar",
);
});
bot.use(router); // registrar el router
bot.start();
// Utilidades de conversión de fechas
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
function getDays(month: number, day: number) {
const bday = new Date();
const now = Date.now();
bday.setMonth(month);
bday.setDate(day);
if (bday.getTime() < now) bday.setFullYear(bday.getFullYear() + 1);
const diff = (bday.getTime() - now) / (1000 * 60 * 60 * 24);
return diff;
}
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
const { Bot, Context, Keyboard, session, SessionFlavor } = require("grammy");
const { Router } = require("@grammyjs/router");
const bot = new Bot("");
// Usar sesiones.
bot.use(session({ initial: () => ({ step: "idle" }) }));
// Definir algunos comandos.
bot.command("start", async (ctx) => {
await ctx.reply(`¡Bienvenido!
¡Puedo decirte en cuántos días es tu cumpleaños!
Envía /birthday para empezar`);
});
bot.command("birthday", async (ctx) => {
const day = ctx.session.dayOfMonth;
const month = ctx.session.month;
if (day !== undefined && month !== undefined) {
// ¡Información ya proporcionada!
await ctx.reply(`¡Tu cumpleaños es en ${getDays(month, day)} días!`);
} else {
// Falta información, introduzca el formulario basado en el router.
ctx.session.step = "day";
await ctx.reply(
"¡Por favor, envíame el día del mes \
de tu cumpleaños como un número!",
);
}
});
// Utilizar el router.
const router = new Router((ctx) => ctx.session.step);
/// Definir el paso que maneja el día.
const day = router.route("day");
day.on("message:text", async (ctx) => {
const day = parseInt(ctx.msg.text, 10);
if (isNaN(day) || day < 1 || 31 < day) {
await ctx.reply("¡Ese no es un día válido, inténtelo de nuevo!");
return;
}
ctx.session.dayOfMonth = day;
// Formulario de avance al paso del mes
ctx.session.step = "month";
await ctx.reply("¡Lo tengo! Ahora, ¡envíame el mes!", {
reply_markup: {
one_time_keyboard: true,
keyboard: new Keyboard()
.text("Jan").text("Feb").text("Mar").row()
.text("Apr").text("May").text("Jun").row()
.text("Jul").text("Aug").text("Sep").row()
.text("Oct").text("Nov").text("Dec").build(),
},
});
});
day.use((ctx) =>
ctx.reply("¡Por favor, envíenme el día en forma de mensaje de texto!")
);
// Definir el paso que maneja el mes.
const month = router.route("month");
month.on("message:text", async (ctx) => {
// No debería ocurrir, a menos que los datos de la sesión estén corruptos.
const day = ctx.session.dayOfMonth;
if (day === undefined) {
await ctx.reply("¡Necesito tu día del mes!");
ctx.session.step = "day";
return;
}
const month = months.indexOf(ctx.msg.text);
if (month === -1) {
await ctx.reply(
"¡Ese no es un mes válido, \
por favor, utilice uno de los botones",
);
return;
}
ctx.session.month = month;
const diff = getDays(month, day);
await ctx.reply(
`Su cumpleaños es en ${months[month]} ${day}.
¡Eso es en ${diff} días!`,
{ reply_markup: { remove_keyboard: true } },
);
ctx.session.step = "idle";
});
month.use((ctx) => ctx.reply("¡Por favor, pulse uno de los botones.!"));
// Definir el paso que maneja todos los demás casos.
router.otherwise(async (ctx) => { // idle
await ctx.reply(
"Envía /cumpleaños para saber cuánto tiempo tienes que esperar",
);
});
bot.use(router); // registrar el router
bot.start();
// Utilidades de conversión de fechas
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
function getDays(month, day) {
const bday = new Date();
const now = Date.now();
bday.setMonth(month);
bday.setDate(day);
if (bday.getTime() < now) bday.setFullYear(bday.getFullYear() + 1);
const diff = (bday.getTime() - now) / (1000 * 60 * 60 * 24);
return diff;
}
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
116
117
118
119
120
121
122
123
124
125
import {
Bot,
Context,
Keyboard,
session,
SessionFlavor,
} from "https://deno.land/x/grammy@v1.30.0/mod.ts";
import { Router } from "https://deno.land/x/grammy_router@v2.0.0/router.ts";
interface SessionData {
step: "idle" | "day" | "month"; // en qué paso del formulario estamos
dayOfMonth?: number; // día de cumpleaños
month?: number; // mes de cumpleaños
}
type MyContext = Context & SessionFlavor<SessionData>;
const bot = new Bot<MyContext>("");
// Usar sesiones.
bot.use(session({ initial: (): SessionData => ({ step: "idle" }) }));
// Definir algunos comandos.
bot.command("start", async (ctx) => {
await ctx.reply(`¡Bienvenido!
¡Puedo decirte en cuántos días es tu cumpleaños!
Envía /birthday para empezar`);
});
bot.command("birthday", async (ctx) => {
const day = ctx.session.dayOfMonth;
const month = ctx.session.month;
if (day !== undefined && month !== undefined) {
// ¡Información ya proporcionada!
await ctx.reply(`¡Tu cumpleaños es en ${getDays(month, day)} días!`);
} else {
// Falta información, introduzca el formulario basado en el router
ctx.session.step = "day";
await ctx.reply(
"¡Por favor, envíame el día del mes \
de tu cumpleaños como un número!",
);
}
});
// Utilizar el router.
const router = new Router<MyContext>((ctx) => ctx.session.step);
// Definir el paso que maneja el día.
const day = router.route("day");
day.on("message:text", async (ctx) => {
const day = parseInt(ctx.msg.text, 10);
if (isNaN(day) || day < 1 || 31 < day) {
await ctx.reply("¡Ese no es un día válido, inténtelo de nuevo!");
return;
}
ctx.session.dayOfMonth = day;
// Formulario de avance al paso del mes
ctx.session.step = "month";
await ctx.reply("¡Lo tengo! Ahora, ¡envíame el mes!", {
reply_markup: {
one_time_keyboard: true,
keyboard: new Keyboard()
.text("Jan").text("Feb").text("Mar").row()
.text("Apr").text("May").text("Jun").row()
.text("Jul").text("Aug").text("Sep").row()
.text("Oct").text("Nov").text("Dec").build(),
},
});
});
day.use((ctx) =>
ctx.reply("¡Por favor, envíenme el día en forma de mensaje de texto!")
);
// Definir el paso que maneja el mes.
const month = router.route("month");
month.on("message:text", async (ctx) => {
// No debería ocurrir, a menos que los datos de la sesión estén corruptos.
const day = ctx.session.dayOfMonth;
if (day === undefined) {
await ctx.reply("¡Necesito tu día del mes!");
ctx.session.step = "day";
return;
}
const month = months.indexOf(ctx.msg.text);
if (month === -1) {
await ctx.reply(
"¡Ese no es un mes válido, \
por favor, utilice uno de los botones",
);
return;
}
ctx.session.month = month;
const diff = getDays(month, day);
await ctx.reply(
`Su cumpleaños es en ${months[month]} ${day}.
¡Eso es en ${diff} días!`,
{ reply_markup: { remove_keyboard: true } },
);
ctx.session.step = "idle";
});
month.use((ctx) => ctx.reply("¡Por favor, pulse uno de los botones.!"));
// Definir el paso que maneja todos los demás casos.
router.otherwise(async (ctx) => { // idle
await ctx.reply(
"Envía /cumpleaños para saber cuánto tiempo tienes que esperar",
);
});
bot.use(router); // registrar el router
bot.start();
// Utilidades de conversión de fechas
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
function getDays(month: number, day: number) {
const bday = new Date();
const now = Date.now();
bday.setMonth(month);
bday.setDate(day);
if (bday.getTime() < now) bday.setFullYear(bday.getFullYear() + 1);
const diff = (bday.getTime() - now) / (1000 * 60 * 60 * 24);
return diff;
}
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
Observe cómo la sesión tiene una propiedad step
que almacena el paso del formulario, es decir, qué valor se está rellenando actualmente. El router se utiliza para saltar entre diferentes middleware que completan los campos month
y day
de la sesión. Si ambos valores son conocidos, el bot calcula los días restantes y los envía de vuelta al usuario.
Resumen del plugin
- Nombre:
router
- Fuente
- Referencia