diff --git a/PATRONS.md b/PATRONS.md deleted file mode 100644 index 5ca2eb0..0000000 --- a/PATRONS.md +++ /dev/null @@ -1,6 +0,0 @@ -# [Patrons!](https://www.patreon.com/bePatron?c=243288) -**These people have sponsored the current version of the project:** -- Daniil Tlenov -- Aurielb -- Yami Odymel -- MoD21k \ No newline at end of file diff --git a/answer_callback_query.go b/answer_callback_query.go index 74380ab..3a65e4a 100644 --- a/answer_callback_query.go +++ b/answer_callback_query.go @@ -10,10 +10,6 @@ type AnswerCallbackQueryParameters struct { // user, 0-200 characters Text string `json:"text,omitempty"` - // 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"` - // 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 @@ -23,6 +19,10 @@ type AnswerCallbackQueryParameters struct { // bot with a parameter. URL string `json:"url,omitempty"` + // 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"` + // The maximum amount of time in seconds that the result of the callback // query may be cached client-side. Telegram apps will support caching // starting in version 3.14. Defaults to 0. @@ -47,7 +47,7 @@ func (bot *Bot) AnswerCallbackQuery(params *AnswerCallbackQueryParameters) (bool return false, err } - resp, err := bot.request(dst, "answerCallbackQuery") + resp, err := bot.request(dst, MethodAnswerCallbackQuery) if err != nil { return false, err } diff --git a/answer_inline_query.go b/answer_inline_query.go index 67d4090..fb9996f 100644 --- a/answer_inline_query.go +++ b/answer_inline_query.go @@ -6,18 +6,6 @@ type AnswerInlineQueryParameters struct { // Unique identifier for the answered query InlineQueryID string `json:"inline_query_id"` - // A JSON-serialized array of results for the inline query - Results []interface{} `json:"results"` - - // The maximum amount of time in seconds that the result of the inline query - // may be cached on the server. Defaults to 300. - CacheTime int `json:"cache_time,omitempty"` - - // Pass True, if results may be cached on the server side only for the user - // that sent the query. By default, results may be returned to any user who - // sends the same query - IsPersonal bool `json:"is_personal,omitempty"` - // Pass the offset that a client should send in the next query with the same // text to receive more results. Pass an empty string if there are no more // results or if you don‘t support pagination. Offset length can’t exceed 64 @@ -33,6 +21,18 @@ type AnswerInlineQueryParameters struct { // presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - // are allowed. SwitchPrivateMessageParameter string `json:"switch_pm_parameter,omitempty"` + + // A JSON-serialized array of results for the inline query + Results []interface{} `json:"results"` + + // The maximum amount of time in seconds that the result of the inline query + // may be cached on the server. Defaults to 300. + CacheTime int `json:"cache_time,omitempty"` + + // Pass True, if results may be cached on the server side only for the user + // that sent the query. By default, results may be returned to any user who + // sends the same query + IsPersonal bool `json:"is_personal,omitempty"` } func NewAnswerInlineQuery(inlineQueryID string, results ...interface{}) *AnswerInlineQueryParameters { @@ -51,7 +51,7 @@ func (bot *Bot) AnswerInlineQuery(params *AnswerInlineQueryParameters) (bool, er return false, err } - resp, err := bot.request(dst, "answerInlineQuery") + resp, err := bot.request(dst, MethodAnswerInlineQuery) if err != nil { return false, err } diff --git a/answer_pre_checkout_query.go b/answer_pre_checkout_query.go index 2f8f6b4..58a5f6a 100644 --- a/answer_pre_checkout_query.go +++ b/answer_pre_checkout_query.go @@ -6,17 +6,17 @@ type AnswerPreCheckoutQueryParameters struct { // Unique identifier for the query to be answered PreCheckoutQueryID string `json:"pre_checkout_query_id"` - // Specify True if everything is alright (goods are available, etc.) and the - // bot is ready to proceed with the order. Use False if there are any - // problems. - Ok bool `json:"ok"` - // Required if ok is False. Error message in human readable form that // explains the reason for failure to proceed with the checkout (e.g. "Sorry, // somebody just bought the last of our amazing black T-shirts while you were // busy filling out your payment details. Please choose a different color or // garment!"). Telegram will display this message to the user. ErrorMessage string `json:"error_message,omitempty"` + + // Specify True if everything is alright (goods are available, etc.) and the + // bot is ready to proceed with the order. Use False if there are any + // problems. + Ok bool `json:"ok"` } func NewAnswerPreCheckoutQuery(preCheckoutQueryID string, ok bool) *AnswerPreCheckoutQueryParameters { @@ -41,7 +41,7 @@ func (bot *Bot) AnswerPreCheckoutQuery(params *AnswerShippingQueryParameters) (b return false, err } - resp, err := bot.request(dst, "answerPreCheckoutQuery") + resp, err := bot.request(dst, MethodAnswerPreCheckoutQuery) if err != nil { return false, err } diff --git a/answer_shipping_query.go b/answer_shipping_query.go index b6d62ba..a2a00cf 100644 --- a/answer_shipping_query.go +++ b/answer_shipping_query.go @@ -6,6 +6,12 @@ type AnswerShippingQueryParameters struct { // Unique identifier for the query to be answered ShippingQueryID string `json:"shipping_query_id"` + // Required if ok is False. Error message in human readable form that + // explains why it is impossible to complete the order (e.g. "Sorry, delivery + // to your desired address is unavailable'). Telegram will display this + // message to the user. + ErrorMessage string `json:"error_message,omitempty"` + // Specify True if delivery to the specified address is possible and False // if there are any problems (for example, if delivery to the specified // address is not possible) @@ -14,12 +20,6 @@ type AnswerShippingQueryParameters struct { // Required if ok is True. A JSON-serialized array of available shipping // options. ShippingOptions []ShippingOption `json:"shipping_options,omitempty"` - - // Required if ok is False. Error message in human readable form that - // explains why it is impossible to complete the order (e.g. "Sorry, delivery - // to your desired address is unavailable'). Telegram will display this - // message to the user. - ErrorMessage string `json:"error_message,omitempty"` } func NewAnswerShippingQuery(shippingQueryID string, ok bool) *AnswerShippingQueryParameters { @@ -40,7 +40,7 @@ func (bot *Bot) AnswerShippingQuery(params *AnswerShippingQueryParameters) (bool return false, err } - resp, err := bot.request(dst, "answerShippingQuery") + resp, err := bot.request(dst, MethodAnswerShippingQuery) if err != nil { return false, err } diff --git a/constants.go b/constants.go new file mode 100644 index 0000000..25108b4 --- /dev/null +++ b/constants.go @@ -0,0 +1,151 @@ +package telegram + +const ( + ActionFindLocation = "find_location" + ActionRecordAudio = "record_audio" + ActionRecordVideo = "record_video" + ActionRecordVideoNote = "record_video_note" + ActionTyping = "typing" + ActionUploadAudio = "upload_audio" + ActionUploadDocument = "upload_document" + ActionUploadPhoto = "upload_photo" + ActionUploadVideo = "upload_video" + ActionUploadVideoNote = "upload_video_note" +) + +const ( + ChatChannel = "channel" + ChatGroup = "group" + ChatPrivate = "private" + ChatSuperGroup = "supergroup" +) + +const ( + EntityBold = "bold" + EntityBotCommand = "bot_command" + EntityCode = "code" + EntityEmail = "email" + EntityHashtag = "hashtag" + EntityItalic = "italic" + EntityMention = "mention" + EntityPre = "pre" + EntityTextLink = "text_link" + EntityTextMention = "text_mention" + EntityURL = "url" +) + +const ( + MethodAddStickerToSet = "addStickerToSet" + MethodAnswerCallbackQuery = "answerCallbackQuery" + MethodAnswerInlineQuery = "answerInlineQuery" + MethodAnswerPreCheckoutQuery = "answerPreCheckoutQuery" + MethodAnswerShippingQuery = "answerShippingQuery" + MethodCreateNewStickerSet = "createNewStickerSet" + MethodDeleteChatPhoto = "deleteChatPhoto" + MethodDeleteChatStickerSet = "deleteChatStickerSet" + MethodDeleteMessage = "deleteMessage" + MethodDeleteStickerFromSet = "deleteStickerFromSet" + MethodDeleteWebhook = "deleteWebhook" + MethodEditMessageCaption = "editMessageCaption" + MethodEditMessageLiveLocation = "editMessageLiveLocation" + MethodEditMessageReplyMarkup = "editMessageReplyMarkup" + MethodEditMessageText = "editMessageText" + MethodExportChatInviteLink = "exportChatInviteLink" + MethodForwardMessage = "forwardMessage" + MethodGetChat = "getChat" + MethodGetChatAdministrators = "getChatAdministrators" + MethodGetChatMember = "getChatMember" + MethodGetChatMembersCount = "getChatMembersCount" + MethodGetFile = "getFile" + MethodGetGameHighScores = "getGameHighScores" + MethodGetMe = "getMe" + MethodGetStickerSet = "getStickerSet" + MethodGetUpdates = "getUpdates" + MethodGetUserProfilePhotos = "getUserProfilePhotos" + MethodGetWebhookInfo = "getWebhookInfo" + MethodKickChatMember = "kickChatMember" + MethodLeaveChat = "leaveChat" + MethodPinChatMessage = "pinChatMessage" + MethodPromoteChatMember = "promoteChatMember" + MethodRestrictChatMember = "restrictChatMember" + MethodSendAudio = "sendAudio" + MethodSendChatAction = "sendChatAction" + MethodSendContact = "sendContact" + MethodSendDocument = "sendDocument" + MethodSendGame = "sendGame" + MethodSendInvoice = "sendInvoice" + MethodSendLocation = "sendLocation" + MethodSendMediaGroup = "sendMediaGroup" + MethodSendMessage = "sendMessage" + MethodSendPhoto = "sendPhoto" + MethodSendSticker = "sendSticker" + MethodSendVenue = "sendVenue" + MethodSendVideo = "sendVideo" + MethodSendVideoNote = "sendVideoNote" + MethodSendVoice = "sendVoice" + MethodSetChatDescription = "setChatDescription" + MethodSetChatPhoto = "setChatPhoto" + MethodSetChatStickerSet = "setChatStickerSet" + MethodSetChatTitle = "setChatTitle" + MethodSetGameScore = "setGameScore" + MethodSetStickerPositionInSet = "setStickerPositionInSet" + MethodSetWebhook = "setWebhook" + MethodStopMessageLiveLocation = "stopMessageLiveLocation" + MethodUnbanChatMember = "unbanChatMember" + MethodUnpinChatMessage = "unpinChatMessage" + MethodUploadStickerFile = "uploadStickerFile" +) + +const ( + ModeHTML = "html" + ModeMarkdown = "markdown" +) + +const ( + MimeHTML = "text/html" + MimeMP4 = "video/mp4" + MimePDF = "application/pdf" + MimeZIP = "application/zip" +) + +const ( + SchemeAttach = "attach" + SchemeTelegram = "tg" +) + +const ( + StatusAdministrator = "administrator" + StatusCreator = "creator" + StatusKicked = "kicked" + StatusLeft = "left" + StatusMember = "member" + StatusRestricted = "restricted" +) + +const ( + TypeArticle = "article" + TypeAudio = "audio" + TypeContact = "contact" + TypeDocument = "document" + TypeGame = "game" + TypeGIF = "gif" + TypeLocation = "location" + TypeMpeg4Gif = "mpeg4_gif" + TypePhoto = "photo" + TypeSticker = "sticker" + TypeVenue = "venue" + TypeVideo = "video" + TypeVoice = "voice" +) + +const ( + UpdateCallbackQuery = "callback_query" + UpdateChannelPost = "channel_post" + UpdateChosenInlineResult = "chosen_inline_result" + UpdateEditedChannelPost = "edited_channel_post" + UpdateEditedMessage = "edited_message" + UpdateInlineQuery = "inline_query" + UpdateMessage = "message" + UpdatePreCheckoutQuery = "pre_checkout_query" + UpdateShippingQuery = "shipping_query" +) diff --git a/delete_chat_photo.go b/delete_chat_photo.go index 2c00f77..cbfb3c3 100644 --- a/delete_chat_photo.go +++ b/delete_chat_photo.go @@ -19,7 +19,7 @@ func (bot *Bot) DeleteChatPhoto(chatID int64) (bool, error) { return false, err } - resp, err := bot.request(dst, "deleteChatPhoto") + resp, err := bot.request(dst, MethodDeleteChatPhoto) if err != nil { return false, err } diff --git a/delete_chat_sticker_set.go b/delete_chat_sticker_set.go index f69d2fc..5759681 100644 --- a/delete_chat_sticker_set.go +++ b/delete_chat_sticker_set.go @@ -17,7 +17,7 @@ func (bot *Bot) DeleteChatStickerSet(chatID int64) (bool, error) { return false, err } - resp, err := bot.request(dst, "deleteChatStickerSet") + resp, err := bot.request(dst, MethodDeleteChatStickerSet) if err != nil { return false, err } diff --git a/delete_message.go b/delete_message.go index 6d649ac..3e9bbda 100644 --- a/delete_message.go +++ b/delete_message.go @@ -25,7 +25,7 @@ func (bot *Bot) DeleteMessage(chatID int64, messageID int) (bool, error) { return false, err } - resp, err := bot.request(dst, "deleteMessage") + resp, err := bot.request(dst, MethodDeleteMessage) if err != nil { return false, err } diff --git a/delete_sticker_from_set.go b/delete_sticker_from_set.go index 37a86c6..d9e4ecc 100644 --- a/delete_sticker_from_set.go +++ b/delete_sticker_from_set.go @@ -14,7 +14,7 @@ func (bot *Bot) DeleteStickerFromSet(sticker string) (bool, error) { return false, err } - resp, err := bot.request(dst, "deleteStickerFromSet") + resp, err := bot.request(dst, MethodDeleteStickerFromSet) if err != nil { return false, err } diff --git a/delete_webhook.go b/delete_webhook.go index ccab299..9130acb 100644 --- a/delete_webhook.go +++ b/delete_webhook.go @@ -5,7 +5,7 @@ import json "github.com/pquerna/ffjson/ffjson" // DeleteWebhook remove webhook integration if you decide to switch back to // getUpdates. Returns True on success. Requires no parameters. func (bot *Bot) DeleteWebhook() (bool, error) { - resp, err := bot.request(nil, "deleteWebhook") + resp, err := bot.request(nil, MethodDeleteWebhook) if err != nil { return false, err } diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..9d29077 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,39 @@ +# Contributor Covenant Code of Conduct +## Our Pledge +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project owner at git@toby3d.ru. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/docs/README.md similarity index 100% rename from README.md rename to docs/README.md diff --git a/docs/SUPPORT.md b/docs/SUPPORT.md new file mode 100644 index 0000000..c898512 --- /dev/null +++ b/docs/SUPPORT.md @@ -0,0 +1,9 @@ +# [Support me on Patreon!](https://www.patreon.com/bePatron?c=243288) +I develop this project in my spare time, and I do it and I will do it free of charge. However, you can make a donation or become a sponsor to make sure that I have enough coffee and pizza for night coding. + +**These people sponsored current version of the project:** +- Aurielb +- Daniil Tlenov +- @kirillDanshin +- MoD21k +- @YamiOdymel \ No newline at end of file diff --git a/edit_message_caption.go b/edit_message_caption.go index 863cda5..9db0809 100644 --- a/edit_message_caption.go +++ b/edit_message_caption.go @@ -36,7 +36,7 @@ func (bot *Bot) EditMessageCaption(params *EditMessageCaptionParameters) (*Messa return nil, err } - resp, err := bot.request(dst, "editMessageCaption") + resp, err := bot.request(dst, MethodEditMessageCaption) if err != nil { return nil, err } diff --git a/edit_message_live_location.go b/edit_message_live_location.go index e1c106a..f38d8dc 100644 --- a/edit_message_live_location.go +++ b/edit_message_live_location.go @@ -44,7 +44,7 @@ func (bot *Bot) EditMessageLiveLocation(params *EditMessageLiveLocationParameter return nil, err } - resp, err := bot.request(dst, "editMessageLiveLocation") + resp, err := bot.request(dst, MethodEditMessageLiveLocation) if err != nil { return nil, err } diff --git a/edit_message_reply_markup.go b/edit_message_reply_markup.go index 405db5f..9039fae 100644 --- a/edit_message_reply_markup.go +++ b/edit_message_reply_markup.go @@ -29,7 +29,7 @@ func (bot *Bot) EditMessageReplyMarkup(params *EditMessageReplyMarkupParameters) return nil, err } - resp, err := bot.request(dst, "editMessageReplyMarkup") + resp, err := bot.request(dst, MethodEditMessageReplyMarkup) if err != nil { return nil, err } diff --git a/edit_message_text.go b/edit_message_text.go index aaefdda..32753b6 100644 --- a/edit_message_text.go +++ b/edit_message_text.go @@ -45,7 +45,7 @@ func (bot *Bot) EditMessageText(params *EditMessageTextParameters) (*Message, er return nil, err } - resp, err := bot.request(dst, "editMessageText") + resp, err := bot.request(dst, MethodEditMessageText) if err != nil { return nil, err } diff --git a/export_chat_invite_link.go b/export_chat_invite_link.go index 4ca874b..abd5f95 100644 --- a/export_chat_invite_link.go +++ b/export_chat_invite_link.go @@ -16,7 +16,7 @@ func (bot *Bot) ExportChatInviteLink(chatID int64) (string, error) { return "", err } - resp, err := bot.request(dst, "exportChatInviteLink") + resp, err := bot.request(dst, MethodExportChatInviteLink) if err != nil { return "", err } diff --git a/forward_message.go b/forward_message.go index 393fcf0..9c756aa 100644 --- a/forward_message.go +++ b/forward_message.go @@ -31,7 +31,7 @@ func (bot *Bot) ForwardMessage(params *ForwardMessageParameters) (*Message, erro return nil, err } - resp, err := bot.request(dst, "forwardMessage") + resp, err := bot.request(dst, MethodForwardMessage) if err != nil { return nil, err } diff --git a/get_chat.go b/get_chat.go index 3330525..4c81d24 100644 --- a/get_chat.go +++ b/get_chat.go @@ -16,7 +16,7 @@ func (bot *Bot) GetChat(chatID int64) (*Chat, error) { return nil, err } - resp, err := bot.request(dst, "getChat") + resp, err := bot.request(dst, MethodGetChat) if err != nil { return nil, err } diff --git a/get_chat_administrators.go b/get_chat_administrators.go index 265d783..30b64a3 100644 --- a/get_chat_administrators.go +++ b/get_chat_administrators.go @@ -17,7 +17,7 @@ func (bot *Bot) GetChatAdministrators(chatID int64) ([]ChatMember, error) { return nil, err } - resp, err := bot.request(dst, "getChatAdministrators") + resp, err := bot.request(dst, MethodGetChatAdministrators) if err != nil { return nil, err } diff --git a/get_chat_member.go b/get_chat_member.go index 03ed3f2..8703178 100644 --- a/get_chat_member.go +++ b/get_chat_member.go @@ -20,7 +20,7 @@ func (bot *Bot) GetChatMember(chatID int64, userID int) (*ChatMember, error) { return nil, err } - resp, err := bot.request(dst, "getChatMember") + resp, err := bot.request(dst, MethodGetChatMember) if err != nil { return nil, err } diff --git a/get_chat_members_count.go b/get_chat_members_count.go index 71cef6b..2355966 100644 --- a/get_chat_members_count.go +++ b/get_chat_members_count.go @@ -15,7 +15,7 @@ func (bot *Bot) GetChatMembersCount(chatID int64) (int, error) { return 0, err } - resp, err := bot.request(dst, "getChatMembersCount") + resp, err := bot.request(dst, MethodGetChatMembersCount) if err != nil { return 0, err } diff --git a/get_file.go b/get_file.go index a0cf9f8..ba66408 100644 --- a/get_file.go +++ b/get_file.go @@ -23,7 +23,7 @@ func (bot *Bot) GetFile(fileID string) (*File, error) { return nil, err } - resp, err := bot.request(dst, "getFile") + resp, err := bot.request(dst, MethodGetFile) if err != nil { return nil, err } diff --git a/get_game_high_scores.go b/get_game_high_scores.go index 692ec9d..b5ce0a0 100644 --- a/get_game_high_scores.go +++ b/get_game_high_scores.go @@ -6,14 +6,14 @@ type GetGameHighScoresParameters struct { // Target user id UserID int `json:"user_id"` - // Required if inline_message_id is not specified. Unique identifier for the - // target chat - ChatID int64 `json:"chat_id,omitempty"` - // Required if inline_message_id is not specified. Identifier of the sent // message MessageID int `json:"message_id,omitempty"` + // Required if inline_message_id is not specified. Unique identifier for the + // target chat + ChatID int64 `json:"chat_id,omitempty"` + // Required if chat_id and message_id are not specified. Identifier of the // inline message InlineMessageID string `json:"inline_message_id,omitempty"` @@ -34,7 +34,7 @@ func (bot *Bot) GetGameHighScores(params *GetGameHighScoresParameters) ([]GameHi return nil, err } - resp, err := bot.request(dst, "getGameHighScores") + resp, err := bot.request(dst, MethodGetGameHighScores) if err != nil { return nil, err } diff --git a/get_me.go b/get_me.go index 62fbae3..d86f0ae 100644 --- a/get_me.go +++ b/get_me.go @@ -5,7 +5,7 @@ import json "github.com/pquerna/ffjson/ffjson" // GetMe testing your bot's auth token. Requires no parameters. Returns basic // information about the bot in form of a User object. func (bot *Bot) GetMe() (*User, error) { - resp, err := bot.request(nil, "getMe") + resp, err := bot.request(nil, MethodGetMe) if err != nil { return nil, err } diff --git a/get_sticker_set.go b/get_sticker_set.go index cdddb51..349c78e 100644 --- a/get_sticker_set.go +++ b/get_sticker_set.go @@ -13,7 +13,7 @@ func (bot *Bot) GetStickerSet(name string) (*StickerSet, error) { return nil, err } - resp, err := bot.request(dst, "getStickerSet") + resp, err := bot.request(dst, MethodGetStickerSet) if err != nil { return nil, err } diff --git a/get_updates.go b/get_updates.go index 7fed73d..3d96062 100644 --- a/get_updates.go +++ b/get_updates.go @@ -42,7 +42,7 @@ func (bot *Bot) GetUpdates(params *GetUpdatesParameters) ([]Update, error) { return nil, err } - resp, err := bot.request(dst, "getUpdates") + resp, err := bot.request(dst, MethodGetUpdates) if err != nil { return nil, err } diff --git a/get_user_profile_photos.go b/get_user_profile_photos.go index 2974ade..7fe1a8d 100644 --- a/get_user_profile_photos.go +++ b/get_user_profile_photos.go @@ -15,7 +15,7 @@ func (bot *Bot) GetUserProfilePhotos(params *GetUserProfilePhotosParameters) (*U return nil, err } - resp, err := bot.request(dst, "getUserProfilePhotos") + resp, err := bot.request(dst, MethodGetUserProfilePhotos) if err != nil { return nil, err } diff --git a/get_webhook_info.go b/get_webhook_info.go index c2596f0..63906f5 100644 --- a/get_webhook_info.go +++ b/get_webhook_info.go @@ -6,7 +6,7 @@ import json "github.com/pquerna/ffjson/ffjson" // returns a WebhookInfo object. If the bot is using getUpdates, will return an // object with the url field empty. func (bot *Bot) GetWebhookInfo() (*WebhookInfo, error) { - resp, err := bot.request(nil, "getWebhookInfo") + resp, err := bot.request(nil, MethodGetWebhookInfo) if err != nil { return nil, err } diff --git a/helpers.go b/helpers.go deleted file mode 100644 index a1308bb..0000000 --- a/helpers.go +++ /dev/null @@ -1,26 +0,0 @@ -package telegram - -import ( - "net/url" - "strconv" -) - -func NewForceReply(selective bool) *ForceReply { - return &ForceReply{ - ForceReply: true, - Selective: selective, - } -} - -func NewInlineMentionURL(id int) *url.URL { - link := &url.URL{ - Scheme: "tg", - Path: "user", - } - - q := link.Query() - q.Add("id", strconv.Itoa(id)) - link.RawQuery = q.Encode() - - return link -} diff --git a/kick_chat_member.go b/kick_chat_member.go index 19aec2b..8ace526 100644 --- a/kick_chat_member.go +++ b/kick_chat_member.go @@ -6,8 +6,8 @@ type KickChatMemberParameters struct { // Unique identifier for the target chat ChatID int64 `json:"chat_id"` - UserID int `json:"user_id"` UntilDate int64 `json:"until_date"` + UserID int `json:"user_id"` } // KickChatMember kick a user from a group, a supergroup or a channel. In the case of supergroups and @@ -24,7 +24,7 @@ func (bot *Bot) KickChatMember(params *KickChatMemberParameters) (bool, error) { return false, err } - resp, err := bot.request(dst, "kickChatMember") + resp, err := bot.request(dst, MethodKickChatMember) if err != nil { return false, err } diff --git a/leave_chat.go b/leave_chat.go index 6523aca..4eaba35 100644 --- a/leave_chat.go +++ b/leave_chat.go @@ -14,7 +14,7 @@ func (bot *Bot) LeaveChat(chatID int64) (bool, error) { return false, err } - resp, err := bot.request(dst, "leaveChat") + resp, err := bot.request(dst, MethodLeaveChat) if err != nil { return false, err } diff --git a/login/check_authorization.go b/login/check_authorization.go index 014c832..6efc220 100644 --- a/login/check_authorization.go +++ b/login/check_authorization.go @@ -4,34 +4,40 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" - "fmt" + "errors" + "net/url" + "strconv" ) +var ErrUserNotDefined = errors.New("user is not defined") + // CheckAuthorization verify the authentication and the integrity of the data // received by comparing the received hash parameter with the hexadecimal // representation of the HMAC-SHA-256 signature of the data-check-string with the // SHA256 hash of the bot's token used as a secret key. -func (user *User) CheckAuthorization(botToken string) (bool, error) { - dataCheckString := fmt.Sprint( - "auth_date=", user.AuthDate.Unix(), - "\n", "first_name=", user.FirstName, - // Eliminate 'hash' to avoid recursion and incorrect data validation. - "\n", "id=", user.ID, - ) +func (app *App) CheckAuthorization(user *User) (bool, error) { + if user == nil { + return false, ErrUserNotDefined + } + + dataCheck := make(url.Values) + dataCheck.Add(KeyAuthDate, string(user.AuthDate)) + dataCheck.Add(KeyFirstName, user.FirstName) + dataCheck.Add(KeyID, strconv.Itoa(user.ID)) // Add optional values if exist if user.LastName != "" { - dataCheckString += fmt.Sprint("\n", "last_name=", user.LastName) + dataCheck.Add(KeyLastName, user.LastName) } if user.PhotoURL != "" { - dataCheckString += fmt.Sprint("\n", "photo_url=", user.PhotoURL) + dataCheck.Add(KeyPhotoURL, user.PhotoURL) } if user.Username != "" { - dataCheckString += fmt.Sprint("\n", "username=", user.Username) + dataCheck.Add(KeyUsername, user.Username) } - secretKey := sha256.Sum256([]byte(botToken)) + secretKey := sha256.Sum256([]byte(app.SecretKey)) hash := hmac.New(sha256.New, secretKey[0:]) - _, err := hash.Write([]byte(dataCheckString)) + _, err := hash.Write([]byte(dataCheck.Encode())) return hex.EncodeToString(hash.Sum(nil)) == user.Hash, err } diff --git a/login/constants.go b/login/constants.go new file mode 100644 index 0000000..9e8d796 --- /dev/null +++ b/login/constants.go @@ -0,0 +1,11 @@ +package login + +const ( + KeyAuthDate = "auth_date" + KeyFirstName = "first_name" + KeyHash = "hash" + KeyID = "id" + KeyLastName = "last_name" + KeyPhotoURL = "photo_url" + KeyUsername = "username" +) diff --git a/login/new.go b/login/new.go index dcfa782..4e75990 100644 --- a/login/new.go +++ b/login/new.go @@ -1,41 +1,11 @@ package login -import ( - "net/url" - "strconv" - "time" -) - -// User contains data about authenticated user. -type User struct { - AuthDate time.Time `json:"auth_date"` - FirstName string `json:"first_name"` - Hash string `json:"hash"` - ID int `json:"id"` - LastName string `json:"last_name,omitempty"` - PhotoURL string `json:"photo_url,omitempty"` - Username string `json:"username,omitempty"` +// App represents a widget which get and validate users authorizations. +type App struct { + SecretKey string } -// New create User structure from input url.Values. -func New(src url.Values) (*User, error) { - authDate, err := strconv.Atoi(src.Get("auth_date")) - if err != nil { - return nil, err - } - - id, err := strconv.Atoi(src.Get("id")) - if err != nil { - return nil, err - } - - return &User{ - AuthDate: time.Unix(int64(authDate), 0), - FirstName: src.Get("first_name"), - Hash: src.Get("hash"), - ID: id, - LastName: src.Get("last_name"), - PhotoURL: src.Get("photo_url"), - Username: src.Get("username"), - }, nil +// New create new app widget for validate authorizations with bot token as secret key. +func New(botToken string) *App { + return &App{SecretKey: botToken} } diff --git a/login/parse_user.go b/login/parse_user.go new file mode 100644 index 0000000..f8eac87 --- /dev/null +++ b/login/parse_user.go @@ -0,0 +1,40 @@ +package login + +import ( + "net/url" + "strconv" +) + +// User contains data about authenticated user. +type User struct { + AuthDate int64 `json:"auth_date"` + FirstName string `json:"first_name"` + Hash string `json:"hash"` + ID int `json:"id"` + LastName string `json:"last_name,omitempty"` + PhotoURL string `json:"photo_url,omitempty"` + Username string `json:"username,omitempty"` +} + +// ParseUser create User structure from input url.Values. +func ParseUser(src url.Values) (*User, error) { + authDate, err := strconv.Atoi(src.Get(KeyAuthDate)) + if err != nil { + return nil, err + } + + id, err := strconv.Atoi(src.Get(KeyID)) + if err != nil { + return nil, err + } + + return &User{ + AuthDate: int64(authDate), + FirstName: src.Get(KeyFirstName), + Hash: src.Get(KeyHash), + ID: id, + LastName: src.Get(KeyLastName), + PhotoURL: src.Get(KeyPhotoURL), + Username: src.Get(KeyUsername), + }, nil +} diff --git a/login/utils.go b/login/utils.go new file mode 100644 index 0000000..8a05ea4 --- /dev/null +++ b/login/utils.go @@ -0,0 +1,28 @@ +package login + +import ( + "fmt" + "time" +) + +// FullName return user first name only or full name if last name is present. +func (user *User) FullName() string { + if user == nil { + return "" + } + + if user.LastName != "" { + return fmt.Sprintln(user.FirstName, user.LastName) + } + + return user.FirstName +} + +// AuthTime convert AuthDate field into time.Time. +func (user *User) AuthTime() time.Time { + if user == nil { + return time.Time{} + } + + return time.Unix(user.AuthDate, 0) +} diff --git a/models.go b/models.go index 5348ee6..557ffdd 100644 --- a/models.go +++ b/models.go @@ -2,77 +2,6 @@ package telegram import "encoding/json" -const ( - ActionFindLocation = "find_location" - ActionRecordAudio = "record_audio" - ActionRecordVideo = "record_video" - ActionRecordVideoNote = "record_video_note" - ActionTyping = "typing" - ActionUploadAudio = "upload_audio" - ActionUploadDocument = "upload_document" - ActionUploadPhoto = "upload_photo" - ActionUploadVideo = "upload_video" - ActionUploadVideoNote = "upload_video_note" - - ChatChannel = "channel" - ChatGroup = "group" - ChatPrivate = "private" - ChatSuperGroup = "supergroup" - - EntityBold = "bold" - EntityBotCommand = "bot_command" - EntityCode = "code" - EntityEmail = "email" - EntityHashtag = "hashtag" - EntityItalic = "italic" - EntityMention = "mention" - EntityPre = "pre" - EntityTextLink = "text_link" - EntityTextMention = "text_mention" - EntityURL = "url" - - ModeHTML = "html" - ModeMarkdown = "markdown" - - MimeHTML = "text/html" - MimeMP4 = "video/mp4" - MimePDF = "application/pdf" - MimeZIP = "application/zip" - - PrefixAttach = "attach://" - - StatusAdministrator = "administrator" - StatusCreator = "creator" - StatusKicked = "kicked" - StatusLeft = "left" - StatusMember = "member" - StatusRestricted = "restricted" - - TypeArticle = "article" - TypeAudio = "audio" - TypeContact = "contact" - TypeDocument = "document" - TypeGame = "game" - TypeGIF = "gif" - TypeLocation = "location" - TypeMpeg4Gif = "mpeg4_gif" - TypePhoto = "photo" - TypeSticker = "sticker" - TypeVenue = "venue" - TypeVideo = "video" - TypeVoice = "voice" - - UpdateCallbackQuery = "callback_query" - UpdateChannelPost = "channel_post" - UpdateChosenInlineResult = "chosen_inline_result" - UpdateEditedChannelPost = "edited_channel_post" - UpdateEditedMessage = "edited_message" - UpdateInlineQuery = "inline_query" - UpdateMessage = "message" - UpdatePreCheckoutQuery = "pre_checkout_query" - UpdateShippingQuery = "shipping_query" -) - type ( // Response represents a response from the Telegram API with the result // stored raw. If ok equals true, the request was successful, and the result @@ -132,6 +61,10 @@ type ( // Webhook URL, may be empty if webhook is not set up URL string `json:"url"` + // Error message in human-readable format for the most recent error that + // happened when trying to deliver an update via webhook + LastErrorMessage string `json:"last_error_message,omitempty"` + // True, if a custom certificate was provided for webhook certificate // checks HasCustomCertificate bool `json:"has_custom_certificate"` @@ -139,18 +72,14 @@ type ( // Number of updates awaiting delivery PendingUpdateCount int `json:"pending_update_count"` - // Unix time for the most recent error that happened when trying to - // deliver an update via webhook - LastErrorDate int64 `json:"last_error_date,omitempty"` - - // Error message in human-readable format for the most recent error that - // happened when trying to deliver an update via webhook - LastErrorMessage string `json:"last_error_message,omitempty"` - // Maximum allowed number of simultaneous HTTPS connections to the // webhook for update delivery MaxConnections int `json:"max_connections,omitempty"` + // Unix time for the most recent error that happened when trying to + // deliver an update via webhook + LastErrorDate int64 `json:"last_error_date,omitempty"` + // A list of update types the bot is subscribed to. Defaults to all // update types AllowedUpdates []string `json:"allowed_updates,omitempty"` @@ -198,12 +127,6 @@ type ( // Last name of the other party in a private chat LastName string `json:"last_name,omitempty"` - // True if a group has ‘All Members Are Admins’ enabled. - AllMembersAreAdministrators bool `json:"all_members_are_administrators,omitempty"` - - // Chat photo. Returned only in getChat. - Photo *ChatPhoto `json:"photo,omitempty"` - // Description, for supergroups and channel chats. Returned only in // getChat. Description string `json:"description,omitempty"` @@ -212,15 +135,21 @@ type ( // getChat. InviteLink string `json:"invite_link,omitempty"` - // Pinned message, for supergroups. Returned only in getChat. - PinnedMessage *Message `json:"pinned_message,omitempty"` - // For supergroups, name of Group sticker set. Returned only in getChat. StickerSetName string `json:"sticker_set_name,omitempty"` + // True if a group has ‘All Members Are Admins’ enabled. + AllMembersAreAdministrators bool `json:"all_members_are_administrators,omitempty"` + // True, if the bot can change group the sticker set. Returned only in // getChat. CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` + + // Chat photo. Returned only in getChat. + Photo *ChatPhoto `json:"photo,omitempty"` + + // Pinned message, for supergroups. Returned only in getChat. + PinnedMessage *Message `json:"pinned_message,omitempty"` } // Message represents a message. @@ -228,42 +157,49 @@ type ( // Unique message identifier inside this chat ID int `json:"message_id"` - // Sender, empty for messages sent to channels - From *User `json:"from,omitempty"` - - // Date the message was sent in Unix time - Date int64 `json:"date"` - - // Conversation the message belongs to - Chat *Chat `json:"chat"` - - // For forwarded messages, sender of the original message - ForwardFrom *User `json:"forward_from,omitempty"` - - // For messages forwarded from channels, information about the original - // channel - ForwardFromChat *Chat `json:"forward_from_chat,omitempty"` - // For messages forwarded from channels, identifier of the original // message in the channel ForwardFromMessageID int `json:"forward_from_message_id,omitempty"` - // For messages forwarded from channels, signature of the post author if - // present - ForwardSignature string `json:"forward_signature,omitempty"` + // Sender, empty for messages sent to channels + From *User `json:"from,omitempty"` + + // For forwarded messages, sender of the original message + ForwardFrom *User `json:"forward_from,omitempty"` + + // A member was removed from the group, information about them (this + // member may be the bot itself) + LeftChatMember *User `json:"left_chat_member,omitempty"` + + // Date the message was sent in Unix time + Date int64 `json:"date"` // For forwarded messages, date the original message was sent in Unix // time ForwardDate int64 `json:"forward_date,omitempty"` - // For replies, the original message. Note that the Message object in - // this field will not contain further reply_to_message fields even if it - // itself is a reply. - ReplyToMessage *Message `json:"reply_to_message,omitempty"` - // Date the message was last edited in Unix time EditDate int64 `json:"edit_date,omitempty"` + // The group has been migrated to a supergroup with the specified + // identifier. + MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` + + // The supergroup has been migrated from a group with the specified + // identifier. + MigrateFromChatID int64 `json:"migrate_from_chat_id,omitempty"` + + // Conversation the message belongs to + Chat *Chat `json:"chat"` + + // For messages forwarded from channels, information about the original + // channel + ForwardFromChat *Chat `json:"forward_from_chat,omitempty"` + + // For messages forwarded from channels, signature of the post author if + // present + ForwardSignature string `json:"forward_signature,omitempty"` + // The unique identifier of a media message group this message belongs to MediaGroupID string `json:"media_group_id,omitempty"` @@ -274,6 +210,25 @@ type ( // characters. Text string `json:"text,omitempty"` + // Caption for the document, photo or video, 0-200 characters + Caption string `json:"caption,omitempty"` + + // A chat title was changed to this value + NewChatTitle string `json:"new_chat_title,omitempty"` + + // The domain name of the website on which the user has logged in. + ConnectedWebsite string `json:"connected_website,omitempty"` + + // For replies, the original message. Note that the Message object in + // this field will not contain further reply_to_message fields even if it + // itself is a reply. + ReplyToMessage *Message `json:"reply_to_message,omitempty"` + + // Specified message was pinned. Note that the Message object in this + // field will not contain further reply_to_message fields even if it is + // itself a reply. + PinnedMessage *Message `json:"pinned_message,omitempty"` + // For text messages, special entities like usernames, URLs, bot // commands, etc. that appear in the text Entities []MessageEntity `json:"entities,omitempty"` @@ -294,6 +249,9 @@ type ( // Message is a photo, available sizes of the photo Photo []PhotoSize `json:"photo,omitempty"` + // A chat photo was change to this value + NewChatPhoto []PhotoSize `json:"new_chat_photo,omitempty"` + // Message is a sticker, information about the sticker Sticker *Sticker `json:"sticker,omitempty"` @@ -306,9 +264,6 @@ type ( // Message is a video note, information about the video message VideoNote *VideoNote `json:"video_note,omitempty"` - // Caption for the document, photo or video, 0-200 characters - Caption string `json:"caption,omitempty"` - // Message is a shared contact, information about the contact Contact *Contact `json:"contact,omitempty"` @@ -322,16 +277,6 @@ type ( // about them (the bot itself may be one of these members) NewChatMembers []User `json:"new_chat_members,omitempty"` - // A member was removed from the group, information about them (this - // member may be the bot itself) - LeftChatMember *User `json:"left_chat_member,omitempty"` - - // A chat title was changed to this value - NewChatTitle string `json:"new_chat_title,omitempty"` - - // A chat photo was change to this value - NewChatPhoto []PhotoSize `json:"new_chat_photo,omitempty"` - // Service message: the chat photo was deleted DeleteChatPhoto bool `json:"delete_chat_photo,omitempty"` @@ -352,28 +297,12 @@ type ( // channel. ChannelChatCreated bool `json:"channel_chat_created,omitempty"` - // The group has been migrated to a supergroup with the specified - // identifier. - MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` - - // The supergroup has been migrated from a group with the specified - // identifier. - MigrateFromChatID int64 `json:"migrate_from_chat_id,omitempty"` - - // Specified message was pinned. Note that the Message object in this - // field will not contain further reply_to_message fields even if it is - // itself a reply. - PinnedMessage *Message `json:"pinned_message,omitempty"` - // Message is an invoice for a payment, information about the invoice. Invoice *Invoice `json:"invoice,omitempty"` // Message is a service message about a successful payment, information // about the payment. SuccessfulPayment *SuccessfulPayment `json:"successful_payment,omitempty"` - - // The domain name of the website on which the user has logged in. - ConnectedWebsite string `json:"connected_website,omitempty"` } // MessageEntity represents one special entity in a text message. For @@ -385,16 +314,16 @@ type ( // text_mention (for users without usernames) Type string `json:"type"` + // For "text_link" only, url that will be opened after user taps on the + // text + URL string `json:"url,omitempty"` + // Offset in UTF-16 code units to the start of the entity Offset int `json:"offset"` // Length of the entity in UTF-16 code units Length int `json:"length"` - // For "text_link" only, url that will be opened after user taps on the - // text - URL string `json:"url,omitempty"` - // For "text_mention" only, the mentioned user User *User `json:"user,omitempty"` } @@ -420,9 +349,6 @@ type ( // Unique identifier for this file FileID string `json:"file_id"` - // Duration of the audio in seconds as defined by sender - Duration int `json:"duration"` - // Performer of the audio as defined by sender or by audio tags Performer string `json:"performer,omitempty"` @@ -432,6 +358,9 @@ type ( // MIME type of the file as defined by sender MimeType string `json:"mime_type,omitempty"` + // Duration of the audio in seconds as defined by sender + Duration int `json:"duration"` + // File size FileSize int `json:"file_size,omitempty"` } @@ -442,15 +371,15 @@ type ( // Unique file identifier FileID string `json:"file_id"` - // Document thumbnail as defined by sender - Thumb *PhotoSize `json:"thumb,omitempty"` - // Original filename as defined by sender FileName string `json:"file_name,omitempty"` // MIME type of the file as defined by sender MimeType string `json:"mime_type,omitempty"` + // Document thumbnail as defined by sender + Thumb *PhotoSize `json:"thumb,omitempty"` + // File size FileSize int `json:"file_size,omitempty"` } @@ -460,6 +389,9 @@ type ( // Unique identifier for this file FileID string `json:"file_id"` + // Mime type of a file as defined by sender + MimeType string `json:"mime_type,omitempty"` + // Video width as defined by sender Width int `json:"width"` @@ -469,14 +401,11 @@ type ( // Duration of the video in seconds as defined by sender Duration int `json:"duration"` - // Video thumbnail - Thumb *PhotoSize `json:"thumb,omitempty"` - - // Mime type of a file as defined by sender - MimeType string `json:"mime_type,omitempty"` - // File size FileSize int `json:"file_size,omitempty"` + + // Video thumbnail + Thumb *PhotoSize `json:"thumb,omitempty"` } // Voice represents a voice note. @@ -484,12 +413,12 @@ type ( // Unique identifier for this file FileID string `json:"file_id"` - // Duration of the audio in seconds as defined by sender - Duration int `json:"duration"` - // MIME type of the file as defined by sender MimeType string `json:"mime_type,omitempty"` + // Duration of the audio in seconds as defined by sender + Duration int `json:"duration"` + // File size FileSize int `json:"file_size,omitempty"` } @@ -506,11 +435,11 @@ type ( // Duration of the video in seconds as defined by sender Duration int `json:"duration"` - // Video thumbnail - Thumb *PhotoSize `json:"thumb,omitempty"` - // File size FileSize int `json:"file_size,omitempty"` + + // Video thumbnail + Thumb *PhotoSize `json:"thumb,omitempty"` } // Contact represents a phone contact. @@ -571,12 +500,12 @@ type ( // Unique identifier for this file FileID string `json:"file_id"` - // File size, if known - FileSize int `json:"file_size,omitempty"` - // File path. Use https://api.telegram.org/file/bot/ to // get the file. FilePath string `json:"file_path,omitempty"` + + // File size, if known + FileSize int `json:"file_size,omitempty"` } // ReplyKeyboardMarkup represents a custom keyboard with reply options (see @@ -720,14 +649,6 @@ type ( // Unique identifier for this query ID string `json:"id"` - // Sender - From *User `json:"from"` - - // Message with the callback button that originated the query. Note that - // message content and message date will not be available if the message - // is too old - Message *Message `json:"message,omitempty"` - // Identifier of the message sent via the bot in inline mode, that // originated the query. InlineMessageID string `json:"inline_message_id,omitempty"` @@ -744,6 +665,14 @@ type ( // Short name of a Game to be returned, serves as the unique identifier // for the game GameShortName string `json:"game_short_name,omitempty"` + + // Sender + From *User `json:"from"` + + // Message with the callback button that originated the query. Note that + // message content and message date will not be available if the message + // is too old + Message *Message `json:"message,omitempty"` } // ForceReply display a reply interface to the user (act as if the user has @@ -922,26 +851,26 @@ type ( // Unique identifier for this file FileID string `json:"file_id"` - // Sticker width - Width int `json:"width"` - - // Sticker height - Height int `json:"height"` - - // Sticker thumbnail in the .webp or .jpg format - Thumb *PhotoSize `json:"thumb,omitempty"` - // Emoji associated with the sticker Emoji string `json:"emoji,omitempty"` // Name of the sticker set to which the sticker belongs SetName string `json:"set_name,omitempty"` - // For mask stickers, the position where the mask should be placed - MaskPosition *MaskPosition `json:"mask_position,omitempty"` + // Sticker width + Width int `json:"width"` + + // Sticker height + Height int `json:"height"` // File size FileSize int `json:"file_size,omitempty"` + + // Sticker thumbnail in the .webp or .jpg format + Thumb *PhotoSize `json:"thumb,omitempty"` + + // For mask stickers, the position where the mask should be placed + MaskPosition *MaskPosition `json:"mask_position,omitempty"` } // StickerSet represents a sticker set. @@ -986,17 +915,17 @@ type ( // Unique identifier for this query ID string `json:"id"` - // Sender - From *User `json:"from"` - - // Sender location, only for bots that request user location - Location *Location `json:"location,omitempty"` - // Text of the query (up to 512 characters) Query string `json:"query"` // Offset of the results to be returned, can be controlled by the bot Offset string `json:"offset"` + + // Sender + From *User `json:"from"` + + // Sender location, only for bots that request user location + Location *Location `json:"location,omitempty"` } // InlineQueryResult represents one result of an inline query. @@ -1034,24 +963,24 @@ type ( //Title of the result Title string `json:"title"` - // Content of the message to be sent - InputMessageContent interface{} `json:"input_message_content"` - - // Inline keyboard attached to the message - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - // URL of the result URL string `json:"url,omitempty"` - // Pass True, if you don't want the URL to be shown in the message - HideURL bool `json:"hide_url,omitempty"` - // Short description of the result Description string `json:"description,omitempty"` // Url of the thumbnail for the result ThumbURL string `json:"thumb_url,omitempty"` + // Content of the message to be sent + InputMessageContent interface{} `json:"input_message_content"` + + // Inline keyboard attached to the message + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + + // Pass True, if you don't want the URL to be shown in the message + HideURL bool `json:"hide_url,omitempty"` + // Thumbnail width ThumbWidth int `json:"thumb_width,omitempty"` @@ -1077,12 +1006,6 @@ type ( // URL of the thumbnail for the photo ThumbURL string `json:"thumb_url"` - // Width of the photo - PhotoWidth int `json:"photo_width,omitempty"` - - // Height of the photo - PhotoHeight int `json:"photo_height,omitempty"` - // Title for the result Title string `json:"title,omitempty"` @@ -1096,6 +1019,12 @@ type ( // fixed-width text or inline URLs in the media caption. ParseMode string `json:"parse_mode,omitempty"` + // Width of the photo + PhotoWidth int `json:"photo_width,omitempty"` + + // Height of the photo + PhotoHeight int `json:"photo_height,omitempty"` + // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` @@ -1117,15 +1046,6 @@ type ( // A valid URL for the GIF file. File size must not exceed 1MB GifURL string `json:"gif_url"` - // Width of the GIF - GifWidth int `json:"gif_width,omitempty"` - - // Height of the GIF - GifHeight int `json:"gif_height,omitempty"` - - // Duration of the GIF - GifDuration int `json:"gif_duration,omitempty"` - // URL of the static thumbnail for the result (jpeg or gif) ThumbURL string `json:"thumb_url"` @@ -1139,6 +1059,15 @@ type ( // fixed-width text or inline URLs in the media caption. ParseMode string `json:"parse_mode,omitempty"` + // Width of the GIF + GifWidth int `json:"gif_width,omitempty"` + + // Height of the GIF + GifHeight int `json:"gif_height,omitempty"` + + // Duration of the GIF + GifDuration int `json:"gif_duration,omitempty"` + // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` @@ -1161,15 +1090,6 @@ type ( // A valid URL for the MP4 file. File size must not exceed 1MB Mpeg4URL string `json:"mpeg4_url"` - // Video width - Mpeg4Width int `json:"mpeg4_width,omitempty"` - - // Video height - Mpeg4Height int `json:"mpeg4_height,omitempty"` - - // Video duration - Mpeg4Duration int `json:"mpeg4_duration,omitempty"` - // URL of the static thumbnail (jpeg or gif) for the result ThumbURL string `json:"thumb_url"` @@ -1179,6 +1099,15 @@ type ( // Caption of the MPEG-4 file to be sent, 0-200 characters Caption string `json:"caption,omitempty"` + // Video width + Mpeg4Width int `json:"mpeg4_width,omitempty"` + + // Video height + Mpeg4Height int `json:"mpeg4_height,omitempty"` + + // Video duration + Mpeg4Duration int `json:"mpeg4_duration,omitempty"` + // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` @@ -1220,6 +1149,9 @@ type ( // fixed-width text or inline URLs in the media caption. ParseMode string `json:"parse_mode,omitempty"` + // Short description of the result + Description string `json:"description,omitempty"` + // Video width VideoWidth int `json:"video_width,omitempty"` @@ -1229,9 +1161,6 @@ type ( // Video duration in seconds VideoDuration int `json:"video_duration,omitempty"` - // Short description of the result - Description string `json:"description,omitempty"` - // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` @@ -1344,15 +1273,15 @@ type ( // Short description of the result Description string `json:"description,omitempty"` + // URL of the thumbnail (jpeg only) for the file + ThumbURL string `json:"thumb_url,omitempty"` + // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` // Content of the message to be sent instead of the file InputMessageContent interface{} `json:"input_message_content,omitempty"` - // URL of the thumbnail (jpeg only) for the file - ThumbURL string `json:"thumb_url,omitempty"` - // Thumbnail width ThumbWidth int `json:"thumb_width,omitempty"` @@ -1371,24 +1300,24 @@ type ( // Unique identifier for this result, 1-64 Bytes ID string `json:"id"` + // Location title + Title string `json:"title"` + + //Url of the thumbnail for the result + ThumbURL string `json:"thumb_url,omitempty"` + // Location latitude in degrees Latitude float32 `json:"latitude"` // Location longitude in degrees Longitude float32 `json:"longitude"` - // Location title - Title string `json:"title"` - //Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` //Content of the message to be sent instead of the location InputMessageContent interface{} `json:"input_message_content,omitempty"` - //Url of the thumbnail for the result - ThumbURL string `json:"thumb_url,omitempty"` - //Thumbnail width ThumbWidth int `json:"thumb_width,omitempty"` @@ -1406,12 +1335,6 @@ type ( // Unique identifier for this result, 1-64 Bytes ID string `json:"id"` - // Latitude of the venue location in degrees - Latitude float32 `json:"latitude"` - - // Longitude of the venue location in degrees - Longitude float32 `json:"longitude"` - // Title of the venue Title string `json:"title"` @@ -1421,15 +1344,21 @@ type ( // Foursquare identifier of the venue if known FoursquareID string `json:"foursquare_id,omitempty"` + // Url of the thumbnail for the result + ThumbURL string `json:"thumb_url,omitempty"` + + // Latitude of the venue location in degrees + Latitude float32 `json:"latitude"` + + // Longitude of the venue location in degrees + Longitude float32 `json:"longitude"` + // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` // Content of the message to be sent instead of the venue InputMessageContent interface{} `json:"input_message_content,omitempty"` - // Url of the thumbnail for the result - ThumbURL string `json:"thumb_url,omitempty"` - // Thumbnail width ThumbWidth int `json:"thumb_width,omitempty"` @@ -1457,15 +1386,15 @@ type ( // Contact's last name LastName string `json:"last_name,omitempty"` + // Url of the thumbnail for the result + ThumbURL string `json:"thumb_url,omitempty"` + // Inline keyboard attached to the message ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` // Content of the message to be sent instead of the contact InputMessageContent interface{} `json:"input_message_content,omitempty"` - // Url of the thumbnail for the result - ThumbURL string `json:"thumb_url,omitempty"` - // Thumbnail width ThumbWidth int `json:"thumb_width,omitempty"` @@ -1806,12 +1735,6 @@ type ( // The unique identifier for the result that was chosen ResultID string `json:"result_id"` - // The user that chose the result - From *User `json:"from"` - - // Sender location, only for bots that require user location - Location *Location `json:"location,omitempty"` - // Identifier of the sent inline message. Available only if there is an // inline keyboard attached to the message. Will be also received in // callback queries and can be used to edit the message. @@ -1819,6 +1742,12 @@ type ( // The query that was used to obtain the result Query string `json:"query"` + + // The user that chose the result + From *User `json:"from"` + + // Sender location, only for bots that require user location + Location *Location `json:"location,omitempty"` } // LabeledPrice represents a portion of the price for goods or services. @@ -1910,6 +1839,18 @@ type ( // Three-letter ISO 4217 currency code Currency string `json:"currency"` + // Bot specified invoice payload + InvoicePayload string `json:"invoice_payload"` + + // Identifier of the shipping option chosen by the user + ShippingOptionID string `json:"shipping_option_id,omitempty"` + + // Telegram payment identifier + TelegramPaymentChargeID string `json:"telegram_payment_charge_id"` + + // Provider payment identifier + ProviderPaymentChargeID string `json:"provider_payment_charge_id"` + // Total price in the smallest units of the currency (integer, not // float/double). For example, for a price of US$ 1.45 pass amount = 145. // See the exp parameter in currencies.json, it shows the number of @@ -1917,20 +1858,8 @@ type ( // of currencies). TotalAmount int `json:"total_amount"` - // Bot specified invoice payload - InvoicePayload string `json:"invoice_payload"` - - // Identifier of the shipping option chosen by the user - ShippingOptionID string `json:"shipping_option_id,omitempty"` - // Order info provided by the user OrderInfo *OrderInfo `json:"order_info,omitempty"` - - // Telegram payment identifier - TelegramPaymentChargeID string `json:"telegram_payment_charge_id"` - - // Provider payment identifier - ProviderPaymentChargeID string `json:"provider_payment_charge_id"` } // ShippingQuery contains information about an incoming shipping query. @@ -1938,12 +1867,12 @@ type ( // Unique query identifier ID string `json:"id"` - // User who sent the query - From *User `json:"from"` - // Bot specified invoice payload InvoicePayload string `json:"invoice_payload"` + // User who sent the query + From *User `json:"from"` + // User specified shipping address ShippingAddress *ShippingAddress `json:"shipping_address"` } @@ -1953,12 +1882,18 @@ type ( // Unique query identifier ID string `json:"id"` - // User who sent the query - From *User `json:"from"` - // Three-letter ISO 4217 currency code Currency string `json:"currency"` + // Bot specified invoice payload + InvoicePayload string `json:"invoice_payload"` + + // Identifier of the shipping option chosen by the user + ShippingOptionID string `json:"shipping_option_id,omitempty"` + + // User who sent the query + From *User `json:"from"` + // Total price in the smallest units of the currency (integer, not // float/double). For example, for a price of US$ 1.45 pass amount = 145. // See the exp parameter in currencies.json, it shows the number of @@ -1966,12 +1901,6 @@ type ( // currencies). TotalAmount int `json:"total_amount"` - // Bot specified invoice payload - InvoicePayload string `json:"invoice_payload"` - - // Identifier of the shipping option chosen by the user - ShippingOptionID string `json:"shipping_option_id,omitempty"` - // Order info provided by the user OrderInfo *OrderInfo `json:"order_info,omitempty"` } @@ -1985,15 +1914,15 @@ type ( // Description of the game Description string `json:"description"` - // Photo that will be displayed in the game message in chats. - Photo []PhotoSize `json:"photo"` - // Brief description of the game or high scores included in the game // message. Can be automatically edited to include current high scores // for the game when the bot calls setGameScore, or manually edited // using editMessageText. 0-4096 characters. Text string `json:"text,omitempty"` + // Photo that will be displayed in the game message in chats. + Photo []PhotoSize `json:"photo"` + // Special entities that appear in text, such as usernames, URLs, bot // commands, etc. TextEntities []MessageEntity `json:"text_entities,omitempty"` @@ -2010,15 +1939,15 @@ type ( // Unique file identifier FileID string `json:"file_id"` - // Animation thumbnail as defined by sender - Thumb *PhotoSize `json:"thumb,omitempty"` - // Original animation filename as defined by sender FileName string `json:"file_name,omitempty"` // MIME type of the file as defined by sender MimeType string `json:"mime_type,omitempty"` + // Animation thumbnail as defined by sender + Thumb *PhotoSize `json:"thumb,omitempty"` + // File size FileSize int `json:"file_size,omitempty"` } @@ -2032,11 +1961,11 @@ type ( // Position in high score table for the game Position int `json:"position"` - // User - User *User `json:"user"` - // Score Score int `json:"score"` + + // User + User *User `json:"user"` } // ReplyMarkup is a JSON-serialized object for an inline keyboard, custom diff --git a/new_bot.go b/new_bot.go deleted file mode 100644 index 3fb4804..0000000 --- a/new_bot.go +++ /dev/null @@ -1,14 +0,0 @@ -package telegram - -type Bot struct { - AccessToken string - Self *User -} - -func NewBot(accessToken string) (*Bot, error) { - var err error - bot := &Bot{AccessToken: accessToken} - - bot.Self, err = bot.GetMe() - return bot, err -} diff --git a/pin_chat_message.go b/pin_chat_message.go index 0c81665..2283584 100644 --- a/pin_chat_message.go +++ b/pin_chat_message.go @@ -19,7 +19,7 @@ func (bot *Bot) PinChatMessage(params *PinChatMessageParameters) (bool, error) { return false, err } - resp, err := bot.request(dst, "pinChatMessage") + resp, err := bot.request(dst, MethodPinChatMessage) if err != nil { return false, err } diff --git a/send_chat_action.go b/send_chat_action.go index f0bc1a0..985615a 100644 --- a/send_chat_action.go +++ b/send_chat_action.go @@ -24,7 +24,7 @@ func (bot *Bot) SendChatAction(chatID int64, action string) (bool, error) { return false, err } - resp, err := bot.request(dst, "sendChatAction") + resp, err := bot.request(dst, MethodSendChatAction) if err != nil { return false, err } diff --git a/send_contact.go b/send_contact.go index 8d93df6..b917f7b 100644 --- a/send_contact.go +++ b/send_contact.go @@ -43,7 +43,7 @@ func (bot *Bot) SendContact(params *SendContactParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "sendContact") + resp, err := bot.request(dst, MethodSendContact) if err != nil { return nil, err } diff --git a/send_document.go b/send_document.go index 0c54119..0ff2d60 100644 --- a/send_document.go +++ b/send_document.go @@ -66,7 +66,7 @@ func (bot *Bot) SendDocument(params *SendDocumentParameters) (*Message, error) { args.Add("disable_notification", strconv.FormatBool(params.DisableNotification)) - resp, err := bot.upload(params.Document, "document", "", "sendDocument", args) + resp, err := bot.Upload(MethodSendDocument, "document", "", params.Document, args) if err != nil { return nil, err } diff --git a/send_game.go b/send_game.go index 8439867..beaedb0 100644 --- a/send_game.go +++ b/send_game.go @@ -37,7 +37,7 @@ func (bot *Bot) SendGame(params *SendGameParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "sendGame") + resp, err := bot.request(dst, MethodSendGame) if err != nil { return nil, err } diff --git a/send_invoice.go b/send_invoice.go index 6780beb..6b930e7 100644 --- a/send_invoice.go +++ b/send_invoice.go @@ -26,10 +26,6 @@ type SendInvoiceParameters struct { // Three-letter ISO 4217 currency code, see more on currencies Currency string `json:"currency"` - // Price breakdown, a list of components (e.g. product price, tax, discount, - // delivery cost, delivery tax, bonus, etc.) - Prices []LabeledPrice `json:"prices"` - // JSON-encoded data about the invoice, which will be shared with the payment // provider. A detailed description of required fields should be provided by // the payment provider. @@ -40,6 +36,10 @@ type SendInvoiceParameters struct { // they are paying for. PhotoURL string `json:"photo_url,omitempty"` + // Price breakdown, a list of components (e.g. product price, tax, discount, + // delivery cost, delivery tax, bonus, etc.) + Prices []LabeledPrice `json:"prices"` + // Photo size PhotoSize int `json:"photo_size,omitempty"` @@ -49,6 +49,9 @@ type SendInvoiceParameters struct { // Photo height PhotoHeight int `json:"photo_height,omitempty"` + // If the message is a reply, ID of the original message + ReplyToMessageID int `json:"reply_to_message_id,omitempty"` + // Pass True, if you require the user's full name to complete the order NeedName bool `json:"need_name,omitempty"` @@ -69,9 +72,6 @@ type SendInvoiceParameters struct { // sound. DisableNotification bool `json:"disable_notification,omitempty"` - // If the message is a reply, ID of the original message - ReplyToMessageID int `json:"reply_to_message_id,omitempty"` - // A JSON-serialized object for an inline keyboard. If empty, one 'Pay total // price' button will be shown. If not empty, the first button must be a Pay // button. @@ -98,7 +98,7 @@ func (bot *Bot) SendInvoice(params *SendInvoiceParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "sendInvoice") + resp, err := bot.request(dst, MethodSendInvoice) if err != nil { return nil, err } diff --git a/send_location.go b/send_location.go index a38662c..3638f96 100644 --- a/send_location.go +++ b/send_location.go @@ -16,13 +16,13 @@ type SendLocationParameters struct { // Locations), should be between 60 and 86400. LivePeriod int `json:"live_period,omitempty"` + // If the message is a reply, ID of the original message + ReplyToMessageID int `json:"reply_to_message_id,omitempty"` + // Sends the message silently. Users will receive a notification with no // sound. DisableNotification bool `json:"disable_notification,omitempty"` - // If the message is a reply, ID of the original message - ReplyToMessageID int `json:"reply_to_message_id,omitempty"` - // A JSON-serialized object for an inline keyboard. If empty, one 'Pay total // price' button will be shown. If not empty, the first button must be a Pay // button. @@ -44,7 +44,7 @@ func (bot *Bot) SendLocation(params *SendLocationParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "sendLocation") + resp, err := bot.request(dst, MethodSendLocation) if err != nil { return nil, err } diff --git a/send_media_group.go b/send_media_group.go index f26ad80..846b6ea 100644 --- a/send_media_group.go +++ b/send_media_group.go @@ -33,7 +33,7 @@ func (bot *Bot) SendMediaGroup(params *SendMediaGroupParameters) ([]Message, err return nil, err } - resp, err := bot.request(dst, "sendMediaGroup") + resp, err := bot.request(dst, MethodSendMediaGroup) if err != nil { return nil, err } diff --git a/send_message.go b/send_message.go index cd78910..58677f7 100644 --- a/send_message.go +++ b/send_message.go @@ -44,7 +44,7 @@ func (bot *Bot) SendMessage(params *SendMessageParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "sendMessage") + resp, err := bot.request(dst, MethodSendMessage) if err != nil { return nil, err } diff --git a/send_photo.go b/send_photo.go index f3c4f28..9992131 100644 --- a/send_photo.go +++ b/send_photo.go @@ -72,7 +72,7 @@ func (bot *Bot) SendPhoto(params *SendPhotoParameters) (*Message, error) { args.Add("disable_notification", strconv.FormatBool(params.DisableNotification)) - resp, err := bot.upload(params.Photo, "photo", "", "sendPhoto", args) + resp, err := bot.Upload(MethodSendPhoto, "photo", "", params.Photo, args) if err != nil { return nil, err } diff --git a/send_venue.go b/send_venue.go index fb542c0..8273736 100644 --- a/send_venue.go +++ b/send_venue.go @@ -51,7 +51,7 @@ func (bot *Bot) SendVenue(params *SendVenueParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "sendVenue") + resp, err := bot.request(dst, MethodSendVenue) if err != nil { return nil, err } diff --git a/set_chat_description.go b/set_chat_description.go index be25cdd..5e938d4 100644 --- a/set_chat_description.go +++ b/set_chat_description.go @@ -21,7 +21,7 @@ func (bot *Bot) SetChatDescription(chatID int64, description string) (bool, erro return false, err } - resp, err := bot.request(dst, "setChatDescription") + resp, err := bot.request(dst, MethodSetChatDescription) if err != nil { return false, err } diff --git a/set_chat_photo.go b/set_chat_photo.go index 2050296..c98e341 100644 --- a/set_chat_photo.go +++ b/set_chat_photo.go @@ -25,7 +25,7 @@ func (bot *Bot) SetChatPhoto(chatID int64, chatPhoto interface{}) (bool, error) defer http.ReleaseArgs(args) args.Add("chat_id", strconv.FormatInt(chatID, 10)) - resp, err := bot.upload(chatPhoto, TypePhoto, "chat_photo", "setChatPhoto", args) + resp, err := bot.Upload(MethodSetChatPhoto, TypePhoto, "chat_photo", chatPhoto, args) if err != nil { return false, err } diff --git a/set_chat_sticker_set.go b/set_chat_sticker_set.go index 3ff1f7d..cb08b7a 100644 --- a/set_chat_sticker_set.go +++ b/set_chat_sticker_set.go @@ -22,7 +22,7 @@ func (bot *Bot) SetChatStickerSet(chatID int64, stickerSetName string) (bool, er return false, err } - resp, err := bot.request(dst, "setChatStickerSet") + resp, err := bot.request(dst, MethodSetChatStickerSet) if err != nil { return false, err } diff --git a/set_chat_title.go b/set_chat_title.go index 0abddc9..7b75d34 100644 --- a/set_chat_title.go +++ b/set_chat_title.go @@ -24,7 +24,7 @@ func (bot *Bot) SetChatTitle(chatID int64, title string) (bool, error) { return false, err } - resp, err := bot.request(dst, "setChatTitle") + resp, err := bot.request(dst, MethodSetChatTitle) if err != nil { return false, err } diff --git a/set_game_score.go b/set_game_score.go index eed9f0b..25ae59e 100644 --- a/set_game_score.go +++ b/set_game_score.go @@ -9,6 +9,10 @@ type SetGameScoreParameters struct { // New score, must be non-negative Score int `json:"score"` + // Required if inline_message_id is not specified. Identifier of the sent + // message + MessageID int `json:"message_id,omitempty"` + // Pass True, if the high score is allowed to decrease. This can be useful // when fixing mistakes or banning cheaters Force bool `json:"force,omitempty"` @@ -21,10 +25,6 @@ type SetGameScoreParameters struct { // target chat ChatID int64 `json:"chat_id,omitempty"` - // Required if inline_message_id is not specified. Identifier of the sent - // message - MessageID int `json:"message_id,omitempty"` - // Required if chat_id and message_id are not specified. Identifier of the // inline message InlineMessageID string `json:"inline_message_id,omitempty"` @@ -47,7 +47,7 @@ func (bot *Bot) SetGameScore(params *SetGameScoreParameters) (*Message, error) { return nil, err } - resp, err := bot.request(dst, "setGameScore") + resp, err := bot.request(dst, MethodSetGameScore) if err != nil { return nil, err } diff --git a/set_sticker_position_in_set.go b/set_sticker_position_in_set.go index ea6db0c..f466004 100644 --- a/set_sticker_position_in_set.go +++ b/set_sticker_position_in_set.go @@ -18,7 +18,7 @@ func (bot *Bot) SetStickerPositionInSet(sticker string, position int) (bool, err return false, err } - resp, err := bot.request(dst, "setStickerPositionInSet") + resp, err := bot.request(dst, MethodSetStickerPositionInSet) if err != nil { return false, err } diff --git a/set_webhook.go b/set_webhook.go index d64ec06..d912613 100644 --- a/set_webhook.go +++ b/set_webhook.go @@ -8,8 +8,6 @@ import ( http "github.com/valyala/fasthttp" ) -const setWebhook = "setWebhook" - type SetWebhookParameters struct { // HTTPS url to send updates to. Use an empty string to remove webhook // integration @@ -72,16 +70,15 @@ func (bot *Bot) SetWebhook(params *SetWebhookParameters) (bool, error) { resp *Response ) if params.Certificate != nil { - resp, err = bot.upload( - params.Certificate, "certificate", "cert.pem", setWebhook, args, - ) + resp, err = bot.Upload(MethodSetWebhook, "certificate", "cert.pem", params.Certificate, args) } else { - dst, err := json.Marshal(params) + var dst []byte + dst, err = json.Marshal(params) if err != nil { return false, err } - resp, err = bot.request(dst, setWebhook) + resp, err = bot.request(dst, MethodSetWebhook) } if err != nil { return false, err diff --git a/test/get_test.go b/test/get_test.go index 6b1ca20..b543cee 100644 --- a/test/get_test.go +++ b/test/get_test.go @@ -8,12 +8,12 @@ import ( func TestGetMe(t *testing.T) { var err error - bot.Self, err = bot.GetMe() + bot.User, err = bot.GetMe() if err != nil { t.Error(err.Error()) t.FailNow() } - if bot.Self == nil { + if bot.User == nil { t.Error("unexpected result: bot user is nil") t.FailNow() } diff --git a/test/send_test.go b/test/send_test.go index bb4c10f..ae96f03 100644 --- a/test/send_test.go +++ b/test/send_test.go @@ -8,13 +8,13 @@ import ( ) const ( - photoFileID = "AgADAgADw6cxG4zHKAkr42N7RwEN3IFShCoABHQwXEtVks4EH2wBAAEC" - documentFileID = "BQADAgADOQADjMcoCcioX1GrDvp3Ag" - audioFileID = "BQADAgADRgADjMcoCdXg3lSIN49lAg" - voiceFileID = "AwADAgADWQADjMcoCeul6r_q52IyAg" - videoFileID = "BAADAgADZgADjMcoCav432kYe0FRAg" - videoNoteFileID = "DQADAgADdQAD70cQSUK41dLsRMqfAg" - stickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg" + photoFileID = "AgADAgADw6cxG4zHKAkr42N7RwEN3IFShCoABHQwXEtVks4EH2wBAAEC" + documentFileID = "BQADAgADOQADjMcoCcioX1GrDvp3Ag" + // audioFileID = "BQADAgADRgADjMcoCdXg3lSIN49lAg" + // voiceFileID = "AwADAgADWQADjMcoCeul6r_q52IyAg" + // videoFileID = "BAADAgADZgADjMcoCav432kYe0FRAg" + // videoNoteFileID = "DQADAgADdQAD70cQSUK41dLsRMqfAg" + // stickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg" ) var ( diff --git a/unban_chat_member.go b/unban_chat_member.go index 59d7702..52d6a85 100644 --- a/unban_chat_member.go +++ b/unban_chat_member.go @@ -22,7 +22,7 @@ func (bot *Bot) UnbanChatMember(chatID int64, userID int) (bool, error) { return false, err } - resp, err := bot.request(dst, "unbanChatMember") + resp, err := bot.request(dst, MethodUnbanChatMember) if err != nil { return false, err } diff --git a/unpin_chat_message.go b/unpin_chat_message.go index f0d35fa..a59398f 100644 --- a/unpin_chat_message.go +++ b/unpin_chat_message.go @@ -16,7 +16,7 @@ func (bot *Bot) UnpinChatMessage(chatID int64) (bool, error) { return false, err } - resp, err := bot.request(dst, "unpinChatMessage") + resp, err := bot.request(dst, MethodUnpinChatMessage) if err != nil { return false, err } diff --git a/upload.go b/upload.go index 4ebae72..2dd54c8 100644 --- a/upload.go +++ b/upload.go @@ -17,7 +17,7 @@ import ( var ErrBadFileType = errors.New("bad file type") /* -upload is a helper method which provide are three ways to send files (photos, stickers, audio, +Upload is a helper method which provide are three ways to send files (photos, stickers, audio, media, etc.): 1. If the file is already stored somewhere on the Telegram servers, you don't need to reupload it: @@ -45,7 +45,7 @@ sendAudio, etc.). voice notes will be sent as files. - Other configurations may work but we can't guarantee that they will. */ -func (bot *Bot) upload(file InputFile, fieldName, fileName, method string, args *http.Args) (*Response, error) { +func (bot *Bot) Upload(method, key, name string, file InputFile, args fmt.Stringer) (*Response, error) { buffer := bytes.NewBuffer(nil) multi := multipart.NewWriter(buffer) @@ -66,49 +66,21 @@ func (bot *Bot) upload(file InputFile, fieldName, fileName, method string, args } } - switch f := file.(type) { + switch src := file.(type) { case string: - if _, err = os.Stat(f); os.IsNotExist(err) { - // Send by 'file_id' - if err = multi.WriteField(fieldName, f); err != nil { - return nil, err - } - } else { - // Upload new - src, err := os.Open(f) - if err != nil { - return nil, err - } - defer src.Close() - - formFile, err := multi.CreateFormFile(fieldName, src.Name()) - if err != nil { - return nil, err - } - if _, err = io.Copy(formFile, src); err != nil { - return nil, err - } - } - case []byte: // Upload new - formFile, err := multi.CreateFormFile(fieldName, fileName) - if err != nil { - return nil, err - } - - if _, err = io.Copy(formFile, bytes.NewReader(f)); err != nil { - return nil, err - } + err = uploadByString(multi, key, src) case *url.URL: // Send by URL - if err = multi.WriteField(fieldName, f.String()); err != nil { - return nil, err - } + err = uploadFromURL(multi, key, src) + case []byte: // Upload new + err = uploadFromMemory(multi, key, name, bytes.NewReader(src)) case io.Reader: // Upload new - if _, err = multi.CreateFormFile(fieldName, fileName); err != nil { - return nil, err - } + err = uploadFromMemory(multi, key, name, src) default: return nil, ErrBadFileType } + if err != nil { + return nil, err + } if err = multi.Close(); err != nil { return nil, err @@ -149,3 +121,51 @@ func (bot *Bot) upload(file InputFile, fieldName, fileName, method string, args return &data, nil } + +func uploadByString(w *multipart.Writer, key, src string) error { + _, err := os.Stat(src) + switch { + case os.IsNotExist(err): + err = uploadFromFileID(w, key, src) + case os.IsExist(err): + err = uploadFromDisk(w, key, src) + } + return err +} + +func uploadFromFileID(w *multipart.Writer, key, src string) error { + return w.WriteField(key, src) +} + +func uploadFromDisk(w *multipart.Writer, key, src string) error { + file, err := os.Open(src) + if err != nil { + return err + } + defer func() { + _ = file.Close() + }() + + var formFile io.Writer + formFile, err = w.CreateFormFile(key, file.Name()) + if err != nil { + return err + } + + _, err = io.Copy(formFile, file) + return err +} + +func uploadFromURL(w *multipart.Writer, key string, src *url.URL) error { + return w.WriteField(key, src.String()) +} + +func uploadFromMemory(w *multipart.Writer, key, value string, src io.Reader) error { + field, err := w.CreateFormFile(key, value) + if err != nil { + return err + } + + _, err = io.Copy(field, src) + return err +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..6b27183 --- /dev/null +++ b/utils.go @@ -0,0 +1,23 @@ +package telegram + +import ( + "net/url" + "strconv" +) + +func NewForceReply() *ForceReply { + return &ForceReply{ForceReply: true} +} + +func NewInlineMentionURL(userID int) *url.URL { + link := &url.URL{ + Scheme: SchemeTelegram, + Path: "user", + } + + q := link.Query() + q.Add("id", strconv.Itoa(userID)) + link.RawQuery = q.Encode() + + return link +} diff --git a/helpers_bot.go b/utils_bot.go similarity index 81% rename from helpers_bot.go rename to utils_bot.go index 492e334..e2de5c2 100644 --- a/helpers_bot.go +++ b/utils_bot.go @@ -6,16 +6,29 @@ import ( "strings" ) +type Bot struct { + AccessToken string + *User +} + +func New(accessToken string) (*Bot, error) { + var err error + bot := &Bot{AccessToken: accessToken} + + bot.User, err = bot.GetMe() + return bot, err +} + func (bot *Bot) IsMessageFromMe(msg *Message) bool { if msg == nil || bot == nil { return false } - if msg.From == nil || bot.Self == nil { + if msg.From == nil || bot.User == nil { return false } - return msg.From.ID == bot.Self.ID + return msg.From.ID == bot.ID } func (bot *Bot) IsForwardFromMe(msg *Message) bool { @@ -27,11 +40,11 @@ func (bot *Bot) IsForwardFromMe(msg *Message) bool { return false } - if bot.Self == nil { + if bot.User == nil { return false } - return msg.ForwardFrom.ID == bot.Self.ID + return msg.ForwardFrom.ID == bot.ID } func (bot *Bot) IsReplyToMe(msg *Message) bool { @@ -60,7 +73,7 @@ func (bot *Bot) IsCommandToMe(msg *Message) bool { return false } - return strings.ToLower(parts[1]) == strings.ToLower(bot.Self.Username) + return strings.ToLower(parts[1]) == strings.ToLower(bot.User.Username) } func (bot *Bot) IsMessageMentionsMe(msg *Message) bool { @@ -68,7 +81,7 @@ func (bot *Bot) IsMessageMentionsMe(msg *Message) bool { return false } - if bot.Self == nil { + if bot.User == nil { return false } @@ -86,7 +99,7 @@ func (bot *Bot) IsMessageMentionsMe(msg *Message) bool { for _, entity := range entities { if entity.IsMention() { - if bot.Self.ID == entity.User.ID { + if bot.ID == entity.User.ID { return true } } @@ -143,18 +156,18 @@ func (bot *Bot) NewRedirectURL(group bool, param string) *url.URL { return nil } - if bot.Self == nil { + if bot.User == nil { return nil } - if bot.Self.Username == "" { + if bot.User.Username == "" { return nil } link := &url.URL{ Scheme: "https", Host: "t.me", - Path: bot.Self.Username, + Path: bot.User.Username, } q := link.Query() diff --git a/helpers_chat.go b/utils_chat.go similarity index 51% rename from helpers_chat.go rename to utils_chat.go index 2369991..331137e 100644 --- a/helpers_chat.go +++ b/utils_chat.go @@ -1,5 +1,7 @@ package telegram +import "fmt" + func (chat *Chat) IsPrivate() bool { if chat == nil { return false @@ -39,3 +41,40 @@ func (chat *Chat) HasPinnedMessage() bool { return chat.PinnedMessage != nil } + +func (chat *Chat) HasStickerSet() bool { + if chat == nil { + return false + } + + return chat.StickerSetName != "" +} + +func (chat *Chat) StickerSet(bot *Bot) *StickerSet { + if !chat.HasStickerSet() { + return nil + } + + if bot == nil { + return nil + } + + set, err := bot.GetStickerSet(chat.StickerSetName) + if err != nil { + return nil + } + + return set +} + +func (chat *Chat) FullName() string { + if chat == nil { + return "" + } + + if chat.LastName != "" { + return fmt.Sprintln(chat.FirstName, chat.LastName) + } + + return chat.FirstName +} diff --git a/helpers_entity.go b/utils_entity.go similarity index 75% rename from helpers_entity.go rename to utils_entity.go index 3d6ad9e..d13516f 100644 --- a/helpers_entity.go +++ b/utils_entity.go @@ -10,28 +10,24 @@ func (entity *MessageEntity) ParseURL(messageText string) *url.URL { return nil } - var err error - link := new(url.URL) - switch { - case entity.IsTextLink(): - link, err = url.Parse(entity.URL) - case entity.IsURL(): - if messageText == "" { - return nil - } + if !entity.IsURL() { + return nil + } - rawMessageText := []rune(messageText) - if len(rawMessageText) < (entity.Offset + entity.Length) { - return nil - } + if messageText == "" { + return nil + } - from := entity.Offset - to := from + entity.Length - rawURL := string([]rune(messageText)[from:to]) - link, err = url.Parse(rawURL) - if err == nil && link.Scheme == "" { - link, err = url.Parse(fmt.Sprint("http://", link)) - } + from := entity.Offset + to := from + entity.Length + text := []rune(messageText) + if len(text) < to { + return nil + } + + link, err := url.Parse(string(text[from:to])) + if err == nil && link.Scheme == "" { + link, err = url.Parse(fmt.Sprint("http://", link)) } if err != nil { return nil @@ -127,3 +123,16 @@ func (entity *MessageEntity) IsURL() bool { return entity.Type == EntityURL } + +func (entity *MessageEntity) TextLink() *url.URL { + if entity == nil { + return nil + } + + link, err := url.Parse(entity.URL) + if err != nil { + return nil + } + + return link +} diff --git a/helpers_inline_keyboard.go b/utils_inline_keyboard.go similarity index 100% rename from helpers_inline_keyboard.go rename to utils_inline_keyboard.go diff --git a/helpers_inline_query_result.go b/utils_inline_query_result.go similarity index 100% rename from helpers_inline_query_result.go rename to utils_inline_query_result.go diff --git a/helpers_input.go b/utils_input.go similarity index 100% rename from helpers_input.go rename to utils_input.go diff --git a/helpers_keyboard.go b/utils_keyboard.go similarity index 100% rename from helpers_keyboard.go rename to utils_keyboard.go diff --git a/helpers_member.go b/utils_member.go similarity index 100% rename from helpers_member.go rename to utils_member.go diff --git a/helpers_message.go b/utils_message.go similarity index 98% rename from helpers_message.go rename to utils_message.go index 463f817..db3143a 100644 --- a/helpers_message.go +++ b/utils_message.go @@ -15,7 +15,6 @@ func (msg *Message) IsCommand(command string) bool { } entity := msg.Entities[0] - isBotCommand := entity.IsBotCommand() && entity.Offset == 0 if command != "" { return isBotCommand && strings.EqualFold(msg.Command(), command) @@ -94,18 +93,23 @@ func (msg *Message) ForwardTime() time.Time { } func (msg *Message) EditTime() time.Time { + var t time.Time if msg == nil { - return time.Time{} + return t } if !msg.HasBeenEdited() { - return time.Time{} + return t } return time.Unix(msg.EditDate, 0) } func (msg *Message) HasBeenEdited() bool { + if msg == nil { + return false + } + return msg.EditDate > 0 } @@ -140,6 +144,7 @@ func (msg *Message) IsSticker() bool { func (msg *Message) IsVideo() bool { return !msg.IsText() && msg.Video != nil } + func (msg *Message) IsVoice() bool { return !msg.IsText() && msg.Voice != nil } diff --git a/helpers_update.go b/utils_update.go similarity index 100% rename from helpers_update.go rename to utils_update.go diff --git a/helpers_user.go b/utils_user.go similarity index 86% rename from helpers_user.go rename to utils_user.go index 60cc8cb..5ebf53d 100644 --- a/helpers_user.go +++ b/utils_user.go @@ -25,7 +25,7 @@ func (user *User) FullName() string { } if user.LastName != "" { - return fmt.Sprint(user.FirstName, " ", user.LastName) + return fmt.Sprintln(user.FirstName, user.LastName) } return user.FirstName