mirror of https://github.com/tucnak/telebot
telebot: restructure and refactor
parent
bd66ef87a9
commit
d90e8974cc
@ -0,0 +1,39 @@
|
|||||||
|
package telebot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmbedRights(t *testing.T) {
|
||||||
|
rights := NoRestrictions()
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"chat_id": "1",
|
||||||
|
"user_id": "2",
|
||||||
|
}
|
||||||
|
embedRights(params, rights)
|
||||||
|
|
||||||
|
expected := map[string]interface{}{
|
||||||
|
"is_anonymous": false,
|
||||||
|
"chat_id": "1",
|
||||||
|
"user_id": "2",
|
||||||
|
"can_be_edited": true,
|
||||||
|
"can_send_messages": true,
|
||||||
|
"can_send_media_messages": true,
|
||||||
|
"can_send_polls": true,
|
||||||
|
"can_send_other_messages": true,
|
||||||
|
"can_add_web_page_previews": true,
|
||||||
|
"can_change_info": false,
|
||||||
|
"can_post_messages": false,
|
||||||
|
"can_edit_messages": false,
|
||||||
|
"can_delete_messages": false,
|
||||||
|
"can_invite_users": false,
|
||||||
|
"can_restrict_members": false,
|
||||||
|
"can_pin_messages": false,
|
||||||
|
"can_promote_members": false,
|
||||||
|
"can_manage_video_chats": false,
|
||||||
|
"can_manage_chat": false,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expected, params)
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package telebot
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// Command represents a bot command.
|
||||||
|
type Command struct {
|
||||||
|
// Text is a text of the command, 1-32 characters.
|
||||||
|
// Can contain only lowercase English letters, digits and underscores.
|
||||||
|
Text string `json:"command"`
|
||||||
|
|
||||||
|
// Description of the command, 3-256 characters.
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandParams controls parameters for commands-related methods (setMyCommands, deleteMyCommands and getMyCommands).
|
||||||
|
type CommandParams struct {
|
||||||
|
Commands []Command `json:"commands,omitempty"`
|
||||||
|
Scope *CommandScope `json:"scope,omitempty"`
|
||||||
|
LanguageCode string `json:"language_code,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandScopeType = string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CommandScopeDefault CommandScopeType = "default"
|
||||||
|
CommandScopeAllPrivateChats CommandScopeType = "all_private_chats"
|
||||||
|
CommandScopeAllGroupChats CommandScopeType = "all_group_chats"
|
||||||
|
CommandScopeAllChatAdmin CommandScopeType = "all_chat_administrators"
|
||||||
|
CommandScopeChat CommandScopeType = "chat"
|
||||||
|
CommandScopeChatAdmin CommandScopeType = "chat_administrators"
|
||||||
|
CommandScopeChatMember CommandScopeType = "chat_member"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommandScope object represents a scope to which bot commands are applied.
|
||||||
|
type CommandScope struct {
|
||||||
|
Type CommandScopeType `json:"type"`
|
||||||
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
|
UserID int64 `json:"user_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands returns the current list of the bot's commands for the given scope and user language.
|
||||||
|
func (b *Bot) Commands(opts ...interface{}) ([]Command, error) {
|
||||||
|
params := extractCommandsParams(opts...)
|
||||||
|
data, err := b.Raw("getMyCommands", params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Result []Command
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &resp); err != nil {
|
||||||
|
return nil, wrapError(err)
|
||||||
|
}
|
||||||
|
return resp.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCommands changes the list of the bot's commands.
|
||||||
|
func (b *Bot) SetCommands(opts ...interface{}) error {
|
||||||
|
params := extractCommandsParams(opts...)
|
||||||
|
_, err := b.Raw("setMyCommands", params)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCommands deletes the list of the bot's commands for the given scope and user language.
|
||||||
|
func (b *Bot) DeleteCommands(opts ...interface{}) error {
|
||||||
|
params := extractCommandsParams(opts...)
|
||||||
|
_, err := b.Raw("deleteMyCommands", params)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractCommandsParams extracts parameters for commands-related methods from the given options.
|
||||||
|
func extractCommandsParams(opts ...interface{}) (params CommandParams) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
switch value := opt.(type) {
|
||||||
|
case []Command:
|
||||||
|
params.Commands = value
|
||||||
|
case string:
|
||||||
|
params.LanguageCode = value
|
||||||
|
case CommandScope:
|
||||||
|
params.Scope = &value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,346 @@
|
|||||||
|
package telebot
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// Update object represents an incoming update.
|
||||||
|
type Update struct {
|
||||||
|
ID int `json:"update_id"`
|
||||||
|
|
||||||
|
Message *Message `json:"message,omitempty"`
|
||||||
|
EditedMessage *Message `json:"edited_message,omitempty"`
|
||||||
|
ChannelPost *Message `json:"channel_post,omitempty"`
|
||||||
|
EditedChannelPost *Message `json:"edited_channel_post,omitempty"`
|
||||||
|
Callback *Callback `json:"callback_query,omitempty"`
|
||||||
|
Query *Query `json:"inline_query,omitempty"`
|
||||||
|
InlineResult *InlineResult `json:"chosen_inline_result,omitempty"`
|
||||||
|
ShippingQuery *ShippingQuery `json:"shipping_query,omitempty"`
|
||||||
|
PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"`
|
||||||
|
Poll *Poll `json:"poll,omitempty"`
|
||||||
|
PollAnswer *PollAnswer `json:"poll_answer,omitempty"`
|
||||||
|
MyChatMember *ChatMemberUpdate `json:"my_chat_member,omitempty"`
|
||||||
|
ChatMember *ChatMemberUpdate `json:"chat_member,omitempty"`
|
||||||
|
ChatJoinRequest *ChatJoinRequest `json:"chat_join_request,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessUpdate processes a single incoming update.
|
||||||
|
// A started bot calls this function automatically.
|
||||||
|
func (b *Bot) ProcessUpdate(u Update) {
|
||||||
|
c := b.NewContext(u)
|
||||||
|
|
||||||
|
if u.Message != nil {
|
||||||
|
m := u.Message
|
||||||
|
|
||||||
|
if m.PinnedMessage != nil {
|
||||||
|
b.handle(OnPinned, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
if m.Text != "" {
|
||||||
|
// Filtering malicious messages
|
||||||
|
if m.Text[0] == '\a' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
match := cmdRx.FindAllStringSubmatch(m.Text, -1)
|
||||||
|
if match != nil {
|
||||||
|
// Syntax: "</command>@<bot> <payload>"
|
||||||
|
command, botName := match[0][1], match[0][3]
|
||||||
|
|
||||||
|
if botName != "" && !strings.EqualFold(b.Me.Username, botName) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Payload = match[0][5]
|
||||||
|
if b.handle(command, c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1:1 satisfaction
|
||||||
|
if b.handle(m.Text, c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.handle(OnText, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.handleMedia(c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Contact != nil {
|
||||||
|
b.handle(OnContact, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Location != nil {
|
||||||
|
b.handle(OnLocation, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Venue != nil {
|
||||||
|
b.handle(OnVenue, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Game != nil {
|
||||||
|
b.handle(OnGame, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Dice != nil {
|
||||||
|
b.handle(OnDice, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Invoice != nil {
|
||||||
|
b.handle(OnInvoice, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Payment != nil {
|
||||||
|
b.handle(OnPayment, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wasAdded := (m.UserJoined != nil && m.UserJoined.ID == b.Me.ID) ||
|
||||||
|
(m.UsersJoined != nil && isUserInList(b.Me, m.UsersJoined))
|
||||||
|
if m.GroupCreated || m.SuperGroupCreated || wasAdded {
|
||||||
|
b.handle(OnAddedToGroup, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.UserJoined != nil {
|
||||||
|
b.handle(OnUserJoined, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.UsersJoined != nil {
|
||||||
|
for _, user := range m.UsersJoined {
|
||||||
|
m.UserJoined = &user
|
||||||
|
b.handle(OnUserJoined, c)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.UserLeft != nil {
|
||||||
|
b.handle(OnUserLeft, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.NewGroupTitle != "" {
|
||||||
|
b.handle(OnNewGroupTitle, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.NewGroupPhoto != nil {
|
||||||
|
b.handle(OnNewGroupPhoto, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.GroupPhotoDeleted {
|
||||||
|
b.handle(OnGroupPhotoDeleted, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.GroupCreated {
|
||||||
|
b.handle(OnGroupCreated, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.SuperGroupCreated {
|
||||||
|
b.handle(OnSuperGroupCreated, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.ChannelCreated {
|
||||||
|
b.handle(OnChannelCreated, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.MigrateTo != 0 {
|
||||||
|
m.MigrateFrom = m.Chat.ID
|
||||||
|
b.handle(OnMigration, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.VideoChatStarted != nil {
|
||||||
|
b.handle(OnVideoChatStarted, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.VideoChatEnded != nil {
|
||||||
|
b.handle(OnVideoChatEnded, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.VideoChatParticipants != nil {
|
||||||
|
b.handle(OnVideoChatParticipants, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.VideoChatScheduled != nil {
|
||||||
|
b.handle(OnVideoChatScheduled, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.WebAppData != nil {
|
||||||
|
b.handle(OnWebApp, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.ProximityAlert != nil {
|
||||||
|
b.handle(OnProximityAlert, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.AutoDeleteTimer != nil {
|
||||||
|
b.handle(OnAutoDeleteTimer, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.EditedMessage != nil {
|
||||||
|
b.handle(OnEdited, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.ChannelPost != nil {
|
||||||
|
m := u.ChannelPost
|
||||||
|
|
||||||
|
if m.PinnedMessage != nil {
|
||||||
|
b.handle(OnPinned, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.handle(OnChannelPost, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.EditedChannelPost != nil {
|
||||||
|
b.handle(OnEditedChannelPost, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Callback != nil {
|
||||||
|
if data := u.Callback.Data; data != "" && data[0] == '\f' {
|
||||||
|
match := cbackRx.FindAllStringSubmatch(data, -1)
|
||||||
|
if match != nil {
|
||||||
|
unique, payload := match[0][1], match[0][3]
|
||||||
|
if handler, ok := b.handlers["\f"+unique]; ok {
|
||||||
|
u.Callback.Unique = unique
|
||||||
|
u.Callback.Data = payload
|
||||||
|
b.runHandler(handler, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.handle(OnCallback, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Query != nil {
|
||||||
|
b.handle(OnQuery, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.InlineResult != nil {
|
||||||
|
b.handle(OnInlineResult, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.ShippingQuery != nil {
|
||||||
|
b.handle(OnShipping, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.PreCheckoutQuery != nil {
|
||||||
|
b.handle(OnCheckout, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Poll != nil {
|
||||||
|
b.handle(OnPoll, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.PollAnswer != nil {
|
||||||
|
b.handle(OnPollAnswer, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.MyChatMember != nil {
|
||||||
|
b.handle(OnMyChatMember, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.ChatMember != nil {
|
||||||
|
b.handle(OnChatMember, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.ChatJoinRequest != nil {
|
||||||
|
b.handle(OnChatJoinRequest, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) handle(end string, c Context) bool {
|
||||||
|
if handler, ok := b.handlers[end]; ok {
|
||||||
|
b.runHandler(handler, c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) handleMedia(c Context) bool {
|
||||||
|
var (
|
||||||
|
m = c.Message()
|
||||||
|
fired = true
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case m.Photo != nil:
|
||||||
|
fired = b.handle(OnPhoto, c)
|
||||||
|
case m.Voice != nil:
|
||||||
|
fired = b.handle(OnVoice, c)
|
||||||
|
case m.Audio != nil:
|
||||||
|
fired = b.handle(OnAudio, c)
|
||||||
|
case m.Animation != nil:
|
||||||
|
fired = b.handle(OnAnimation, c)
|
||||||
|
case m.Document != nil:
|
||||||
|
fired = b.handle(OnDocument, c)
|
||||||
|
case m.Sticker != nil:
|
||||||
|
fired = b.handle(OnSticker, c)
|
||||||
|
case m.Video != nil:
|
||||||
|
fired = b.handle(OnVideo, c)
|
||||||
|
case m.VideoNote != nil:
|
||||||
|
fired = b.handle(OnVideoNote, c)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fired {
|
||||||
|
return b.handle(OnMedia, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) runHandler(h HandlerFunc, c Context) {
|
||||||
|
f := func() {
|
||||||
|
if err := h(c); err != nil {
|
||||||
|
b.OnError(err, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b.synchronous {
|
||||||
|
f()
|
||||||
|
} else {
|
||||||
|
go f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUserInList(user *User, list []User) bool {
|
||||||
|
for _, user2 := range list {
|
||||||
|
if user.ID == user2.ID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -1,306 +0,0 @@
|
|||||||
package telebot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var defaultOnError = func(err error, c Context) {
|
|
||||||
if c != nil {
|
|
||||||
log.Println(c.Update().ID, err)
|
|
||||||
} else {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) debug(err error) {
|
|
||||||
if b.verbose {
|
|
||||||
b.OnError(err, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func verbose(method string, payload interface{}, data []byte) {
|
|
||||||
body, _ := json.Marshal(payload)
|
|
||||||
body = bytes.ReplaceAll(body, []byte(`\"`), []byte(`"`))
|
|
||||||
body = bytes.ReplaceAll(body, []byte(`"{`), []byte(`{`))
|
|
||||||
body = bytes.ReplaceAll(body, []byte(`}"`), []byte(`}`))
|
|
||||||
|
|
||||||
indent := func(b []byte) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
json.Indent(&buf, b, "", " ")
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf(
|
|
||||||
"[verbose] telebot: sent request\nMethod: %v\nParams: %v\nResponse: %v",
|
|
||||||
method, indent(body), indent(data),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) runHandler(h HandlerFunc, c Context) {
|
|
||||||
f := func() {
|
|
||||||
if err := h(c); err != nil {
|
|
||||||
b.OnError(err, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b.synchronous {
|
|
||||||
f()
|
|
||||||
} else {
|
|
||||||
go f()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyMiddleware(h HandlerFunc, m ...MiddlewareFunc) HandlerFunc {
|
|
||||||
for i := len(m) - 1; i >= 0; i-- {
|
|
||||||
h = m[i](h)
|
|
||||||
}
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrapError returns new wrapped telebot-related error.
|
|
||||||
func wrapError(err error) error {
|
|
||||||
return fmt.Errorf("telebot: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractOk checks given result for error. If result is ok returns nil.
|
|
||||||
// In other cases it extracts API error. If error is not presented
|
|
||||||
// in errors.go, it will be prefixed with `unknown` keyword.
|
|
||||||
func extractOk(data []byte) error {
|
|
||||||
var e struct {
|
|
||||||
Ok bool `json:"ok"`
|
|
||||||
Code int `json:"error_code"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Parameters map[string]interface{} `json:"parameters"`
|
|
||||||
}
|
|
||||||
if json.NewDecoder(bytes.NewReader(data)).Decode(&e) != nil {
|
|
||||||
return nil // FIXME
|
|
||||||
}
|
|
||||||
if e.Ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := Err(e.Description)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
case ErrGroupMigrated:
|
|
||||||
migratedTo, ok := e.Parameters["migrate_to_chat_id"]
|
|
||||||
if !ok {
|
|
||||||
return NewError(e.Code, e.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
return GroupError{
|
|
||||||
err: err.(*Error),
|
|
||||||
MigratedTo: int64(migratedTo.(float64)),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch e.Code {
|
|
||||||
case http.StatusTooManyRequests:
|
|
||||||
retryAfter, ok := e.Parameters["retry_after"]
|
|
||||||
if !ok {
|
|
||||||
return NewError(e.Code, e.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = FloodError{
|
|
||||||
err: NewError(e.Code, e.Description),
|
|
||||||
RetryAfter: int(retryAfter.(float64)),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("telegram: %s (%d)", e.Description, e.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractMessage extracts common Message result from given data.
|
|
||||||
// Should be called after extractOk or b.Raw() to handle possible errors.
|
|
||||||
func extractMessage(data []byte) (*Message, error) {
|
|
||||||
var resp struct {
|
|
||||||
Result *Message
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(data, &resp); err != nil {
|
|
||||||
var resp struct {
|
|
||||||
Result bool
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(data, &resp); err != nil {
|
|
||||||
return nil, wrapError(err)
|
|
||||||
}
|
|
||||||
if resp.Result {
|
|
||||||
return nil, ErrTrueResult
|
|
||||||
}
|
|
||||||
return nil, wrapError(err)
|
|
||||||
}
|
|
||||||
return resp.Result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractOptions(how []interface{}) *SendOptions {
|
|
||||||
opts := &SendOptions{}
|
|
||||||
|
|
||||||
for _, prop := range how {
|
|
||||||
switch opt := prop.(type) {
|
|
||||||
case *SendOptions:
|
|
||||||
opts = opt.copy()
|
|
||||||
case *ReplyMarkup:
|
|
||||||
if opt != nil {
|
|
||||||
opts.ReplyMarkup = opt.copy()
|
|
||||||
}
|
|
||||||
case Option:
|
|
||||||
switch opt {
|
|
||||||
case NoPreview:
|
|
||||||
opts.DisableWebPagePreview = true
|
|
||||||
case Silent:
|
|
||||||
opts.DisableNotification = true
|
|
||||||
case AllowWithoutReply:
|
|
||||||
opts.AllowWithoutReply = true
|
|
||||||
case ForceReply:
|
|
||||||
if opts.ReplyMarkup == nil {
|
|
||||||
opts.ReplyMarkup = &ReplyMarkup{}
|
|
||||||
}
|
|
||||||
opts.ReplyMarkup.ForceReply = true
|
|
||||||
case OneTimeKeyboard:
|
|
||||||
if opts.ReplyMarkup == nil {
|
|
||||||
opts.ReplyMarkup = &ReplyMarkup{}
|
|
||||||
}
|
|
||||||
opts.ReplyMarkup.OneTimeKeyboard = true
|
|
||||||
case RemoveKeyboard:
|
|
||||||
if opts.ReplyMarkup == nil {
|
|
||||||
opts.ReplyMarkup = &ReplyMarkup{}
|
|
||||||
}
|
|
||||||
opts.ReplyMarkup.RemoveKeyboard = true
|
|
||||||
case Protected:
|
|
||||||
opts.Protected = true
|
|
||||||
default:
|
|
||||||
panic("telebot: unsupported flag-option")
|
|
||||||
}
|
|
||||||
case ParseMode:
|
|
||||||
opts.ParseMode = opt
|
|
||||||
case Entities:
|
|
||||||
opts.Entities = opt
|
|
||||||
default:
|
|
||||||
panic("telebot: unsupported send-option")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractCommandsParams extracts parameters for commands-related methods from the given options.
|
|
||||||
func extractCommandsParams(opts ...interface{}) (params CommandParams) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
switch value := opt.(type) {
|
|
||||||
case []Command:
|
|
||||||
params.Commands = value
|
|
||||||
case string:
|
|
||||||
params.LanguageCode = value
|
|
||||||
case CommandScope:
|
|
||||||
params.Scope = &value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) embedSendOptions(params map[string]string, opt *SendOptions) {
|
|
||||||
if b.parseMode != ModeDefault {
|
|
||||||
params["parse_mode"] = b.parseMode
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.ReplyTo != nil && opt.ReplyTo.ID != 0 {
|
|
||||||
params["reply_to_message_id"] = strconv.Itoa(opt.ReplyTo.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.DisableWebPagePreview {
|
|
||||||
params["disable_web_page_preview"] = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.DisableNotification {
|
|
||||||
params["disable_notification"] = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.ParseMode != ModeDefault {
|
|
||||||
params["parse_mode"] = opt.ParseMode
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opt.Entities) > 0 {
|
|
||||||
delete(params, "parse_mode")
|
|
||||||
entities, _ := json.Marshal(opt.Entities)
|
|
||||||
|
|
||||||
if params["caption"] != "" {
|
|
||||||
params["caption_entities"] = string(entities)
|
|
||||||
} else {
|
|
||||||
params["entities"] = string(entities)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.AllowWithoutReply {
|
|
||||||
params["allow_sending_without_reply"] = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.ReplyMarkup != nil {
|
|
||||||
processButtons(opt.ReplyMarkup.InlineKeyboard)
|
|
||||||
replyMarkup, _ := json.Marshal(opt.ReplyMarkup)
|
|
||||||
params["reply_markup"] = string(replyMarkup)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.Protected {
|
|
||||||
params["protect_content"] = "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processButtons(keys [][]InlineButton) {
|
|
||||||
if keys == nil || len(keys) < 1 || len(keys[0]) < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range keys {
|
|
||||||
for j := range keys[i] {
|
|
||||||
key := &keys[i][j]
|
|
||||||
if key.Unique != "" {
|
|
||||||
// Format: "\f<callback_name>|<data>"
|
|
||||||
data := key.Data
|
|
||||||
if data == "" {
|
|
||||||
key.Data = "\f" + key.Unique
|
|
||||||
} else {
|
|
||||||
key.Data = "\f" + key.Unique + "|" + data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func embedRights(p map[string]interface{}, rights Rights) {
|
|
||||||
data, _ := json.Marshal(rights)
|
|
||||||
_ = json.Unmarshal(data, &p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func thumbnailToFilemap(thumb *Photo) map[string]File {
|
|
||||||
if thumb != nil {
|
|
||||||
return map[string]File{"thumb": thumb.File}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUserInList(user *User, list []User) bool {
|
|
||||||
for _, user2 := range list {
|
|
||||||
if user.ID == user2.ID {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func intsToStrs(ns []int) (s []string) {
|
|
||||||
for _, n := range ns {
|
|
||||||
s = append(s, strconv.Itoa(n))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
package telebot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestExtractOk(t *testing.T) {
|
|
||||||
data := []byte(`{"ok": true, "result": {}}`)
|
|
||||||
require.NoError(t, extractOk(data))
|
|
||||||
|
|
||||||
data = []byte(`{
|
|
||||||
"ok": false,
|
|
||||||
"error_code": 400,
|
|
||||||
"description": "Bad Request: reply message not found"
|
|
||||||
}`)
|
|
||||||
assert.EqualError(t, extractOk(data), ErrNotFoundToReply.Error())
|
|
||||||
|
|
||||||
data = []byte(`{
|
|
||||||
"ok": false,
|
|
||||||
"error_code": 429,
|
|
||||||
"description": "Too Many Requests: retry after 8",
|
|
||||||
"parameters": {"retry_after": 8}
|
|
||||||
}`)
|
|
||||||
assert.Equal(t, FloodError{
|
|
||||||
err: NewError(429, "Too Many Requests: retry after 8"),
|
|
||||||
RetryAfter: 8,
|
|
||||||
}, extractOk(data))
|
|
||||||
|
|
||||||
data = []byte(`{
|
|
||||||
"ok": false,
|
|
||||||
"error_code": 400,
|
|
||||||
"description": "Bad Request: group chat was upgraded to a supergroup chat",
|
|
||||||
"parameters": {"migrate_to_chat_id": -100123456789}
|
|
||||||
}`)
|
|
||||||
assert.Equal(t, GroupError{
|
|
||||||
err: ErrGroupMigrated,
|
|
||||||
MigratedTo: -100123456789,
|
|
||||||
}, extractOk(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtractMessage(t *testing.T) {
|
|
||||||
data := []byte(`{"ok":true,"result":true}`)
|
|
||||||
_, err := extractMessage(data)
|
|
||||||
assert.Equal(t, ErrTrueResult, err)
|
|
||||||
|
|
||||||
data = []byte(`{"ok":true,"result":{"foo":"bar"}}`)
|
|
||||||
_, err = extractMessage(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmbedRights(t *testing.T) {
|
|
||||||
rights := NoRestrictions()
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"chat_id": "1",
|
|
||||||
"user_id": "2",
|
|
||||||
}
|
|
||||||
embedRights(params, rights)
|
|
||||||
|
|
||||||
expected := map[string]interface{}{
|
|
||||||
"is_anonymous": false,
|
|
||||||
"chat_id": "1",
|
|
||||||
"user_id": "2",
|
|
||||||
"can_be_edited": true,
|
|
||||||
"can_send_messages": true,
|
|
||||||
"can_send_media_messages": true,
|
|
||||||
"can_send_polls": true,
|
|
||||||
"can_send_other_messages": true,
|
|
||||||
"can_add_web_page_previews": true,
|
|
||||||
"can_change_info": false,
|
|
||||||
"can_post_messages": false,
|
|
||||||
"can_edit_messages": false,
|
|
||||||
"can_delete_messages": false,
|
|
||||||
"can_invite_users": false,
|
|
||||||
"can_restrict_members": false,
|
|
||||||
"can_pin_messages": false,
|
|
||||||
"can_promote_members": false,
|
|
||||||
"can_manage_video_chats": false,
|
|
||||||
"can_manage_chat": false,
|
|
||||||
}
|
|
||||||
assert.Equal(t, expected, params)
|
|
||||||
}
|
|
Loading…
Reference in New Issue