Massive refactoring and file structure changes.

- Callback structs has been moved to callbacks.go
- User / Chat and its methods -> chat.go
- Concept of Editable and Message Signature introduced
- No more types.go, it's been refactored into media.go + friends
pull/108/head
Ian Byrd 7 years ago
parent 264d5cd726
commit dbc2cd7f6d
No known key found for this signature in database
GPG Key ID: 598F598CA3B8055F

@ -17,10 +17,6 @@ import (
"github.com/pkg/errors"
)
func wrapSystem(err error) error {
return errors.Wrap(err, "system error")
}
func (b *Bot) sendCommand(method string, payload interface{}) ([]byte, error) {
url := fmt.Sprintf("https://api.telegram.org/bot%s/%s", b.Token, method)
@ -143,6 +139,16 @@ func (b *Bot) getMe() (User, error) {
}
// Update object represents an incoming update.
type Update struct {
ID int64 `json:"update_id"`
Payload *Message `json:"message"`
// optional
Callback *Callback `json:"callback_query"`
Query *Query `json:"inline_query"`
}
func (b *Bot) getUpdates(offset int64, timeout time.Duration) (upd []Update, err error) {
params := map[string]string{
"offset": strconv.FormatInt(offset, 10),
@ -154,22 +160,22 @@ func (b *Bot) getUpdates(offset int64, timeout time.Duration) (upd []Update, err
return
}
var updatesRecieved struct {
var updatesReceived struct {
Ok bool
Result []Update
Description string
}
err = json.Unmarshal(updatesJSON, &updatesRecieved)
err = json.Unmarshal(updatesJSON, &updatesReceived)
if err != nil {
err = errors.Wrap(err, "bad response json")
return
}
if !updatesRecieved.Ok {
err = errors.Errorf("api error: %s", updatesRecieved.Description)
if !updatesReceived.Ok {
err = errors.Errorf("api error: %s", updatesReceived.Description)
return
}
return updatesRecieved.Result, nil
return updatesReceived.Result, nil
}

@ -104,6 +104,21 @@ func (b *Bot) poll(
}
}
func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
params := map[string]string{
"chat_id": to.Destination(),
"text": text,
}
embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendMessage", params)
if err != nil {
return nil, err
}
return extractMsgResponse(respJSON)
}
// Send accepts 2+ arguments, starting with destination chat, followed by
// some Sendable (or string!) and optional send options.
//
@ -429,7 +444,7 @@ func (b *Bot) GetUserProfilePhotos(recipient Recipient) ([][]Photo, error) {
// GetChatMember return information about a member of a chat.
//
// Returns a ChatMember object on success.
func (b *Bot) GetChatMember(recipient Recipient, user User) (*ChatMember, error) {
func (b *Bot) GetChatMember(recipient Recipient, user User) (ChatMember, error) {
params := map[string]string{
"chat_id": recipient.Destination(),
"user_id": user.Destination(),
@ -437,22 +452,22 @@ func (b *Bot) GetChatMember(recipient Recipient, user User) (*ChatMember, error)
respJSON, err := b.sendCommand("getChatMember", params)
if err != nil {
return nil, err
return ChatMember{}, err
}
var resp struct {
Ok bool
Result *ChatMember
Result ChatMember
Description string `json:"description"`
}
err = json.Unmarshal(respJSON, &resp)
if err != nil {
return nil, errors.Wrap(err, "bad response json")
return ChatMember{}, errors.Wrap(err, "bad response json")
}
if !resp.Ok {
return nil, errors.Errorf("api error: %s", resp.Description)
return ChatMember{}, errors.Errorf("api error: %s", resp.Description)
}
return resp.Result, nil
@ -470,39 +485,22 @@ func (b *Bot) GetFileDirectURL(fileID string) (string, error) {
// EditMessageText used to edit already sent message with known recepient and message id.
//
// On success, returns edited message object
func (b *Bot) EditMessageText(recipient Recipient, messageID int, message string, sendOptions *SendOptions) (*Message, error) {
func (b *Bot) Edit(chatID string, messageID int, message string, how ...interface{}) (*Message, error) {
params := map[string]string{
"chat_id": recipient.Destination(),
"chat_id": chatID,
"message_id": strconv.Itoa(messageID),
"text": message,
}
if sendOptions != nil {
embedSendOptions(params, sendOptions)
}
options := extractOptions(how)
embedSendOptions(params, options)
respJSON, err := b.sendCommand("editMessageText", params)
if err != nil {
return nil, err
}
var resp struct {
Ok bool
Description string
Message Message `json:"result"`
}
err = json.Unmarshal(respJSON, &resp)
if err != nil {
return nil, err
}
if !resp.Ok {
return nil, fmt.Errorf("telebot: %s", resp.Description)
}
return &resp.Message, err
return extractMsgResponse(respJSON)
}
// EditInlineMessageText used to edit already sent inline message with known inline message id.

@ -0,0 +1,45 @@
package telebot
// Callback object represents a query from a callback button in an
// inline keyboard.
type Callback struct {
ID string `json:"id"`
// For message sent to channels, Sender may be empty
Sender *User `json:"from"`
// Message will be set if the button that originated the query
// was attached to a message sent by a bot.
Message *Message `json:"message"`
// MessageID will be set if the button was attached to a message
// sent via the bot in inline mode.
MessageID string `json:"inline_message_id"`
// Data associated with the callback button. Be aware that
// a bad client can send arbitrary data in this field.
Data string `json:"data"`
}
// CallbackResponse builds a response to a Callback query.
//
// See also: https://core.telegram.org/bots/api#answerCallbackQuery
type CallbackResponse struct {
// The ID of the callback to which this is a response.
// It is not necessary to specify this field manually.
CallbackID string `json:"callback_query_id"`
// Text of the notification. If not specified, nothing will be shown to the user.
Text string `json:"text,omitempty"`
// (Optional) If true, an alert will be shown by the client instead
// of a notification at the top of the chat screen. Defaults to false.
ShowAlert bool `json:"show_alert,omitempty"`
// (Optional) URL that will be opened by the user's client.
// If you have created a Game and accepted the conditions via @Botfather
// specify the URL that opens your game
// note that this will only work if the query comes from a callback_game button.
// Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.
URL string `json:"url,omitempty"`
}

@ -0,0 +1,63 @@
package telebot
import "strconv"
// User object represents a Telegram user, bot
type User struct {
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
}
// Recipient returns user ID (see Recipient interface).
func (u *User) Recipient() string {
return strconv.Itoa(u.ID)
}
// Chat object represents a Telegram user, bot, group or a channel.
type Chat struct {
ID int64 `json:"id"`
// See telebot.ChatType and consts.
Type ChatType `json:"type"`
// Won't be there for ChatPrivate.
Title string `json:"title"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
}
// Recipient returns chat ID (see Recipient interface).
func (c *Chat) Recipient() string {
if c.Type == ChatChannel {
return "@" + c.Username
}
return strconv.FormatInt(c.ID, 10)
}
// ChatMember object represents information about a single chat member.
type ChatMember struct {
User *User `json:"user"`
Status MemberStatus `json:"status"`
// Due for banned/restricted, Unixtime.
Until int64 `json:"until_date,omitempty"`
CanBeEdited bool `json:"can_be_edited,omitempty"`
CanChangeInfo bool `json:"can_change_info,omitempty"`
CanPostMessages bool `json:"can_post_messages,omitempty"`
CanEditMessages bool `json:"can_edit_messages,omitempty"`
CanDeleteMessages bool `json:"can_delete_messages,omitempty"`
CanInviteUsers bool `json:"can_invite_users,omitempty"`
CanRestrictMembers bool `json:"can_restrict_members,omitempty"`
CanPinMessages bool `json:"can_pin_messages,omitempty"`
CanPromoteMembers bool `json:"can_promote_members,omitempty"`
CanSendMessages bool `json:"can_send_messages,omitempty"`
CanSendMedia bool `json:"can_send_media_messages,omitempty"`
CanSendOther bool `json:"can_send_other_messages,omitempty"`
CanAddPreviews bool `json:"can_add_web_page_previews,omitempty"`
}

@ -0,0 +1,28 @@
package telebot
// Editable is an interface for all objects that
// provide "message signature", a pair of 32-bit
// message ID and 64-bit chat ID, both required
// for edit operations.
//
// Use case: DB model struct for messages to-be
// edited with, say two collums: msg_id,chat_id
// could easily implement MessageSig() making
// instances of stored messages editable.
type Editable interface {
// For inline messages, return chatID = 0
MessageSig() (messageID int, chatID int64)
}
// StoredMessage is an example struct suitable for being
// stored in the database as-is or being embedded into
// a larger struct, which is often the case (you might
// want to store some metadata alongside, or might not.)
type StoredMessage struct {
MessageID string `sql:"message_id" json:"message_id"`
ChatID int64 `sql:"chat_id" json:"chat_id"`
}
func (x StoredMessage) MessageSig() (int, int) {
return x.MessageID, x.ChatID
}

@ -0,0 +1,123 @@
package telebot
// Photo object represents a photo (with or without caption).
type Photo struct {
File
Width int `json:"width"`
Height int `json:"height"`
Caption string `json:"caption,omitempty"`
}
// Audio object represents an audio file.
type Audio struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
// Title (optional) as defined by sender or by audio tags.
Title string `json:"title"`
// Performer (optional) is defined by sender or by audio tags.
Performer string `json:"performer"`
// MIME type (optional) of the file as defined by sender.
Mime string `json:"mime_type"`
Caption string `json:"caption,omitempty"`
}
// Voice object represents a voice note.
type Voice struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
// MIME type (optional) of the file as defined by sender.
Mime string `json:"mime_type"`
Caption string `json:"caption,omitempty"`
}
// Document object represents a general file (as opposed to Photo or Audio).
// Telegram users can send files of any type of up to 1.5 GB in size.
type Document struct {
File
// Document thumbnail as defined by sender.
Preview Photo `json:"thumb"`
// Original filename as defined by sender.
FileName string `json:"file_name"`
// MIME type of the file as defined by sender.
Mime string `json:"mime_type"`
Caption string `json:"caption,omitempty"`
}
// Sticker object represents a WebP image, so-called sticker.
type Sticker struct {
File
Width int `json:"width"`
Height int `json:"height"`
// Sticker thumbnail in .webp or .jpg format.
Thumbnail Photo `json:"thumb"`
// Associated emoji
Emoji string `json:"emoji"`
}
// Video object represents an MP4-encoded video.
type Video struct {
Audio
Width int `json:"width"`
Height int `json:"height"`
// Text description of the video as defined by sender.
Caption string `json:"caption,omitempty"`
// Video thumbnail.
Thumbnail Photo `json:"thumb"`
}
// This object represents a video message (available in Telegram apps
// as of v.4.0).
type VideoNote struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
// Video note thumbnail.
Thumbnail Photo `json:"thumb"`
}
// Contact object represents a contact to Telegram user
type Contact struct {
UserID int `json:"user_id"`
PhoneNumber string `json:"phone_number"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
// Location object represents geographic position.
type Location struct {
Lat float32 `json:"latitude"`
Lng float32 `json:"longitude"`
}
// Venue object represents a venue location with name, address and
// optional foursquare ID.
type Venue struct {
Location Location `json:"location"`
Title string `json:"title"`
Address string `json:"address"`
FoursquareID string `json:"foursquare_id,omitempty"`
}

@ -37,6 +37,9 @@ type Message struct {
// Author signature (in channels).
Signature string `json:"author_signature"`
// Some messages containing media, may as well have a caption.
Caption string `json:"caption,omitempty"`
// For an audio recording, information about it.
Audio *Audio `json:"audio"`
@ -146,8 +149,6 @@ type Message struct {
Entities []MessageEntity `json:"entities,omitempty"`
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
Caption string `json:"caption,omitempty"`
}
// MessageEntity object represents "special" parts of text messages,
@ -171,11 +172,6 @@ type MessageEntity struct {
User *User `json:"user,omitempty"`
}
// Origin returns an origin of message: group chat / personal.
func (m *Message) Origin() *User {
return m.Sender
}
// Time returns the moment of message creation in local time.
func (m *Message) Time() time.Time {
return time.Unix(int64(m.Unixtime), 0)
@ -184,18 +180,28 @@ func (m *Message) Time() time.Time {
// IsForwarded says whether message is forwarded copy of another
// message or not.
func (m *Message) IsForwarded() bool {
return m.OriginalSender != nil || m.OriginalChat != nil
return m.OriginalChat != nil
}
// IsReply says whether message is reply to another message or not.
// IsReply says whether message is a reply to another message.
func (m *Message) IsReply() bool {
return m.ReplyTo != nil
}
// IsPersonal returns true, if message is a personal message,
// returns false if sent to group chat.
func (m *Message) IsPersonal() bool {
return !m.Chat.IsGroupChat()
// IsPrivate returns true, if it's a personal message.
func (m *Message) Private() bool {
return m.Chat.Type == ChatPrivate
}
// FromGroup returns true, if message came from a group OR
// a super group.
func (m *Message) FromGroup() bool {
return m.Chat.Type == ChatGroup || m.Chat.Type == ChatSuperGroup
}
// FromChannel returns true, if message came from a channel.
func (m *Message) FromChannel() bool {
return m.Chat.Type == ChatChannel
}
// IsService returns true, if message is a service message,
@ -205,31 +211,16 @@ func (m *Message) IsPersonal() bool {
// typically occur on some global action. For instance, when
// anyone leaves the chat or chat title changes.
func (m *Message) IsService() bool {
service := false
if m.UserJoined != nil {
service = true
}
if m.UserLeft != nil {
service = true
}
if m.NewChatTitle != "" {
service = true
}
if len(m.NewChatPhoto) > 0 {
service = true
}
if m.ChatPhotoDeleted {
service = true
}
if m.ChatCreated {
service = true
}
return service
fact := false
fact = fact || (m.UserJoined != nil)
fact = fact || (m.UserLeft != nil)
fact = fact || (m.NewChatTitle != "")
fact = fact || (len(m.NewChatPhoto) > 0)
fact = fact || m.ChatPhotoDeleted
fact = fact || m.ChatCreated
fact = fact || m.SuperGroupCreated
fact = fact || (m.MigrateTo != m.MigrateFrom != 0)
return fact
}

@ -1,6 +1,11 @@
package telebot
// Option is a shorcut flag for certain SendOptions.
// Option is a shorcut flag type for certain message features
// (so-called options). It means that instead of passing
// fully-fledged SendOptions* to Send(), you can use these
// flags instead.
//
// Supported options are defined as iota-constants.
type Option int
const (
@ -17,8 +22,13 @@ const (
OneTimeKeyboard
)
// SendOptions represents a set of custom options that could
// be appled to messages sent.
// SendOptions has most complete control over in what way the message
// must be sent, providing an API-complete set of custom properties
// and options.
//
// Despite its power, SendOptions is rather inconvenient to use all
// the way through bot logic, so you might want to consider storing
// and re-using it somewhere or be using Option flags instead.
type SendOptions struct {
// If the message is a reply, original message.
ReplyTo *Message
@ -36,7 +46,9 @@ type SendOptions struct {
ParseMode ParseMode
}
// ReplyMarkup specifies convenient options for bot-user communications.
// ReplyMarkup controls two convenient options for bot-user communications
// such as reply keyboard and inline "keyboard" (a grid of buttons as a part
// of the message).
type ReplyMarkup struct {
// ForceReply forces Telegram clients to display
// a reply interface to the user (act as if the user
@ -74,3 +86,28 @@ type ReplyMarkup struct {
// sender of the original message.
Selective bool `json:"selective,omitempty"`
}
// KeyboardButton represents a button displayed in reply-keyboard.
type KeyboardButton struct {
Text string `json:"text"`
Contact bool `json:"request_contact,omitempty"`
Location bool `json:"request_location,omitempty"`
}
// InlineKeyboardMarkup represents an inline keyboard that appears
// right next to the message it belongs to.
type InlineKeyboardMarkup struct {
// Array of button rows, each represented by
// an Array of KeyboardButton objects.
InlineKeyboard [][]InlineButton `json:"inline_keyboard,omitempty"`
}
// InlineButton represents a button displayed in the message.
type InlineButton struct {
Text string `json:"text"`
URL string `json:"url,omitempty"`
Data string `json:"callback_data,omitempty"`
InlineQuery string `json:"switch_inline_query,omitempty"`
}

@ -2,19 +2,20 @@ package telebot
import "fmt"
func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
params := map[string]string{
"chat_id": to.Destination(),
"text": text,
}
embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendMessage", params)
if err != nil {
return nil, err
}
// Recipient is any possible endpoint you can send
// messages to: either user, group or a channel.
type Recipient interface {
// Must return legit Telegram chat_id or username
Recipient() string
}
return extractMsgResponse(respJSON)
// Sendable is any object that can send itself.
//
// This is pretty cool, since it lets bots implement
// custom Sendables for complex kind of media or
// chat objects spanning across multiple messages.
type Sendable interface {
Send(*Bot, Recipient, *SendOptions) (*Message, error)
}
func (p *Photo) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {

@ -1,289 +0,0 @@
package telebot
import "strconv"
// Sendable is any object that can send itself.
//
// This is pretty cool, since it lets bots implement
// custom Sendables with certain properties.
type Sendable interface {
Send(*Bot, Recipient, *SendOptions) (*Message, error)
}
// Recipient is basically any possible endpoint you can send
// messages to. It's usually a distinct user or a chat.
type Recipient interface {
// ID of user or group chat, @Username for channel
Destination() string
}
// User object represents a Telegram user, bot
//
// object represents a group chat if Title is empty.
type User struct {
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
}
// Destination is internal user ID.
func (u *User) Destination() string {
return strconv.Itoa(u.ID)
}
// Chat object represents a Telegram user, bot or group chat.
//
// Type of chat, can be either “private”, “group”, "supergroup" or “channel”
type Chat struct {
ID int64 `json:"id"`
// See telebot.ChatType and consts.
Type ChatType `json:"type"`
// Won't be there for ChatPrivate.
Title string `json:"title"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
}
// Destination is internal chat ID.
func (c *Chat) Destination() string {
ret := "@" + c.Username
if c.Type != "channel" {
ret = strconv.FormatInt(c.ID, 10)
}
return ret
}
// IsGroupChat returns true if chat object represents a group chat.
func (c *Chat) IsGroupChat() bool {
return c.Type != "private"
}
// Update object represents an incoming update.
type Update struct {
ID int64 `json:"update_id"`
Payload *Message `json:"message"`
// optional
Callback *Callback `json:"callback_query"`
Query *Query `json:"inline_query"`
}
// Photo object represents a photo (with or without caption).
type Photo struct {
File
Width int `json:"width"`
Height int `json:"height"`
Caption string `json:"caption,omitempty"`
}
// Audio object represents an audio file.
type Audio struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
// Title (optional) as defined by sender or by audio tags.
Title string `json:"title"`
// Performer (optional) is defined by sender or by audio tags.
Performer string `json:"performer"`
// MIME type (optional) of the file as defined by sender.
Mime string `json:"mime_type"`
Caption string `json:"caption,omitempty"`
}
// Voice object represents a voice note.
type Voice struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
// MIME type (optional) of the file as defined by sender.
Mime string `json:"mime_type"`
Caption string `json:"caption,omitempty"`
}
// Document object represents a general file (as opposed to Photo or Audio).
// Telegram users can send files of any type of up to 1.5 GB in size.
type Document struct {
File
// Document thumbnail as defined by sender.
Preview Photo `json:"thumb"`
// Original filename as defined by sender.
FileName string `json:"file_name"`
// MIME type of the file as defined by sender.
Mime string `json:"mime_type"`
Caption string `json:"caption,omitempty"`
}
// Sticker object represents a WebP image, so-called sticker.
type Sticker struct {
File
Width int `json:"width"`
Height int `json:"height"`
// Sticker thumbnail in .webp or .jpg format.
Thumbnail Photo `json:"thumb"`
// Associated emoji
Emoji string `json:"emoji"`
}
// Video object represents an MP4-encoded video.
type Video struct {
Audio
Width int `json:"width"`
Height int `json:"height"`
// Text description of the video as defined by sender.
Caption string `json:"caption,omitempty"`
// Video thumbnail.
Thumbnail Photo `json:"thumb"`
}
// This object represents a video message (available in Telegram apps
// as of v.4.0).
type VideoNote struct {
File
// Duration of the recording in seconds as defined by sender.
Duration int `json:"duration"`
// Video note thumbnail.
Thumbnail Photo `json:"thumb"`
}
// Contact object represents a contact to Telegram user
type Contact struct {
UserID int `json:"user_id"`
PhoneNumber string `json:"phone_number"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
// Location object represents geographic position.
type Location struct {
Lat float32 `json:"latitude"`
Lng float32 `json:"longitude"`
}
// Venue object represents a venue location with name, address and
// optional foursquare ID.
type Venue struct {
Location Location `json:"location"`
Title string `json:"title"`
Address string `json:"address"`
FoursquareID string `json:"foursquare_id,omitempty"`
}
// KeyboardButton represents a button displayed in reply-keyboard.
type KeyboardButton struct {
Text string `json:"text"`
Contact bool `json:"request_contact,omitempty"`
Location bool `json:"request_location,omitempty"`
}
// InlineButton represents a button displayed in the message.
type InlineButton struct {
Text string `json:"text"`
URL string `json:"url,omitempty"`
Data string `json:"callback_data,omitempty"`
InlineQuery string `json:"switch_inline_query,omitempty"`
}
// InlineKeyboardMarkup represents an inline keyboard that appears
// right next to the message it belongs to.
type InlineKeyboardMarkup struct {
// Array of button rows, each represented by
// an Array of KeyboardButton objects.
InlineKeyboard [][]InlineButton `json:"inline_keyboard,omitempty"`
}
// Callback object represents a query from a callback button in an
// inline keyboard.
type Callback struct {
ID string `json:"id"`
// For message sent to channels, Sender may be empty
Sender *User `json:"from"`
// Message will be set if the button that originated the query
// was attached to a message sent by a bot.
Message *Message `json:"message"`
// MessageID will be set if the button was attached to a message
// sent via the bot in inline mode.
MessageID string `json:"inline_message_id"`
// Data associated with the callback button. Be aware that
// a bad client can send arbitrary data in this field.
Data string `json:"data"`
}
// CallbackResponse builds a response to a Callback query.
//
// See also: https://core.telegram.org/bots/api#answerCallbackQuery
type CallbackResponse struct {
// The ID of the callback to which this is a response.
// It is not necessary to specify this field manually.
CallbackID string `json:"callback_query_id"`
// Text of the notification. If not specified, nothing will be shown to the user.
Text string `json:"text,omitempty"`
// (Optional) If true, an alert will be shown by the client instead
// of a notification at the top of the chat screen. Defaults to false.
ShowAlert bool `json:"show_alert,omitempty"`
// (Optional) URL that will be opened by the user's client.
// If you have created a Game and accepted the conditions via @Botfather
// specify the URL that opens your game
// note that this will only work if the query comes from a callback_game button.
// Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.
URL string `json:"url,omitempty"`
}
// ChatMember object represents information about a single chat member.
type ChatMember struct {
User *User `json:"user"`
Status MemberStatus `json:"status"`
// Due for banned/restricted, Unixtime.
Until int64 `json:"until_date,omitempty"`
CanBeEdited bool `json:"can_be_edited,omitempty"`
CanChangeInfo bool `json:"can_change_info,omitempty"`
CanPostMessages bool `json:"can_post_messages,omitempty"`
CanEditMessages bool `json:"can_edit_messages,omitempty"`
CanDeleteMessages bool `json:"can_delete_messages,omitempty"`
CanInviteUsers bool `json:"can_invite_users,omitempty"`
CanRestrictMembers bool `json:"can_restrict_members,omitempty"`
CanPinMessages bool `json:"can_pin_messages,omitempty"`
CanPromoteMembers bool `json:"can_promote_members,omitempty"`
CanSendMessages bool `json:"can_send_messages,omitempty"`
CanSendMedia bool `json:"can_send_media_messages,omitempty"`
CanSendOther bool `json:"can_send_other_messages,omitempty"`
CanAddPreviews bool `json:"can_add_web_page_previews,omitempty"`
}

@ -8,6 +8,10 @@ import (
"github.com/pkg/errors"
)
func wrapSystem(err error) error {
return errors.Wrap(err, "system error")
}
func extractMsgResponse(respJSON []byte) (*Message, error) {
var resp struct {
Ok bool

Loading…
Cancel
Save