diff --git a/bridge/telegram/handlers.go b/bridge/telegram/handlers.go index 5ffb1eca..985397ef 100644 --- a/bridge/telegram/handlers.go +++ b/bridge/telegram/handlers.go @@ -11,7 +11,7 @@ import ( "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/helper" "github.com/davecgh/go-spew/spew" - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + tgbotapi "github.com/matterbridge/telegram-bot-api/v6" ) func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *tgbotapi.Message) *tgbotapi.Message { @@ -20,6 +20,11 @@ func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited * if posted.Text == "/chatId" { chatID := strconv.FormatInt(posted.Chat.ID, 10) + // Handle chat topics + if posted.IsTopicMessage { + chatID = chatID + "/" + strconv.Itoa(posted.MessageThreadID) + } + _, err := b.Send(config.Message{ Channel: chatID, Text: fmt.Sprintf("ID of this chat: %s", chatID), @@ -91,7 +96,8 @@ func (b *Btelegram) handleForwarded(rmsg *config.Message, message *tgbotapi.Mess // handleQuoting handles quoting of previous messages func (b *Btelegram) handleQuoting(rmsg *config.Message, message *tgbotapi.Message) { - if message.ReplyToMessage != nil { + // Used to check if the message was a reply to the root topic + if message.ReplyToMessage != nil && !(message.ReplyToMessage.MessageID == message.MessageThreadID) { //nolint:nestif usernameReply := "" if message.ReplyToMessage.From != nil { if b.GetBool("UseFirstName") { @@ -211,9 +217,14 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) { // set the ID's from the channel or group message rmsg.ID = strconv.Itoa(message.MessageID) rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10) + if message.MessageThreadID != 0 { + rmsg.Channel += "/" + strconv.Itoa(message.MessageThreadID) + } // preserve threading from telegram reply - if message.ReplyToMessage != nil { + if message.ReplyToMessage != nil && + // Used to check if the message was a reply to the root topic + !(message.ReplyToMessage.MessageID == message.MessageThreadID) { rmsg.ParentID = strconv.Itoa(message.ReplyToMessage.MessageID) } @@ -326,12 +337,12 @@ func (b *Btelegram) maybeConvertWebp(name *string, data *[]byte) { // handleDownloadFile handles file download func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error { - size := 0 + size := int64(0) var url, name, text string switch { case message.Sticker != nil: text, name, url = b.getDownloadInfo(message.Sticker.FileID, ".webp", true) - size = message.Sticker.FileSize + size = int64(message.Sticker.FileSize) case message.Voice != nil: text, name, url = b.getDownloadInfo(message.Voice.FileID, ".ogg", true) size = message.Voice.FileSize @@ -348,7 +359,7 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa text = " " + message.Document.FileName + " : " + url case message.Photo != nil: photos := message.Photo - size = photos[len(photos)-1].FileSize + size = int64(photos[len(photos)-1].FileSize) text, name, url = b.getDownloadInfo(photos[len(photos)-1].FileID, "", true) } @@ -452,7 +463,7 @@ func (b *Btelegram) handleEdit(msg *config.Message, chatid int64) (string, error } // handleUploadFile handles native upload of files -func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, parentID int) (string, error) { +func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, threadid int, parentID int) (string, error) { var media []interface{} for _, f := range msg.Extra["file"] { fi := f.(config.FileInfo) @@ -502,7 +513,7 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64, parentID } } - return b.sendMediaFiles(msg, chatid, parentID, media) + return b.sendMediaFiles(msg, chatid, threadid, parentID, media) } func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string { diff --git a/bridge/telegram/telegram.go b/bridge/telegram/telegram.go index 43cb818f..e7885ca3 100644 --- a/bridge/telegram/telegram.go +++ b/bridge/telegram/telegram.go @@ -10,7 +10,7 @@ import ( "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/helper" - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + tgbotapi "github.com/matterbridge/telegram-bot-api/v6" ) const ( @@ -86,11 +86,41 @@ func TGGetParseMode(b *Btelegram, username string, text string) (textout string, return textout, parsemode } +func (b *Btelegram) getIds(channel string) (int64, int, error) { + var chatid int64 + topicid := 0 + + // get the chatid + if strings.Contains(channel, "/") { //nolint:nestif + s := strings.Split(channel, "/") + if len(s) < 2 { + b.Log.Errorf("Invalid channel format: %#v\n", channel) + return 0, 0, nil + } + id, err := strconv.ParseInt(s[0], 10, 64) + if err != nil { + return 0, 0, err + } + chatid = id + tid, err := strconv.Atoi(s[1]) + if err != nil { + return 0, 0, err + } + topicid = tid + } else { + id, err := strconv.ParseInt(channel, 10, 64) + if err != nil { + return 0, 0, err + } + chatid = id + } + return chatid, topicid, nil +} + func (b *Btelegram) Send(msg config.Message) (string, error) { b.Log.Debugf("=> Receiving %#v", msg) - // get the chatid - chatid, err := strconv.ParseInt(msg.Channel, 10, 64) + chatid, topicid, err := b.getIds(msg.Channel) if err != nil { return "", err } @@ -123,13 +153,13 @@ func (b *Btelegram) Send(msg config.Message) (string, error) { // Upload a file if it exists if msg.Extra != nil { for _, rmsg := range helper.HandleExtra(&msg, b.General) { - if _, msgErr := b.sendMessage(chatid, rmsg.Username, rmsg.Text, parentID); msgErr != nil { + if _, msgErr := b.sendMessage(chatid, topicid, rmsg.Username, rmsg.Text, parentID); msgErr != nil { b.Log.Errorf("sendMessage failed: %s", msgErr) } } // check if we have files to upload (from slack, telegram or mattermost) if len(msg.Extra["file"]) > 0 { - return b.handleUploadFile(&msg, chatid, parentID) + return b.handleUploadFile(&msg, chatid, topicid, parentID) } } @@ -143,7 +173,7 @@ func (b *Btelegram) Send(msg config.Message) (string, error) { // Ignore empty text field needs for prevent double messages from whatsapp to telegram // when sending media with text caption if msg.Text != "" { - return b.sendMessage(chatid, msg.Username, msg.Text, parentID) + return b.sendMessage(chatid, topicid, msg.Username, msg.Text, parentID) } return "", nil @@ -157,9 +187,12 @@ func (b *Btelegram) getFileDirectURL(id string) string { return res } -func (b *Btelegram) sendMessage(chatid int64, username, text string, parentID int) (string, error) { +func (b *Btelegram) sendMessage(chatid int64, topicid int, username, text string, parentID int) (string, error) { m := tgbotapi.NewMessage(chatid, "") m.Text, m.ParseMode = TGGetParseMode(b, username, text) + if topicid != 0 { + m.BaseChat.MessageThreadID = topicid + } m.ReplyToMessageID = parentID m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview") @@ -171,11 +204,19 @@ func (b *Btelegram) sendMessage(chatid int64, username, text string, parentID in } // sendMediaFiles native upload media files via media group -func (b *Btelegram) sendMediaFiles(msg *config.Message, chatid int64, parentID int, media []interface{}) (string, error) { +func (b *Btelegram) sendMediaFiles(msg *config.Message, chatid int64, threadid int, parentID int, media []interface{}) (string, error) { if len(media) == 0 { return "", nil } - mg := tgbotapi.MediaGroupConfig{ChatID: chatid, ChannelUsername: msg.Username, Media: media, ReplyToMessageID: parentID} + mg := tgbotapi.MediaGroupConfig{ + BaseChat: tgbotapi.BaseChat{ + ChatID: chatid, + MessageThreadID: threadid, + ChannelUsername: msg.Username, + ReplyToMessageID: parentID, + }, + Media: media, + } messages, err := b.c.SendMediaGroup(mg) if err != nil { return "", err diff --git a/go.mod b/go.mod index 91adf773..c4c8c24a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/d5/tengo/v2 v2.13.0 github.com/davecgh/go-spew v1.1.1 github.com/fsnotify/fsnotify v1.6.0 - github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c github.com/google/gops v0.3.27 github.com/gorilla/schema v1.2.0 @@ -29,6 +28,7 @@ require ( github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75 github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d + github.com/matterbridge/telegram-bot-api/v6 v6.5.0 github.com/mattermost/mattermost-server/v5 v5.39.3 github.com/mattermost/mattermost-server/v6 v6.7.2 github.com/mattn/godown v0.0.1 diff --git a/go.sum b/go.sum index ac913120..f2087fe6 100644 --- a/go.sum +++ b/go.sum @@ -607,8 +607,6 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= -github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -1085,6 +1083,8 @@ github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d97130 github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU= github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d h1:aI0ANEzy3dMv3vEAMQ80AItNie0fBR9ZxE2sAedORmM= github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d/go.mod h1:Zg8PH1P/1CNUxozQ8blnjAV9PA4Qn2qWf33cX5yNKGM= +github.com/matterbridge/telegram-bot-api/v6 v6.5.0 h1:wCnHWvt4WGhfognQsuu2OnHyqENBdJRf2mReYTCXggQ= +github.com/matterbridge/telegram-bot-api/v6 v6.5.0/go.mod h1:/hSLrs8h/xNsQglQXjwXJ92iZU8XfTGkYUQ7KVDWEVo= github.com/mattermost/go-i18n v1.11.0/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34= github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8= github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34= diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/.gitignore b/vendor/github.com/matterbridge/telegram-bot-api/v6/.gitignore similarity index 77% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/.gitignore rename to vendor/github.com/matterbridge/telegram-bot-api/v6/.gitignore index eb7a23b2..d3083e5f 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/.gitignore +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/.gitignore @@ -2,3 +2,4 @@ coverage.out tmp/ book/ +.vscode/ diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/LICENSE.txt b/vendor/github.com/matterbridge/telegram-bot-api/v6/LICENSE.txt similarity index 100% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/LICENSE.txt rename to vendor/github.com/matterbridge/telegram-bot-api/v6/LICENSE.txt diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/README.md b/vendor/github.com/matterbridge/telegram-bot-api/v6/README.md similarity index 96% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/README.md rename to vendor/github.com/matterbridge/telegram-bot-api/v6/README.md index 45d3e1db..b18d15dd 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/README.md +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/README.md @@ -85,9 +85,9 @@ func main() { log.Printf("Authorized on account %s", bot.Self.UserName) - wh, _ := tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem") + wh, _ := tgbotapi.NewWebhookWithCert("https://www.example.com:8443/"+bot.Token, "cert.pem") - _, err = bot.SetWebhook(wh) + _, err = bot.Request(wh) if err != nil { log.Fatal(err) } diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/book.toml b/vendor/github.com/matterbridge/telegram-bot-api/v6/book.toml similarity index 100% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/book.toml rename to vendor/github.com/matterbridge/telegram-bot-api/v6/book.toml diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/bot.go similarity index 94% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/bot.go index c58afec4..39037b8d 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/bot.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net/http" "net/url" @@ -151,7 +150,7 @@ func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) } // if debug, read response body - data, err := ioutil.ReadAll(responseBody) + data, err := io.ReadAll(responseBody) if err != nil { return nil, err } @@ -494,6 +493,8 @@ func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http. ch := make(chan Update, bot.Buffer) func(w http.ResponseWriter, r *http.Request) { + defer close(ch) + update, err := bot.HandleUpdate(r) if err != nil { errMsg, _ := json.Marshal(map[string]string{"error": err.Error()}) @@ -504,7 +505,6 @@ func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http. } ch <- *update - close(ch) }(w, r) return ch @@ -681,12 +681,7 @@ func (bot *BotAPI) GetMyCommandsWithConfig(config GetMyCommandsConfig) ([]BotCom // forwardMessage, but the copied message doesn't have a link to the original // message. Returns the MessageID of the sent message on success. func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) { - params, err := config.params() - if err != nil { - return MessageID{}, err - } - - resp, err := bot.MakeRequest(config.method(), params) + resp, err := bot.Request(config) if err != nil { return MessageID{}, err } @@ -697,6 +692,33 @@ func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) { return messageID, err } +// AnswerWebAppQuery sets the result of an interaction with a Web App and send a +// corresponding message on behalf of the user to the chat from which the query originated. +func (bot *BotAPI) AnswerWebAppQuery(config AnswerWebAppQueryConfig) (SentWebAppMessage, error) { + var sentWebAppMessage SentWebAppMessage + + resp, err := bot.Request(config) + if err != nil { + return sentWebAppMessage, err + } + + err = json.Unmarshal(resp.Result, &sentWebAppMessage) + return sentWebAppMessage, err +} + +// GetMyDefaultAdministratorRights gets the current default administrator rights of the bot. +func (bot *BotAPI) GetMyDefaultAdministratorRights(config GetMyDefaultAdministratorRightsConfig) (ChatAdministratorRights, error) { + var rights ChatAdministratorRights + + resp, err := bot.Request(config) + if err != nil { + return rights, err + } + + err = json.Unmarshal(resp.Result, &rights) + return rights, err +} + // EscapeText takes an input text and escape Telegram markup symbols. // In this way we can send a text without being afraid of having to escape the characters manually. // Note that you don't have to include the formatting style in the input text, or it will be escaped too. diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/configs.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/configs.go similarity index 82% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/configs.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/configs.go index 5e43af49..09c9c468 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/configs.go +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/configs.go @@ -265,7 +265,9 @@ func (CloseConfig) params() (Params, error) { // BaseChat is base type for all chat config types. type BaseChat struct { ChatID int64 // required + MessageThreadID int ChannelUsername string + ProtectContent bool ReplyToMessageID int ReplyMarkup interface{} DisableNotification bool @@ -276,9 +278,11 @@ func (chat *BaseChat) params() (Params, error) { params := make(Params) params.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) + params.AddNonZero("message_thread_id", chat.MessageThreadID) params.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) params.AddBool("disable_notification", chat.DisableNotification) params.AddBool("allow_sending_without_reply", chat.AllowSendingWithoutReply) + params.AddBool("protect_content", chat.ProtectContent) err := params.AddInterface("reply_markup", chat.ReplyMarkup) @@ -319,6 +323,21 @@ func (edit BaseEdit) params() (Params, error) { return params, err } +// BaseSpoiler is base type of structures with spoilers. +type BaseSpoiler struct { + HasSpoiler bool +} + +func (spoiler BaseSpoiler) params() (Params, error) { + params := make(Params) + + if spoiler.HasSpoiler { + params.AddBool("has_spoiler", true) + } + + return params, nil +} + // MessageConfig contains information about a SendMessage request. type MessageConfig struct { BaseChat @@ -403,6 +422,7 @@ func (config CopyMessageConfig) method() string { // PhotoConfig contains information about a SendPhoto request. type PhotoConfig struct { BaseFile + BaseSpoiler Thumb RequestFileData Caption string ParseMode string @@ -418,6 +438,15 @@ func (config PhotoConfig) params() (Params, error) { params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) err = params.AddInterface("caption_entities", config.CaptionEntities) + if err != nil { + return params, err + } + + p1, err := config.BaseSpoiler.params() + if err != nil { + return params, err + } + params.Merge(p1) return params, err } @@ -553,6 +582,7 @@ func (config StickerConfig) files() []RequestFile { // VideoConfig contains information about a SendVideo request. type VideoConfig struct { BaseFile + BaseSpoiler Thumb RequestFileData Duration int Caption string @@ -572,6 +602,15 @@ func (config VideoConfig) params() (Params, error) { params.AddNonEmpty("parse_mode", config.ParseMode) params.AddBool("supports_streaming", config.SupportsStreaming) err = params.AddInterface("caption_entities", config.CaptionEntities) + if err != nil { + return params, err + } + + p1, err := config.BaseSpoiler.params() + if err != nil { + return params, err + } + params.Merge(p1) return params, err } @@ -599,6 +638,7 @@ func (config VideoConfig) files() []RequestFile { // AnimationConfig contains information about a SendAnimation request. type AnimationConfig struct { BaseFile + BaseSpoiler Duration int Thumb RequestFileData Caption string @@ -616,6 +656,15 @@ func (config AnimationConfig) params() (Params, error) { params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) err = params.AddInterface("caption_entities", config.CaptionEntities) + if err != nil { + return params, err + } + + p1, err := config.BaseSpoiler.params() + if err != nil { + return params, err + } + params.Merge(p1) return params, err } @@ -972,13 +1021,15 @@ func (config GetGameHighScoresConfig) method() string { // ChatActionConfig contains information about a SendChatAction request. type ChatActionConfig struct { BaseChat - Action string // required + MessageThreadID int + Action string // required } func (config ChatActionConfig) params() (Params, error) { params, err := config.BaseChat.params() params["action"] = config.Action + params.AddNonZero("message_thread_id", config.MessageThreadID) return params, err } @@ -1162,6 +1213,7 @@ type WebhookConfig struct { MaxConnections int AllowedUpdates []string DropPendingUpdates bool + SecretToken string } func (config WebhookConfig) method() string { @@ -1179,6 +1231,7 @@ func (config WebhookConfig) params() (Params, error) { params.AddNonZero("max_connections", config.MaxConnections) err := params.AddInterface("allowed_updates", config.AllowedUpdates) params.AddBool("drop_pending_updates", config.DropPendingUpdates) + params.AddNonEmpty("secret_token", config.SecretToken) return params, err } @@ -1240,6 +1293,29 @@ func (config InlineConfig) params() (Params, error) { return params, err } +// AnswerWebAppQueryConfig is used to set the result of an interaction with a +// Web App and send a corresponding message on behalf of the user to the chat +// from which the query originated. +type AnswerWebAppQueryConfig struct { + // WebAppQueryID is the unique identifier for the query to be answered. + WebAppQueryID string `json:"web_app_query_id"` + // Result is an InlineQueryResult object describing the message to be sent. + Result interface{} `json:"result"` +} + +func (config AnswerWebAppQueryConfig) method() string { + return "answerWebAppQuery" +} + +func (config AnswerWebAppQueryConfig) params() (Params, error) { + params := make(Params) + + params["web_app_query_id"] = config.WebAppQueryID + err := params.AddInterface("result", config.Result) + + return params, err +} + // CallbackConfig contains information on making a CallbackQuery response. type CallbackConfig struct { CallbackQueryID string `json:"callback_query_id"` @@ -1324,8 +1400,9 @@ type KickChatMemberConfig = BanChatMemberConfig // RestrictChatMemberConfig contains fields to restrict members of chat type RestrictChatMemberConfig struct { ChatMemberConfig - UntilDate int64 - Permissions *ChatPermissions + UntilDate int64 + UseIndependentChatPermissions bool + Permissions *ChatPermissions } func (config RestrictChatMemberConfig) method() string { @@ -1337,6 +1414,7 @@ func (config RestrictChatMemberConfig) params() (Params, error) { params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) params.AddNonZero64("user_id", config.UserID) + params.AddBool("use_independent_chat_permissions", config.UseIndependentChatPermissions) err := params.AddInterface("permissions", config.Permissions) params.AddNonZero64("until_date", config.UntilDate) @@ -1353,11 +1431,12 @@ type PromoteChatMemberConfig struct { CanPostMessages bool CanEditMessages bool CanDeleteMessages bool - CanManageVoiceChats bool + CanManageVideoChats bool CanInviteUsers bool CanRestrictMembers bool CanPinMessages bool CanPromoteMembers bool + CanManageTopics bool } func (config PromoteChatMemberConfig) method() string { @@ -1376,11 +1455,12 @@ func (config PromoteChatMemberConfig) params() (Params, error) { params.AddBool("can_post_messages", config.CanPostMessages) params.AddBool("can_edit_messages", config.CanEditMessages) params.AddBool("can_delete_messages", config.CanDeleteMessages) - params.AddBool("can_manage_voice_chats", config.CanManageVoiceChats) + params.AddBool("can_manage_video_chats", config.CanManageVideoChats) params.AddBool("can_invite_users", config.CanInviteUsers) params.AddBool("can_restrict_members", config.CanRestrictMembers) params.AddBool("can_pin_messages", config.CanPinMessages) params.AddBool("can_promote_members", config.CanPromoteMembers) + params.AddBool("can_manage_topics", config.CanManageTopics) return params, nil } @@ -1500,7 +1580,8 @@ func (ChatAdministratorsConfig) method() string { // restrict members. type SetChatPermissionsConfig struct { ChatConfig - Permissions *ChatPermissions + UseIndependentChatPermissions bool + Permissions *ChatPermissions } func (SetChatPermissionsConfig) method() string { @@ -1511,6 +1592,7 @@ func (config SetChatPermissionsConfig) params() (Params, error) { params := make(Params) params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddBool("use_independent_chat_permissions", config.UseIndependentChatPermissions) err := params.AddInterface("permissions", config.Permissions) return params, err @@ -1759,6 +1841,64 @@ func (config InvoiceConfig) method() string { return "sendInvoice" } +// InvoiceLinkConfig contains information for createInvoiceLink method +type InvoiceLinkConfig struct { + Title string //Required + Description string //Required + Payload string //Required + ProviderToken string //Required + Currency string //Required + Prices []LabeledPrice //Required + MaxTipAmount int + SuggestedTipAmounts []int + ProviderData string + PhotoURL string + PhotoSize int + PhotoWidth int + PhotoHeight int + NeedName bool + NeedPhoneNumber bool + NeedEmail bool + NeedShippingAddress bool + SendPhoneNumberToProvider bool + SendEmailToProvider bool + IsFlexible bool +} + +func (config InvoiceLinkConfig) params() (Params, error) { + params := make(Params) + + params["title"] = config.Title + params["description"] = config.Description + params["payload"] = config.Payload + params["provider_token"] = config.ProviderToken + params["currency"] = config.Currency + if err := params.AddInterface("prices", config.Prices); err != nil { + return params, err + } + + params.AddNonZero("max_tip_amount", config.MaxTipAmount) + err := params.AddInterface("suggested_tip_amounts", config.SuggestedTipAmounts) + params.AddNonEmpty("provider_data", config.ProviderData) + params.AddNonEmpty("photo_url", config.PhotoURL) + params.AddNonZero("photo_size", config.PhotoSize) + params.AddNonZero("photo_width", config.PhotoWidth) + params.AddNonZero("photo_height", config.PhotoHeight) + params.AddBool("need_name", config.NeedName) + params.AddBool("need_phone_number", config.NeedPhoneNumber) + params.AddBool("need_email", config.NeedEmail) + params.AddBool("need_shipping_address", config.NeedShippingAddress) + params.AddBool("send_phone_number_to_provider", config.SendPhoneNumberToProvider) + params.AddBool("send_email_to_provider", config.SendEmailToProvider) + params.AddBool("is_flexible", config.IsFlexible) + + return params, err +} + +func (config InvoiceLinkConfig) method() string { + return "createInvoiceLink" +} + // ShippingConfig contains information for answerShippingQuery request. type ShippingConfig struct { ShippingQueryID string // required @@ -1782,7 +1922,7 @@ func (config ShippingConfig) params() (Params, error) { return params, err } -// PreCheckoutConfig conatins information for answerPreCheckoutQuery request. +// PreCheckoutConfig contains information for answerPreCheckoutQuery request. type PreCheckoutConfig struct { PreCheckoutQueryID string // required OK bool // required @@ -1979,6 +2119,24 @@ func (config GetStickerSetConfig) params() (Params, error) { return params, nil } +// GetCustomEmojiStickersConfig get information about +// custom emoji stickers by their identifiers. +type GetCustomEmojiStickersConfig struct { + CustomEmojiIDs []string +} + +func (config GetCustomEmojiStickersConfig) params() (Params, error) { + params := make(Params) + + params.AddInterface("custom_emoji_ids", config.CustomEmojiIDs) + + return params, nil +} + +func (config GetCustomEmojiStickersConfig) method() string { + return "getCustomEmojiStickers" +} + // UploadStickerConfig allows you to upload a sticker for use in a set later. type UploadStickerConfig struct { UserID int64 @@ -2013,8 +2171,9 @@ type NewStickerSetConfig struct { Title string PNGSticker RequestFileData TGSSticker RequestFileData + StickerType string Emojis string - ContainsMasks bool + ContainsMasks bool // deprecated MaskPosition *MaskPosition } @@ -2028,11 +2187,10 @@ func (config NewStickerSetConfig) params() (Params, error) { params.AddNonZero64("user_id", config.UserID) params["name"] = config.Name params["title"] = config.Title - params["emojis"] = config.Emojis params.AddBool("contains_masks", config.ContainsMasks) - + params.AddNonEmpty("sticker_type", string(config.StickerType)) err := params.AddInterface("mask_position", config.MaskPosition) return params, err @@ -2195,16 +2353,246 @@ func (config DeleteChatStickerSetConfig) params() (Params, error) { return params, nil } +// GetForumTopicIconStickersConfig allows you to get custom emoji stickers, +// which can be used as a forum topic icon by any user. +type GetForumTopicIconStickersConfig struct{} + +func (config GetForumTopicIconStickersConfig) method() string { + return "getForumTopicIconStickers" +} + +func (config GetForumTopicIconStickersConfig) params() (Params, error) { + return nil, nil +} + +// BaseForum is a base type for all forum config types. +type BaseForum struct { + ChatID int64 + SuperGroupUsername string +} + +func (config BaseForum) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + + return params, nil +} + +// CreateForumTopicConfig allows you to create a topic +// in a forum supergroup chat. +type CreateForumTopicConfig struct { + BaseForum + Name string + IconColor int + IconCustomEmojiID string +} + +func (config CreateForumTopicConfig) method() string { + return "createForumTopic" +} + +func (config CreateForumTopicConfig) params() (Params, error) { + params := make(Params) + + params.AddNonEmpty("name", config.Name) + params.AddNonZero("icon_color", config.IconColor) + params.AddNonEmpty("icon_custom_emoji_id", config.IconCustomEmojiID) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// EditForumTopicConfig allows you to edit +// name and icon of a topic in a forum supergroup chat. +type EditForumTopicConfig struct { + BaseForum + MessageThreadID int + Name string + IconCustomEmojiID string +} + +func (config EditForumTopicConfig) method() string { + return "editForumTopic" +} + +func (config EditForumTopicConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero("message_thread_id", config.MessageThreadID) + params.AddNonEmpty("name", config.Name) + params.AddNonEmpty("icon_custom_emoji_id", config.IconCustomEmojiID) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// CloseForumTopicConfig allows you to close +// an open topic in a forum supergroup chat. +type CloseForumTopicConfig struct { + BaseForum + MessageThreadID int +} + +func (config CloseForumTopicConfig) method() string { + return "closeForumTopic" +} + +func (config CloseForumTopicConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero("message_thread_id", config.MessageThreadID) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// ReopenForumTopicConfig allows you to reopen +// an closed topic in a forum supergroup chat. +type ReopenForumTopicConfig struct { + BaseForum + MessageThreadID int +} + +func (config ReopenForumTopicConfig) method() string { + return "reopenForumTopic" +} + +func (config ReopenForumTopicConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("message_thread_id", config.MessageThreadID) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// DeleteForumTopicConfig allows you to delete a forum topic +// along with all its messages in a forum supergroup chat. +type DeleteForumTopicConfig struct { + BaseForum + MessageThreadID int +} + +func (config DeleteForumTopicConfig) method() string { + return "deleteForumTopic" +} + +func (config DeleteForumTopicConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero("message_thread_id", config.MessageThreadID) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// UnpinAllForumTopicMessagesConfig allows you to clear the list +// of pinned messages in a forum topic. +type UnpinAllForumTopicMessagesConfig struct { + BaseForum + MessageThreadID int +} + +func (config UnpinAllForumTopicMessagesConfig) method() string { + return "unpinAllForumTopicMessages" +} + +func (config UnpinAllForumTopicMessagesConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("message_thread_id", config.MessageThreadID) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// UnpinAllForumTopicMessagesConfig allows you to edit the name of +// the 'General' topic in a forum supergroup chat. +// The bot must be an administrator in the chat for this to work +// and must have can_manage_topics administrator rights. Returns True on success. +type EditGeneralForumTopicConfig struct { + BaseForum + Name string +} + +func (config EditGeneralForumTopicConfig) method() string { + return "editGeneralForumTopic" +} + +func (config EditGeneralForumTopicConfig) params() (Params, error) { + params := make(Params) + + params.AddNonEmpty("name", config.Name) + + p1, _ := config.BaseForum.params() + params.Merge(p1) + + return params, nil +} + +// CloseGeneralForumTopicConfig allows you to to close an open 'General' topic +// in a forum supergroup chat. The bot must be an administrator in the chat +// for this to work and must have the can_manage_topics administrator rights. +// Returns True on success. +type CloseGeneralForumTopicConfig struct{ BaseForum } + +func (config CloseGeneralForumTopicConfig) method() string { + return "closeGeneralForumTopic" +} + +// CloseGeneralForumTopicConfig allows you to reopen a closed 'General' topic +// in a forum supergroup chat. The bot must be an administrator in the chat +// for this to work and must have the can_manage_topics administrator rights. +// The topic will be automatically unhidden if it was hidden. +// Returns True on success. +type ReopenGeneralForumTopicConfig struct{ BaseForum } + +func (config ReopenGeneralForumTopicConfig) method() string { + return "reopenGeneralForumTopic" +} + +// HideGeneralForumTopicConfig allows you to hide the 'General' topic +// in a forum supergroup chat. The bot must be an administrator in the chat +// for this to work and must have the can_manage_topics administrator rights. +// The topic will be automatically closed if it was open. +// Returns True on success. +type HideGeneralForumTopicConfig struct{ BaseForum } + +func (config HideGeneralForumTopicConfig) method() string { + return "hideGeneralForumTopic" +} + +// UnhideGeneralForumTopicConfig allows you to unhide the 'General' topic +// in a forum supergroup chat. The bot must be an administrator in the chat +// for this to work and must have the can_manage_topics administrator rights. +// Returns True on success. +type UnhideGeneralForumTopicConfig struct{ BaseForum } + +func (config UnhideGeneralForumTopicConfig) method() string { + return "unhideGeneralForumTopic" +} + // MediaGroupConfig allows you to send a group of media. // // Media consist of InputMedia items (InputMediaPhoto, InputMediaVideo). type MediaGroupConfig struct { - ChatID int64 - ChannelUsername string - - Media []interface{} - DisableNotification bool - ReplyToMessageID int + BaseChat + Media []interface{} } func (config MediaGroupConfig) method() string { @@ -2212,13 +2600,16 @@ func (config MediaGroupConfig) method() string { } func (config MediaGroupConfig) params() (Params, error) { - params := make(Params) + params, err := config.BaseChat.params() + if err != nil { + return nil, err + } params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) params.AddBool("disable_notification", config.DisableNotification) params.AddNonZero("reply_to_message_id", config.ReplyToMessageID) - err := params.AddInterface("media", prepareInputMediaForParams(config.Media)) + err = params.AddInterface("media", prepareInputMediaForParams(config.Media)) return params, err } @@ -2313,6 +2704,81 @@ func (config DeleteMyCommandsConfig) params() (Params, error) { return params, err } +// SetChatMenuButtonConfig changes the bot's menu button in a private chat, +// or the default menu button. +type SetChatMenuButtonConfig struct { + ChatID int64 + ChannelUsername string + + MenuButton *MenuButton +} + +func (config SetChatMenuButtonConfig) method() string { + return "setChatMenuButton" +} + +func (config SetChatMenuButtonConfig) params() (Params, error) { + params := make(Params) + + if err := params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername); err != nil { + return params, err + } + err := params.AddInterface("menu_button", config.MenuButton) + + return params, err +} + +type GetChatMenuButtonConfig struct { + ChatID int64 + ChannelUsername string +} + +func (config GetChatMenuButtonConfig) method() string { + return "getChatMenuButton" +} + +func (config GetChatMenuButtonConfig) params() (Params, error) { + params := make(Params) + + err := params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + + return params, err +} + +type SetMyDefaultAdministratorRightsConfig struct { + Rights ChatAdministratorRights + ForChannels bool +} + +func (config SetMyDefaultAdministratorRightsConfig) method() string { + return "setMyDefaultAdministratorRights" +} + +func (config SetMyDefaultAdministratorRightsConfig) params() (Params, error) { + params := make(Params) + + err := params.AddInterface("rights", config.Rights) + params.AddBool("for_channels", config.ForChannels) + + return params, err +} + +type GetMyDefaultAdministratorRightsConfig struct { + ForChannels bool +} + +func (config GetMyDefaultAdministratorRightsConfig) method() string { + return "getMyDefaultAdministratorRights" +} + +func (config GetMyDefaultAdministratorRightsConfig) params() (Params, error) { + params := make(Params) + + params.AddBool("for_channels", config.ForChannels) + + return params, nil +} + // prepareInputMediaParam evaluates a single InputMedia and determines if it // needs to be modified for a successful upload. If it returns nil, then the // value does not need to be included in the params. Otherwise, it will return diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/helpers.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/helpers.go similarity index 94% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/helpers.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/helpers.go index 8220e74e..bdf2ee97 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/helpers.go +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/helpers.go @@ -1,7 +1,14 @@ package tgbotapi import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" "net/url" + "sort" + "strings" ) // NewMessage creates a new Message. @@ -171,7 +178,9 @@ func NewVoice(chatID int64, file RequestFileData) VoiceConfig { // two to ten InputMediaPhoto or InputMediaVideo. func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig { return MediaGroupConfig{ - ChatID: chatID, + BaseChat: BaseChat{ + ChatID: chatID, + }, Media: files, } } @@ -567,7 +576,7 @@ func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTex } } -// NewEditMessageTextAndMarkup allows you to edit the text and replymarkup of a message. +// NewEditMessageTextAndMarkup allows you to edit the text and reply markup of a message. func NewEditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) EditMessageTextConfig { return EditMessageTextConfig{ BaseEdit: BaseEdit{ @@ -618,6 +627,15 @@ func NewKeyboardButton(text string) KeyboardButton { } } +// NewKeyboardButtonWebApp creates a keyboard button with text +// which goes to a WebApp. +func NewKeyboardButtonWebApp(text string, webapp WebAppInfo) KeyboardButton { + return KeyboardButton{ + Text: text, + WebApp: &webapp, + } +} + // NewKeyboardButtonContact creates a keyboard button that requests // user contact information upon click. func NewKeyboardButtonContact(text string) KeyboardButton { @@ -673,6 +691,15 @@ func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton { } } +// NewInlineKeyboardButtonWebApp creates an inline keyboard button with text +// which goes to a WebApp. +func NewInlineKeyboardButtonWebApp(text string, webapp WebAppInfo) InlineKeyboardButton { + return InlineKeyboardButton{ + Text: text, + WebApp: &webapp, + } +} + // NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text // which goes to a LoginURL. func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton { @@ -925,3 +952,38 @@ func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig { return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode} } + +// ValidateWebAppData validate data received via the Web App +// https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app +func ValidateWebAppData(token, telegramInitData string) (bool, error) { + initData, err := url.ParseQuery(telegramInitData) + if err != nil { + return false, fmt.Errorf("error parsing data %w", err) + } + + dataCheckString := make([]string, 0, len(initData)) + for k, v := range initData { + if k == "hash" { + continue + } + if len(v) > 0 { + dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0])) + } + } + + sort.Strings(dataCheckString) + + secret := hmac.New(sha256.New, []byte("WebAppData")) + secret.Write([]byte(token)) + + hHash := hmac.New(sha256.New, secret.Sum(nil)) + hHash.Write([]byte(strings.Join(dataCheckString, "\n"))) + + hash := hex.EncodeToString(hHash.Sum(nil)) + + if initData.Get("hash") != hash { + return false, errors.New("hash not equal") + } + + return true, nil +} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/log.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/log.go similarity index 100% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/log.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/log.go diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/params.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/params.go similarity index 93% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/params.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/params.go index 134f85e4..118af364 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/params.go +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/params.go @@ -95,3 +95,10 @@ func (p Params) AddFirstValid(key string, args ...interface{}) error { return nil } + +// Merge merges two sets of parameters. Overwrites old fields if present +func (p *Params) Merge(p1 Params) { + for k, v := range p1 { + (*p)[k] = v + } +} \ No newline at end of file diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/passport.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/passport.go similarity index 100% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/passport.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/passport.go diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/types.go b/vendor/github.com/matterbridge/telegram-bot-api/v6/types.go similarity index 82% rename from vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/types.go rename to vendor/github.com/matterbridge/telegram-bot-api/v6/types.go index 847f1b14..97fce226 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/types.go +++ b/vendor/github.com/matterbridge/telegram-bot-api/v6/types.go @@ -101,19 +101,19 @@ type Update struct { // unblocked by the user. // // optional - MyChatMember *ChatMemberUpdated `json:"my_chat_member"` + MyChatMember *ChatMemberUpdated `json:"my_chat_member,omitempty"` // ChatMember is a chat member's status was updated in a chat. The bot must // be an administrator in the chat and must explicitly specify "chat_member" // in the list of allowed_updates to receive these updates. // // optional - ChatMember *ChatMemberUpdated `json:"chat_member"` + ChatMember *ChatMemberUpdated `json:"chat_member,omitempty"` // ChatJoinRequest is a request to join the chat has been sent. The bot must // have the can_invite_users administrator right in the chat to receive // these updates. // // optional - ChatJoinRequest *ChatJoinRequest `json:"chat_join_request"` + ChatJoinRequest *ChatJoinRequest `json:"chat_join_request,omitempty"` } // SentFrom returns the user who sent an update. Can be nil, if Telegram did not provide information @@ -183,6 +183,14 @@ type User struct { // // optional IsBot bool `json:"is_bot,omitempty"` + // IsPremium true, if user has Telegram Premium + // + // optional + IsPremium bool `json:"is_premium,omitempty"` + // AddedToAttachmentMenu true, if this user added the bot to the attachment menu + // + // optional + AddedToAttachmentMenu bool `json:"added_to_attachment_menu,omitempty"` // FirstName user's or bot's first name FirstName string `json:"first_name"` // LastName user's or bot's last name @@ -257,8 +265,22 @@ type Chat struct { // // optional LastName string `json:"last_name,omitempty"` + // IsForum is true if the supergroup chat is a forum (has topics enabled) + // + // optional + IsForum bool `json:"is_forum,omitempty"` // Photo is a chat photo Photo *ChatPhoto `json:"photo"` + // If non-empty, the list of all active chat usernames; + // for private chats, supergroups and channels. Returned only in getChat. + // + // optional + ActiveUsernames []string `json:"active_usernames,omitempty"` + // Custom emoji identifier of emoji status of the other party + // in a private chat. Returned only in getChat. + // + // optional + EmojiStatusCustomEmojiID string `json:"emoji_status_custom_emoji_id,omitempty"` // Bio is the bio of the other party in a private chat. Returned only in // getChat // @@ -270,6 +292,24 @@ type Chat struct { // // optional HasPrivateForwards bool `json:"has_private_forwards,omitempty"` + // HasRestrictedVoiceAndVideoMessages if the privacy settings of the other party + // restrict sending voice and video note messages + // in the private chat. Returned only in getChat. + // + // optional + HasRestrictedVoiceAndVideoMessages bool `json:"has_restricted_voice_and_video_messages,omitempty"` + // JoinToSendMessages is true, if users need to join the supergroup + // before they can send messages. + // Returned only in getChat + // + // optional + JoinToSendMessages bool `json:"join_to_send_messages,omitempty"` + // JoinByRequest is true, if all users directly joining the supergroup + // need to be approved by supergroup administrators. + // Returned only in getChat. + // + // optional + JoinByRequest bool `json:"join_by_request,omitempty"` // Description for groups, supergroups and channel chats // // optional @@ -290,7 +330,7 @@ type Chat struct { // optional Permissions *ChatPermissions `json:"permissions,omitempty"` // SlowModeDelay is for supergroups, the minimum allowed delay between - // consecutive messages sent by each unpriviledged user. Returned only in + // consecutive messages sent by each unprivileged user. Returned only in // getChat. // // optional @@ -300,6 +340,17 @@ type Chat struct { // // optional MessageAutoDeleteTime int `json:"message_auto_delete_time,omitempty"` + // HasAggressiveAntiSpamEnabled is true if aggressive anti-spam checks are enabled + // in the supergroup. The field is only available to chat administrators. + // Returned only in getChat. + // + // optional + HasAggressiveAntiSpamEnabled bool `json:"has_aggressive_anti_spam_enabled,omitempty"` + // HasHiddenMembers is true if non-administrators can only get + // the list of bots and administrators in the chat. + // + // optional + HasHiddenMembers bool `json:"has_hidden_members,omitempty"` // HasProtectedContent is true if messages from the chat can't be forwarded // to other chats. Returned only in getChat. // @@ -325,7 +376,7 @@ type Chat struct { // connected. Returned only in getChat. // // optional - Location *ChatLocation `json:"location"` + Location *ChatLocation `json:"location,omitempty"` } // IsPrivate returns if the Chat is a private conversation. @@ -357,6 +408,11 @@ func (c Chat) ChatConfig() ChatConfig { type Message struct { // MessageID is a unique message identifier inside this chat MessageID int `json:"message_id"` + // Unique identifier of a message thread to which the message belongs; + // for supergroups only + // + // optional + MessageThreadID int `json:"message_thread_id,omitempty"` // From is a sender, empty for messages sent to channels; // // optional @@ -400,6 +456,10 @@ type Message struct { // // optional ForwardDate int `json:"forward_date,omitempty"` + // IsTopicMessage true if the message is sent to a forum topic + // + // optional + IsTopicMessage bool `json:"is_topic_message,omitempty"` // IsAutomaticForward is true if the message is a channel post that was // automatically forwarded to the connected discussion group. // @@ -445,6 +505,11 @@ type Message struct { // // optional Animation *Animation `json:"animation,omitempty"` + // PremiumAnimation message is an animation, information about the animation. + // For backward compatibility, when this field is set, the document field will also be set; + // + // optional + PremiumAnimation *Animation `json:"premium_animation,omitempty"` // Audio message is an audio file, information about the file; // // optional @@ -481,6 +546,10 @@ type Message struct { // // optional CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // HasSpoiler True, if the message media is covered by a spoiler animation + // + // optional + HasMediaSpoiler bool `json:"has_media_spoiler,omitempty"` // Contact message is a shared contact, information about the contact; // // optional @@ -553,7 +622,7 @@ type Message struct { // settings changed in the chat. // // optional - MessageAutoDeleteTimerChanged *MessageAutoDeleteTimerChanged `json:"message_auto_delete_timer_changed"` + MessageAutoDeleteTimerChanged *MessageAutoDeleteTimerChanged `json:"message_auto_delete_timer_changed,omitempty"` // MigrateToChatID is 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. @@ -585,11 +654,24 @@ type Message struct { // // optional SuccessfulPayment *SuccessfulPayment `json:"successful_payment,omitempty"` + // UserShared is a service message: a user was shared with the bot + // + // optional + UserShared *UserShared `json:"user_shared,omitempty"` + // ChatShared is a service message: a chat was shared with the bot + // + // optional + ChatShared *ChatShared `json:"chat_shared,omitempty"` // ConnectedWebsite is the domain name of the website on which the user has // logged in; // // optional ConnectedWebsite string `json:"connected_website,omitempty"` + // WriteAccessAllowed is a service message: the user allowed the bot + // added to the attachment menu to write messages + // + // optional + WriteAccessAllowed *WriteAccessAllowed `json:"write_access_allowed,omitempty"` // PassportData is a Telegram Passport data; // // optional @@ -598,24 +680,52 @@ type Message struct { // triggered another user's proximity alert while sharing Live Location // // optional - ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered"` - // VoiceChatScheduled is a service message: voice chat scheduled. + ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered,omitempty"` + // ForumTopicCreated is a service message: forum topic created // // optional - VoiceChatScheduled *VoiceChatScheduled `json:"voice_chat_scheduled"` - // VoiceChatStarted is a service message: voice chat started. + ForumTopicCreated *ForumTopicCreated `json:"forum_topic_created,omitempty"` + // ForumTopicClosed is a service message: forum topic edited // // optional - VoiceChatStarted *VoiceChatStarted `json:"voice_chat_started"` - // VoiceChatEnded is a service message: voice chat ended. + ForumTopicEdited *ForumTopicEdited `json:"forum_topic_edited,omitempty"` + // ForumTopicClosed is a service message: forum topic closed // // optional - VoiceChatEnded *VoiceChatEnded `json:"voice_chat_ended"` - // VoiceChatParticipantsInvited is a service message: new participants - // invited to a voice chat. + ForumTopicClosed *ForumTopicClosed `json:"forum_topic_closed,omitempty"` + // ForumTopicReopened is a service message: forum topic reopened // // optional - VoiceChatParticipantsInvited *VoiceChatParticipantsInvited `json:"voice_chat_participants_invited"` + ForumTopicReopened *ForumTopicReopened `json:"forum_topic_reopened,omitempty"` + // GeneralForumTopicHidden is a service message: the 'General' forum topic hidden + // + // optional + GeneralForumTopicHidden *GeneralForumTopicHidden `json:"general_forum_topic_hidden,omitempty"` + // GeneralForumTopicUnhidden is a service message: the 'General' forum topic unhidden + // + // optional + GeneralForumTopicUnhidden *GeneralForumTopicUnhidden `json:"general_forum_topic_unhidden,omitempty"` + // VideoChatScheduled is a service message: video chat scheduled. + // + // optional + VideoChatScheduled *VideoChatScheduled `json:"video_chat_scheduled,omitempty"` + // VideoChatStarted is a service message: video chat started. + // + // optional + VideoChatStarted *VideoChatStarted `json:"video_chat_started,omitempty"` + // VideoChatEnded is a service message: video chat ended. + // + // optional + VideoChatEnded *VideoChatEnded `json:"video_chat_ended,omitempty"` + // VideoChatParticipantsInvited is a service message: new participants + // invited to a video chat. + // + // optional + VideoChatParticipantsInvited *VideoChatParticipantsInvited `json:"video_chat_participants_invited,omitempty"` + // WebAppData is a service message: data sent by a Web App. + // + // optional + WebAppData *WebAppData `json:"web_app_data,omitempty"` // ReplyMarkup is the Inline keyboard attached to the message. // login_url buttons are represented as ordinary url buttons. // @@ -712,10 +822,12 @@ type MessageEntity struct { // “italic” (italic text), // “underline” (underlined text), // “strikethrough” (strikethrough text), + // "spoiler" (spoiler message), // “code” (monowidth string), // “pre” (monowidth block), // “text_link” (for clickable text URLs), // “text_mention” (for users without usernames) + // “text_mention” (for inline custom emoji stickers) Type string `json:"type"` // Offset in UTF-16 code units to the start of the entity Offset int `json:"offset"` @@ -733,6 +845,10 @@ type MessageEntity struct { // // optional Language string `json:"language,omitempty"` + // CustomEmojiID for “custom_emoji” only, unique identifier of the custom emoji + // + // optional + CustomEmojiID string `json:"custom_emoji_id"` } // ParseURL attempts to parse a URL contained within a MessageEntity. @@ -749,6 +865,12 @@ func (e MessageEntity) IsMention() bool { return e.Type == "mention" } +// IsTextMention returns true if the type of the message entity is "text_mention" +// (At this time, the user field exists, and occurs when tagging a member without a username) +func (e MessageEntity) IsTextMention() bool { + return e.Type == "text_mention" +} + // IsHashtag returns true if the type of the message entity is "hashtag". func (e MessageEntity) IsHashtag() bool { return e.Type == "hashtag" @@ -843,7 +965,7 @@ type Animation struct { // FileSize file size // // optional - FileSize int `json:"file_size,omitempty"` + FileSize int64 `json:"file_size,omitempty"` } // Audio represents an audio file to be treated as music by the Telegram clients. @@ -876,7 +998,7 @@ type Audio struct { // FileSize file size // // optional - FileSize int `json:"file_size,omitempty"` + FileSize int64 `json:"file_size,omitempty"` // Thumbnail is the album cover to which the music file belongs // // optional @@ -907,7 +1029,7 @@ type Document struct { // FileSize file size // // optional - FileSize int `json:"file_size,omitempty"` + FileSize int64 `json:"file_size,omitempty"` } // Video represents a video file. @@ -940,7 +1062,7 @@ type Video struct { // FileSize file size // // optional - FileSize int `json:"file_size,omitempty"` + FileSize int64 `json:"file_size,omitempty"` } // VideoNote object represents a video message. @@ -982,7 +1104,7 @@ type Voice struct { // FileSize file size // // optional - FileSize int `json:"file_size,omitempty"` + FileSize int64 `json:"file_size,omitempty"` } // Contact represents a phone contact. @@ -1134,6 +1256,15 @@ type Venue struct { GooglePlaceType string `json:"google_place_type,omitempty"` } +// WebAppData Contains data sent from a Web App to the bot. +type WebAppData struct { + // Data is the data. Be aware that a bad client can send arbitrary data in this field. + Data string `json:"data"` + // ButtonText is the text of the web_app keyboard button, from which the Web App + // was opened. Be aware that a bad client can send arbitrary data in this field. + ButtonText string `json:"button_text"` +} + // ProximityAlertTriggered represents a service message sent when a user in the // chat triggers a proximity alert sent by another user. type ProximityAlertTriggered struct { @@ -1152,37 +1283,109 @@ type MessageAutoDeleteTimerChanged struct { MessageAutoDeleteTime int `json:"message_auto_delete_time"` } -// VoiceChatScheduled represents a service message about a voice chat scheduled +// ForumTopicCreated represents a service message about a new forum topic +// created in the chat. +type ForumTopicCreated struct { + // Name is the name of topic + Name string `json:"name"` + // IconColor is the color of the topic icon in RGB format + IconColor int `json:"icon_color"` + // IconCustomEmojiID is the unique identifier of the custom emoji + // shown as the topic icon + // + // optional + IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"` +} + +// ForumTopicClosed represents a service message about a forum topic +// closed in the chat. Currently holds no information. +type ForumTopicClosed struct { +} + +// ForumTopicEdited object represents a service message about an edited forum topic. +type ForumTopicEdited struct { + // Name is the new name of the topic, if it was edited + // + // optional + Name string `json:"name,omitempty"` + // IconCustomEmojiID is the new identifier of the custom emoji + // shown as the topic icon, if it was edited; + // an empty string if the icon was removed + // + // optional + IconCustomEmojiID *string `json:"icon_custom_emoji_id,omitempty"` +} + +// ForumTopicReopened represents a service message about a forum topic +// reopened in the chat. Currently holds no information. +type ForumTopicReopened struct { +} + +// GeneralForumTopicHidden represents a service message about General forum topic +// hidden in the chat. Currently holds no information. +type GeneralForumTopicHidden struct { +} + +// GeneralForumTopicUnhidden represents a service message about General forum topic +// unhidden in the chat. Currently holds no information. +type GeneralForumTopicUnhidden struct { +} + +// UserShared object contains information about the user whose identifier +// was shared with the bot using a KeyboardButtonRequestUser button. +type UserShared struct { + // RequestID is an indentifier of the request. + RequestID int `json:"request_id"` + // UserID in an identifier of the shared user. + UserID int64 `json:"user_id"` +} + +// ChatShared contains information about the chat whose identifier +// was shared with the bot using a KeyboardButtonRequestChat button. +type ChatShared struct { + // RequestID is an indentifier of the request. + RequestID int `json:"request_id"` + // ChatID is an identifier of the shared chat. + ChatID int64 `json:"chat_id"` +} + +// WriteAccessAllowed represents a service message about a user +// allowing a bot added to the attachment menu to write messages. +// Currently holds no information. +type WriteAccessAllowed struct { +} + +// VideoChatScheduled represents a service message about a voice chat scheduled // in the chat. -type VoiceChatScheduled struct { +type VideoChatScheduled struct { // Point in time (Unix timestamp) when the voice chat is supposed to be // started by a chat administrator StartDate int `json:"start_date"` } // Time converts the scheduled start date into a Time. -func (m *VoiceChatScheduled) Time() time.Time { +func (m *VideoChatScheduled) Time() time.Time { return time.Unix(int64(m.StartDate), 0) } -// VoiceChatStarted represents a service message about a voice chat started in +// VideoChatStarted represents a service message about a voice chat started in // the chat. -type VoiceChatStarted struct{} +type VideoChatStarted struct{} -// VoiceChatEnded represents a service message about a voice chat ended in the +// VideoChatEnded represents a service message about a voice chat ended in the // chat. -type VoiceChatEnded struct { +type VideoChatEnded struct { // Voice chat duration; in seconds. Duration int `json:"duration"` } -// VoiceChatParticipantsInvited represents a service message about new members +// VideoChatParticipantsInvited represents a service message about new members // invited to a voice chat. -type VoiceChatParticipantsInvited struct { +type VideoChatParticipantsInvited struct { // New members that were invited to the voice chat. // // optional - Users []User `json:"users"` + Users []User `json:"users,omitempty"` } // UserProfilePhotos contains a set of user profile photos. @@ -1205,7 +1408,7 @@ type File struct { // FileSize file size, if known // // optional - FileSize int `json:"file_size,omitempty"` + FileSize int64 `json:"file_size,omitempty"` // FilePath file path // // optional @@ -1219,10 +1422,24 @@ func (f *File) Link(token string) string { return fmt.Sprintf(FileEndpoint, token, f.FilePath) } +// WebAppInfo contains information about a Web App. +type WebAppInfo struct { + // URL is the HTTPS URL of a Web App to be opened with additional data as + // specified in Initializing Web Apps. + URL string `json:"url"` +} + // ReplyKeyboardMarkup represents a custom keyboard with reply options. type ReplyKeyboardMarkup struct { // Keyboard is an array of button rows, each represented by an Array of KeyboardButton objects Keyboard [][]KeyboardButton `json:"keyboard"` + // IsPersistent requests clients to always show the keyboard + // when the regular keyboard is hidden. + // Defaults to false, in which case the custom keyboard can be hidden + // and opened with a keyboard icon. + // + // optional + IsPersistent bool `json:"is_persistent"` // ResizeKeyboard requests clients to resize the keyboard vertically for optimal fit // (e.g., make the keyboard smaller if there are just two rows of buttons). // Defaults to false, in which case the custom keyboard @@ -1264,6 +1481,20 @@ type KeyboardButton struct { // Text of the button. If none of the optional fields are used, // it will be sent as a message when the button is pressed. Text string `json:"text"` + // RequestUser if specified, pressing the button will open + // a list of suitable users. Tapping on any user will send + // their identifier to the bot in a "user_shared" service message. + // Available in private chats only. + // + // optional + RequestUser *KeyboardButtonRequestUser `json:"request_user,omitempty"` + // RequestChat if specified, pressing the button will open + // a list of suitable chats. Tapping on a chat will send + // its identifier to the bot in a "chat_shared" service message. + // Available in private chats only. + // + // optional + RequestChat *KeyboardButtonRequestChat `json:"request_chat,omitempty"` // RequestContact if True, the user's phone number will be sent // as a contact when the button is pressed. // Available in private chats only. @@ -1276,11 +1507,83 @@ type KeyboardButton struct { // // optional RequestLocation bool `json:"request_location,omitempty"` - // RequestPoll if True, the user will be asked to create a poll and send it + // RequestPoll if specified, the user will be asked to create a poll and send it // to the bot when the button is pressed. Available in private chats only // // optional RequestPoll *KeyboardButtonPollType `json:"request_poll,omitempty"` + // WebApp if specified, the described Web App will be launched when the button + // is pressed. The Web App will be able to send a “web_app_data” service + // message. Available in private chats only. + // + // optional + WebApp *WebAppInfo `json:"web_app,omitempty"` +} + +// KeyboardButtonRequestUser defines the criteria used to request +// a suitable user. The identifier of the selected user will be shared +// with the bot when the corresponding button is pressed. +type KeyboardButtonRequestUser struct { + // RequestID is a signed 32-bit identifier of the request. + RequestID int `json:"request_id"` + // UserIsBot pass True to request a bot, + // pass False to request a regular user. + // If not specified, no additional restrictions are applied. + // + // optional + UserIsBot bool `json:"user_is_bot,omitempty"` + // UserIsPremium pass True to request a premium user, + // pass False to request a non-premium user. + // If not specified, no additional restrictions are applied. + // + // optional + UserIsPremium bool `json:"user_is_premium,omitempty"` +} + +// KeyboardButtonRequestChat defines the criteria used to request +// a suitable chat. The identifier of the selected chat will be shared +// with the bot when the corresponding button is pressed. +type KeyboardButtonRequestChat struct { + // RequestID is a signed 32-bit identifier of the request. + RequestID int `json:"request_id"` + // ChatIsChannel pass True to request a channel chat, + // pass False to request a group or a supergroup chat. + ChatIsChannel bool `json:"chat_is_channel"` + // ChatIsForum pass True to request a forum supergroup, + // pass False to request a non-forum chat. + // If not specified, no additional restrictions are applied. + // + // optional + ChatIsForum bool `json:"chat_is_forum,omitempty"` + // ChatHasUsername pass True to request a supergroup or a channel with a username, + // pass False to request a chat without a username. + // If not specified, no additional restrictions are applied. + // + // optional + ChatHasUsername bool `json:"chat_has_username,omitempty"` + // ChatIsCreated pass True to request a chat owned by the user. + // Otherwise, no additional restrictions are applied. + // + // optional + ChatIsCreated bool `json:"chat_is_created,omitempty"` + // UserAdministratorRights is a JSON-serialized object listing + // the required administrator rights of the user in the chat. + // If not specified, no additional restrictions are applied. + // + // optional + UserAdministratorRights *ChatAdministratorRights `json:"user_administrator_rights,omitempty"` + // BotAdministratorRights is a JSON-serialized object listing + // the required administrator rights of the bot in the chat. + // The rights must be a subset of user_administrator_rights. + // If not specified, no additional restrictions are applied. + // + // optional + BotAdministratorRights *ChatAdministratorRights `json:"bot_administrator_rights,omitempty"` + // BotIsMember pass True to request a chat with the bot as a member. + // Otherwise, no additional restrictions are applied. + // + // optional + BotIsMember bool `json:"bot_is_member,omitempty"` } // KeyboardButtonPollType represents type of poll, which is allowed to @@ -1347,6 +1650,12 @@ type InlineKeyboardButton struct { // // optional CallbackData *string `json:"callback_data,omitempty"` + // WebApp is the Description of the Web App that will be launched when the user presses the button. + // The Web App will be able to send an arbitrary message on behalf of the user using the method + // answerWebAppQuery. Available only in private chats between a user and the bot. + // + // optional + WebApp *WebAppInfo `json:"web_app,omitempty"` // SwitchInlineQuery 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. @@ -1505,7 +1814,7 @@ type ChatInviteLink struct { // be approved by chat administrators. // // optional - CreatesJoinRequest bool `json:"creates_join_request"` + CreatesJoinRequest bool `json:"creates_join_request,omitempty"` // IsPrimary is true, if the link is primary. IsPrimary bool `json:"is_primary"` // IsRevoked is true, if the link is revoked. @@ -1513,22 +1822,37 @@ type ChatInviteLink struct { // Name is the name of the invite link. // // optional - Name string `json:"name"` + Name string `json:"name,omitempty"` // ExpireDate is the point in time (Unix timestamp) when the link will // expire or has been expired. // // optional - ExpireDate int `json:"expire_date"` + ExpireDate int `json:"expire_date,omitempty"` // MemberLimit is the maximum number of users that can be members of the // chat simultaneously after joining the chat via this invite link; 1-99999. // // optional - MemberLimit int `json:"member_limit"` + MemberLimit int `json:"member_limit,omitempty"` // PendingJoinRequestCount is the number of pending join requests created // using this link. // // optional - PendingJoinRequestCount int `json:"pending_join_request_count"` + PendingJoinRequestCount int `json:"pending_join_request_count,omitempty"` +} + +type ChatAdministratorRights struct { + IsAnonymous bool `json:"is_anonymous"` + CanManageChat bool `json:"can_manage_chat"` + CanDeleteMessages bool `json:"can_delete_messages"` + CanManageVideoChats bool `json:"can_manage_video_chats"` + CanRestrictMembers bool `json:"can_restrict_members"` + CanPromoteMembers bool `json:"can_promote_members"` + CanChangeInfo bool `json:"can_change_info"` + CanInviteUsers bool `json:"can_invite_users"` + CanPostMessages bool `json:"can_post_messages"` + CanEditMessages bool `json:"can_edit_messages"` + CanPinMessages bool `json:"can_pin_messages"` + CanManageTopics bool `json:"can_manage_topics"` } // ChatMember contains information about one member of a chat. @@ -1552,7 +1876,7 @@ type ChatMember struct { // in the chat is hidden // // optional - IsAnonymous bool `json:"is_anonymous"` + IsAnonymous bool `json:"is_anonymous,omitempty"` // UntilDate restricted and kicked only. // Date when restrictions will be lifted for this user; // unix time. @@ -1571,7 +1895,7 @@ type ChatMember struct { // any other administrator privilege. // // optional - CanManageChat bool `json:"can_manage_chat"` + CanManageChat bool `json:"can_manage_chat,omitempty"` // CanPostMessages administrators only. // True, if the administrator can post in the channel; // channels only. @@ -1589,11 +1913,11 @@ type ChatMember struct { // // optional CanDeleteMessages bool `json:"can_delete_messages,omitempty"` - // CanManageVoiceChats administrators only. - // True, if the administrator can manage voice chats. + // CanManageVideoChats administrators only. + // True, if the administrator can manage video chats. // // optional - CanManageVoiceChats bool `json:"can_manage_voice_chats"` + CanManageVideoChats bool `json:"can_manage_video_chats,omitempty"` // CanRestrictMembers administrators only. // True, if the administrator can restrict, ban or unban chat members. // @@ -1621,6 +1945,12 @@ type ChatMember struct { // // optional CanPinMessages bool `json:"can_pin_messages,omitempty"` + // CanManageTopics administrators and restricted only. + // True, if the user is allowed to create, rename, + // close, and reopen forum topics; supergroups only + // + // optional + CanManageTopics bool `json:"can_manage_topics,omitempty"` // IsMember is true, if the user is a member of the chat at the moment of // the request IsMember bool `json:"is_member"` @@ -1628,11 +1958,36 @@ type ChatMember struct { // // optional CanSendMessages bool `json:"can_send_messages,omitempty"` - // CanSendMediaMessages restricted only. - // True, if the user is allowed to send text messages, contacts, locations and venues + // CanSendAudios restricted only. + // True, if the user is allowed to send audios // // optional - CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` + CanSendAudios bool + // CanSendDocuments restricted only. + // True, if the user is allowed to send documents + // + // optional + CanSendDocuments bool + // CanSendPhotos is restricted only. + // True, if the user is allowed to send photos + // + // optional + CanSendPhotos bool + // CanSendVideos restricted only. + // True, if the user is allowed to send videos + // + // optional + CanSendVideos bool + // CanSendVideoNotes restricted only. + // True, if the user is allowed to send video notes + // + // optional + CanSendVideoNotes bool + // CanSendVoiceNotes restricted only. + // True, if the user is allowed to send voice notes + // + // optional + CanSendVoiceNotes bool // CanSendPolls restricted only. // True, if the user is allowed to send polls // @@ -1663,6 +2018,27 @@ func (chat ChatMember) HasLeft() bool { return chat.Status == "left" } // WasKicked returns if the ChatMember was kicked from the chat. func (chat ChatMember) WasKicked() bool { return chat.Status == "kicked" } +// SetCanSendMediaMessages is a method to replace field "can_send_media_messages". +// It sets CanSendAudio, CanSendDocuments, CanSendPhotos, CanSendVideos, +// CanSendVideoNotes, CanSendVoiceNotes to passed value. +func (chat *ChatMember) SetCanSendMediaMessages(b bool) { + chat.CanSendAudios = b + chat.CanSendDocuments = b + chat.CanSendPhotos = b + chat.CanSendVideos = b + chat.CanSendVideoNotes = b + chat.CanSendVoiceNotes = b +} + +// CanSendMediaMessages method to replace field "can_send_media_messages". +// It returns true if CanSendAudio and CanSendDocuments and CanSendPhotos and CanSendVideos and +// CanSendVideoNotes and CanSendVoiceNotes are true. +func (chat *ChatMember) CanSendMediaMessages() bool { + return chat.CanSendAudios && chat.CanSendDocuments && + chat.CanSendPhotos && chat.CanSendVideos && + chat.CanSendVideoNotes && chat.CanSendVoiceNotes +} + // ChatMemberUpdated represents changes in the status of a chat member. type ChatMemberUpdated struct { // Chat the user belongs to. @@ -1679,7 +2055,7 @@ type ChatMemberUpdated struct { // for joining by invite link events only. // // optional - InviteLink *ChatInviteLink `json:"invite_link"` + InviteLink *ChatInviteLink `json:"invite_link,omitempty"` } // ChatJoinRequest represents a join request sent to a chat. @@ -1688,16 +2064,18 @@ type ChatJoinRequest struct { Chat Chat `json:"chat"` // User that sent the join request. From User `json:"from"` + // UserChatID identifier of a private chat with the user who sent the join request. + UserChatID int64 `json:"user_chat_id"` // Date the request was sent in Unix time. Date int `json:"date"` // Bio of the user. // // optional - Bio string `json:"bio"` + Bio string `json:"bio,omitempty"` // InviteLink is the link that was used by the user to send the join request. // // optional - InviteLink *ChatInviteLink `json:"invite_link"` + InviteLink *ChatInviteLink `json:"invite_link,omitempty"` } // ChatPermissions describes actions that a non-administrator user is @@ -1708,12 +2086,30 @@ type ChatPermissions struct { // // optional CanSendMessages bool `json:"can_send_messages,omitempty"` - // CanSendMediaMessages is true, if the user is allowed to send audios, - // documents, photos, videos, video notes and voice notes, implies - // can_send_messages + // CanSendAudios is true, if the user is allowed to send audios // // optional - CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` + CanSendAudios bool + // CanSendDocuments is true, if the user is allowed to send documents + // + // optional + CanSendDocuments bool + // CanSendPhotos is true, if the user is allowed to send photos + // + // optional + CanSendPhotos bool + // CanSendVideos is true, if the user is allowed to send videos + // + // optional + CanSendVideos bool + // CanSendVideoNotes is true, if the user is allowed to send video notes + // + // optional + CanSendVideoNotes bool + // CanSendVoiceNotes is true, if the user is allowed to send voice notes + // + // optional + CanSendVoiceNotes bool // CanSendPolls is true, if the user is allowed to send polls, implies // can_send_messages // @@ -1744,6 +2140,32 @@ type ChatPermissions struct { // // optional CanPinMessages bool `json:"can_pin_messages,omitempty"` + // CanManageTopics is true, if the user is allowed to create forum topics. + // If omitted defaults to the value of can_pin_messages + // + // optional + CanManageTopics bool `json:"can_manage_topics,omitempty"` +} + +// SetCanSendMediaMessages is a method to replace field "can_send_media_messages". +// It sets CanSendAudio, CanSendDocuments, CanSendPhotos, CanSendVideos, +// CanSendVideoNotes, CanSendVoiceNotes to passed value. +func (c *ChatPermissions) SetCanSendMediaMessages(b bool) { + c.CanSendAudios = b + c.CanSendDocuments = b + c.CanSendPhotos = b + c.CanSendVideos = b + c.CanSendVideoNotes = b + c.CanSendVoiceNotes = b +} + +// CanSendMediaMessages method to replace field "can_send_media_messages". +// It returns true if CanSendAudio and CanSendDocuments and CanSendPhotos and CanSendVideos and +// CanSendVideoNotes and CanSendVoiceNotes are true. +func (c *ChatPermissions) CanSendMediaMessages() bool { + return c.CanSendAudios && c.CanSendDocuments && + c.CanSendPhotos && c.CanSendVideos && + c.CanSendVideoNotes && c.CanSendVoiceNotes } // ChatLocation represents a location to which a chat is connected. @@ -1756,6 +2178,21 @@ type ChatLocation struct { Address string `json:"address"` } +// ForumTopic represents a forum topic. +type ForumTopic struct { + // MessageThreadID is the unique identifier of the forum topic + MessageThreadID int `json:"message_thread_id"` + // Name is the name of the topic + Name string `json:"name"` + // IconColor is the color of the topic icon in RGB format + IconColor int `json:"icon_color"` + // IconCustomEmojiID is the unique identifier of the custom emoji + // shown as the topic icon + // + // optional + IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"` +} + // BotCommand represents a bot command. type BotCommand struct { // Command text of the command, 1-32 characters. @@ -1775,6 +2212,20 @@ type BotCommandScope struct { UserID int64 `json:"user_id,omitempty"` } +// MenuButton describes the bot's menu button in a private chat. +type MenuButton struct { + // Type is the type of menu button, must be one of: + // - `commands` + // - `web_app` + // - `default` + Type string `json:"type"` + // Text is the text on the button, for `web_app` type. + Text string `json:"text,omitempty"` + // WebApp is the description of the Web App that will be launched when the + // user presses the button for the `web_app` type. + WebApp *WebAppInfo `json:"web_app,omitempty"` +} + // ResponseParameters are various errors that can be returned in APIResponse. type ResponseParameters struct { // The group has been migrated to a supergroup with the specified identifier. @@ -1814,7 +2265,11 @@ type BaseInputMedia struct { // which can be specified instead of parse_mode // // optional - CaptionEntities []MessageEntity `json:"caption_entities"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // HasSpoiler pass True, if the photo needs to be covered with a spoiler animation + // + // optional + HasSpoiler bool `json:"has_spoiler,omitempty"` } // InputMediaPhoto is a photo to send as part of a media group. @@ -1846,6 +2301,10 @@ type InputMediaVideo struct { // // optional SupportsStreaming bool `json:"supports_streaming,omitempty"` + // HasSpoiler pass True, if the video needs to be covered with a spoiler animation + // + // optional + HasSpoiler bool `json:"has_spoiler,omitempty"` } // InputMediaAnimation is an animation to send as part of a media group. @@ -1868,6 +2327,10 @@ type InputMediaAnimation struct { // // optional Duration int `json:"duration,omitempty"` + // HasSpoiler pass True, if the photo needs to be covered with a spoiler animation + // + // optional + HasSpoiler bool `json:"has_spoiler,omitempty"` } // InputMediaAudio is an audio to send as part of a media group. @@ -1908,6 +2371,13 @@ type InputMediaDocument struct { DisableContentTypeDetection bool `json:"disable_content_type_detection,omitempty"` } +// Constant values for sticker types +const ( + StickerTypeRegular = "regular" + StickerTypeMask = "mask" + StickerTypeCustomEmoji = "custom_emoji" +) + // Sticker represents a sticker. type Sticker struct { // FileID is an identifier for this file, which can be used to download or @@ -1917,6 +2387,10 @@ type Sticker struct { // 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"` + // Type is a type of the sticker, currently one of “regular”, + // “mask”, “custom_emoji”. The type of the sticker is independent + // from its format, which is determined by the fields is_animated and is_video. + Type string `json:"type"` // Width sticker width Width int `json:"width"` // Height sticker height @@ -1925,6 +2399,10 @@ type Sticker struct { // // optional IsAnimated bool `json:"is_animated,omitempty"` + // IsVideo true, if the sticker is a video sticker + // + // optional + IsVideo bool `json:"is_video,omitempty"` // Thumbnail sticker thumbnail in the .WEBP or .JPG format // // optional @@ -1937,26 +2415,55 @@ type Sticker struct { // // optional SetName string `json:"set_name,omitempty"` + // PremiumAnimation for premium regular stickers, premium animation for the sticker + // + // optional + PremiumAnimation *File `json:"premium_animation,omitempty"` // MaskPosition is for mask stickers, the position where the mask should be // placed // // optional MaskPosition *MaskPosition `json:"mask_position,omitempty"` + // CustomEmojiID for custom emoji stickers, unique identifier of the custom emoji + // + // optional + CustomEmojiID string `json:"custom_emoji_id,omitempty"` // FileSize // // optional FileSize int `json:"file_size,omitempty"` } +// IsRegular returns if the Sticker is regular +func (s Sticker) IsRegular() bool { + return s.Type == StickerTypeRegular +} + +// IsMask returns if the Sticker is mask +func (s Sticker) IsMask() bool { + return s.Type == StickerTypeMask +} + +// IsCustomEmoji returns if the Sticker is custom emoji +func (s Sticker) IsCustomEmoji() bool { + return s.Type == StickerTypeCustomEmoji +} + // StickerSet represents a sticker set. type StickerSet struct { // Name sticker set name Name string `json:"name"` // Title sticker set title Title string `json:"title"` + // StickerType of stickers in the set, currently one of “regular”, “mask”, “custom_emoji” + StickerType string `json:"sticker_type"` // IsAnimated true, if the sticker set contains animated stickers IsAnimated bool `json:"is_animated"` + // IsVideo true, if the sticker set contains video stickers + IsVideo bool `json:"is_video"` // ContainsMasks true, if the sticker set contains masks + // + // deprecated. Use sticker_type instead ContainsMasks bool `json:"contains_masks"` // Stickers list of all set stickers Stickers []Sticker `json:"stickers"` @@ -1964,6 +2471,21 @@ type StickerSet struct { Thumbnail *PhotoSize `json:"thumb"` } +// IsRegular returns if the StickerSet is regular +func (s StickerSet) IsRegular() bool { + return s.StickerType == StickerTypeRegular +} + +// IsMask returns if the StickerSet is mask +func (s StickerSet) IsMask() bool { + return s.StickerType == StickerTypeMask +} + +// IsCustomEmoji returns if the StickerSet is custom emoji +func (s StickerSet) IsCustomEmoji() bool { + return s.StickerType == StickerTypeCustomEmoji +} + // MaskPosition describes the position on faces where a mask should be placed // by default. type MaskPosition struct { @@ -2043,6 +2565,9 @@ type WebhookInfo struct { // // optional LastErrorMessage string `json:"last_error_message,omitempty"` + // LastSynchronizationErrorDate is the unix time of the most recent error that + // happened when trying to synchronize available updates with Telegram datacenters. + LastSynchronizationErrorDate int `json:"last_synchronization_error_date,omitempty"` // MaxConnections maximum allowed number of simultaneous // HTTPS connections to the webhook for update delivery. // @@ -2077,7 +2602,7 @@ type InlineQuery struct { // unless the request was sent from a secret chat // // optional - ChatType string `json:"chat_type"` + ChatType string `json:"chat_type,omitempty"` // Location sender location, only for bots that request user location. // // optional @@ -2590,23 +3115,23 @@ type InlineQueryResultLocation struct { // measured in meters; 0-1500 // // optional - HorizontalAccuracy float64 `json:"horizontal_accuracy"` + HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` // LivePeriod is the period in seconds for which the location can be // updated, should be between 60 and 86400. // // optional - LivePeriod int `json:"live_period"` + LivePeriod int `json:"live_period,omitempty"` // Heading is for live locations, a direction in which the user is moving, // in degrees. Must be between 1 and 360 if specified. // // optional - Heading int `json:"heading"` + Heading int `json:"heading,omitempty"` // ProximityAlertRadius is for live locations, a maximum distance for // proximity alerts about approaching another chat member, in meters. Must // be between 1 and 100000 if specified. // // optional - ProximityAlertRadius int `json:"proximity_alert_radius"` + ProximityAlertRadius int `json:"proximity_alert_radius,omitempty"` // ReplyMarkup inline keyboard attached to the message // // optional @@ -2640,15 +3165,15 @@ type InlineQueryResultMPEG4GIF struct { // Width video width // // optional - Width int `json:"mpeg4_width"` + Width int `json:"mpeg4_width,omitempty"` // Height vVideo height // // optional - Height int `json:"mpeg4_height"` + Height int `json:"mpeg4_height,omitempty"` // Duration video duration // // optional - Duration int `json:"mpeg4_duration"` + Duration int `json:"mpeg4_duration,omitempty"` // ThumbURL url of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result. ThumbURL string `json:"thumb_url"` // Title for the result @@ -2896,6 +3421,16 @@ type ChosenInlineResult struct { Query string `json:"query"` } +// SentWebAppMessage contains information about an inline message sent by a Web App +// on behalf of a user. +type SentWebAppMessage struct { + // Identifier of the sent inline message. Available only if there is an inline + // keyboard attached to the message. + // + // optional + InlineMessageID string `json:"inline_message_id,omitempty"` +} + // InputTextMessageContent contains text for displaying // as an inline query result. type InputTextMessageContent struct { @@ -2929,7 +3464,7 @@ type InputLocationMessageContent struct { // measured in meters; 0-1500 // // optional - HorizontalAccuracy float64 `json:"horizontal_accuracy"` + HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` // LivePeriod is the period in seconds for which the location can be // updated, should be between 60 and 86400 // @@ -2939,13 +3474,13 @@ type InputLocationMessageContent struct { // in degrees. Must be between 1 and 360 if specified. // // optional - Heading int `json:"heading"` + Heading int `json:"heading,omitempty"` // ProximityAlertRadius is for live locations, a maximum distance for // proximity alerts about approaching another chat member, in meters. Must // be between 1 and 100000 if specified. // // optional - ProximityAlertRadius int `json:"proximity_alert_radius"` + ProximityAlertRadius int `json:"proximity_alert_radius,omitempty"` } // InputVenueMessageContent contains a venue for displaying @@ -2970,11 +3505,11 @@ type InputVenueMessageContent struct { // GooglePlaceID is the Google Places identifier of the venue // // optional - GooglePlaceID string `json:"google_place_id"` + GooglePlaceID string `json:"google_place_id,omitempty"` // GooglePlaceType is the Google Places type of the venue // // optional - GooglePlaceType string `json:"google_place_type"` + GooglePlaceType string `json:"google_place_type,omitempty"` } // InputContactMessageContent contains a contact for displaying diff --git a/vendor/modules.txt b/vendor/modules.txt index 1b9fa5bf..2f650c86 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -83,9 +83,6 @@ github.com/fsnotify/fsnotify # github.com/go-asn1-ber/asn1-ber v1.5.3 ## explicit; go 1.13 github.com/go-asn1-ber/asn1-ber -# github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 -## explicit; go 1.16 -github.com/go-telegram-bot-api/telegram-bot-api/v5 # github.com/golang-jwt/jwt v3.2.2+incompatible ## explicit github.com/golang-jwt/jwt @@ -234,6 +231,9 @@ github.com/matterbridge/logrus-prefixed-formatter # github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d ## explicit; go 1.17 github.com/matterbridge/matterclient +# github.com/matterbridge/telegram-bot-api/v6 v6.5.0 +## explicit; go 1.16 +github.com/matterbridge/telegram-bot-api/v6 # github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 ## explicit github.com/mattermost/go-i18n/i18n