aboutsummaryrefslogtreecommitdiff
path: root/api/telegram
diff options
context:
space:
mode:
authorShulhan <m.shulhan@gmail.com>2020-04-10 14:27:04 +0700
committerShulhan <m.shulhan@gmail.com>2020-04-11 03:50:26 +0700
commitdd21606afcd3bef6243d37592ec6ee47f1d03898 (patch)
tree3142930cb6364bd51daa2ff0762823b2d6196558 /api/telegram
parent33b2a03dfbd90bf53e09fa2210aff8a84bf81fd8 (diff)
downloadpakakeh.go-dd21606afcd3bef6243d37592ec6ee47f1d03898.tar.xz
api/telegram/bot: Go package for Telegram API Bot
Diffstat (limited to 'api/telegram')
-rw-r--r--api/telegram/bot/animation.go11
-rw-r--r--api/telegram/bot/audio.go21
-rw-r--r--api/telegram/bot/bot.go459
-rw-r--r--api/telegram/bot/bot_test.go80
-rw-r--r--api/telegram/bot/callback_game.go8
-rw-r--r--api/telegram/bot/callback_query.go41
-rw-r--r--api/telegram/bot/chat.go79
-rw-r--r--api/telegram/bot/chat_permissions.go44
-rw-r--r--api/telegram/bot/chat_photo.go30
-rw-r--r--api/telegram/bot/chosen_inline_result.go29
-rw-r--r--api/telegram/bot/command.go71
-rw-r--r--api/telegram/bot/command_test.go72
-rw-r--r--api/telegram/bot/commands.go9
-rw-r--r--api/telegram/bot/contact.go20
-rw-r--r--api/telegram/bot/dice.go12
-rw-r--r--api/telegram/bot/doc.go12
-rw-r--r--api/telegram/bot/document.go30
-rw-r--r--api/telegram/bot/encrypted_credential.go21
-rw-r--r--api/telegram/bot/encrypted_passport_element.go96
-rw-r--r--api/telegram/bot/game.go36
-rw-r--r--api/telegram/bot/inline_keyboard_button.go50
-rw-r--r--api/telegram/bot/inline_keyboard_markup.go15
-rw-r--r--api/telegram/bot/inline_query.go23
-rw-r--r--api/telegram/bot/invoice.go27
-rw-r--r--api/telegram/bot/location.go11
-rw-r--r--api/telegram/bot/login_url.go39
-rw-r--r--api/telegram/bot/mask_position.go30
-rw-r--r--api/telegram/bot/message.go191
-rw-r--r--api/telegram/bot/message_entity.go49
-rw-r--r--api/telegram/bot/message_forward.go30
-rw-r--r--api/telegram/bot/message_request.go37
-rw-r--r--api/telegram/bot/options.go93
-rw-r--r--api/telegram/bot/order_info.go22
-rw-r--r--api/telegram/bot/passport_data.go17
-rw-r--r--api/telegram/bot/passport_file.go27
-rw-r--r--api/telegram/bot/photo_size.go28
-rw-r--r--api/telegram/bot/poll.go40
-rw-r--r--api/telegram/bot/poll_answer.go20
-rw-r--r--api/telegram/bot/poll_option.go16
-rw-r--r--api/telegram/bot/pre_checkout_query.go36
-rw-r--r--api/telegram/bot/response.go59
-rw-r--r--api/telegram/bot/response_parameters.go23
-rw-r--r--api/telegram/bot/shipping_address.go28
-rw-r--r--api/telegram/bot/shipping_query.go22
-rw-r--r--api/telegram/bot/sticker.go31
-rw-r--r--api/telegram/bot/successful_payment.go35
-rw-r--r--api/telegram/bot/update.go65
-rw-r--r--api/telegram/bot/user.go40
-rw-r--r--api/telegram/bot/venue.go25
-rw-r--r--api/telegram/bot/video.go18
-rw-r--r--api/telegram/bot/video_note.go20
-rw-r--r--api/telegram/bot/voice.go8
-rw-r--r--api/telegram/bot/webhook.go47
-rw-r--r--api/telegram/bot/webhook_info.go37
54 files changed, 2440 insertions, 0 deletions
diff --git a/api/telegram/bot/animation.go b/api/telegram/bot/animation.go
new file mode 100644
index 00000000..a3c906c8
--- /dev/null
+++ b/api/telegram/bot/animation.go
@@ -0,0 +1,11 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Animation represents an animation file (GIF or H.264/MPEG-4 AVC video
+// without sound).
+//
+type Animation Video
diff --git a/api/telegram/bot/audio.go b/api/telegram/bot/audio.go
new file mode 100644
index 00000000..034df5d2
--- /dev/null
+++ b/api/telegram/bot/audio.go
@@ -0,0 +1,21 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Audio represents an audio file to be treated as music by the Telegram
+// clients.
+type Audio struct {
+ Document
+
+ // Duration of the audio in seconds as defined by sender.
+ Duration int `json:"duration"`
+
+ // Optional. Performer of the audio as defined by sender or by audio
+ // tags.
+ Performer string `json:"performer"`
+
+ // Optional. Title of the audio as defined by sender or by audio tags.
+ Title string `json:"title"`
+}
diff --git a/api/telegram/bot/bot.go b/api/telegram/bot/bot.go
new file mode 100644
index 00000000..7359913d
--- /dev/null
+++ b/api/telegram/bot/bot.go
@@ -0,0 +1,459 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import (
+ "context"
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "log"
+ stdhttp "net/http"
+ "strconv"
+
+ "github.com/shuLhan/share/lib/errors"
+ "github.com/shuLhan/share/lib/http"
+)
+
+// List of message parse mode.
+const (
+ ParseModeMarkdownV2 = "MarkdownV2"
+ ParseModeHTML = "HTML"
+)
+
+//
+// List of Update types.
+//
+// This types can be used to set AllowedUpdates on Options.Webhook.
+//
+const (
+ // New incoming message of any kind — text, photo, sticker, etc.
+ UpdateTypeMessage = "message"
+
+ // New version of a message that is known to the bot and was edited.
+ UpdateTypeEditedMessage = "edited_message"
+
+ // New incoming channel post of any kind — text, photo, sticker, etc.
+ UpdateTypeChannelPost = "channel_post"
+
+ // New version of a channel post that is known to the bot and was
+ // edited.
+ UpdateTypeEditedChannelPost = "edited_channel_post"
+
+ // New incoming inline query
+ UpdateTypeInlineQuery = "inline_query"
+
+ // The result of an inline query that was chosen by a user and sent to
+ // their chat partner.
+ UpdateTypeChosenInlineResult = "chosen_inline_result"
+
+ // New incoming callback query.
+ UpdateTypeCallbackQuery = "callback_query"
+
+ // New incoming shipping query.
+ // Only for invoices with flexible price.
+ UpdateTypeShippingQuery = "shipping_query"
+
+ // New incoming pre-checkout query.
+ // Contains full information about checkout.
+ UpdateTypePreCheckoutQuery = "pre_checkout_query"
+
+ // New poll state.
+ // Bots receive only updates about stopped polls and polls, which are
+ // sent by the bot.
+ UpdateTypePoll = "poll"
+
+ // A user changed their answer in a non-anonymous poll.
+ // Bots receive new votes only in polls that were sent by the bot
+ // itself.
+ UpdateTypePollAnswer = "poll_answer"
+)
+
+const (
+ defURL = "https://api.telegram.org/bot"
+)
+
+// List of API methods.
+const (
+ methodDeleteWebhook = "deleteWebhook"
+ methodGetMe = "getMe"
+ methodGetMyComands = "getMyCommands"
+ methodGetWebhookInfo = "getWebhookInfo"
+ methodSendMessage = "sendMessage"
+ methodSetMyCommands = "setMyCommands"
+ methodSetWebhook = "setWebhook"
+)
+
+const (
+ paramNameURL = "url"
+ paramNameCertificate = "certificate"
+ paramNameMaxConnections = "max_connections"
+ paramNameAllowedUpdates = "allowed_updates"
+)
+
+//
+// Bot for Telegram using webHook.
+//
+type Bot struct {
+ opts Options
+ client *http.Client
+ webhook *http.Server
+ user *User
+ commands commands
+ err chan error
+}
+
+//
+// New create and initialize new Telegram bot.
+//
+func New(opts Options) (bot *Bot, err error) {
+ err = opts.init()
+ if err != nil {
+ return nil, fmt.Errorf("bot.New: %w", err)
+ }
+
+ serverURL := defURL + opts.Token + "/"
+ bot = &Bot{
+ opts: opts,
+ client: http.NewClient(serverURL),
+ }
+
+ fmt.Printf("Bot options: %+v\n", opts)
+ fmt.Printf("Bot options Webhook: %+v\n", opts.Webhook)
+
+ // Check if Bot Token is correct by issuing "getMe" method to API
+ // server.
+ bot.user, err = bot.GetMe()
+ if err != nil {
+ return nil, err
+ }
+
+ return bot, nil
+}
+
+//
+// DeleteWebhook remove webhook integration if you decide to switch back to
+// getUpdates. Returns True on success. Requires no parameters.
+//
+func (bot *Bot) DeleteWebhook() (err error) {
+ resBody, err := bot.client.PostForm(methodDeleteWebhook, nil)
+ if err != nil {
+ return fmt.Errorf("DeleteWebhook: %w", err)
+ }
+
+ res := &response{}
+ err = json.Unmarshal(resBody, res)
+ if err != nil {
+ return fmt.Errorf("DeleteWebhook: %w", err)
+ }
+
+ return nil
+}
+
+//
+// GetMe A simple method for 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 *User, err error) {
+ resBody, err := bot.client.Get(methodGetMe, nil)
+ if err != nil {
+ return nil, fmt.Errorf("GetMe: %w", err)
+ }
+
+ user = &User{}
+ res := &response{
+ Result: user,
+ }
+ err = res.unpack(resBody)
+ if err != nil {
+ return nil, fmt.Errorf("GetMe: %w", err)
+ }
+
+ return user, nil
+}
+
+//
+// GetMyCommands get the current list of the bot's commands.
+//
+func (bot *Bot) GetMyCommands() (cmds []Command, err error) {
+ resBody, err := bot.client.Get(methodGetWebhookInfo, nil)
+ if err != nil {
+ return nil, fmt.Errorf("GetMyCommands: %w", err)
+ }
+
+ res := &response{
+ Result: cmds,
+ }
+ err = res.unpack(resBody)
+ if err != nil {
+ return nil, fmt.Errorf("GetMyCommands: %w", err)
+ }
+
+ return cmds, nil
+}
+
+//
+// GetWebhookInfo get current webhook status. Requires no parameters.
+// On success, returns a WebhookInfo object.
+// If the bot is using getUpdates, will return an object with the url field
+// empty.
+func (bot *Bot) GetWebhookInfo() (webhookInfo *WebhookInfo, err error) {
+ resBody, err := bot.client.Get(methodGetWebhookInfo, nil)
+ if err != nil {
+ return nil, fmt.Errorf("GetWebhookInfo: %w", err)
+ }
+
+ webhookInfo = &WebhookInfo{}
+ res := &response{
+ Result: webhookInfo,
+ }
+ err = res.unpack(resBody)
+ if err != nil {
+ return nil, fmt.Errorf("GetWebhookInfo: %w", err)
+ }
+
+ return webhookInfo, nil
+}
+
+//
+// SendMessage send text messages using defined parse mode to specific
+// user.
+//
+func (bot *Bot) SendMessage(parent *Message, parseMode, text string) (
+ msg *Message, err error,
+) {
+ req := messageRequest{
+ ChatID: parent.Chat.ID,
+ Text: text,
+ ParseMode: parseMode,
+ }
+
+ resBody, err := bot.client.PostJSON(methodSendMessage, req)
+ if err != nil {
+ return nil, fmt.Errorf("SendMessage: %w", err)
+ }
+
+ msg = &Message{}
+ res := response{
+ Result: msg,
+ }
+ err = res.unpack(resBody)
+ if err != nil {
+ return nil, fmt.Errorf("SendMessage: %w", err)
+ }
+
+ return msg, nil
+}
+
+//
+// SetMyCommands change the list of the bot's commands.
+//
+// The value of each Command in the list must be valid according to
+// description in Command type; this is including length and characters.
+//
+func (bot *Bot) SetMyCommands(cmds []Command) (err error) {
+ if len(cmds) == 0 {
+ return nil
+ }
+ for _, cmd := range cmds {
+ err = cmd.validate()
+ if err != nil {
+ return fmt.Errorf("SetMyCommands: %w", err)
+ }
+ }
+
+ bot.commands.Commands = cmds
+
+ resBody, err := bot.client.PostJSON(methodSetMyCommands, &bot.commands)
+ if err != nil {
+ return fmt.Errorf("SetMyCommands: %w", err)
+ }
+
+ res := &response{}
+ err = res.unpack(resBody)
+ if err != nil {
+ return fmt.Errorf("SetMyCommands: %w", err)
+ }
+
+ return nil
+}
+
+//
+// Start the Bot.
+//
+// If the Webhook option is not nil it will start listening to updates through
+// webhook.
+//
+func (bot *Bot) Start() (err error) {
+ if bot.opts.Webhook != nil {
+ return bot.startWebhook()
+ }
+ return nil
+}
+
+//
+// Stop the Bot.
+//
+func (bot *Bot) Stop() (err error) {
+ if bot.webhook != nil {
+ err = bot.webhook.Shutdown(context.TODO())
+ if err != nil {
+ log.Println("bot: Stop: ", err)
+ }
+
+ bot.webhook = nil
+ }
+
+ return nil
+}
+
+func (bot *Bot) setWebhook() (err error) {
+ params := make(map[string][]byte)
+
+ webhookURL := bot.opts.Webhook.URL + "/" + bot.opts.Token
+
+ params[paramNameURL] = []byte(webhookURL)
+ if len(bot.opts.Webhook.Certificate) > 0 {
+ params[paramNameCertificate] = bot.opts.Webhook.Certificate
+ }
+ if bot.opts.Webhook.MaxConnections > 0 {
+ str := strconv.Itoa(bot.opts.Webhook.MaxConnections)
+ params[paramNameMaxConnections] = []byte(str)
+ }
+ if len(bot.opts.Webhook.AllowedUpdates) > 0 {
+ allowedUpdates, err := json.Marshal(&bot.opts.Webhook.AllowedUpdates)
+ if err != nil {
+ return fmt.Errorf("setWebhook: %w", err)
+ }
+ params[paramNameAllowedUpdates] = allowedUpdates
+ }
+
+ resBody, err := bot.client.PostFormData(methodSetWebhook, params)
+ if err != nil {
+ return fmt.Errorf("setWebhook: %w", err)
+ }
+
+ res := &response{}
+
+ err = json.Unmarshal(resBody, res)
+ if err != nil {
+ return fmt.Errorf("setWebhook: %w", err)
+ }
+
+ fmt.Printf("setWebhook: response: %+v\n", res)
+
+ return nil
+}
+
+//
+// startWebhook start the HTTP server to receive Update from Telegram API
+// server and register the Webhook.
+//
+func (bot *Bot) startWebhook() (err error) {
+ err = bot.createServer()
+ if err != nil {
+ return fmt.Errorf("startWebhook: %w", err)
+ }
+
+ bot.err = make(chan error)
+
+ go func() {
+ bot.err <- bot.webhook.Start()
+ }()
+
+ err = bot.setWebhook()
+ if err != nil {
+ return fmt.Errorf("startWebhook: %w", err)
+ }
+
+ return <-bot.err
+}
+
+//
+// createServer start the HTTP server for receiving Update.
+//
+func (bot *Bot) createServer() (err error) {
+ serverOpts := &http.ServerOptions{
+ Address: bot.opts.Webhook.ListenAddress,
+ }
+
+ if bot.opts.Webhook.ListenCertificate != nil {
+ tlsConfig := &tls.Config{}
+ tlsConfig.Certificates = append(
+ tlsConfig.Certificates,
+ *bot.opts.Webhook.ListenCertificate,
+ )
+ serverOpts.Conn = &stdhttp.Server{
+ TLSConfig: tlsConfig,
+ }
+ }
+
+ bot.webhook, err = http.NewServer(serverOpts)
+ if err != nil {
+ return fmt.Errorf("createServer: %w", err)
+ }
+
+ epToken := &http.Endpoint{
+ Method: http.RequestMethodPost,
+ Path: "/" + bot.opts.Token,
+ RequestType: http.RequestTypeJSON,
+ ResponseType: http.ResponseTypeNone,
+ Call: bot.handleWebhook,
+ }
+
+ err = bot.webhook.RegisterEndpoint(epToken)
+ if err != nil {
+ return fmt.Errorf("createServer: %w", err)
+ }
+
+ return nil
+}
+
+//
+// handleWebhook handle Updates from Webhook.
+//
+func (bot *Bot) handleWebhook(
+ res stdhttp.ResponseWriter, req *stdhttp.Request, reqBody []byte,
+) (
+ resBody []byte, err error,
+) {
+ update := Update{}
+
+ err = json.Unmarshal(reqBody, &update)
+ if err != nil {
+ return nil, errors.Internal(err)
+ }
+
+ var isHandled bool
+
+ if len(bot.commands.Commands) > 0 && update.Message != nil {
+ isHandled = bot.handleUpdateCommand(update)
+ }
+
+ // If no Command handler found, forward it to global handler.
+ if !isHandled {
+ bot.opts.HandleUpdate(update)
+ }
+
+ return resBody, nil
+}
+
+func (bot *Bot) handleUpdateCommand(update Update) bool {
+ ok := update.Message.parseCommandArgs()
+ if !ok {
+ return false
+ }
+
+ for _, cmd := range bot.commands.Commands {
+ if cmd.Command == update.Message.Command {
+ if cmd.Handler != nil {
+ cmd.Handler(update)
+ }
+ return true
+ }
+ }
+ return false
+}
diff --git a/api/telegram/bot/bot_test.go b/api/telegram/bot/bot_test.go
new file mode 100644
index 00000000..365e5e84
--- /dev/null
+++ b/api/telegram/bot/bot_test.go
@@ -0,0 +1,80 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import (
+ "log"
+ "os"
+ "testing"
+)
+
+const (
+ testListenAddress = ":1928"
+)
+
+var (
+ testBot *Bot
+)
+
+func TestMain(m *testing.M) {
+ startTestBot()
+
+ os.Exit(m.Run())
+}
+
+func startTestBot() {
+ var err error
+
+ opts := Options{
+ HandleUpdate: testHandleUpdate,
+ Webhook: &Webhook{
+ ListenAddress: testListenAddress,
+ },
+ }
+
+ testBot, err = New(opts)
+ if err != nil {
+ log.Println("startTestBot: ", err)
+ }
+
+ if testBot != nil {
+ go func() {
+ err := testBot.Start()
+ if err != nil {
+ log.Println(err)
+ }
+ }()
+ }
+}
+
+func testHandleUpdate(update Update) {
+ log.Printf("testHandleUpdate: %+v", update)
+}
+
+func TestBot_GetMe(t *testing.T) {
+ if testBot == nil {
+ t.Skip()
+ }
+
+ user, err := testBot.GetMe()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ t.Logf("GetMe: %+v", user)
+}
+
+func TestBot_GetWebhookInfo(t *testing.T) {
+ if testBot == nil {
+ t.Skip()
+ }
+
+ whInfo, err := testBot.GetWebhookInfo()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ t.Logf("GetWebhookInfo: %+v", whInfo)
+}
diff --git a/api/telegram/bot/callback_game.go b/api/telegram/bot/callback_game.go
new file mode 100644
index 00000000..4b53d578
--- /dev/null
+++ b/api/telegram/bot/callback_game.go
@@ -0,0 +1,8 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// CallbackGame A placeholder, currently holds no information.
+type CallbackGame struct{}
diff --git a/api/telegram/bot/callback_query.go b/api/telegram/bot/callback_query.go
new file mode 100644
index 00000000..b9186ffa
--- /dev/null
+++ b/api/telegram/bot/callback_query.go
@@ -0,0 +1,41 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// CallbackQuery represents an incoming callback query from a callback button
+// in an inline keyboard.
+//
+// If the button that originated the query was attached to a message sent by
+// the bot, the field message will be present.
+// If the button was attached to a message sent via the bot (in inline mode),
+// the field inline_message_id will be present.
+// Exactly one of the fields data or game_short_name will be present.
+type CallbackQuery struct {
+ ID string `json:"id"` // Unique identifier for this query
+ From *User `json:"from"` // Sender
+
+ // Optional. 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"`
+
+ // Optional. Identifier of the message sent via the bot in inline
+ // mode, that originated the query.
+ InlineMessageID string `json:"inline_message_id"`
+
+ // Global identifier, uniquely corresponding to the chat to which the
+ // message with the callback button was sent. Useful for high scores
+ // in games.
+ ChatInstance string `json:"chat_instance"`
+
+ // Optional. Data associated with the callback button.
+ // Be aware that a bad client can send arbitrary data in this field.
+ Data string `json:"data"`
+
+ // Optional. Short name of a Game to be returned, serves as the unique
+ // identifier for the game.
+ GameShortName string `sjon:"game_short_name"`
+}
diff --git a/api/telegram/bot/chat.go b/api/telegram/bot/chat.go
new file mode 100644
index 00000000..0b39a95d
--- /dev/null
+++ b/api/telegram/bot/chat.go
@@ -0,0 +1,79 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// List of chat types.
+const (
+ ChatTypeChannel = "channel"
+ ChatTypeGroup = "group"
+ ChatTypePrivate = "private"
+ ChatTypeSupergroup = "supergroup"
+)
+
+//
+// Chat represents a chat.
+//
+type Chat struct {
+
+ // Unique identifier for this chat.
+ //
+ // This number may be greater than 32 bits and some programming
+ // languages may have difficulty/silent defects in interpreting it.
+ // But it is smaller than 52 bits, so a signed 64 bit integer or
+ // double-precision float type are safe for storing this identifier.
+ ID int64 `json:"id"`
+
+ // Type of chat, can be either “private”, “group”, “supergroup” or
+ // “channel”.
+ Type string `json:"type"`
+
+ // Optional. Title, for supergroups, channels and group chats.
+ Title string `json:"title"`
+
+ // Optional. Username, for private chats, supergroups and channels if
+ // available.
+ Username string `json:"username"`
+
+ // Optional. First name of the other party in a private chat.
+ FirstName string `json:"first_name"`
+
+ // Optional. Last name of the other party in a private chat.
+ LastName string `json:"last_name"`
+
+ // Optional. Chat photo. Returned only in getChat.
+ Photo *ChatPhoto `json:"chat_photo"`
+
+ // Optional. Description, for groups, supergroups and channel chats.
+ // Returned only in getChat.
+ Description string `json:"description"`
+
+ // Optional. Chat invite link, for groups, supergroups and channel
+ // chats.
+ // Each administrator in a chat generates their own invite links, so
+ // the bot must first generate the link using exportChatInviteLink.
+ // Returned only in getChat.
+ InviteLink string `json:"invite_link"`
+
+ // Optional. Pinned message, for groups, supergroups and channels.
+ // Returned only in getChat.
+ PinnedMesage *Message `json:"pinned_mesage"`
+
+ // Optional. Default chat member permissions, for groups and
+ // supergroups. Returned only in getChat.
+ Permissions *ChatPermissions `json:"permissions"`
+
+ // Optional. For supergroups, the minimum allowed delay between
+ // consecutive messages sent by each unpriviledged user. Returned only
+ // in getChat.
+ SlowModeDelay int `json:"slow_mode_delay"`
+
+ // Optional. For supergroups, name of group sticker set. Returned only
+ // in getChat.
+ StickerSetName string `json:"sticker_set_name"`
+
+ // Optional. True, if the bot can change the group sticker set.
+ // Returned only in getChat.
+ CanSetStickerSet bool `json:"can_set_sticker_set"`
+}
diff --git a/api/telegram/bot/chat_permissions.go b/api/telegram/bot/chat_permissions.go
new file mode 100644
index 00000000..956d28e3
--- /dev/null
+++ b/api/telegram/bot/chat_permissions.go
@@ -0,0 +1,44 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// ChatPermissions describes actions that a non-administrator user is allowed
+// to take in a chat.
+//
+type ChatPermissions struct {
+ // Optional. True, if the user is allowed to send text messages,
+ // contacts, locations and venues.
+ CanSendMessages bool `json:"can_send_messages"`
+
+ // Optional. True, if the user is allowed to send audios, documents,
+ // photos, videos, video notes and voice notes, implies
+ // can_send_messages.
+ CanSendMediaMessages bool `json:"can_send_media_messages"`
+
+ // Optional. True, if the user is allowed to send polls, implies
+ // can_send_messages.
+ CanSendPolls bool `json:"can_send_polls"`
+
+ // Optional. True, if the user is allowed to send animations, games,
+ // stickers and use inline bots, implies can_send_media_messages.
+ CanSendOtherMessages bool `json:"can_send_other_messages"`
+
+ // Optional. True, if the user is allowed to add web page previews to
+ // their messages, implies can_send_media_messages.
+ CanAddWebPagePreviews bool `json:"can_add_web_page_previews"`
+
+ // Optional. True, if the user is allowed to change the chat title,
+ // photo and other settings. Ignored in public supergroups.
+ CanChangeInfo bool `json:"can_change_info"`
+
+ // Optional. True, if the user is allowed to invite new users to the
+ // chat.
+ CanInviteUsers bool `json:"can_invite_users"`
+
+ // Optional. True, if the user is allowed to pin messages. Ignored in
+ // public supergroups.
+ CanPinMessages bool `json:"can_pin_messages"`
+}
diff --git a/api/telegram/bot/chat_photo.go b/api/telegram/bot/chat_photo.go
new file mode 100644
index 00000000..a4259548
--- /dev/null
+++ b/api/telegram/bot/chat_photo.go
@@ -0,0 +1,30 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// ChatPhoto represents a chat photo.
+//
+type ChatPhoto struct {
+ // File identifier of small (160x160) chat photo.
+ // This file_id can be used only for photo download and only for as
+ // long as the photo is not changed.
+ SmallFileID string `json:"small_file_id"`
+
+ // Unique file identifier of small (160x160) chat photo, which is
+ // supposed to be the same over time and for different bots.
+ // Can't be used to download or reuse the file.
+ SmallFileUniqueID string `json:"small_file_unique_id"`
+
+ // File identifier of big (640x640) chat photo.
+ // This file_id can be used only for photo download and only for as
+ // long as the photo is not changed.
+ BigFileID string `json:"big_file_id"`
+
+ // Unique file identifier of big (640x640) chat photo, which is
+ // supposed to be the same over time and for different bots.
+ // Can't be used to download or reuse the file.
+ BigFileUniqueID string `json:"big_file_unique_id"`
+}
diff --git a/api/telegram/bot/chosen_inline_result.go b/api/telegram/bot/chosen_inline_result.go
new file mode 100644
index 00000000..6b819b80
--- /dev/null
+++ b/api/telegram/bot/chosen_inline_result.go
@@ -0,0 +1,29 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// ChosenInlineResult represents a result of an inline query that was chosen
+// by the user and sent to their chat partner.
+//
+type ChosenInlineResult struct {
+ // The unique identifier for the result that was chosen.
+ ResultID string `json:"result_id"`
+
+ // The user that chose the result.
+ From *User `json:"from"`
+
+ // The query that was used to obtain the result
+ Query string `json:"query"`
+
+ // Optional. Sender location, only for bots that require user
+ // location.
+ Location *Location `json:"location"`
+
+ // Optional. 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.
+ InlineMessageID string `json:"inline_message_id"`
+}
diff --git a/api/telegram/bot/command.go b/api/telegram/bot/command.go
new file mode 100644
index 00000000..6d80343a
--- /dev/null
+++ b/api/telegram/bot/command.go
@@ -0,0 +1,71 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import (
+ "fmt"
+)
+
+//
+// Command represents a bot command.
+//
+type Command struct {
+ // Text of the command, 1-32 characters. Can contain only lowercase
+ // English letters, digits and underscores.
+ Command string `json:"command"`
+
+ // Description of the command, 3-256 characters.
+ Description string `json:"description"`
+
+ // Function that will be called when Bot receive the command.
+ // Handler can read command and its arguments through Message.Command
+ // and Message.CommandArgs.
+ Handler UpdateHandler `json:"-"`
+}
+
+//
+// validate will return an error if command is not valid.
+//
+func (cmd *Command) validate() error {
+ if len(cmd.Command) == 0 || len(cmd.Command) > 32 {
+ return errCommandLength(cmd.Command)
+ }
+ for x := 0; x < len(cmd.Command); x++ {
+ b := cmd.Command[x]
+ if b >= 'a' && b <= 'z' {
+ continue
+ }
+ if b >= '0' && b <= '9' {
+ continue
+ }
+ if b == '_' {
+ continue
+ }
+ return errCommandValue(cmd.Command)
+ }
+ if len(cmd.Description) < 3 || len(cmd.Description) > 256 {
+ return errDescLength(cmd.Command)
+ }
+ if cmd.Handler == nil {
+ return errHandlerNil(cmd.Command)
+ }
+ return nil
+}
+
+func errCommandLength(cmd string) error {
+ return fmt.Errorf("%q: the Command length must be between 1-32 characters", cmd)
+}
+
+func errCommandValue(cmd string) error {
+ return fmt.Errorf("%q: command can contain only lowercase English letter, digits, and underscores", cmd)
+}
+
+func errDescLength(cmd string) error {
+ return fmt.Errorf("%q: the Description length must be between 3-256 characters", cmd)
+}
+
+func errHandlerNil(cmd string) error {
+ return fmt.Errorf("%q: the Command's Handler is not set", cmd)
+}
diff --git a/api/telegram/bot/command_test.go b/api/telegram/bot/command_test.go
new file mode 100644
index 00000000..aa38a801
--- /dev/null
+++ b/api/telegram/bot/command_test.go
@@ -0,0 +1,72 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import (
+ "testing"
+
+ "github.com/shuLhan/share/lib/ascii"
+ "github.com/shuLhan/share/lib/test"
+)
+
+func TestCommand_validate(t *testing.T) {
+ s33 := string(ascii.Random([]byte(ascii.Letters), 33))
+
+ cases := []struct {
+ desc string
+ cmd Command
+ exp error
+ }{{
+ desc: "with empty command",
+ cmd: Command{},
+ exp: errCommandLength(""),
+ }, {
+ desc: "with invalid command character '!'",
+ cmd: Command{
+ Command: "a!",
+ Description: "1234",
+ },
+ exp: errCommandValue("a!"),
+ }, {
+ desc: "with uppercase",
+ cmd: Command{
+ Command: "Help",
+ Description: string(ascii.Random([]byte(ascii.Letters), 257)),
+ },
+ exp: errCommandValue("Help"),
+ }, {
+ desc: "with command too long",
+ cmd: Command{
+ Command: s33,
+ Description: "1234",
+ },
+ exp: errCommandLength(s33),
+ }, {
+ desc: "with description too short",
+ cmd: Command{
+ Command: "help",
+ Description: "12",
+ },
+ exp: errDescLength("help"),
+ }, {
+ desc: "with description too long",
+ cmd: Command{
+ Command: "help",
+ Description: string(ascii.Random([]byte(ascii.Letters), 257)),
+ },
+ exp: errDescLength("help"),
+ }, {
+ desc: "Perfect",
+ cmd: Command{
+ Command: "help",
+ Description: "Bantuan",
+ Handler: func(up Update) {},
+ },
+ }}
+
+ for _, c := range cases {
+ test.Assert(t, c.desc, c.exp, c.cmd.validate(), true)
+ }
+}
diff --git a/api/telegram/bot/commands.go b/api/telegram/bot/commands.go
new file mode 100644
index 00000000..494e5747
--- /dev/null
+++ b/api/telegram/bot/commands.go
@@ -0,0 +1,9 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+type commands struct {
+ Commands []Command `json:"commands"`
+}
diff --git a/api/telegram/bot/contact.go b/api/telegram/bot/contact.go
new file mode 100644
index 00000000..ecec3cf4
--- /dev/null
+++ b/api/telegram/bot/contact.go
@@ -0,0 +1,20 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Contact represents a phone contact.
+type Contact struct {
+ PhoneNumber string `json:"phone_number"` // Contact's phone number.
+ FirstName string `json:"first_name"` // Contact's first name.
+
+ // Optional. Contact's last name.
+ LastName string `json:"last_name"`
+
+ // Optional. Contact's user identifier in Telegram
+ UserID int64 `json:"user_id"`
+
+ // Optional. Additional data about the contact in the form of a vCard
+ VCard string `json:"vcard"`
+}
diff --git a/api/telegram/bot/dice.go b/api/telegram/bot/dice.go
new file mode 100644
index 00000000..2ab09c2a
--- /dev/null
+++ b/api/telegram/bot/dice.go
@@ -0,0 +1,12 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Dice represents a dice with random value from 1 to 6. (Yes, we're aware of
+// the “proper” singular of die. But it's awkward, and we decided to help it
+// change. One dice at a time!)
+type Dice struct {
+ Value int `json:"value"` // Value of the dice, 1-6
+}
diff --git a/api/telegram/bot/doc.go b/api/telegram/bot/doc.go
new file mode 100644
index 00000000..357c36d2
--- /dev/null
+++ b/api/telegram/bot/doc.go
@@ -0,0 +1,12 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//
+// Package bot implement the Telegram Bot API
+// https://core.telegram.org/bots/api.
+//
+// The Bot API is an HTTP-based interface created for developers keen on
+// building bots for Telegram.
+//
+package bot
diff --git a/api/telegram/bot/document.go b/api/telegram/bot/document.go
new file mode 100644
index 00000000..e9979bd1
--- /dev/null
+++ b/api/telegram/bot/document.go
@@ -0,0 +1,30 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Document represents a general file (as opposed to photos, voice messages
+// and audio files).
+type Document struct {
+ // Identifier for this file, which can be used to download or reuse
+ // the file.
+ FileID string `json:"file_id"`
+
+ // Unique identifier for this file, which is supposed to be the same
+ // over time and for different bots. Can't be used to download or
+ // reuse the file.
+ FileUniqueID string `json:"file_unique_id"`
+
+ // Optional. MIME type of the file as defined by sender.
+ MimeType string `json:"mime_type"`
+
+ // Optional. Original filename as defined by sender.
+ FileName string `json:"file_name"`
+
+ // Optional. File size.
+ FileSize int `json:"file_size"`
+
+ // Optional. Document thumbnail as defined by sender.
+ Thumb *PhotoSize `json:"thumb"`
+}
diff --git a/api/telegram/bot/encrypted_credential.go b/api/telegram/bot/encrypted_credential.go
new file mode 100644
index 00000000..d4a92e55
--- /dev/null
+++ b/api/telegram/bot/encrypted_credential.go
@@ -0,0 +1,21 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// EncryptedCredentials
+type EncryptedCredentials struct {
+ // Base64-encoded encrypted JSON-serialized data with unique user's
+ // payload, data hashes and secrets required for
+ // EncryptedPassportElement decryption and authentication.
+ Data string `json:"data"`
+
+ // Base64-encoded data hash for data authentication
+ Hash string `json:"hash"`
+
+ // Base64-encoded secret, encrypted with the bot's public RSA key,
+ // required for data decryption.
+ Secret string `json:"secret"`
+}
diff --git a/api/telegram/bot/encrypted_passport_element.go b/api/telegram/bot/encrypted_passport_element.go
new file mode 100644
index 00000000..052cc2c4
--- /dev/null
+++ b/api/telegram/bot/encrypted_passport_element.go
@@ -0,0 +1,96 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// EncryptedPassportElement contains information about documents or other
+// Telegram Passport elements shared with the bot by the user.
+//
+type EncryptedPassportElement struct {
+ //
+ // Element type. One of “personal_details”, “passport”,
+ // “driver_license”, “identity_card”, “internal_passport”, “address”,
+ // “utility_bill”, “bank_statement”, “rental_agreement”,
+ // “passport_registration”, “temporary_registration”, “phone_number”,
+ // “email”.
+ //
+ Type string `json:"type"`
+
+ //
+ // Optional. Base64-encoded encrypted Telegram Passport element data
+ // provided by the user, available for “personal_details”, “passport”,
+ // “driver_license”, “identity_card”, “internal_passport” and
+ // “address” types.
+ // Can be decrypted and verified using the accompanying
+ // EncryptedCredentials.
+ //
+ Data string `json:"data"`
+
+ //
+ // Optional. User's verified phone number, available only for
+ // “phone_number” type.
+ //
+ PhoneNumber string `json:"phone_number"`
+
+ //
+ // Optional. User's verified email address, available only for “email”
+ // type.
+ //
+ Email string `json:"email"`
+
+ //
+ // Optional. Array of encrypted files with documents provided by the
+ // user, available for “utility_bill”, “bank_statement”,
+ // “rental_agreement”, “passport_registration” and
+ // “temporary_registration” types.
+ // Files can be decrypted and verified using the accompanying
+ // EncryptedCredentials.
+ //
+ Files []PassportFile `json:"files"`
+
+ //
+ // Optional. Encrypted file with the front side of the document,
+ // provided by the user.
+ // Available for “passport”, “driver_license”, “identity_card” and
+ // “internal_passport”. The file can be decrypted and verified using
+ // the accompanying EncryptedCredentials.
+ //
+ FrontSide *PassportFile `json:"front_size"`
+
+ //
+ // Optional. Encrypted file with the reverse side of the document,
+ // provided by the user.
+ // Available for “driver_license” and “identity_card”.
+ // The file can be decrypted and verified using the accompanying
+ // EncryptedCredentials.
+ //
+ ReverseSide *PassportFile `json:"reverse_side"`
+
+ //
+ // Optional. Encrypted file with the selfie of the user holding a
+ // document, provided by the user; available for “passport”,
+ // “driver_license”, “identity_card” and “internal_passport”.
+ // The file can be decrypted and verified using the accompanying
+ // EncryptedCredentials.
+ //
+ Selfie *PassportFile `json:"selfie"`
+
+ //
+ // Optional. Array of encrypted files with translated versions of
+ // documents provided by the user.
+ // Available if requested for “passport”, “driver_license”,
+ // “identity_card”, “internal_passport”, “utility_bill”,
+ // “bank_statement”, “rental_agreement”, “passport_registration” and
+ // “temporary_registration” types.
+ //
+ // Files can be decrypted and verified using the accompanying
+ // EncryptedCredentials.
+ //
+ Translation []PassportFile `json:"translation"`
+
+ // Base64-encoded element hash for using in
+ // PassportElementErrorUnspecified
+ Hash string `json:"hash"`
+}
diff --git a/api/telegram/bot/game.go b/api/telegram/bot/game.go
new file mode 100644
index 00000000..2860ae22
--- /dev/null
+++ b/api/telegram/bot/game.go
@@ -0,0 +1,36 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Game represents a game.
+// Use BotFather to create and edit games, their short names will act as
+// unique identifiers.
+//
+type Game struct {
+ Title string `json:"title"` // Title of the game.
+ Description string `json:"description"` // Description of the game.
+
+ // Photo that will be displayed in the game message in chats.
+ Photo []PhotoSize `json:"photo"`
+
+ //
+ // Optional. 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"`
+
+ // Optional. Special entities that appear in text, such as usernames,
+ // URLs, bot commands, etc.
+ TextEntities []MessageEntity `json:"text_entities"`
+
+ // Optional. Animation that will be displayed in the game message in
+ // chats. Upload via BotFather.
+ Animation *Animation `json:"animation"`
+}
diff --git a/api/telegram/bot/inline_keyboard_button.go b/api/telegram/bot/inline_keyboard_button.go
new file mode 100644
index 00000000..3bcc7f8d
--- /dev/null
+++ b/api/telegram/bot/inline_keyboard_button.go
@@ -0,0 +1,50 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// InlineKeyboardButton represents one button of an inline keyboard. You must
+// use exactly one of the optional fields.
+//
+type InlineKeyboardButton struct {
+ // Label text on the button.
+ Text string `json:"text"`
+
+ // Optional. HTTP or tg:// url to be opened when button is pressed.
+ URL string `json:"url"`
+
+ // Optional. An HTTP URL used to automatically authorize the user. Can
+ // be used as a replacement for the Telegram Login Widget.
+ LoginUrl *LoginUrl `json:"login_url"`
+
+ // Optional. Data to be sent in a callback query to the bot when
+ // button is pressed, 1-64 bytes.
+ CallbackData string `json:"callback_data"`
+
+ // Optional. If set, pressing the button will prompt the user to
+ // select one of their chats, open that chat and insert the bot‘s
+ // username and the specified inline query in the input field. Can be
+ // empty, in which case just the bot’s username will be inserted.
+ SwitchInlineQuery string `json:"switch_inline_query"`
+
+ // Optional. If set, pressing the button will insert the bot‘s
+ // username and the specified inline query in the current chat's input
+ // field. Can be empty, in which case only the bot’s username will be
+ // inserted.
+ SwitchInlineQueryCurrentChat string `json:"switch_inline_query_current_chat"`
+
+ // Optional. Description of the game that will be launched when the
+ // user presses the button.
+ //
+ // NOTE: This type of button must always be the first button in the
+ // first row.
+ CallbackGame *CallbackGame `json:"callback_game"`
+
+ // Optional. Specify True, to send a Pay button.
+ //
+ // NOTE: This type of button must always be the first button in the
+ // first row.
+ Pay bool `json:"pay"`
+}
diff --git a/api/telegram/bot/inline_keyboard_markup.go b/api/telegram/bot/inline_keyboard_markup.go
new file mode 100644
index 00000000..bb85c1ae
--- /dev/null
+++ b/api/telegram/bot/inline_keyboard_markup.go
@@ -0,0 +1,15 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// 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
+ // InlineKeyboardButton objects.
+ InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"`
+}
diff --git a/api/telegram/bot/inline_query.go b/api/telegram/bot/inline_query.go
new file mode 100644
index 00000000..ffd4c8b3
--- /dev/null
+++ b/api/telegram/bot/inline_query.go
@@ -0,0 +1,23 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// InlineQuery represents an incoming inline query.
+// When the user sends an empty query, your bot could return some default or
+// trending results.
+//
+type InlineQuery struct {
+ ID string `json:"id"` // Unique identifier for this qery
+ From *User `json:"from"` // Sender
+ Query string `json:"query"` // Text of the query (up to 256 characters).
+
+ // Offset of the results to be returned, can be controlled by the bot.
+ Offset string `json:"offset"`
+
+ // Optional. Sender location, only for bots that request user
+ // location.
+ Location *Location `json:"location"`
+}
diff --git a/api/telegram/bot/invoice.go b/api/telegram/bot/invoice.go
new file mode 100644
index 00000000..b5befba6
--- /dev/null
+++ b/api/telegram/bot/invoice.go
@@ -0,0 +1,27 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Invoice contains basic information about an invoice.
+//
+type Invoice struct {
+ Title string `json:"title"` // Product name
+ Description string `json:"description"` // Product description
+
+ // Unique bot deep-linking parameter that can be used to generate
+ // this invoice.
+ StartParameter string `json:"start_parameter"`
+
+ // Three-letter ISO 4217 currency code
+ Currency string `json:"currency"`
+
+ // 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 digits past the decimal point for each currency (2 for the
+ // majority of currencies).
+ TotalAmount int `json:"total_amount"`
+}
diff --git a/api/telegram/bot/location.go b/api/telegram/bot/location.go
new file mode 100644
index 00000000..0b1f1630
--- /dev/null
+++ b/api/telegram/bot/location.go
@@ -0,0 +1,11 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Location represents a point on the map.
+type Location struct {
+ Longitude float64 `json:"longitude"` // Longitude as defined by sender.
+ Latitude float64 `json:"latitude"` // Latitude as defined by sender.
+}
diff --git a/api/telegram/bot/login_url.go b/api/telegram/bot/login_url.go
new file mode 100644
index 00000000..86c62796
--- /dev/null
+++ b/api/telegram/bot/login_url.go
@@ -0,0 +1,39 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// LoginUrl represents a parameter of the inline keyboard button used to
+// automatically authorize a user.
+// Serves as a great replacement for the Telegram Login Widget when the user
+// is coming from Telegram.
+// All the user needs to do is tap/click a button and confirm that they want
+// to log in.
+type LoginUrl struct {
+ // An HTTP URL to be opened with user authorization data added to the
+ // query string when the button is pressed. If the user refuses to
+ // provide authorization data, the original URL without information
+ // about the user will be opened. The data added is the same as
+ // described in Receiving authorization data.
+ //
+ // NOTE: You must always check the hash of the received data to verify
+ // the authentication and the integrity of the data as described in
+ // Checking authorization.
+ Url string `json:"url"`
+
+ // Optional. New text of the button in forwarded messages.
+ ForwardText string `json:"forward_text"`
+
+ // Optional. Username of a bot, which will be used for user
+ // authorization. See Setting up a bot for more details. If not
+ // specified, the current bot's username will be assumed. The url's
+ // domain must be the same as the domain linked with the bot. See
+ // Linking your domain to the bot for more details.
+ BotUsername string `json:"bot_username"`
+
+ // Optional. Pass True to request the permission for your bot to send
+ // messages to the user.
+ RequestWriteAccess bool `json:"request_write_access"`
+}
diff --git a/api/telegram/bot/mask_position.go b/api/telegram/bot/mask_position.go
new file mode 100644
index 00000000..3b120011
--- /dev/null
+++ b/api/telegram/bot/mask_position.go
@@ -0,0 +1,30 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// MaskPosition describes the position on faces where a mask should be placed
+// by default.
+//
+type MaskPosition struct {
+ // The part of the face relative to which the mask should be placed.
+ // One of “forehead”, “eyes”, “mouth”, or “chin”.
+ Point string `json:"point"`
+
+ // Shift by X-axis measured in widths of the mask scaled to the face
+ // size, from left to right.
+ // For example, choosing -1.0 will place mask just to the left of the
+ // default mask position.
+ XShift float64 `json:"x_shift"`
+
+ // Shift by Y-axis measured in heights of the mask scaled to the face
+ // size, from top to bottom.
+ // For example, 1.0 will place the mask just below the default mask
+ // position.
+ YShift float64 `json:"y_shift"`
+
+ // Mask scaling coefficient. For example, 2.0 means double size.
+ Scale float64 `json:"scale"`
+}
diff --git a/api/telegram/bot/message.go b/api/telegram/bot/message.go
new file mode 100644
index 00000000..85bfccc9
--- /dev/null
+++ b/api/telegram/bot/message.go
@@ -0,0 +1,191 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import "strings"
+
+//
+// Message represents a message.
+//
+type Message struct {
+ MessageForward
+
+ ID int `json:"message_id"` // Unique message identifier inside this chat.
+ Date int `json:"date"` // Date the message was sent in Unix time.
+ Chat *Chat `json:"chat"` // Conversation the message belongs to.
+
+ // Optional. Sender, empty for messages sent to channels.
+ From *User `json:"from"`
+
+ // Optional. Date the message was last edited in Unix time
+ EditDate int `json:"edit_date"`
+
+ // Optional. 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.
+ ReplyTo *Message `json:"reply_to_message"`
+
+ // Optional. The unique identifier of a media message group this
+ // message belongs to.
+ MediaGroupID string `json:"media_group_id"`
+
+ // Optional. Signature of the post author for messages in channels.
+ AuthorSignature string `json:"author_signature"`
+
+ // Optional. For text messages, the actual UTF-8 text of the message,
+ // 0-4096 characters.
+ Text string `json:"text"`
+
+ // Optional. For text messages, special entities like usernames, URLs,
+ // bot commands, etc. that appear in the text.
+ Entities []MessageEntity `json:"entities"`
+
+ // Optional. Message is an audio file, information about the file
+ Audio *Audio `json:"audio"`
+
+ // Optional. Message is a general file, information about the file.
+ Document *Document `json:"document"`
+
+ // Optional. Message is an animation, information about the animation.
+ // For backward compatibility, when this field is set, the document
+ // field will also be set.
+ Animation *Animation `json:"animation"`
+
+ // Optional. Message is a game, information about the game.
+ Game *Game `json:"game"`
+
+ // Optional. Message is a photo, available sizes of the photo.
+ Photo []PhotoSize `json:"photo"`
+
+ // Optional. Message is a sticker, information about the sticker.
+ Sticker *Sticker `json:"sticker"`
+
+ // Optional. Message is a video, information about the video.
+ Video *Video `json:"video"`
+
+ // Optional. Message is a voice message, information about the file.
+ Voice *Voice `json:"voice"`
+
+ // Optional. Message is a video note, information about the video
+ // message.
+ VideoNote *VideoNote `json:"video_note"`
+
+ // Optional. Caption for the animation, audio, document, photo, video
+ // or voice, 0-1024 characters.
+ Caption string `json:"caption"`
+
+ // Optional. For messages with a caption, special entities like
+ // usernames, URLs, bot commands, etc. that appear in the caption.
+ CaptionEntities []MessageEntity `json:"caption_entities"`
+
+ // Optional. Message is a shared contact, information about the
+ // contact.
+ Contact *Contact `json:"contact"`
+
+ // Optional. Message is a shared location, information about the
+ // location.
+ Location *Location `json:"location"`
+
+ // Optional. Message is a venue, information about the venue.
+ Venue *Venue `json:"venue"`
+
+ // Optional. Message is a native poll, information about the poll.
+ Poll *Poll `json:"poll"`
+
+ // Optional. Message is a dice with random value from 1 to 6.
+ Dice *Dice `json:"dice"`
+
+ // Optional. New members that were added to the group or supergroup
+ // and information about them (the bot itself may be one of these
+ // members).
+ NewMembers []*User `json:"new_chat_members"`
+
+ // Optional. A member was removed from the group, information about
+ // them (this member may be the bot itself).
+ LeftMembers []*User `json:"left_chat_members"`
+
+ // Optional. A chat title was changed to this value.
+ NewChatTitle string `json:"new_chat_title"`
+
+ // Optional. A chat photo was change to this value.
+ NewChatPhoto []PhotoSize `json:"new_chat_photo"`
+
+ // Optional. Service message: the chat photo was deleted.
+ IsChatPhotoDeleted bool `json:"delete_chat_photo"`
+
+ // Optional. Service message: the group has been created.
+ IsGroupChatCreated bool `json:"group_chat_created"`
+
+ // Optional. Service message: the supergroup has been created. This
+ // field can‘t be received in a message coming through updates,
+ // because bot can’t be a member of a supergroup when it is created.
+ // It can only be found in reply_to_message if someone replies to a
+ // very first message in a directly created supergroup.
+ IsSupergroupChatCreated bool `json:"supergroup_chat_created"`
+
+ // Optional. Service message: the channel has been created.
+ // This field can‘t be received in a message coming through updates,
+ // because bot can’t be a member of a channel when it is created.
+ // It can only be found in reply_to_message if someone replies to a
+ // very first message in a channel.
+ IsChannelChatCreated bool `json:"channel_chat_created"`
+
+ // Optional. The group has been migrated to a supergroup with the
+ // specified identifier.
+ MigrateToChatID int64 `json:"migrate_to_chat_id"`
+
+ // Optional. The supergroup has been migrated from a group with the
+ // specified identifier.
+ MigrateFromChatID int64 `json:"migrate_from_chat_id"`
+
+ // Optional. 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"`
+
+ // Optional. Message is an invoice for a payment, information about
+ // the invoice.
+ Invoice *Invoice `json:"invoice"`
+
+ // Optional. Message is a service message about a successful payment,
+ // information about the payment.
+ SuccessfulPayment *SuccessfulPayment `json:"successful_payment"`
+
+ // Optional. The domain name of the website on which the user has
+ // logged in.
+ ConnectedWebsite string `json:"connected_website"`
+
+ // Optional. Telegram Passport data.
+ PassportData *PassportData `json:"passport_data"`
+
+ // Optional. Inline keyboard attached to the message.
+ ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup"`
+
+ Command string // It will contains the Command name.
+ CommandArgs string // It will contains the Command arguments.
+}
+
+//
+// parseCommandArgs parse the Text to get the command and its arguments.
+//
+func (msg *Message) parseCommandArgs() bool {
+ var cmdEntity *MessageEntity
+
+ for x, ent := range msg.Entities {
+ if ent.Type == EntityTypeBotCommand {
+ cmdEntity = &msg.Entities[x]
+ break
+ }
+ }
+ if cmdEntity == nil {
+ return false
+ }
+
+ msg.Command = msg.Text[cmdEntity.Offset+1 : cmdEntity.Length]
+ start := cmdEntity.Offset + cmdEntity.Length
+ msg.CommandArgs = strings.TrimSpace(msg.Text[start:])
+
+ return true
+}
diff --git a/api/telegram/bot/message_entity.go b/api/telegram/bot/message_entity.go
new file mode 100644
index 00000000..efff39c9
--- /dev/null
+++ b/api/telegram/bot/message_entity.go
@@ -0,0 +1,49 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// List of message entity types
+const (
+ EntityTypeMention = "mention" // @username
+ EntityTypeHashtag = "hashtag" // #hashtag
+ EntityTypeBotCommand = "bot_command" // /start@jobs_bot
+ EntityTypeURL = "url" // https://x.y
+ EntityTypeEmail = "email" // a@b.c
+ EntityTypePhoneNumber = "phone_number" //+1-234
+ EntityTypeBold = "bold" // bold text
+ EntityTypeItalic = "italic" // italic text
+ EntityTypeUnderline = "underline" // underlined text
+ EntityTypeStrikethrough = "strikethrough" // strikethrough text
+ EntityTypeCode = "code" // monowidth string
+ EntityTypePre = "pre" // monowidth block
+ EntityTypeTextLink = "text_link" // for clickable text URLs
+ EntityTypeTextMention = "text_mention" // for users without usernames.
+)
+
+//
+// MessageEntity represents one special entity in a text message. For example,
+// hashtags, usernames, URLs, etc.
+//
+type MessageEntity struct {
+ // Type of the entity.
+ Type string `json:"type"`
+
+ // 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"`
+
+ // Optional. For “text_link” only, url that will be opened after user
+ // taps on the text.
+ URL string `json:"url"`
+
+ // Optional. For “text_mention” only, the mentioned user.
+ User *User `json:"user"`
+
+ // Optional. For “pre” only, the programming language of the entity
+ // text.
+ Language string `json:"language"`
+}
diff --git a/api/telegram/bot/message_forward.go b/api/telegram/bot/message_forward.go
new file mode 100644
index 00000000..f3e74346
--- /dev/null
+++ b/api/telegram/bot/message_forward.go
@@ -0,0 +1,30 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+type MessageForward struct {
+ // Optional. For messages forwarded from channels, identifier of the
+ // original message in the channel.
+ ForwardID int64 `json:"forward_from_message_id"`
+
+ // Optional. For forwarded messages, date the original message was
+ // sent in Unix time.
+ ForwardDate int64 `json:"forward_date"`
+
+ // Optional. For forwarded messages, sender of the original message.
+ ForwardFrom *User `json:"forward_from"`
+
+ // Optional. For messages forwarded from channels, information about
+ // the original channel.
+ ForwardChat *Chat `json:"forward_from_chat"`
+
+ // Optional. For messages forwarded from channels, signature of the post
+ // author if present.
+ ForwardSignature string `json:"forward_signature"`
+
+ // Optional. Sender's name for messages forwarded from users who
+ // disallow adding a link to their account in forwarded messages.
+ ForwardSenderName string `json:"forward_sender_name"`
+}
diff --git a/api/telegram/bot/message_request.go b/api/telegram/bot/message_request.go
new file mode 100644
index 00000000..17f8ce7b
--- /dev/null
+++ b/api/telegram/bot/message_request.go
@@ -0,0 +1,37 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// messageRequest represents internal message to be used on sendMessage
+//
+type messageRequest struct {
+ // Unique identifier for the target chat or username of the target
+ // channel (in the format @channelusername).
+ ChatID interface{} `json:"chat_id"`
+
+ // Text of the message to be sent, 1-4096 characters after entities
+ // parsing.
+ Text string `json:"text"`
+
+ // Send Markdown or HTML, if you want Telegram apps to show bold,
+ // italic, fixed-width text or inline URLs in your bot's message.
+ ParseMode string `json:"parse_mode,omitempty"`
+
+ // Disables link previews for links in this message.
+ DisableWebPagePreview bool `json:"disable_web_page_preview,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 int64 `json:"reply_to_message_id,omitempty"`
+
+ // Additional interface options. A JSON-serialized object for an
+ // inline keyboard, custom reply keyboard, instructions to remove
+ // reply keyboard or to force a reply from the user.
+ ReplyMarkup interface{} `json:"reply_markup,omitempty"`
+}
diff --git a/api/telegram/bot/options.go b/api/telegram/bot/options.go
new file mode 100644
index 00000000..f798ca11
--- /dev/null
+++ b/api/telegram/bot/options.go
@@ -0,0 +1,93 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import (
+ "errors"
+ "os"
+)
+
+const (
+ // EnvToken define the environment variable for setting the Telegram
+ // Bot token.
+ // The environment variable has higher priority than Options parameter
+ // that passed in New() function.
+ EnvToken = "TELEGRAM_TOKEN"
+
+ // EnvWebhookURL define the environment variable for setting the
+ // Telegram Webhook URL.
+ // The environment variable has higher priority than Options parameter
+ // that passed in New() function.
+ EnvWebhookURL = "TELEGRAM_WEBHOOK_URL"
+)
+
+const (
+ defListenAddress = ":80"
+ defListenAddressTLS = ":443"
+)
+
+type UpdateHandler func(update Update)
+
+//
+// Options to create new Bot.
+//
+type Options struct {
+ // Required. Your Bot authentication token.
+ // This option will be overriden by environment variable
+ // TELEGRAM_TOKEN.
+ Token string
+
+ // Required. The function that will be called when Bot receiving
+ // Updates.
+ HandleUpdate UpdateHandler
+
+ // Optional. Set this options if the Bot want to receive updates
+ // using Webhook.
+ Webhook *Webhook
+}
+
+//
+// init check for required fields and initialize empty fields with default
+// value.
+//
+func (opts *Options) init() (err error) {
+ // Set the Telegram token and Webhook URL from environment, if its not
+ // empty.
+ env := os.Getenv(EnvToken)
+ if len(env) > 0 {
+ opts.Token = env
+ }
+ env = os.Getenv(EnvWebhookURL)
+ if len(env) > 0 {
+ if opts.Webhook == nil {
+ opts.Webhook = &Webhook{}
+ }
+ opts.Webhook.URL = env
+ }
+
+ if len(opts.Token) == 0 {
+ return errors.New("empty Token")
+ }
+ if opts.HandleUpdate == nil {
+ return errors.New("field HandleUpdate must be set to non nil")
+ }
+ if opts.Webhook == nil {
+ return errors.New("empty Webhook URL")
+ }
+ if len(opts.Webhook.URL) == 0 {
+ // Even thought empty URL is allowed by API, which
+ // means to clear the previous setWebhook, use the
+ // DeleteWebhook instead for consistency.
+ return errors.New("empty Webhook URL")
+ }
+ if len(opts.Webhook.ListenAddress) == 0 {
+ if opts.Webhook.ListenCertificate == nil {
+ opts.Webhook.ListenAddress = defListenAddress
+ } else {
+ opts.Webhook.ListenAddress = defListenAddressTLS
+ }
+ }
+ return nil
+}
diff --git a/api/telegram/bot/order_info.go b/api/telegram/bot/order_info.go
new file mode 100644
index 00000000..ca003cbc
--- /dev/null
+++ b/api/telegram/bot/order_info.go
@@ -0,0 +1,22 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// OrderInfo represents information about an order.
+//
+type OrderInfo struct {
+ // Optional. User name
+ Name string `json:"name"`
+
+ // Optional. User's phone number
+ PhoneNumber string `json:"phone_number"`
+
+ // Optional. User email
+ Email string `json:"email"`
+
+ // Optional. User shipping address
+ ShippingAddress *ShippingAddress `json:"shipping_address"`
+}
diff --git a/api/telegram/bot/passport_data.go b/api/telegram/bot/passport_data.go
new file mode 100644
index 00000000..2adac683
--- /dev/null
+++ b/api/telegram/bot/passport_data.go
@@ -0,0 +1,17 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// PassportData contains information about Telegram Passport data shared with
+// the bot by the user.
+type PassportData struct {
+ // Array with information about documents and other Telegram Passport
+ // elements that was shared with the bot.
+ data []EncryptedPassportElement
+
+ // Encrypted credentials required to decrypt the data.
+ Credentials EncryptedCredentials
+}
diff --git a/api/telegram/bot/passport_file.go b/api/telegram/bot/passport_file.go
new file mode 100644
index 00000000..e8f001d8
--- /dev/null
+++ b/api/telegram/bot/passport_file.go
@@ -0,0 +1,27 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// PassportFile represents a file uploaded to Telegram Passport.
+// Currently all Telegram Passport files are in JPEG format when decrypted and
+// don't exceed 10MB.
+//
+type PassportFile struct {
+ // Identifier for this file, which can be used to download or reuse
+ // the file.
+ FileID string `json:"file_id"`
+
+ // Unique identifier for this file, which is supposed to be the same
+ // over time and for different bots. Can't be used to download or
+ // reuse the file.
+ FileUniqueID string `json:"file_unique_id"`
+
+ // File size.
+ FileSize int `json:"file_size"`
+
+ // Unix time when the file was uploaded.
+ FileDate int `json:"file_date"`
+}
diff --git a/api/telegram/bot/photo_size.go b/api/telegram/bot/photo_size.go
new file mode 100644
index 00000000..1eabf6c2
--- /dev/null
+++ b/api/telegram/bot/photo_size.go
@@ -0,0 +1,28 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// PhotoSize represents one size of a photo or a file / sticker thumbnail.
+//
+type PhotoSize struct {
+ // Identifier for this file, which can be used to download or reuse
+ // the file.
+ FileID string `json:"file_id"`
+
+ // Unique identifier for this file, which is supposed to be the same
+ // over time and for different bots. Can't be used to download or
+ // reuse the file.
+ FileUniqueID string `json:"file_unique_id"`
+
+ // Photo width.
+ Width int `json:"width"`
+
+ // Photo height.
+ Height int `json:"height"`
+
+ // Optional. File size.
+ FileSize int `json:"file_size"`
+}
diff --git a/api/telegram/bot/poll.go b/api/telegram/bot/poll.go
new file mode 100644
index 00000000..1c3a7829
--- /dev/null
+++ b/api/telegram/bot/poll.go
@@ -0,0 +1,40 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Poll contains information about a poll.
+//
+type Poll struct {
+ // Unique poll identifier
+ ID string `json:"id"`
+
+ // Poll type, currently can be “regular” or “quiz”.
+ Type string `json:"type"`
+
+ // Poll question, 1-255 characters.
+ Question string `json:"question"`
+
+ // List of poll options.
+ Options []PollOption `json:"options"`
+
+ // Optional. 0-based identifier of the correct answer option.
+ // Available only for polls in the quiz mode, which are closed, or was
+ // sent (not forwarded) by the bot or to the private chat with the
+ // bot.
+ CorrectOptionID int `json:"correct_option_id"`
+
+ // Total number of users that voted in the poll.
+ TotalVoterCount int `json:"total_voter_count"`
+
+ // True, if the poll is closed.
+ IsClosed bool `json:"is_closed"`
+
+ // True, if the poll is anonymous.
+ IsAnonymous bool `json:"is_anonymous"`
+
+ // True, if the poll allows multiple answers.
+ AllowsMultipleAnswers bool `json:"allow_multiple_answers"`
+}
diff --git a/api/telegram/bot/poll_answer.go b/api/telegram/bot/poll_answer.go
new file mode 100644
index 00000000..678fb8ff
--- /dev/null
+++ b/api/telegram/bot/poll_answer.go
@@ -0,0 +1,20 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// PollAnswer represents an answer of a user in a non-anonymous poll.
+//
+type PollAnswer struct {
+ // Unique poll identifier
+ PollID string `json:"poll_id"`
+
+ // The user, who changed the answer to the poll.
+ User *User `json:"user"`
+
+ // 0-based identifiers of answer options, chosen by the user. May be
+ // empty if the user retracted their vote.
+ OptionIDs []int `json:"option_ids"`
+}
diff --git a/api/telegram/bot/poll_option.go b/api/telegram/bot/poll_option.go
new file mode 100644
index 00000000..fb2888be
--- /dev/null
+++ b/api/telegram/bot/poll_option.go
@@ -0,0 +1,16 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// PollOption contains information about one answer option in a poll.
+//
+type PollOption struct {
+ // Option text, 1-100 characters.
+ Text string `json:"text"`
+
+ // Number of users that voted for this option.
+ VoterCount int `json:"voter_count"`
+}
diff --git a/api/telegram/bot/pre_checkout_query.go b/api/telegram/bot/pre_checkout_query.go
new file mode 100644
index 00000000..dec14be7
--- /dev/null
+++ b/api/telegram/bot/pre_checkout_query.go
@@ -0,0 +1,36 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// PreCheckoutQuery contains information about an incoming pre-checkout query.
+//
+type PreCheckoutQuery struct {
+ // 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"`
+
+ // 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 digits past the decimal point for each currency (2 for the
+ // majority of currencies).
+ TotalAmount int `json:"total_amount"`
+
+ // Bot specified invoice payload.
+ InvoicePayload string `json:"invoice_payload"`
+
+ // Optional. Identifier of the shipping option chosen by the user.
+ ShippingOptionID string `json:"shipping_option_id"`
+
+ // Optional. Order info provided by the user.
+ OrderInfo *OrderInfo `json:"order_info"`
+}
diff --git a/api/telegram/bot/response.go b/api/telegram/bot/response.go
new file mode 100644
index 00000000..74f2503b
--- /dev/null
+++ b/api/telegram/bot/response.go
@@ -0,0 +1,59 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/shuLhan/share/lib/errors"
+)
+
+const (
+ errMigrateToChatID = "The group has been migrated to a supergroup with the specified ID %d"
+ errFloodControl = "Client exceeding flood control, retry after %d seconds"
+)
+
+//
+// response is the internal, generic response from API.
+//
+type response struct {
+ Ok bool `json:"ok"`
+ Description string `json:"description"`
+ ErrorCode int `json:"error_code"`
+ Parameters *responseParameters `json:"parameters"`
+ Result interface{} `json:"result"`
+}
+
+//
+// unpack the JSON response.
+//
+// Any non Ok response will be returned as lib/errors.E with following
+// mapping: Description become E.Message, ErrorCode become E.Code.
+//
+func (res *response) unpack(in []byte) (err error) {
+ err = json.Unmarshal(in, res)
+ if err != nil {
+ return fmt.Errorf("bot: response.unpack: %w", err)
+ }
+ if !res.Ok {
+ var paramsInfo string
+ if res.Parameters != nil {
+ if res.Parameters.MigrateToChatID != 0 {
+ paramsInfo = fmt.Sprintf(errMigrateToChatID,
+ res.Parameters.MigrateToChatID)
+ }
+ if res.Parameters.RetryAfter != 0 {
+ paramsInfo += fmt.Sprintf(errFloodControl,
+ res.Parameters.RetryAfter)
+ }
+ }
+ return &errors.E{
+ Code: res.ErrorCode,
+ Message: res.Description + "." + paramsInfo,
+ }
+ }
+ return nil
+}
diff --git a/api/telegram/bot/response_parameters.go b/api/telegram/bot/response_parameters.go
new file mode 100644
index 00000000..8ff2e6b8
--- /dev/null
+++ b/api/telegram/bot/response_parameters.go
@@ -0,0 +1,23 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// responseParameters contains information about why a request was
+// unsuccessful.
+//
+type responseParameters struct {
+ // Optional. The group has been migrated to a supergroup with the
+ // specified identifier. This number may be greater than 32 bits and
+ // some programming languages may have difficulty/silent defects in
+ // interpreting it. But it is smaller than 52 bits, so a signed 64 bit
+ // integer or double-precision float type are safe for storing this
+ // identifier.
+ MigrateToChatID int `json:"migrate_to_chat_id"`
+
+ // Optional. In case of exceeding flood control, the number of seconds
+ // left to wait before the request can be repeated.
+ RetryAfter int `json:"retry_after"`
+}
diff --git a/api/telegram/bot/shipping_address.go b/api/telegram/bot/shipping_address.go
new file mode 100644
index 00000000..230cb6fe
--- /dev/null
+++ b/api/telegram/bot/shipping_address.go
@@ -0,0 +1,28 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// ShippingAddress represents a shipping address.
+//
+type ShippingAddress struct {
+ // ISO 3166-1 alpha-2 country code.
+ CountryCode string `json:"country_code"`
+
+ // State, if applicable.
+ State string `json:"state"`
+
+ // City.
+ City string `json:"city"`
+
+ // First line for the address.
+ StreetLine1 string `json:"street_line1"`
+
+ // Second line for the address.
+ StreetLine2 string `json:"street_line2"`
+
+ // Address post code.
+ PostCode string `json:"post_code"`
+}
diff --git a/api/telegram/bot/shipping_query.go b/api/telegram/bot/shipping_query.go
new file mode 100644
index 00000000..dabb129c
--- /dev/null
+++ b/api/telegram/bot/shipping_query.go
@@ -0,0 +1,22 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// ShippingQuery contains information about an incoming shipping query.
+//
+type ShippingQuery struct {
+ // 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 specified shipping address.
+ ShippingAddress *ShippingAddress `json:"shipping_address"`
+}
diff --git a/api/telegram/bot/sticker.go b/api/telegram/bot/sticker.go
new file mode 100644
index 00000000..095c30b5
--- /dev/null
+++ b/api/telegram/bot/sticker.go
@@ -0,0 +1,31 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Sticker represents a sticker.
+//
+type Sticker struct {
+ Document
+
+ // Sticker width.
+ Width int `json:"width"`
+
+ // Sticker height.
+ Height int `json:"height"`
+
+ // Optional. Emoji associated with the sticker.
+ Emoji string `json:"emoji"`
+
+ // Optional. Name of the sticker set to which the sticker belongs.
+ SetName string `json:"set_name"`
+
+ // Optional. For mask stickers, the position where the mask should be
+ // placed.
+ MaskPosition *MaskPosition `json:"mask_position"`
+
+ // True, if the sticker is animated.
+ IsAnimated bool `json:"is_animated"`
+}
diff --git a/api/telegram/bot/successful_payment.go b/api/telegram/bot/successful_payment.go
new file mode 100644
index 00000000..117d8101
--- /dev/null
+++ b/api/telegram/bot/successful_payment.go
@@ -0,0 +1,35 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// SuccessfulPayment contains basic information about a successful payment.
+//
+type SuccessfulPayment struct {
+ // Three-letter ISO 4217 currency code.
+ Currency string `json:"currency"`
+
+ // 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 digits past the decimal point for each currency (2 for the
+ // majority of currencies).
+ TotalAmount int `json:"total_amount"`
+
+ // Bot specified invoice payload.
+ InvoicePayload string `json:"invoice_payload"`
+
+ // Optional. Identifier of the shipping option chosen by the user.
+ ShippingOptionID string `json:"shipping_option_id"`
+
+ // Optional. Order info provided by the user.
+ OrderInfo *OrderInfo `json:"order_info"`
+
+ // Telegram payment identifier.
+ TelegramPaymentChargeID string `json:"telegram_payment_charge_id"`
+
+ // Provider payment identifier.
+ ProviderPaymentChargeID string `json:"telegram_payment_charge_id"`
+}
diff --git a/api/telegram/bot/update.go b/api/telegram/bot/update.go
new file mode 100644
index 00000000..b0c8cbc3
--- /dev/null
+++ b/api/telegram/bot/update.go
@@ -0,0 +1,65 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Update an object represents an incoming update.
+// At most one of the optional parameters can be present in any given update.
+//
+type Update struct {
+ // The update‘s unique identifier.
+ // Update identifiers start from a certain positive number and
+ // increase sequentially.
+ // This ID becomes especially handy if you’re using Webhooks, since it
+ // allows you to ignore repeated updates or to restore the correct
+ // update sequence, should they get out of order.
+ // If there are no new updates for at least a week, then identifier of
+ // the next update will be chosen randomly instead of sequentially.
+ ID int64 `json:"update_id"`
+
+ // Optional. New incoming message of any kind — text, photo, sticker,
+ // etc.
+ Message *Message `json:"Message"`
+
+ // Optional. New version of a message that is known to the bot and was
+ // edited.
+ EditedMessage *Message `json:"edited_message"`
+
+ // Optional. New incoming channel post of any kind — text, photo,
+ // sticker, etc..
+ ChannelPost *Message `json:"channel_post"`
+
+ // Optional. New version of a channel post that is known to the bot
+ // and was edited.
+ EditedChannelPost *Message `json:"edited_channel_post"`
+
+ // Optional. New incoming inline query.
+ InlineQuery *InlineQuery `json:"inline_query"`
+
+ // Optional. The result of an inline query that was chosen by a user
+ // and sent to their chat partner.
+ // Please see our documentation on the feedback collecting for details
+ // on how to enable these updates for your bot.
+ ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result"`
+
+ // Optional. New incoming callback query.
+ CallbackQuery *CallbackQuery `json:"callback_query"`
+
+ // Optional. New incoming shipping query. Only for invoices with
+ // flexible price.
+ ShippingQuery *ShippingQuery `json:"shipping_query"`
+
+ // Optional. New incoming pre-checkout query. Contains full
+ // information about checkout.
+ PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"`
+
+ // Optional. New poll state. Bots receive only updates about stopped
+ // polls and polls, which are sent by the bot.
+ Poll *Poll `json:"poll"`
+
+ // Optional. A user changed their answer in a non-anonymous poll. Bots
+ // receive new votes only in polls that were sent by the bot itself.
+ PollAnswer *PollAnswer `json:"poll_answer"`
+}
diff --git a/api/telegram/bot/user.go b/api/telegram/bot/user.go
new file mode 100644
index 00000000..fee31e63
--- /dev/null
+++ b/api/telegram/bot/user.go
@@ -0,0 +1,40 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// User represents a Telegram user or bot.
+//
+type User struct {
+ // Unique identifier for this user or bot
+ ID int `json:"id"`
+
+ // User‘s or bot’s first name.
+ FirstName string `json:"first_name"`
+
+ // Optional. User‘s or bot’s last name.
+ LastName string `json:"last_name"`
+
+ // Optional. User‘s or bot’s username.
+ Username string `json:"username"`
+
+ // Optional. IETF language tag of the user's language.
+ LanguageCode string `json:"language_code"`
+
+ // True, if this user is a bot
+ IsBot bool `json:"is_bot"`
+
+ // Optional. True, if the bot can be invited to groups. Returned only
+ // in getMe.
+ CanJoinGroups bool `json:"can_join_groups"`
+
+ // Optional. True, if privacy mode is disabled for the bot. Returned
+ // only in getMe.
+ CanReadAllGroupMessages bool `json:"can_read_all_group_messages"`
+
+ // Optional. True, if the bot supports inline queries. Returned only
+ // in getMe.
+ SupportsInlineQueries bool `json:"supports_inline_queries"`
+}
diff --git a/api/telegram/bot/venue.go b/api/telegram/bot/venue.go
new file mode 100644
index 00000000..edfb8298
--- /dev/null
+++ b/api/telegram/bot/venue.go
@@ -0,0 +1,25 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Venue represents a venue.
+type Venue struct {
+ // Venue location.
+ Location Location `json:"location"`
+
+ // Name of the venue.
+ Title string `json:"title"`
+
+ // Address of the venue.
+ Address string `json:"address"`
+
+ // Optional. Foursquare identifier of the venue.
+ FoursquareID string `json:"foursquare_id"`
+
+ // Optional. Foursquare type of the venue. (For example,
+ // “arts_entertainment/default”, “arts_entertainment/aquarium” or
+ // “food/icecream”).
+ FoursquareType string `json:"foursquare_type"`
+}
diff --git a/api/telegram/bot/video.go b/api/telegram/bot/video.go
new file mode 100644
index 00000000..4b742326
--- /dev/null
+++ b/api/telegram/bot/video.go
@@ -0,0 +1,18 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// Video represents a video file.
+//
+type Video struct {
+ Document
+
+ Width int `json:"width"` // Video width as defined by sender.
+ Height int `json:"height"` // Video height as defined by sender.
+
+ // Duration of the video in seconds as defined by sender.
+ Duration int `json:"duration"`
+}
diff --git a/api/telegram/bot/video_note.go b/api/telegram/bot/video_note.go
new file mode 100644
index 00000000..e9f9314e
--- /dev/null
+++ b/api/telegram/bot/video_note.go
@@ -0,0 +1,20 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// VideoNote represents a video message (available in Telegram apps as of
+// v.4.0).
+//
+type VideoNote struct {
+ Document
+
+ // Video width and height (diameter of the video message) as defined
+ // by sender.
+ Length int `json:"length"`
+
+ // Duration of the video in seconds as defined by sender.
+ Duration int `json:"duration"`
+}
diff --git a/api/telegram/bot/voice.go b/api/telegram/bot/voice.go
new file mode 100644
index 00000000..affcc3b4
--- /dev/null
+++ b/api/telegram/bot/voice.go
@@ -0,0 +1,8 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+// Voice represents a voice note.
+type Voice Audio
diff --git a/api/telegram/bot/webhook.go b/api/telegram/bot/webhook.go
new file mode 100644
index 00000000..cbaf7eac
--- /dev/null
+++ b/api/telegram/bot/webhook.go
@@ -0,0 +1,47 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+import "crypto/tls"
+
+//
+// Webhook contains options to set Webhook to receive updates.
+//
+type Webhook struct {
+ // HTTPS url to send updates to.
+ // This option will be overriden by environment variable
+ // TELEGRAM_WEBHOOK_URL.
+ URL string
+
+ // Optional. Upload your public key certificate so that the root
+ // certificate in use can be checked.
+ Certificate []byte
+
+ // Optional.
+ // Maximum allowed number of simultaneous HTTPS connections
+ // to the webhook for update delivery, 1-100.
+ // Defaults to 40.
+ // Use lower values to limit the load on your bot‘s server, and higher
+ // values to increase your bot’s throughput.
+ MaxConnections int
+
+ // Optional. A JSON-serialized list of the update types you want your
+ // bot to receive.
+ // For example, specify ["message", "edited_channel_post",
+ // "callback_query"] to only receive updates of these types.
+ //
+ // Specify an empty list to receive all updates regardless of type
+ // (default). If not specified, the previous setting will be used.
+ AllowedUpdates []string
+
+ // Optional. The address and port where the Bot will listen for
+ // Webhook in the following format "<address>:<port>".
+ // Default to ":80" if ListenCertificate is nil or ":443" if not nil.
+ ListenAddress string
+
+ // Optional. The certificate for Bot server when listening for
+ // Webhook.
+ ListenCertificate *tls.Certificate
+}
diff --git a/api/telegram/bot/webhook_info.go b/api/telegram/bot/webhook_info.go
new file mode 100644
index 00000000..2206bf9b
--- /dev/null
+++ b/api/telegram/bot/webhook_info.go
@@ -0,0 +1,37 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bot
+
+//
+// WebhookInfo contains information about the current status of a webhook.
+//
+type WebhookInfo struct {
+ // Webhook URL, may be empty if webhook is not set up.
+ Url string `json:"url"`
+
+ // Number of updates awaiting delivery
+ PendingUpdateCount int `json:"pending_update_count"`
+
+ // Optional. Unix time for the most recent error that happened when
+ // trying to deliver an update via webhook.
+ LastErrorDate int `json:"last_error_date"`
+
+ // Optional. 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"`
+
+ // Optional. Maximum allowed number of simultaneous HTTPS connections
+ // to the webhook for update delivery.
+ MaxConnections int `json:"max_connections"`
+
+ // Optional. A list of update types the bot is subscribed to. Defaults
+ // to all update types.
+ AllowedUpdates []string `json:"allowed_updates"`
+
+ // True, if a custom certificate was provided for webhook certificate
+ // checks.
+ HasCustomCertificate bool `json:"has_custom_certificate"`
+}