-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbuttons.go
166 lines (147 loc) · 4.2 KB
/
buttons.go
1
2
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
package tbcomctl
import (
"errors"
"fmt"
"strconv"
tb "gopkg.in/telebot.v3"
)
// Button is the control button.
type Button struct {
Name string `json:"n"`
Value int `json:"v"`
}
// label outputs the label for the ratingInfo. If counter is set, will output a
// decimal representation of value after a separator sep.
func (b *Button) label(showCounter bool, sep string) string {
if showCounter {
return b.Name + sep + strconv.FormatInt(int64(b.Value), 10)
}
return b.Name
}
func (b *Button) String() string {
return fmt.Sprintf("<Button name: %s, value: %d>", b.Name, b.Value)
}
type buttons struct {
maxButtons int
}
func (b *buttons) SetMaxButtons(n int) {
if n <= 0 || maxButtons < n {
n = defNumButtons
}
b.maxButtons = n
}
type PostButtons struct {
*buttons
cbFn tb.HandlerFunc
}
type PBOption func(*PostButtons)
func PBOptMaxButtons(n int) PBOption {
return func(pb *PostButtons) {
pb.buttons.SetMaxButtons(n)
}
}
// NewPostButtons creates an instance of PostButtons. The callbackFunction is
// the function that will be assigned and called for each button press, so it
// should handle all possible values.
func NewPostButtons(callbackFn func(c tb.Context) error, opts ...PBOption) *PostButtons {
pb := &PostButtons{
cbFn: callbackFn,
buttons: &buttons{maxButtons: defNumButtons},
}
for _, o := range opts {
o(pb)
}
return pb
}
// Markup returns the markup with buttons labeled with labels.
func (pb *PostButtons) Markup(c tb.Context, labels []string, pattern ...uint) (*tb.ReplyMarkup, error) {
if len(pattern) == 0 {
return ButtonMarkup(c, labels, pb.maxButtons, pb.cbFn), nil
}
markup, err := ButtonPatternMarkup(c, labels, pattern, pb.cbFn)
if err != nil {
panic(err)
}
return markup, nil
}
// ButtonMarkup returns the button markup for the message. It creates handlers
// for all the buttons assigning the cbFn callback function to each of them.
// Values must be unique. maxRowButtons is maximum number of buttons in a row.
func ButtonMarkup(c tb.Context, values []string, maxRowButtons int, cbFn func(c tb.Context) error) *tb.ReplyMarkup {
if maxRowButtons <= 0 || defNumButtons < maxRowButtons {
maxRowButtons = defNumButtons
}
markup, btns := createButtons(c, values, cbFn)
markup.Inline(OrganizeButtons(btns, maxRowButtons)...)
return markup
}
func ButtonPatternMarkup(c tb.Context, values []string, pattern []uint, cbFn tb.HandlerFunc) (*tb.ReplyMarkup, error) {
markup, btns := createButtons(c, values, cbFn)
rows, err := organizeButtonsPattern(btns, pattern)
if err != nil {
return nil, err
}
markup.Inline(rows...)
return markup, nil
}
func bot(b tb.API) *tb.Bot {
return b.(*tb.Bot)
}
func createButtons(c tb.Context, values []string, cbFn func(c tb.Context) error) (*tb.ReplyMarkup, []tb.Btn) {
markup := new(tb.ReplyMarkup)
var btns []tb.Btn
for _, label := range values {
btn := markup.Data(label, hash(label), label)
btns = append(btns, btn)
bot(c.Bot()).Handle(&btn, cbFn)
}
return markup, btns
}
// OrganizeButtons organizes buttons in rows, at most btnInRow per row.
func OrganizeButtons(btns []tb.Btn, btnInRow int) []tb.Row {
var rows []tb.Row
var buttons []tb.Btn
for i, btn := range btns {
if i%btnInRow == 0 {
if len(buttons) > 0 {
rows = append(rows, buttons)
}
buttons = make([]tb.Btn, 0, btnInRow)
}
buttons = append(buttons, btn)
}
if 0 < len(buttons) && len(buttons) <= btnInRow {
rows = append(rows, buttons)
}
return rows
}
func organizeButtonsPattern(btns []tb.Btn, pattern []uint) ([]tb.Row, error) {
if len(btns) == 0 {
return nil, errors.New("no buttons to organize")
}
// check total number, must not exceed sum of buttons in pattern
sum := 0
for i, perRow := range pattern {
if perRow < 1 {
return nil, fmt.Errorf("patterns can't have < 1 buttons in a row (row index: %d)", i)
}
sum += int(perRow)
}
if sum < len(btns) {
return nil, fmt.Errorf("can't fit %d buttons in this pattern: %v", len(btns), pattern)
}
var rows []tb.Row
var offset uint = 0
for _, perRow := range pattern {
var row tb.Row
if offset >= uint(len(btns)) {
break
}
for i := offset; i-offset < perRow; i++ {
row = append(row, btns[i])
}
rows = append(rows, row)
offset += uint(len(row))
}
return rows, nil
}