UNPKG

3.69 kBPlain TextView Raw
1import {
2 ForceReply,
3 InlineKeyboardButton,
4 InlineKeyboardMarkup,
5 KeyboardButton,
6 ReplyKeyboardMarkup,
7 ReplyKeyboardRemove,
8} from './core/types/typegram'
9import { is2D } from './core/helpers/check'
10
11type Hideable<B> = B & { hide?: boolean }
12type HideableKBtn = Hideable<KeyboardButton>
13type HideableIKBtn = Hideable<InlineKeyboardButton>
14
15export class Markup<
16 T extends
17 | InlineKeyboardMarkup
18 | ReplyKeyboardMarkup
19 | ReplyKeyboardRemove
20 | ForceReply
21> {
22 constructor(readonly reply_markup: T) {}
23
24 selective<T extends ForceReply | ReplyKeyboardMarkup>(
25 this: Markup<T>,
26 value = true
27 ) {
28 return new Markup<T>({ ...this.reply_markup, selective: value })
29 }
30
31 placeholder<T extends ForceReply | ReplyKeyboardMarkup>(
32 this: Markup<T>,
33 placeholder: string
34 ) {
35 return new Markup<T>({
36 ...this.reply_markup,
37 input_field_placeholder: placeholder,
38 })
39 }
40
41 resize(this: Markup<ReplyKeyboardMarkup>, value = true) {
42 return new Markup<ReplyKeyboardMarkup>({
43 ...this.reply_markup,
44 resize_keyboard: value,
45 })
46 }
47
48 oneTime(this: Markup<ReplyKeyboardMarkup>, value = true) {
49 return new Markup<ReplyKeyboardMarkup>({
50 ...this.reply_markup,
51 one_time_keyboard: value,
52 })
53 }
54}
55
56export * as button from './button'
57
58export function removeKeyboard(): Markup<ReplyKeyboardRemove> {
59 return new Markup<ReplyKeyboardRemove>({ remove_keyboard: true })
60}
61
62export function forceReply(): Markup<ForceReply> {
63 return new Markup<ForceReply>({ force_reply: true })
64}
65
66export function keyboard(buttons: HideableKBtn[][]): Markup<ReplyKeyboardMarkup>
67export function keyboard(
68 buttons: HideableKBtn[],
69 options?: Partial<KeyboardBuildingOptions<HideableKBtn>>
70): Markup<ReplyKeyboardMarkup>
71export function keyboard(
72 buttons: HideableKBtn[] | HideableKBtn[][],
73 options?: Partial<KeyboardBuildingOptions<HideableKBtn>>
74): Markup<ReplyKeyboardMarkup> {
75 const keyboard = buildKeyboard(buttons, {
76 columns: 1,
77 ...options,
78 })
79 return new Markup<ReplyKeyboardMarkup>({ keyboard })
80}
81
82export function inlineKeyboard(
83 buttons: HideableIKBtn[][]
84): Markup<InlineKeyboardMarkup>
85export function inlineKeyboard(
86 buttons: HideableIKBtn[],
87 options?: Partial<KeyboardBuildingOptions<HideableIKBtn>>
88): Markup<InlineKeyboardMarkup>
89export function inlineKeyboard(
90 buttons: HideableIKBtn[] | HideableIKBtn[][],
91 options?: Partial<KeyboardBuildingOptions<HideableIKBtn>>
92): Markup<InlineKeyboardMarkup> {
93 const inlineKeyboard = buildKeyboard(buttons, {
94 columns: buttons.length,
95 ...options,
96 })
97 return new Markup<InlineKeyboardMarkup>({ inline_keyboard: inlineKeyboard })
98}
99
100interface KeyboardBuildingOptions<B extends HideableKBtn | HideableIKBtn> {
101 wrap?: (btn: B, index: number, currentRow: B[]) => boolean
102 columns: number
103}
104
105function buildKeyboard<B extends HideableKBtn | HideableIKBtn>(
106 buttons: B[] | B[][],
107 options: KeyboardBuildingOptions<B>
108): B[][] {
109 const result: B[][] = []
110 if (!Array.isArray(buttons)) {
111 return result
112 }
113 if (is2D(buttons)) {
114 return buttons.map((row) => row.filter((button) => !button.hide))
115 }
116 const wrapFn =
117 options.wrap !== undefined
118 ? options.wrap
119 : (_btn: B, _index: number, currentRow: B[]) =>
120 currentRow.length >= options.columns
121 let currentRow: B[] = []
122 let index = 0
123 for (const btn of buttons.filter((button) => !button.hide)) {
124 if (wrapFn(btn, index, currentRow) && currentRow.length > 0) {
125 result.push(currentRow)
126 currentRow = []
127 }
128 currentRow.push(btn)
129 index++
130 }
131 if (currentRow.length > 0) {
132 result.push(currentRow)
133 }
134 return result
135}