@yaebal/contexts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/lib/generated/business-connection.d.ts +14 -0
- package/lib/generated/business-connection.d.ts.map +1 -0
- package/lib/generated/business-connection.js +18 -0
- package/lib/generated/business-connection.js.map +1 -0
- package/lib/generated/business-message.d.ts +208 -0
- package/lib/generated/business-message.d.ts.map +1 -0
- package/lib/generated/business-message.js +406 -0
- package/lib/generated/business-message.js.map +1 -0
- package/lib/generated/callback-query.d.ts +200 -0
- package/lib/generated/callback-query.d.ts.map +1 -0
- package/lib/generated/callback-query.js +390 -0
- package/lib/generated/callback-query.js.map +1 -0
- package/lib/generated/channel-post.d.ts +208 -0
- package/lib/generated/channel-post.d.ts.map +1 -0
- package/lib/generated/channel-post.js +406 -0
- package/lib/generated/channel-post.js.map +1 -0
- package/lib/generated/chat-boost.d.ts +144 -0
- package/lib/generated/chat-boost.d.ts.map +1 -0
- package/lib/generated/chat-boost.js +278 -0
- package/lib/generated/chat-boost.js.map +1 -0
- package/lib/generated/chat-join-request.d.ts +196 -0
- package/lib/generated/chat-join-request.d.ts.map +1 -0
- package/lib/generated/chat-join-request.js +382 -0
- package/lib/generated/chat-join-request.js.map +1 -0
- package/lib/generated/chat-member.d.ts +196 -0
- package/lib/generated/chat-member.d.ts.map +1 -0
- package/lib/generated/chat-member.js +382 -0
- package/lib/generated/chat-member.js.map +1 -0
- package/lib/generated/chosen-inline-result.d.ts +46 -0
- package/lib/generated/chosen-inline-result.d.ts.map +1 -0
- package/lib/generated/chosen-inline-result.js +82 -0
- package/lib/generated/chosen-inline-result.js.map +1 -0
- package/lib/generated/deleted-business-messages.d.ts +144 -0
- package/lib/generated/deleted-business-messages.d.ts.map +1 -0
- package/lib/generated/deleted-business-messages.js +278 -0
- package/lib/generated/deleted-business-messages.js.map +1 -0
- package/lib/generated/edited-business-message.d.ts +208 -0
- package/lib/generated/edited-business-message.d.ts.map +1 -0
- package/lib/generated/edited-business-message.js +406 -0
- package/lib/generated/edited-business-message.js.map +1 -0
- package/lib/generated/edited-channel-post.d.ts +208 -0
- package/lib/generated/edited-channel-post.d.ts.map +1 -0
- package/lib/generated/edited-channel-post.js +406 -0
- package/lib/generated/edited-channel-post.js.map +1 -0
- package/lib/generated/edited-message.d.ts +208 -0
- package/lib/generated/edited-message.d.ts.map +1 -0
- package/lib/generated/edited-message.js +406 -0
- package/lib/generated/edited-message.js.map +1 -0
- package/lib/generated/index.d.ts +88 -0
- package/lib/generated/index.d.ts.map +1 -0
- package/lib/generated/index.js +88 -0
- package/lib/generated/index.js.map +1 -0
- package/lib/generated/inline-query.d.ts +48 -0
- package/lib/generated/inline-query.d.ts.map +1 -0
- package/lib/generated/inline-query.js +86 -0
- package/lib/generated/inline-query.js.map +1 -0
- package/lib/generated/message-reaction-count.d.ts +154 -0
- package/lib/generated/message-reaction-count.d.ts.map +1 -0
- package/lib/generated/message-reaction-count.js +298 -0
- package/lib/generated/message-reaction-count.js.map +1 -0
- package/lib/generated/message-reaction.d.ts +158 -0
- package/lib/generated/message-reaction.d.ts.map +1 -0
- package/lib/generated/message-reaction.js +306 -0
- package/lib/generated/message-reaction.js.map +1 -0
- package/lib/generated/message.d.ts +208 -0
- package/lib/generated/message.d.ts.map +1 -0
- package/lib/generated/message.js +406 -0
- package/lib/generated/message.js.map +1 -0
- package/lib/generated/my-chat-member.d.ts +196 -0
- package/lib/generated/my-chat-member.d.ts.map +1 -0
- package/lib/generated/my-chat-member.js +382 -0
- package/lib/generated/my-chat-member.js.map +1 -0
- package/lib/generated/poll-answer.d.ts +14 -0
- package/lib/generated/poll-answer.d.ts.map +1 -0
- package/lib/generated/poll-answer.js +18 -0
- package/lib/generated/poll-answer.js.map +1 -0
- package/lib/generated/poll.d.ts +10 -0
- package/lib/generated/poll.d.ts.map +1 -0
- package/lib/generated/poll.js +10 -0
- package/lib/generated/poll.js.map +1 -0
- package/lib/generated/pre-checkout-query.d.ts +48 -0
- package/lib/generated/pre-checkout-query.d.ts.map +1 -0
- package/lib/generated/pre-checkout-query.js +86 -0
- package/lib/generated/pre-checkout-query.js.map +1 -0
- package/lib/generated/purchased-paid-media.d.ts +46 -0
- package/lib/generated/purchased-paid-media.d.ts.map +1 -0
- package/lib/generated/purchased-paid-media.js +82 -0
- package/lib/generated/purchased-paid-media.js.map +1 -0
- package/lib/generated/removed-chat-boost.d.ts +144 -0
- package/lib/generated/removed-chat-boost.d.ts.map +1 -0
- package/lib/generated/removed-chat-boost.js +278 -0
- package/lib/generated/removed-chat-boost.js.map +1 -0
- package/lib/generated/shipping-query.d.ts +48 -0
- package/lib/generated/shipping-query.d.ts.map +1 -0
- package/lib/generated/shipping-query.js +86 -0
- package/lib/generated/shipping-query.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +1 -0
- package/lib/index.test.d.ts +2 -0
- package/lib/index.test.d.ts.map +1 -0
- package/lib/index.test.js +202 -0
- package/lib/index.test.js.map +1 -0
- package/lib/sugar/business-message.d.ts +69 -0
- package/lib/sugar/business-message.d.ts.map +1 -0
- package/lib/sugar/business-message.js +6 -0
- package/lib/sugar/business-message.js.map +1 -0
- package/lib/sugar/callback-query.d.ts +21 -0
- package/lib/sugar/callback-query.d.ts.map +1 -0
- package/lib/sugar/callback-query.js +19 -0
- package/lib/sugar/callback-query.js.map +1 -0
- package/lib/sugar/channel-post.d.ts +69 -0
- package/lib/sugar/channel-post.d.ts.map +1 -0
- package/lib/sugar/channel-post.js +6 -0
- package/lib/sugar/channel-post.js.map +1 -0
- package/lib/sugar/chat-join-request.d.ts +9 -0
- package/lib/sugar/chat-join-request.d.ts.map +1 -0
- package/lib/sugar/chat-join-request.js +13 -0
- package/lib/sugar/chat-join-request.js.map +1 -0
- package/lib/sugar/edited-business-message.d.ts +69 -0
- package/lib/sugar/edited-business-message.d.ts.map +1 -0
- package/lib/sugar/edited-business-message.js +6 -0
- package/lib/sugar/edited-business-message.js.map +1 -0
- package/lib/sugar/edited-channel-post.d.ts +69 -0
- package/lib/sugar/edited-channel-post.d.ts.map +1 -0
- package/lib/sugar/edited-channel-post.js +6 -0
- package/lib/sugar/edited-channel-post.js.map +1 -0
- package/lib/sugar/edited-message.d.ts +69 -0
- package/lib/sugar/edited-message.d.ts.map +1 -0
- package/lib/sugar/edited-message.js +6 -0
- package/lib/sugar/edited-message.js.map +1 -0
- package/lib/sugar/inline-query.d.ts +14 -0
- package/lib/sugar/inline-query.d.ts.map +1 -0
- package/lib/sugar/inline-query.js +11 -0
- package/lib/sugar/inline-query.js.map +1 -0
- package/lib/sugar/message-mixin.d.ts +55 -0
- package/lib/sugar/message-mixin.d.ts.map +1 -0
- package/lib/sugar/message-mixin.js +74 -0
- package/lib/sugar/message-mixin.js.map +1 -0
- package/lib/sugar/message.d.ts +69 -0
- package/lib/sugar/message.d.ts.map +1 -0
- package/lib/sugar/message.js +6 -0
- package/lib/sugar/message.js.map +1 -0
- package/lib/sugar/pre-checkout-query.d.ts +11 -0
- package/lib/sugar/pre-checkout-query.d.ts.map +1 -0
- package/lib/sugar/pre-checkout-query.js +8 -0
- package/lib/sugar/pre-checkout-query.js.map +1 -0
- package/lib/sugar/shipping-query.d.ts +11 -0
- package/lib/sugar/shipping-query.d.ts.map +1 -0
- package/lib/sugar/shipping-query.js +8 -0
- package/lib/sugar/shipping-query.js.map +1 -0
- package/package.json +50 -0
- package/src/generated/business-connection.ts +22 -0
- package/src/generated/business-message.ts +410 -0
- package/src/generated/callback-query.ts +394 -0
- package/src/generated/channel-post.ts +410 -0
- package/src/generated/chat-boost.ts +282 -0
- package/src/generated/chat-join-request.ts +386 -0
- package/src/generated/chat-member.ts +386 -0
- package/src/generated/chosen-inline-result.ts +86 -0
- package/src/generated/deleted-business-messages.ts +282 -0
- package/src/generated/edited-business-message.ts +410 -0
- package/src/generated/edited-channel-post.ts +410 -0
- package/src/generated/edited-message.ts +410 -0
- package/src/generated/index.ts +127 -0
- package/src/generated/inline-query.ts +90 -0
- package/src/generated/message-reaction-count.ts +302 -0
- package/src/generated/message-reaction.ts +310 -0
- package/src/generated/message.ts +410 -0
- package/src/generated/my-chat-member.ts +386 -0
- package/src/generated/poll-answer.ts +22 -0
- package/src/generated/poll.ts +15 -0
- package/src/generated/pre-checkout-query.ts +90 -0
- package/src/generated/purchased-paid-media.ts +86 -0
- package/src/generated/removed-chat-boost.ts +282 -0
- package/src/generated/shipping-query.ts +90 -0
- package/src/index.test.ts +241 -0
- package/src/index.ts +9 -0
- package/src/sugar/business-message.ts +5 -0
- package/src/sugar/callback-query.ts +41 -0
- package/src/sugar/channel-post.ts +5 -0
- package/src/sugar/chat-join-request.ts +13 -0
- package/src/sugar/edited-business-message.ts +5 -0
- package/src/sugar/edited-channel-post.ts +5 -0
- package/src/sugar/edited-message.ts +5 -0
- package/src/sugar/inline-query.ts +20 -0
- package/src/sugar/message-mixin.ts +137 -0
- package/src/sugar/message.ts +7 -0
- package/src/sugar/pre-checkout-query.ts +19 -0
- package/src/sugar/shipping-query.ts +17 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
CallbackQueryContext,
|
|
5
|
+
ChannelPostContext,
|
|
6
|
+
MessageContext,
|
|
7
|
+
ShippingQueryContext,
|
|
8
|
+
contextFor,
|
|
9
|
+
} from "./index.js";
|
|
10
|
+
|
|
11
|
+
interface Call {
|
|
12
|
+
m: string;
|
|
13
|
+
// biome-ignore lint/suspicious/noExplicitAny: test recorder
|
|
14
|
+
p: any;
|
|
15
|
+
}
|
|
16
|
+
const recorder = () => {
|
|
17
|
+
const calls: Call[] = [];
|
|
18
|
+
const api = {
|
|
19
|
+
call: (m: string, p: unknown) => {
|
|
20
|
+
calls.push({ m, p });
|
|
21
|
+
return Promise.resolve({});
|
|
22
|
+
},
|
|
23
|
+
} as never;
|
|
24
|
+
return { api, calls };
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
test("MessageContext merges payload fields onto the context", () => {
|
|
28
|
+
const { api } = recorder();
|
|
29
|
+
const ctx = new MessageContext(api, {
|
|
30
|
+
update_id: 1,
|
|
31
|
+
message: {
|
|
32
|
+
message_id: 5,
|
|
33
|
+
date: 0,
|
|
34
|
+
chat: { id: 42, type: "private" },
|
|
35
|
+
from: { id: 7, is_bot: false, first_name: "u" },
|
|
36
|
+
text: "hi",
|
|
37
|
+
},
|
|
38
|
+
} as never);
|
|
39
|
+
assert.equal(ctx.text, "hi");
|
|
40
|
+
assert.equal(ctx.message_id, 5);
|
|
41
|
+
assert.equal(ctx.chat.id, 42);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("auto-generated reply/delete fill chat_id + message_id", () => {
|
|
45
|
+
const { api, calls } = recorder();
|
|
46
|
+
const ctx = new MessageContext(api, {
|
|
47
|
+
update_id: 1,
|
|
48
|
+
message: { message_id: 5, date: 0, chat: { id: 42, type: "private" } },
|
|
49
|
+
} as never);
|
|
50
|
+
|
|
51
|
+
ctx.reply({ text: "yo" });
|
|
52
|
+
assert.deepEqual(calls[0], {
|
|
53
|
+
m: "sendMessage",
|
|
54
|
+
p: { chat_id: 42, reply_parameters: { message_id: 5 }, text: "yo" },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
calls.length = 0;
|
|
58
|
+
ctx.delete();
|
|
59
|
+
assert.deepEqual(calls[0], { m: "deleteMessage", p: { chat_id: 42, message_id: 5 } });
|
|
60
|
+
|
|
61
|
+
calls.length = 0;
|
|
62
|
+
ctx.send({ text: "hello" }); // send (no reply_parameters)
|
|
63
|
+
assert.deepEqual(calls[0], { m: "sendMessage", p: { chat_id: 42, text: "hello" } });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("sugar: positional send/reply build the params object", () => {
|
|
67
|
+
const { api, calls } = recorder();
|
|
68
|
+
const ctx = new MessageContext(api, {
|
|
69
|
+
update_id: 1,
|
|
70
|
+
message: { message_id: 5, date: 0, chat: { id: 42, type: "private" } },
|
|
71
|
+
} as never);
|
|
72
|
+
|
|
73
|
+
ctx.send("hi", { parse_mode: "HTML" });
|
|
74
|
+
assert.deepEqual(calls[0], {
|
|
75
|
+
m: "sendMessage",
|
|
76
|
+
p: { chat_id: 42, text: "hi", parse_mode: "HTML" },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
calls.length = 0;
|
|
80
|
+
ctx.reply("yo");
|
|
81
|
+
assert.deepEqual(calls[0], {
|
|
82
|
+
m: "sendMessage",
|
|
83
|
+
p: { chat_id: 42, reply_parameters: { message_id: 5 }, text: "yo" },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
calls.length = 0;
|
|
87
|
+
ctx.react("🔥");
|
|
88
|
+
assert.deepEqual(calls[0], {
|
|
89
|
+
m: "setMessageReaction",
|
|
90
|
+
p: { chat_id: 42, message_id: 5, reaction: [{ type: "emoji", emoji: "🔥" }] },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
calls.length = 0;
|
|
94
|
+
ctx.editText("edited");
|
|
95
|
+
assert.deepEqual(calls[0], {
|
|
96
|
+
m: "editMessageText",
|
|
97
|
+
p: { chat_id: 42, message_id: 5, text: "edited" },
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("sugar: react accepts custom emoji, shorthand objects and arrays", () => {
|
|
102
|
+
const { api, calls } = recorder();
|
|
103
|
+
const ctx = new MessageContext(api, {
|
|
104
|
+
update_id: 1,
|
|
105
|
+
message: { message_id: 5, date: 0, chat: { id: 42, type: "private" } },
|
|
106
|
+
} as never);
|
|
107
|
+
const reactionOf = () => (calls[0]?.p as { reaction: unknown[] }).reaction;
|
|
108
|
+
|
|
109
|
+
ctx.react("🔥", "12345"); // emoji + custom emoji id
|
|
110
|
+
assert.deepEqual(reactionOf(), [{ type: "custom_emoji", custom_emoji_id: "12345" }]);
|
|
111
|
+
|
|
112
|
+
calls.length = 0;
|
|
113
|
+
ctx.react({ custom_emoji_id: "999" }); // shorthand custom object
|
|
114
|
+
assert.deepEqual(reactionOf(), [{ type: "custom_emoji", custom_emoji_id: "999" }]);
|
|
115
|
+
|
|
116
|
+
calls.length = 0;
|
|
117
|
+
ctx.react({ emoji: "🎉" }); // shorthand emoji object
|
|
118
|
+
assert.deepEqual(reactionOf(), [{ type: "emoji", emoji: "🎉" }]);
|
|
119
|
+
|
|
120
|
+
calls.length = 0;
|
|
121
|
+
ctx.react([{ emoji: "👍" }, { custom_emoji_id: "1" }], { is_big: true }); // array + extra
|
|
122
|
+
assert.deepEqual(calls[0], {
|
|
123
|
+
m: "setMessageReaction",
|
|
124
|
+
p: {
|
|
125
|
+
chat_id: 42,
|
|
126
|
+
message_id: 5,
|
|
127
|
+
reaction: [
|
|
128
|
+
{ type: "emoji", emoji: "👍" },
|
|
129
|
+
{ type: "custom_emoji", custom_emoji_id: "1" },
|
|
130
|
+
],
|
|
131
|
+
is_big: true,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("sugar: convenience getters + react() clears reactions", () => {
|
|
137
|
+
const { api, calls } = recorder();
|
|
138
|
+
const ctx = new MessageContext(api, {
|
|
139
|
+
update_id: 1,
|
|
140
|
+
message: {
|
|
141
|
+
message_id: 5,
|
|
142
|
+
date: 0,
|
|
143
|
+
chat: { id: 42, type: "private" },
|
|
144
|
+
from: { id: 7, is_bot: false, first_name: "u" },
|
|
145
|
+
},
|
|
146
|
+
} as never);
|
|
147
|
+
assert.equal(ctx.chatId, 42);
|
|
148
|
+
assert.equal(ctx.senderId, 7);
|
|
149
|
+
|
|
150
|
+
ctx.react(); // no args → clear
|
|
151
|
+
assert.deepEqual(calls[0], {
|
|
152
|
+
m: "setMessageReaction",
|
|
153
|
+
p: { chat_id: 42, message_id: 5, reaction: [] },
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("sugar: callback answer accepts a bare string and no args", () => {
|
|
158
|
+
const { api, calls } = recorder();
|
|
159
|
+
const ctx = new CallbackQueryContext(api, {
|
|
160
|
+
update_id: 1,
|
|
161
|
+
callback_query: { id: "q1", from: { id: 7, is_bot: false, first_name: "u" }, data: "x" },
|
|
162
|
+
} as never);
|
|
163
|
+
|
|
164
|
+
ctx.answer("done");
|
|
165
|
+
assert.deepEqual(calls[0], {
|
|
166
|
+
m: "answerCallbackQuery",
|
|
167
|
+
p: { callback_query_id: "q1", text: "done" },
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
calls.length = 0;
|
|
171
|
+
ctx.answer();
|
|
172
|
+
assert.deepEqual(calls[0], { m: "answerCallbackQuery", p: { callback_query_id: "q1" } });
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("CallbackQueryContext.answer fills callback_query_id", () => {
|
|
176
|
+
const { api, calls } = recorder();
|
|
177
|
+
const ctx = new CallbackQueryContext(api, {
|
|
178
|
+
update_id: 1,
|
|
179
|
+
callback_query: {
|
|
180
|
+
id: "q1",
|
|
181
|
+
from: { id: 7, is_bot: false, first_name: "u" },
|
|
182
|
+
message: { message_id: 5, date: 0, chat: { id: 42, type: "private" } },
|
|
183
|
+
data: "x",
|
|
184
|
+
},
|
|
185
|
+
} as never);
|
|
186
|
+
|
|
187
|
+
assert.equal(ctx.data, "x");
|
|
188
|
+
ctx.answer({ text: "ok" });
|
|
189
|
+
assert.deepEqual(calls[0], {
|
|
190
|
+
m: "answerCallbackQuery",
|
|
191
|
+
p: { callback_query_id: "q1", text: "ok" },
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("shared message mixin applies to other message-based contexts", () => {
|
|
196
|
+
const { api, calls } = recorder();
|
|
197
|
+
const ctx = new ChannelPostContext(api, {
|
|
198
|
+
update_id: 1,
|
|
199
|
+
channel_post: { message_id: 9, date: 0, chat: { id: -100, type: "channel" } },
|
|
200
|
+
} as never);
|
|
201
|
+
assert.equal(ctx.chatId, -100);
|
|
202
|
+
|
|
203
|
+
ctx.react("👍");
|
|
204
|
+
assert.deepEqual(calls[0], {
|
|
205
|
+
m: "setMessageReaction",
|
|
206
|
+
p: { chat_id: -100, message_id: 9, reaction: [{ type: "emoji", emoji: "👍" }] },
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
calls.length = 0;
|
|
210
|
+
ctx.send("hi");
|
|
211
|
+
assert.deepEqual(calls[0], { m: "sendMessage", p: { chat_id: -100, text: "hi" } });
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("shipping query answer takes a positional ok", () => {
|
|
215
|
+
const { api, calls } = recorder();
|
|
216
|
+
const ctx = new ShippingQueryContext(api, {
|
|
217
|
+
update_id: 1,
|
|
218
|
+
shipping_query: { id: "sq1", from: { id: 7, is_bot: false, first_name: "u" } },
|
|
219
|
+
} as never);
|
|
220
|
+
assert.equal(ctx.senderId, 7);
|
|
221
|
+
|
|
222
|
+
ctx.answer(true);
|
|
223
|
+
assert.deepEqual(calls[0], {
|
|
224
|
+
m: "answerShippingQuery",
|
|
225
|
+
p: { shipping_query_id: "sq1", ok: true },
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("contextFor builds the right class per update type", () => {
|
|
230
|
+
const { api } = recorder();
|
|
231
|
+
const m = contextFor("message", api, {
|
|
232
|
+
update_id: 1,
|
|
233
|
+
message: { message_id: 1, date: 0, chat: { id: 1, type: "private" } },
|
|
234
|
+
} as never);
|
|
235
|
+
assert.ok(m instanceof MessageContext);
|
|
236
|
+
const c = contextFor("callback_query", api, {
|
|
237
|
+
update_id: 1,
|
|
238
|
+
callback_query: { id: "q", from: { id: 1, is_bot: false, first_name: "u" } },
|
|
239
|
+
} as never);
|
|
240
|
+
assert.ok(c instanceof CallbackQueryContext);
|
|
241
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yaebal/contexts — a context class per Update type (`MessageContext`,
|
|
3
|
+
* `CallbackQueryContext`, …), each merging the raw payload fields and exposing
|
|
4
|
+
* shortcut methods (`reply`, `editText`, `delete`, `answer`, …) that are
|
|
5
|
+
* AUTO-GENERATED from the Bot API methods + the ids the context carries.
|
|
6
|
+
* Regenerate with `pnpm --filter @yaebal/contexts generate`.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./generated/index.js";
|
|
9
|
+
export type { ReactionInput } from "./sugar/message.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { BusinessMessageContextBase } from "../generated/business-message.js";
|
|
2
|
+
import { MessageSugar } from "./message-mixin.js";
|
|
3
|
+
|
|
4
|
+
/** BusinessMessageContext = generated base + the shared Message sugar. */
|
|
5
|
+
export class BusinessMessageContext extends MessageSugar(BusinessMessageContextBase) {}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type * as t from "@yaebal/types";
|
|
2
|
+
import { CallbackQueryContextBase } from "../generated/callback-query.js";
|
|
3
|
+
|
|
4
|
+
type AnswerExtra = Omit<t.AnswerCallbackQueryParams, "callback_query_id" | "text">;
|
|
5
|
+
type EditExtra = Omit<t.EditMessageTextParams, "chat_id" | "message_id" | "text">;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CallbackQueryContext = generated base + hand-written ergonomic overloads.
|
|
9
|
+
* Positional-string sugar for `answer` (the toast you fire on every tap) and
|
|
10
|
+
* `editText` (editing the message the button is attached to).
|
|
11
|
+
*/
|
|
12
|
+
export class CallbackQueryContext extends CallbackQueryContextBase {
|
|
13
|
+
/** Id of the chat the button's message is in (if still accessible). */
|
|
14
|
+
get chatId(): number | undefined {
|
|
15
|
+
return this.message?.chat?.id;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Answer the callback query. Pass a string to flash text to the user. */
|
|
19
|
+
override answer(text: string, params?: AnswerExtra): Promise<boolean>;
|
|
20
|
+
override answer(
|
|
21
|
+
params?: Omit<t.AnswerCallbackQueryParams, "callback_query_id">,
|
|
22
|
+
): Promise<boolean>;
|
|
23
|
+
override answer(
|
|
24
|
+
a?: string | Omit<t.AnswerCallbackQueryParams, "callback_query_id">,
|
|
25
|
+
b?: AnswerExtra,
|
|
26
|
+
): Promise<boolean> {
|
|
27
|
+
return super.answer(typeof a === "string" ? { text: a, ...b } : (a ?? {}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Edit the attached message's text. Pass a string for the common case. */
|
|
31
|
+
override editText(text: string, params?: EditExtra): Promise<t.Message | boolean>;
|
|
32
|
+
override editText(
|
|
33
|
+
params: Omit<t.EditMessageTextParams, "chat_id" | "message_id">,
|
|
34
|
+
): Promise<t.Message | boolean>;
|
|
35
|
+
override editText(
|
|
36
|
+
a: string | Omit<t.EditMessageTextParams, "chat_id" | "message_id">,
|
|
37
|
+
b?: EditExtra,
|
|
38
|
+
): Promise<t.Message | boolean> {
|
|
39
|
+
return super.editText(typeof a === "string" ? { text: a, ...b } : a);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ChannelPostContextBase } from "../generated/channel-post.js";
|
|
2
|
+
import { MessageSugar } from "./message-mixin.js";
|
|
3
|
+
|
|
4
|
+
/** ChannelPostContext = generated base + the shared Message sugar. */
|
|
5
|
+
export class ChannelPostContext extends MessageSugar(ChannelPostContextBase) {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ChatJoinRequestContextBase } from "../generated/chat-join-request.js";
|
|
2
|
+
|
|
3
|
+
/** ChatJoinRequestContext = generated base + `approve()` / `decline()` shortcuts. */
|
|
4
|
+
export class ChatJoinRequestContext extends ChatJoinRequestContextBase {
|
|
5
|
+
/** Approve this chat join request. */
|
|
6
|
+
approve(): Promise<boolean> {
|
|
7
|
+
return this.approveChatJoinRequest();
|
|
8
|
+
}
|
|
9
|
+
/** Decline this chat join request. */
|
|
10
|
+
decline(): Promise<boolean> {
|
|
11
|
+
return this.declineChatJoinRequest();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EditedBusinessMessageContextBase } from "../generated/edited-business-message.js";
|
|
2
|
+
import { MessageSugar } from "./message-mixin.js";
|
|
3
|
+
|
|
4
|
+
/** EditedBusinessMessageContext = generated base + the shared Message sugar. */
|
|
5
|
+
export class EditedBusinessMessageContext extends MessageSugar(EditedBusinessMessageContextBase) {}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EditedChannelPostContextBase } from "../generated/edited-channel-post.js";
|
|
2
|
+
import { MessageSugar } from "./message-mixin.js";
|
|
3
|
+
|
|
4
|
+
/** EditedChannelPostContext = generated base + the shared Message sugar. */
|
|
5
|
+
export class EditedChannelPostContext extends MessageSugar(EditedChannelPostContextBase) {}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EditedMessageContextBase } from "../generated/edited-message.js";
|
|
2
|
+
import { MessageSugar } from "./message-mixin.js";
|
|
3
|
+
|
|
4
|
+
/** EditedMessageContext = generated base + the shared Message sugar. */
|
|
5
|
+
export class EditedMessageContext extends MessageSugar(EditedMessageContextBase) {}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type * as t from "@yaebal/types";
|
|
2
|
+
import { InlineQueryContextBase } from "../generated/inline-query.js";
|
|
3
|
+
|
|
4
|
+
type AnswerExtra = Omit<t.AnswerInlineQueryParams, "inline_query_id" | "results">;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* InlineQueryContext = generated base + hand-written ergonomic overloads.
|
|
8
|
+
* Positional sugar for `answer` — pass the results array directly.
|
|
9
|
+
*/
|
|
10
|
+
export class InlineQueryContext extends InlineQueryContextBase {
|
|
11
|
+
/** Answer the inline query with a list of results. */
|
|
12
|
+
override answer(results: t.InlineQueryResult[], params?: AnswerExtra): Promise<boolean>;
|
|
13
|
+
override answer(params: Omit<t.AnswerInlineQueryParams, "inline_query_id">): Promise<boolean>;
|
|
14
|
+
override answer(
|
|
15
|
+
a: t.InlineQueryResult[] | Omit<t.AnswerInlineQueryParams, "inline_query_id">,
|
|
16
|
+
b?: AnswerExtra,
|
|
17
|
+
): Promise<boolean> {
|
|
18
|
+
return super.answer(Array.isArray(a) ? { results: a, ...b } : a);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Api } from "@yaebal/core";
|
|
2
|
+
import type * as t from "@yaebal/types";
|
|
3
|
+
|
|
4
|
+
/** A friendly reaction: an emoji string, a `{ emoji }` / `{ custom_emoji_id }` shorthand, or a raw `ReactionType`. */
|
|
5
|
+
export type ReactionInput =
|
|
6
|
+
| string
|
|
7
|
+
| { emoji: string }
|
|
8
|
+
| { custom_emoji_id: string }
|
|
9
|
+
| t.ReactionType;
|
|
10
|
+
|
|
11
|
+
function toReaction(r: ReactionInput): t.ReactionType {
|
|
12
|
+
if (typeof r === "string") return { type: "emoji", emoji: r };
|
|
13
|
+
if ("type" in r) return r;
|
|
14
|
+
if ("custom_emoji_id" in r) return { type: "custom_emoji", custom_emoji_id: r.custom_emoji_id };
|
|
15
|
+
return { type: "emoji", emoji: r.emoji };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type SendExtra = Omit<t.SendMessageParams, "chat_id" | "text">;
|
|
19
|
+
type EditExtra = Omit<t.EditMessageTextParams, "chat_id" | "message_id" | "text">;
|
|
20
|
+
type CaptionExtra = Omit<t.EditMessageCaptionParams, "chat_id" | "message_id" | "caption">;
|
|
21
|
+
type ReactExtra = Omit<t.SetMessageReactionParams, "chat_id" | "message_id" | "reaction">;
|
|
22
|
+
|
|
23
|
+
/** Fields every Message-based context carries (merged from the payload). */
|
|
24
|
+
interface MessageFields {
|
|
25
|
+
readonly api: Api;
|
|
26
|
+
chat: t.Chat;
|
|
27
|
+
message_id: number;
|
|
28
|
+
from?: t.User;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// biome-ignore lint/suspicious/noExplicitAny: mixin base constructor
|
|
32
|
+
type Ctor<T> = abstract new (...args: any[]) => T;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adds the ergonomic positional overloads + convenience getters shared by every
|
|
36
|
+
* Message-based context (message, channel_post, edited_*, business_*). Methods
|
|
37
|
+
* call `this.api.call` directly (so the mixin needs no `super`).
|
|
38
|
+
*/
|
|
39
|
+
export function MessageSugar<TBase extends Ctor<MessageFields>>(Base: TBase) {
|
|
40
|
+
abstract class Sugared extends Base {
|
|
41
|
+
/** Send a new message to this chat. Pass a string for the common case. */
|
|
42
|
+
send(text: string, params?: SendExtra): Promise<t.Message>;
|
|
43
|
+
send(params: Omit<t.SendMessageParams, "chat_id">): Promise<t.Message>;
|
|
44
|
+
send(a: string | Omit<t.SendMessageParams, "chat_id">, b?: SendExtra): Promise<t.Message> {
|
|
45
|
+
const params = typeof a === "string" ? { text: a, ...b } : a;
|
|
46
|
+
return this.api.call<t.Message>("sendMessage", { chat_id: this.chat.id, ...params });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Reply to this message. Pass a string for the common case. */
|
|
50
|
+
reply(text: string, params?: SendExtra): Promise<t.Message>;
|
|
51
|
+
reply(params: Omit<t.SendMessageParams, "chat_id">): Promise<t.Message>;
|
|
52
|
+
reply(a: string | Omit<t.SendMessageParams, "chat_id">, b?: SendExtra): Promise<t.Message> {
|
|
53
|
+
const params = typeof a === "string" ? { text: a, ...b } : a;
|
|
54
|
+
return this.api.call<t.Message>("sendMessage", {
|
|
55
|
+
chat_id: this.chat.id,
|
|
56
|
+
reply_parameters: { message_id: this.message_id },
|
|
57
|
+
...params,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Edit this message's text. Pass a string for the common case. */
|
|
62
|
+
editText(text: string, params?: EditExtra): Promise<t.Message | boolean>;
|
|
63
|
+
editText(
|
|
64
|
+
params: Omit<t.EditMessageTextParams, "chat_id" | "message_id">,
|
|
65
|
+
): Promise<t.Message | boolean>;
|
|
66
|
+
editText(
|
|
67
|
+
a: string | Omit<t.EditMessageTextParams, "chat_id" | "message_id">,
|
|
68
|
+
b?: EditExtra,
|
|
69
|
+
): Promise<t.Message | boolean> {
|
|
70
|
+
const params = typeof a === "string" ? { text: a, ...b } : a;
|
|
71
|
+
return this.api.call<t.Message | boolean>("editMessageText", {
|
|
72
|
+
chat_id: this.chat.id,
|
|
73
|
+
message_id: this.message_id,
|
|
74
|
+
...params,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Edit this message's caption. Pass a string for the common case. */
|
|
79
|
+
editCaption(caption: string, params?: CaptionExtra): Promise<t.Message | boolean>;
|
|
80
|
+
editCaption(
|
|
81
|
+
params: Omit<t.EditMessageCaptionParams, "chat_id" | "message_id">,
|
|
82
|
+
): Promise<t.Message | boolean>;
|
|
83
|
+
editCaption(
|
|
84
|
+
a: string | Omit<t.EditMessageCaptionParams, "chat_id" | "message_id">,
|
|
85
|
+
b?: CaptionExtra,
|
|
86
|
+
): Promise<t.Message | boolean> {
|
|
87
|
+
const params = typeof a === "string" ? { caption: a, ...b } : a;
|
|
88
|
+
return this.api.call<t.Message | boolean>("editMessageCaption", {
|
|
89
|
+
chat_id: this.chat.id,
|
|
90
|
+
message_id: this.message_id,
|
|
91
|
+
...params,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Clear all reactions on this message. */
|
|
96
|
+
react(): Promise<boolean>;
|
|
97
|
+
/** React with a single emoji. */
|
|
98
|
+
react(emoji: string, params?: ReactExtra): Promise<boolean>;
|
|
99
|
+
/** React with a custom emoji (the emoji string is a readable fallback). */
|
|
100
|
+
react(emoji: string, customEmojiId: string, params?: ReactExtra): Promise<boolean>;
|
|
101
|
+
/** React with one or many reactions (emoji / `{ emoji }` / `{ custom_emoji_id }` / raw). */
|
|
102
|
+
react(reactions: ReactionInput | ReactionInput[], params?: ReactExtra): Promise<boolean>;
|
|
103
|
+
/** React with raw `setMessageReaction` params. */
|
|
104
|
+
react(params: Omit<t.SetMessageReactionParams, "chat_id" | "message_id">): Promise<boolean>;
|
|
105
|
+
react(
|
|
106
|
+
a?:
|
|
107
|
+
| string
|
|
108
|
+
| ReactionInput
|
|
109
|
+
| ReactionInput[]
|
|
110
|
+
| Omit<t.SetMessageReactionParams, "chat_id" | "message_id">,
|
|
111
|
+
b?: string | ReactExtra,
|
|
112
|
+
c?: ReactExtra,
|
|
113
|
+
): Promise<boolean> {
|
|
114
|
+
let params: Omit<t.SetMessageReactionParams, "chat_id" | "message_id">;
|
|
115
|
+
if (a === undefined) {
|
|
116
|
+
params = { reaction: [] };
|
|
117
|
+
} else if (typeof a === "string") {
|
|
118
|
+
params =
|
|
119
|
+
typeof b === "string"
|
|
120
|
+
? { reaction: [{ type: "custom_emoji", custom_emoji_id: b }], ...c }
|
|
121
|
+
: { reaction: [{ type: "emoji", emoji: a }], ...b };
|
|
122
|
+
} else if (Array.isArray(a)) {
|
|
123
|
+
params = { reaction: a.map(toReaction), ...(b as ReactExtra) };
|
|
124
|
+
} else if ("emoji" in a || "custom_emoji_id" in a || "type" in a) {
|
|
125
|
+
params = { reaction: [toReaction(a as ReactionInput)], ...(b as ReactExtra) };
|
|
126
|
+
} else {
|
|
127
|
+
params = a;
|
|
128
|
+
}
|
|
129
|
+
return this.api.call<boolean>("setMessageReaction", {
|
|
130
|
+
chat_id: this.chat.id,
|
|
131
|
+
message_id: this.message_id,
|
|
132
|
+
...params,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Sugared;
|
|
137
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessageContextBase } from "../generated/message.js";
|
|
2
|
+
import { MessageSugar } from "./message-mixin.js";
|
|
3
|
+
|
|
4
|
+
export type { ReactionInput } from "./message-mixin.js";
|
|
5
|
+
|
|
6
|
+
/** MessageContext = generated base + the shared Message sugar (positional overloads + getters). */
|
|
7
|
+
export class MessageContext extends MessageSugar(MessageContextBase) {}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as t from "@yaebal/types";
|
|
2
|
+
import { PreCheckoutQueryContextBase } from "../generated/pre-checkout-query.js";
|
|
3
|
+
|
|
4
|
+
type Extra = Omit<t.AnswerPreCheckoutQueryParams, "pre_checkout_query_id" | "ok">;
|
|
5
|
+
|
|
6
|
+
/** PreCheckoutQueryContext = generated base + a positional `answer(ok)`. */
|
|
7
|
+
export class PreCheckoutQueryContext extends PreCheckoutQueryContextBase {
|
|
8
|
+
/** Answer the pre-checkout query. Pass `true` to approve, `false` (+ error_message) to reject. */
|
|
9
|
+
override answer(ok: boolean, params?: Extra): Promise<boolean>;
|
|
10
|
+
override answer(
|
|
11
|
+
params: Omit<t.AnswerPreCheckoutQueryParams, "pre_checkout_query_id">,
|
|
12
|
+
): Promise<boolean>;
|
|
13
|
+
override answer(
|
|
14
|
+
a: boolean | Omit<t.AnswerPreCheckoutQueryParams, "pre_checkout_query_id">,
|
|
15
|
+
b?: Extra,
|
|
16
|
+
): Promise<boolean> {
|
|
17
|
+
return super.answer(typeof a === "boolean" ? { ok: a, ...b } : a);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type * as t from "@yaebal/types";
|
|
2
|
+
import { ShippingQueryContextBase } from "../generated/shipping-query.js";
|
|
3
|
+
|
|
4
|
+
type Extra = Omit<t.AnswerShippingQueryParams, "shipping_query_id" | "ok">;
|
|
5
|
+
|
|
6
|
+
/** ShippingQueryContext = generated base + a positional `answer(ok)`. */
|
|
7
|
+
export class ShippingQueryContext extends ShippingQueryContextBase {
|
|
8
|
+
/** Answer the shipping query. Pass `true`/`false` for the common case. */
|
|
9
|
+
override answer(ok: boolean, params?: Extra): Promise<boolean>;
|
|
10
|
+
override answer(params: Omit<t.AnswerShippingQueryParams, "shipping_query_id">): Promise<boolean>;
|
|
11
|
+
override answer(
|
|
12
|
+
a: boolean | Omit<t.AnswerShippingQueryParams, "shipping_query_id">,
|
|
13
|
+
b?: Extra,
|
|
14
|
+
): Promise<boolean> {
|
|
15
|
+
return super.answer(typeof a === "boolean" ? { ok: a, ...b } : a);
|
|
16
|
+
}
|
|
17
|
+
}
|