Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/client/interfaces/Markup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export declare namespace KeyboardButton {
export interface CommonButton {
/** Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed */
text: string;
/** Custom emoji identifier shown before the button text. */
icon_custom_emoji_id?: string;
/** Optional visual button style. */
style?: "default" | "primary" | "success" | "danger";
}
export interface RequestUsersButton extends CommonButton {
/** If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a “users_shared” service message. Available in private chats only. */
Expand Down
129 changes: 106 additions & 23 deletions src/util/markup/KeyboardBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,46 @@ import type {
ReplyKeyboardMarkup,
} from "../../client/interfaces/Markup";

const styleMap = {
red: "danger",
green: "success",
blue: "primary",
} as const;
/**
* Additional keyboard button options.
*/
type MarkupOptions = {
/**
* The custom emoji id shown before the button text.
*/
icon?: string;

/**
* The style of the button.
*/
style?: KeyboardButton.CommonButton["style"] | "red" | "green" | "blue";
};

function replacedMarkupOptions(options?: MarkupOptions) {
if (!options) return {};

let style: KeyboardButton.CommonButton["style"] | undefined;
switch (options.style) {
case "red":
case "green":
case "blue":
style = styleMap[options.style];
break;
default:
style = options.style;
}

return {
...(options.icon ? { icon_custom_emoji_id: options.icon } : {}),
...(style ? { style } : {}),
};
}

/**
* Represents a custom keyboard for Telegram bots.
*/
Expand Down Expand Up @@ -66,17 +106,23 @@ class KeyboardBuilder {
* @param text - The button text.
* @returns The current instance for chaining.
*/
text(text: string): this {
return this.add(KeyboardBuilder.text(text));
text(text: string, options?: MarkupOptions): this {
return this.add(KeyboardBuilder.text(text, options));
}

/**
* Creates a text button.
* @param text - The button text.
* @returns The created text button.
*/
static text(text: string): KeyboardButton.CommonButton {
return { text };
static text(
text: string,
options?: MarkupOptions,
): KeyboardButton.CommonButton {
return {
text,
...replacedMarkupOptions(options),
};
}

/**
Expand All @@ -90,8 +136,11 @@ class KeyboardBuilder {
text: string,
requestId: number,
options: Omit<KeyboardButtonRequestUsers, "request_id"> = {},
buttonOptions?: MarkupOptions,
): this {
return this.add(KeyboardBuilder.requestUsers(text, requestId, options));
return this.add(
KeyboardBuilder.requestUsers(text, requestId, options, buttonOptions),
);
}

/**
Expand All @@ -105,8 +154,13 @@ class KeyboardBuilder {
text: string,
requestId: number,
options: Omit<KeyboardButtonRequestUsers, "request_id"> = {},
buttonOptions?: MarkupOptions,
): KeyboardButton.RequestUsersButton {
return { text, request_users: { request_id: requestId, ...options } };
return {
text,
request_users: { request_id: requestId, ...options },
...replacedMarkupOptions(buttonOptions),
};
}

/**
Expand All @@ -122,8 +176,11 @@ class KeyboardBuilder {
options: Omit<KeyboardButtonRequestChat, "request_id"> = {
chat_is_channel: false,
},
buttonOptions?: MarkupOptions,
): this {
return this.add(KeyboardBuilder.requestChat(text, requestId, options));
return this.add(
KeyboardBuilder.requestChat(text, requestId, options, buttonOptions),
);
}

/**
Expand All @@ -139,44 +196,55 @@ class KeyboardBuilder {
options: Omit<KeyboardButtonRequestChat, "request_id"> = {
chat_is_channel: false,
},
buttonOptions?: MarkupOptions,
): KeyboardButton.RequestChatButton {
return { text, request_chat: { request_id: requestId, ...options } };
return {
text,
request_chat: { request_id: requestId, ...options },
...replacedMarkupOptions(buttonOptions),
};
}

/**
* Adds a request contact button to the keyboard.
* @param text - The button text.
* @returns The current instance for chaining.
*/
requestContact(text: string): this {
return this.add(KeyboardBuilder.requestContact(text));
requestContact(text: string, options?: MarkupOptions): this {
return this.add(KeyboardBuilder.requestContact(text, options));
}

/**
* Creates a request contact button.
* @param text - The button text.
* @returns The created request contact button.
*/
static requestContact(text: string): KeyboardButton.RequestContactButton {
return { text, request_contact: true };
static requestContact(
text: string,
options?: MarkupOptions,
): KeyboardButton.RequestContactButton {
return { text, request_contact: true, ...replacedMarkupOptions(options) };
}

/**
* Adds a request location button to the keyboard.
* @param text - The button text.
* @returns The current instance for chaining.
*/
requestLocation(text: string): this {
return this.add(KeyboardBuilder.requestLocation(text));
requestLocation(text: string, options?: MarkupOptions): this {
return this.add(KeyboardBuilder.requestLocation(text, options));
}

/**
* Creates a request location button.
* @param text - The button text.
* @returns The created request location button.
*/
static requestLocation(text: string): KeyboardButton.RequestLocationButton {
return { text, request_location: true };
static requestLocation(
text: string,
options?: MarkupOptions,
): KeyboardButton.RequestLocationButton {
return { text, request_location: true, ...replacedMarkupOptions(options) };
}

/**
Expand All @@ -185,8 +253,12 @@ class KeyboardBuilder {
* @param type - The type of the poll button.
* @returns The current instance for chaining.
*/
requestPoll(text: string, type?: KeyboardButtonPollType["type"]): this {
return this.add(KeyboardBuilder.requestPoll(text, type));
requestPoll(
text: string,
type?: KeyboardButtonPollType["type"],
options?: MarkupOptions,
): this {
return this.add(KeyboardBuilder.requestPoll(text, type, options));
}

/**
Expand All @@ -198,8 +270,9 @@ class KeyboardBuilder {
static requestPoll(
text: string,
type: KeyboardButtonPollType["type"] = "regular",
options?: MarkupOptions,
): KeyboardButton.RequestPollButton {
return { text, request_poll: { type } };
return { text, request_poll: { type }, ...replacedMarkupOptions(options) };
}

/**
Expand All @@ -208,8 +281,8 @@ class KeyboardBuilder {
* @param url - The URL of the web app.
* @returns The current instance for chaining.
*/
webApp(text: string, url: string): this {
return this.add(KeyboardBuilder.webApp(text, url));
webApp(text: string, url: string, options?: MarkupOptions): this {
return this.add(KeyboardBuilder.webApp(text, url, options));
}

/**
Expand All @@ -218,8 +291,12 @@ class KeyboardBuilder {
* @param url - The URL of the web app.
* @returns The created web app button.
*/
static webApp(text: string, url: string): KeyboardButton.WebAppButton {
return { text, web_app: { url } };
static webApp(
text: string,
url: string,
options?: MarkupOptions,
): KeyboardButton.WebAppButton {
return { text, web_app: { url }, ...replacedMarkupOptions(options) };
}

/**
Expand Down Expand Up @@ -377,6 +454,12 @@ class KeyboardBuilder {
if (typeof buttonA === "string" && typeof buttonB === "string") {
if (buttonA !== buttonB) return false;
} else if (typeof buttonA === "object" && typeof buttonB === "object") {
if (
buttonA.icon_custom_emoji_id !== buttonB.icon_custom_emoji_id ||
buttonA.style !== buttonB.style
)
return false;

if ("text" in buttonA && "text" in buttonB) {
if (buttonA.text !== buttonB.text) return false;
} else {
Expand Down
4 changes: 4 additions & 0 deletions typings/telegram/Markup.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export declare namespace KeyboardButton {
export interface CommonButton {
/** Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed */
text: string;
/** Custom emoji identifier shown before the button text. */
icon_custom_emoji_id?: string;
/** Optional visual button style. */
style?: "default" | "primary" | "success" | "danger";
}
export interface RequestUsersButton extends CommonButton {
/** If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a “users_shared” service message. Available in private chats only. */
Expand Down
Loading