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 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
+ ![Example results](https://i.ibb.co/1tgyYQCv/IMG-20260211-054905-332.jpg)
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
- constructor(buttonsPerRow = 2, autoWrapMaxChars = 0) {
4
- this.buttonsPerRow = buttonsPerRow;
5
- this.autoWrapMaxChars = autoWrapMaxChars; // 0 = disabled
6
- this._buttons = []; // flat list of buttons + row markers
7
- }
8
-
9
- // ---------- internal ----------
10
- _pushButton(btn) {
11
- this._buttons.push(btn);
12
- return this;
13
- }
14
-
15
- // ---------- button helpers ----------
16
- addCallbackButton(text, callback_data) {
17
- if (!text || !callback_data) {
18
- throw new Error("Callback button requires text and callback_data");
19
- }
20
-
21
- return this._pushButton({
22
- text,
23
- callback_data,
24
- });
25
- }
26
-
27
- addUrlButton(text, url) {
28
- if (!text || !url) {
29
- throw new Error("URL button requires text and url");
30
- }
31
-
32
- return this._pushButton({
33
- text,
34
- url,
35
- });
36
- }
37
-
38
- addPayButton(text) {
39
- if (!text) {
40
- throw new Error("Pay button requires text");
41
- }
42
-
43
- return this._pushButton({
44
- text,
45
- pay: true,
46
- });
47
- }
48
-
49
- addCustomButton(buttonObject) {
50
- if (!buttonObject || !buttonObject.text) {
51
- throw new Error("Custom button must be a valid InlineKeyboardButton object");
52
- }
53
-
54
- return this._pushButton(buttonObject);
55
- }
56
-
57
- // ---------- layout controls ----------
58
- setButtonsPerRow(n) {
59
- this.buttonsPerRow = Math.max(1, Math.floor(n));
60
- return this;
61
- }
62
-
63
- setAutoWrapMaxChars(n) {
64
- this.autoWrapMaxChars = Math.max(0, Math.floor(n));
65
- return this;
66
- }
67
-
68
- newRow() {
69
- this._buttons.push({ __newRow: true });
70
- return this;
71
- }
72
-
73
- // ---------- config-based API ----------
74
- _addButtonFromConfig(btn) {
75
- const { type, text } = btn;
76
-
77
- if (!type || !text) {
78
- throw new Error("Button must have at least { type, text }");
79
- }
80
-
81
- switch (type) {
82
- case "callback":
83
- if (!btn.data) throw new Error("Callback button requires `data`");
84
- this.addCallbackButton(text, btn.data);
85
- break;
86
-
87
- case "url":
88
- if (!btn.url) throw new Error("URL button requires `url`");
89
- this.addUrlButton(text, btn.url);
90
- break;
91
-
92
- case "pay":
93
- this.addPayButton(text);
94
- break;
95
-
96
- case "custom":
97
- if (!btn.button) throw new Error("Custom button requires `button`");
98
- this.addCustomButton(btn.button);
99
- break;
100
-
101
- default:
102
- throw new Error(`Unknown button type: ${type}`);
103
- }
104
- }
105
-
106
- addButtons(config) {
107
- // Case 1: array of buttons
108
- if (Array.isArray(config)) {
109
- for (const btn of config) {
110
- this._addButtonFromConfig(btn);
111
- }
112
- return this;
113
- }
114
-
115
- // Case 2: grouped config
116
- const { type, buttons } = config;
117
- if (!type || !Array.isArray(buttons)) {
118
- throw new Error("addButtons: invalid config");
119
- }
120
-
121
- for (const btn of buttons) {
122
- this._addButtonFromConfig({ type, ...btn });
123
- }
124
-
125
- return this;
126
- }
127
-
128
- // ---------- layout engine ----------
129
- _layoutButtons() {
130
- const rows = [];
131
- let row = [];
132
- let rowChars = 0;
133
-
134
- const pushRow = () => {
135
- if (row.length > 0) {
136
- rows.push(row);
137
- row = [];
138
- rowChars = 0;
139
- }
140
- };
141
-
142
- for (const b of this._buttons) {
143
- if (b.__newRow) {
144
- pushRow();
145
- continue;
146
- }
147
-
148
- const textLength = String(b.text || "").length;
149
-
150
- if (
151
- this.autoWrapMaxChars > 0 &&
152
- row.length > 0 &&
153
- rowChars + textLength > this.autoWrapMaxChars
154
- ) {
155
- pushRow();
156
- }
157
-
158
- if (row.length >= this.buttonsPerRow) {
159
- pushRow();
160
- }
161
-
162
- row.push(b);
163
- rowChars += textLength;
164
- }
165
-
166
- pushRow();
167
- return rows;
168
- }
169
-
170
- // ---------- final output ----------
171
- build() {
172
- return {
173
- reply_markup: {
174
- inline_keyboard: this._layoutButtons(),
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telegram-inline-keyboard-builder",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Universal inline keyboard builder for Telegram APIs",
5
5
  "main": "core/InlineKeyboardBuilder.js",
6
6
  "type": "module",