-
Notifications
You must be signed in to change notification settings - Fork 3
/
rating.go
100 lines (82 loc) · 2.61 KB
/
rating.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
package tbcomctl
import (
"errors"
"net/http"
"strconv"
"strings"
tb "gopkg.in/telebot.v3"
)
// Rating is a struct for attaching post rating.
type Rating struct {
commonCtl
hasRating bool // show post rating between up/down vote buttons
hasCounter bool // show counter of total upvotes-downvotes.
rateFn RatingFunc //
}
// RatingFunc is the function called by callback, given the message, user
// and the button index it should update the records and return the new buttons
// with updated values for the posting, it must maintain count of votes inhouse.
type RatingFunc func(tb.Editable, *tb.User, int) ([2]Button, error)
type RBOption func(*Rating)
// RBOptShowVoteCounter enables post rating between up/down vote buttons
func RBOptShowVoteCounter(b bool) RBOption {
return func(rb *Rating) {
rb.hasCounter = b
}
}
// RBOptShowPostRating enables counter of total upvotes/downvotes.
func RBOptShowPostRating(b bool) RBOption {
return func(rb *Rating) {
rb.hasRating = b
}
}
type RatingType int
func NewRating(fn RatingFunc, opts ...RBOption) *Rating {
rb := &Rating{
commonCtl: newCommonCtl("rating"),
rateFn: fn,
}
for _, opt := range opts {
opt(rb)
}
return rb
}
func (rb *Rating) Markup(b *tb.Bot, btns [2]Button) *tb.ReplyMarkup {
const rbPrefix = "rating"
return rb.multibuttonMarkup(b, btns[:], rb.hasCounter, rbPrefix, rb.callback)
}
var ErrAlreadyVoted = errors.New("already voted")
func (rb *Rating) callback(c tb.Context) error {
respErr := tb.CallbackResponse{Text: MsgUnexpected}
data := c.Data()
btnIdx, err := strconv.Atoi(data)
if err != nil {
lg.Printf("failed to get the button index from data: %s", data)
c.Respond(&respErr)
return err
}
// get existing value for the post
buttons, valErr := rb.rateFn(c.Message(), c.Sender(), btnIdx)
if valErr != nil && valErr != ErrAlreadyVoted {
lg.Printf("failed to get the data from the rating callback: %s", valErr)
dlg.Printf("callback: %s", Sdump(c.Callback()))
c.Respond(&respErr)
return valErr
}
var msg string
// update the post with new buttons
if valErr != ErrAlreadyVoted {
if err := c.Edit(rb.Markup(bot(c.Bot()), buttons)); err != nil {
if e, ok := err.(*tb.Error); ok && e.Code == http.StatusBadRequest && strings.Contains(e.Description, "exactly the same") {
// same button pressed - not an error.
lg.Printf("%s: same button pressed", Userinfo(c.Sender()))
} else {
lg.Printf("failed to edit the message: %v: %s", c.Message(), err)
c.Respond(&respErr)
return err
}
}
msg = PrinterContext(c, rb.fallbackLang).Sprint(MsgVoteCounted)
}
return c.Respond(&tb.CallbackResponse{Text: msg})
}