telegram-inline-keyboard-builder 2.0.1 β 2.1.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/README.md +73 -0
- package/core/InlineKeyboardBuilder.js +249 -176
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,79 @@
|
|
|
3
3
|
# Inline Keyboard Builder (v2) Universal inline keyboard builder for Telegram Bots.
|
|
4
4
|
Produces **pure Telegram Bot API compliant JSON**, usable with **any library** (Telegraf, node-telegram-bot-api, Pyrogram, Aiogram, Puregram, Telebotβ¦).
|
|
5
5
|
|
|
6
|
+
## π₯ New update π₯
|
|
7
|
+
- Added color style for premium Telegram buttons and icons
|
|
8
|
+
- Builder method typing
|
|
9
|
+
|
|
10
|
+
## How does this feature work?
|
|
11
|
+
Simply specify a new parameter to the function to add the URL and class.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
addCallbackButton(text, callback_data, options = {})
|
|
15
|
+
addCUrlButton(text, callback_data, options = {})
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The options must contain at least one of these parameters: either `icon_custom_emoji_id` or `style`
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// Example
|
|
22
|
+
const keyboard = new InlineKeyboardBuilder(1)
|
|
23
|
+
.addCallbackButton("blue button", "click", {
|
|
24
|
+
style: "primary"
|
|
25
|
+
})
|
|
26
|
+
.addCallbackButton("blue button with icon", "click", {
|
|
27
|
+
icon_custom_emoji_id: "4963511421280192936",
|
|
28
|
+
style: "primary"
|
|
29
|
+
})
|
|
30
|
+
.addCallbackButton("Just a icon","click",{
|
|
31
|
+
icon_custom_emoji_id: "4963511421280192936"
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> **Warning**: `icon_custom_emoji_id` only works if the bot owner has a Telegram premium subscription.
|
|
36
|
+
|
|
37
|
+
## Example Usage (telegraf)
|
|
38
|
+
```
|
|
39
|
+
// start command
|
|
40
|
+
bot.start(async ctx => {
|
|
41
|
+
const keyboard = new InlineKeyboardBuilder(1)
|
|
42
|
+
.addCallbackButton("blue", "click", {
|
|
43
|
+
style: "primary"
|
|
44
|
+
})
|
|
45
|
+
.addCallbackButton("blue with icon", "click", {
|
|
46
|
+
icon_custom_emoji_id: "4963511421280192936",
|
|
47
|
+
style: "primary"
|
|
48
|
+
})
|
|
49
|
+
.addCallbackButton("green", "click", {
|
|
50
|
+
style: "success"
|
|
51
|
+
})
|
|
52
|
+
.addCallbackButton("green with icon", "click", {
|
|
53
|
+
icon_custom_emoji_id: "4963511421280192936",
|
|
54
|
+
style: "success"
|
|
55
|
+
})
|
|
56
|
+
.addCallbackButton("red", "danger",{
|
|
57
|
+
style: "danger"
|
|
58
|
+
})
|
|
59
|
+
.addCallbackButton("red with icon", "click", {
|
|
60
|
+
icon_custom_emoji_id: "4963511421280192936",
|
|
61
|
+
style: "danger"
|
|
62
|
+
})
|
|
63
|
+
.addCallbackButton("Just a icon","click",{
|
|
64
|
+
icon_custom_emoji_id: "4963511421280192936"
|
|
65
|
+
})
|
|
66
|
+
await ctx.reply(
|
|
67
|
+
"π New Button style π₯π₯π₯",
|
|
68
|
+
keyboard.build()
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Results
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
## other log
|
|
78
|
+
|
|
6
79
|
> Version 2 removes adapters and focuses on a single universal output:
|
|
7
80
|
> **valid `inline_keyboard` JSON as expected by Telegram API**.
|
|
8
81
|
|
|
@@ -1,178 +1,251 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Builder class for creating Telegram inline keyboards with optional
|
|
3
|
+
* custom styles, premium emojis, and automatic layout.
|
|
4
|
+
*/
|
|
2
5
|
export class InlineKeyboardBuilder {
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new InlineKeyboardBuilder instance.
|
|
8
|
+
*
|
|
9
|
+
* @param {number} [buttonsPerRow=2] - Number of buttons per row.
|
|
10
|
+
* @param {number} [autoWrapMaxChars=0] - Maximum characters per row before auto-wrapping. 0 = disabled.
|
|
11
|
+
*/
|
|
12
|
+
constructor(buttonsPerRow = 2, autoWrapMaxChars = 0) {
|
|
13
|
+
this.buttonsPerRow = buttonsPerRow;
|
|
14
|
+
this.autoWrapMaxChars = autoWrapMaxChars;
|
|
15
|
+
this._buttons = []; // Flat list of buttons with optional row markers
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ---------- internal ----------
|
|
19
|
+
/**
|
|
20
|
+
* Adds a button to the internal list.
|
|
21
|
+
* @private
|
|
22
|
+
* @param {object} btn - The button object to push.
|
|
23
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
24
|
+
*/
|
|
25
|
+
_pushButton(btn) {
|
|
26
|
+
this._buttons.push(btn);
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ---------- button helpers ----------
|
|
31
|
+
/**
|
|
32
|
+
* Adds a callback button with optional style or emoji.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} text - Button text.
|
|
35
|
+
* @param {string} callback_data - Data sent in callback query.
|
|
36
|
+
* @param {object} [options] - Optional button options (style, icon_custom_emoji_id).
|
|
37
|
+
* @param {"primary"|"danger"|"success"} [options.style] - Optional button style.
|
|
38
|
+
* @param {string} [options.icon_custom_emoji_id] - Optional premium emoji.You need premium account for use this option
|
|
39
|
+
* @throws {Error} If text or callback_data is missing.
|
|
40
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
41
|
+
*/
|
|
42
|
+
addCallbackButton(text, callback_data, options = {}) {
|
|
43
|
+
if (!text || !callback_data) {
|
|
44
|
+
throw new Error("Callback button requires text and callback_data");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { style, icon_custom_emoji_id } = options;
|
|
48
|
+
|
|
49
|
+
if (style && !["primary", "danger", "success"].includes(style)) {
|
|
50
|
+
throw new Error("Invalid style. Allowed: primary, danger, success");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this._pushButton({
|
|
54
|
+
text,
|
|
55
|
+
callback_data,
|
|
56
|
+
style,
|
|
57
|
+
icon_custom_emoji_id
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Adds a URL button with optional style or emoji.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} text - Button text.
|
|
65
|
+
* @param {string} url - URL to open.
|
|
66
|
+
* @param {object} [options] - Optional button options (style, icon_custom_emoji_id).
|
|
67
|
+
* @param {"primary"|"danger"|"success"} [options.style] - Optional button style.
|
|
68
|
+
* @param {string} [options.icon_custom_emoji_id] - Optional premium emoji.You need premium account for use this option
|
|
69
|
+
* @throws {Error} If text or URL is missing.
|
|
70
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
71
|
+
*/
|
|
72
|
+
addUrlButton(text, url, options = {}) {
|
|
73
|
+
if (!text || !url) {
|
|
74
|
+
throw new Error("URL button requires text and url");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { style, icon_custom_emoji_id } = options;
|
|
78
|
+
|
|
79
|
+
if (style && !["primary", "danger", "success"].includes(style)) {
|
|
80
|
+
throw new Error("Invalid style. Allowed: primary, danger, success");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return this._pushButton({ text, url, style, icon_custom_emoji_id });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Adds a pay button.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} text - Button text.
|
|
90
|
+
* @throws {Error} If text is missing.
|
|
91
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
92
|
+
*/
|
|
93
|
+
addPayButton(text) {
|
|
94
|
+
if (!text) {
|
|
95
|
+
throw new Error("Pay button requires text");
|
|
96
|
+
}
|
|
97
|
+
return this._pushButton({ text, pay: true });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Adds a fully custom button object.
|
|
102
|
+
*
|
|
103
|
+
* @param {object} buttonObject - Must have at least a `text` property.
|
|
104
|
+
* @throws {Error} If the button object is invalid.
|
|
105
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
106
|
+
*/
|
|
107
|
+
addCustomButton(buttonObject) {
|
|
108
|
+
if (!buttonObject || !buttonObject.text) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Custom button must be a valid InlineKeyboardButton object"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return this._pushButton(buttonObject);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ---------- layout controls ----------
|
|
117
|
+
/**
|
|
118
|
+
* Sets the number of buttons per row.
|
|
119
|
+
* @param {number} n - Must be at least 1.
|
|
120
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
121
|
+
*/
|
|
122
|
+
setButtonsPerRow(n) {
|
|
123
|
+
this.buttonsPerRow = Math.max(1, Math.floor(n));
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sets the maximum characters per row before auto-wrapping.
|
|
129
|
+
* @param {number} n - 0 disables auto-wrap.
|
|
130
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
131
|
+
*/
|
|
132
|
+
setAutoWrapMaxChars(n) {
|
|
133
|
+
this.autoWrapMaxChars = Math.max(0, Math.floor(n));
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Forces a new row in the keyboard.
|
|
139
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
140
|
+
*/
|
|
141
|
+
newRow() {
|
|
142
|
+
this._buttons.push({ __newRow: true });
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ---------- config-based API ----------
|
|
147
|
+
/**
|
|
148
|
+
* Adds a button from a config object.
|
|
149
|
+
* @private
|
|
150
|
+
* @param {object} btn - Button config { type, text, ... }.
|
|
151
|
+
*/
|
|
152
|
+
_addButtonFromConfig(btn) {
|
|
153
|
+
const { type, text } = btn;
|
|
154
|
+
if (!type || !text)
|
|
155
|
+
throw new Error("Button must have at least { type, text }");
|
|
156
|
+
switch (type) {
|
|
157
|
+
case "callback":
|
|
158
|
+
if (!btn.data)
|
|
159
|
+
throw new Error("Callback button requires `data`");
|
|
160
|
+
this.addCallbackButton(text, btn.data);
|
|
161
|
+
break;
|
|
162
|
+
case "url":
|
|
163
|
+
if (!btn.url) throw new Error("URL button requires `url`");
|
|
164
|
+
this.addUrlButton(text, btn.url);
|
|
165
|
+
break;
|
|
166
|
+
case "pay":
|
|
167
|
+
this.addPayButton(text);
|
|
168
|
+
break;
|
|
169
|
+
case "custom":
|
|
170
|
+
if (!btn.button)
|
|
171
|
+
throw new Error("Custom button requires `button`");
|
|
172
|
+
this.addCustomButton(btn.button);
|
|
173
|
+
break;
|
|
174
|
+
default:
|
|
175
|
+
throw new Error(`Unknown button type: ${type}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Adds multiple buttons from config.
|
|
181
|
+
* @param {Array|object} config - Array of button configs or grouped config.
|
|
182
|
+
* @returns {InlineKeyboardBuilder} The instance for chaining.
|
|
183
|
+
*/
|
|
184
|
+
addButtons(config) {
|
|
185
|
+
if (Array.isArray(config)) {
|
|
186
|
+
for (const btn of config) this._addButtonFromConfig(btn);
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
const { type, buttons } = config;
|
|
190
|
+
if (!type || !Array.isArray(buttons))
|
|
191
|
+
throw new Error("addButtons: invalid config");
|
|
192
|
+
for (const btn of buttons) this._addButtonFromConfig({ type, ...btn });
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------- layout engine ----------
|
|
197
|
+
/**
|
|
198
|
+
* Internal method that lays out buttons into rows based on configuration.
|
|
199
|
+
* @private
|
|
200
|
+
* @returns {Array<Array<object>>} Array of rows with buttons.
|
|
201
|
+
*/
|
|
202
|
+
_layoutButtons() {
|
|
203
|
+
const rows = [];
|
|
204
|
+
let row = [];
|
|
205
|
+
let rowChars = 0;
|
|
206
|
+
|
|
207
|
+
const pushRow = () => {
|
|
208
|
+
if (row.length > 0) {
|
|
209
|
+
rows.push(row);
|
|
210
|
+
row = [];
|
|
211
|
+
rowChars = 0;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
for (const b of this._buttons) {
|
|
216
|
+
if (b.__newRow) {
|
|
217
|
+
pushRow();
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const textLength = String(b.text || "").length;
|
|
221
|
+
if (
|
|
222
|
+
this.autoWrapMaxChars > 0 &&
|
|
223
|
+
row.length > 0 &&
|
|
224
|
+
rowChars + textLength > this.autoWrapMaxChars
|
|
225
|
+
) {
|
|
226
|
+
pushRow();
|
|
227
|
+
}
|
|
228
|
+
if (row.length >= this.buttonsPerRow) {
|
|
229
|
+
pushRow();
|
|
230
|
+
}
|
|
231
|
+
row.push(b);
|
|
232
|
+
rowChars += textLength;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
pushRow();
|
|
236
|
+
return rows;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ---------- final output ----------
|
|
240
|
+
/**
|
|
241
|
+
* Builds the final Telegram reply_markup object.
|
|
242
|
+
* @returns {object} Telegram inline_keyboard reply_markup.
|
|
243
|
+
*/
|
|
244
|
+
build() {
|
|
245
|
+
return {
|
|
246
|
+
reply_markup: {
|
|
247
|
+
inline_keyboard: this._layoutButtons()
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
178
251
|
}
|