Add support for Telegram topics (telegram) (#1942)

Topics are surfaced by appending /<topic-id> to the channel setting for the gateway. 
An example for the topic with ID of 16 would be:

```
[[gateway.inout]]
account="telegram.mytelegram"
channel="-100xxxxxxxxxx/16"
```
This commit is contained in:
Thom Dickson 2023-03-14 18:03:04 -04:00 committed by GitHub
parent c2b8e298d8
commit 601f48a50e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1266 additions and 121 deletions

View File

@ -11,7 +11,7 @@ import (
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper" "github.com/42wim/matterbridge/bridge/helper"
"github.com/davecgh/go-spew/spew" "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 { 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" { if posted.Text == "/chatId" {
chatID := strconv.FormatInt(posted.Chat.ID, 10) chatID := strconv.FormatInt(posted.Chat.ID, 10)
// Handle chat topics
if posted.IsTopicMessage {
chatID = chatID + "/" + strconv.Itoa(posted.MessageThreadID)
}
_, err := b.Send(config.Message{ _, err := b.Send(config.Message{
Channel: chatID, Channel: chatID,
Text: fmt.Sprintf("ID of this chat: %s", 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 // handleQuoting handles quoting of previous messages
func (b *Btelegram) handleQuoting(rmsg *config.Message, message *tgbotapi.Message) { 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 := "" usernameReply := ""
if message.ReplyToMessage.From != nil { if message.ReplyToMessage.From != nil {
if b.GetBool("UseFirstName") { 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 // set the ID's from the channel or group message
rmsg.ID = strconv.Itoa(message.MessageID) rmsg.ID = strconv.Itoa(message.MessageID)
rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10) rmsg.Channel = strconv.FormatInt(message.Chat.ID, 10)
if message.MessageThreadID != 0 {
rmsg.Channel += "/" + strconv.Itoa(message.MessageThreadID)
}
// preserve threading from telegram reply // 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) rmsg.ParentID = strconv.Itoa(message.ReplyToMessage.MessageID)
} }
@ -326,12 +337,12 @@ func (b *Btelegram) maybeConvertWebp(name *string, data *[]byte) {
// handleDownloadFile handles file download // handleDownloadFile handles file download
func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error { func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Message) error {
size := 0 size := int64(0)
var url, name, text string var url, name, text string
switch { switch {
case message.Sticker != nil: case message.Sticker != nil:
text, name, url = b.getDownloadInfo(message.Sticker.FileID, ".webp", true) text, name, url = b.getDownloadInfo(message.Sticker.FileID, ".webp", true)
size = message.Sticker.FileSize size = int64(message.Sticker.FileSize)
case message.Voice != nil: case message.Voice != nil:
text, name, url = b.getDownloadInfo(message.Voice.FileID, ".ogg", true) text, name, url = b.getDownloadInfo(message.Voice.FileID, ".ogg", true)
size = message.Voice.FileSize size = message.Voice.FileSize
@ -348,7 +359,7 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa
text = " " + message.Document.FileName + " : " + url text = " " + message.Document.FileName + " : " + url
case message.Photo != nil: case message.Photo != nil:
photos := message.Photo 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) 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 // 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{} var media []interface{}
for _, f := range msg.Extra["file"] { for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo) 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 { func (b *Btelegram) handleQuote(message, quoteNick, quoteMessage string) string {

View File

@ -10,7 +10,7 @@ import (
"github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper" "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 ( const (
@ -86,11 +86,41 @@ func TGGetParseMode(b *Btelegram, username string, text string) (textout string,
return textout, parsemode 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) { func (b *Btelegram) Send(msg config.Message) (string, error) {
b.Log.Debugf("=> Receiving %#v", msg) b.Log.Debugf("=> Receiving %#v", msg)
// get the chatid chatid, topicid, err := b.getIds(msg.Channel)
chatid, err := strconv.ParseInt(msg.Channel, 10, 64)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -123,13 +153,13 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
// Upload a file if it exists // Upload a file if it exists
if msg.Extra != nil { if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) { 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) b.Log.Errorf("sendMessage failed: %s", msgErr)
} }
} }
// check if we have files to upload (from slack, telegram or mattermost) // check if we have files to upload (from slack, telegram or mattermost)
if len(msg.Extra["file"]) > 0 { 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 // Ignore empty text field needs for prevent double messages from whatsapp to telegram
// when sending media with text caption // when sending media with text caption
if msg.Text != "" { if msg.Text != "" {
return b.sendMessage(chatid, msg.Username, msg.Text, parentID) return b.sendMessage(chatid, topicid, msg.Username, msg.Text, parentID)
} }
return "", nil return "", nil
@ -157,9 +187,12 @@ func (b *Btelegram) getFileDirectURL(id string) string {
return res 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 := tgbotapi.NewMessage(chatid, "")
m.Text, m.ParseMode = TGGetParseMode(b, username, text) m.Text, m.ParseMode = TGGetParseMode(b, username, text)
if topicid != 0 {
m.BaseChat.MessageThreadID = topicid
}
m.ReplyToMessageID = parentID m.ReplyToMessageID = parentID
m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview") 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 // 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 { if len(media) == 0 {
return "", nil 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) messages, err := b.c.SendMediaGroup(mg)
if err != nil { if err != nil {
return "", err return "", err

2
go.mod
View File

@ -11,7 +11,6 @@ require (
github.com/d5/tengo/v2 v2.13.0 github.com/d5/tengo/v2 v2.13.0
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.6.0 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/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c
github.com/google/gops v0.3.27 github.com/google/gops v0.3.27
github.com/gorilla/schema v1.2.0 github.com/gorilla/schema v1.2.0
@ -29,6 +28,7 @@ require (
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75 github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d 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/v5 v5.39.3
github.com/mattermost/mattermost-server/v6 v6.7.2 github.com/mattermost/mattermost-server/v6 v6.7.2
github.com/mattn/godown v0.0.1 github.com/mattn/godown v0.0.1

4
go.sum
View File

@ -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-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-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-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 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 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= 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/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 h1:aI0ANEzy3dMv3vEAMQ80AItNie0fBR9ZxE2sAedORmM=
github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d/go.mod h1:Zg8PH1P/1CNUxozQ8blnjAV9PA4Qn2qWf33cX5yNKGM= 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.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 h1:Khvh6waxG1cHc4Cz5ef9n3XVCxRWpAKUtqg9PJl5+y8=
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34= github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404/go.mod h1:RyS7FDNQlzF1PsjbJWHRI35exqaKGSO9qD4iv8QjE34=

View File

@ -2,3 +2,4 @@
coverage.out coverage.out
tmp/ tmp/
book/ book/
.vscode/

View File

@ -85,9 +85,9 @@ func main() {
log.Printf("Authorized on account %s", bot.Self.UserName) 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 { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
@ -151,7 +150,7 @@ func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse)
} }
// if debug, read response body // if debug, read response body
data, err := ioutil.ReadAll(responseBody) data, err := io.ReadAll(responseBody)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -494,6 +493,8 @@ func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.
ch := make(chan Update, bot.Buffer) ch := make(chan Update, bot.Buffer)
func(w http.ResponseWriter, r *http.Request) { func(w http.ResponseWriter, r *http.Request) {
defer close(ch)
update, err := bot.HandleUpdate(r) update, err := bot.HandleUpdate(r)
if err != nil { if err != nil {
errMsg, _ := json.Marshal(map[string]string{"error": err.Error()}) errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
@ -504,7 +505,6 @@ func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.
} }
ch <- *update ch <- *update
close(ch)
}(w, r) }(w, r)
return ch 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 // forwardMessage, but the copied message doesn't have a link to the original
// message. Returns the MessageID of the sent message on success. // message. Returns the MessageID of the sent message on success.
func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) { func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) {
params, err := config.params() resp, err := bot.Request(config)
if err != nil {
return MessageID{}, err
}
resp, err := bot.MakeRequest(config.method(), params)
if err != nil { if err != nil {
return MessageID{}, err return MessageID{}, err
} }
@ -697,6 +692,33 @@ func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) {
return messageID, err 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. // 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. // 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. // Note that you don't have to include the formatting style in the input text, or it will be escaped too.

View File

@ -265,7 +265,9 @@ func (CloseConfig) params() (Params, error) {
// BaseChat is base type for all chat config types. // BaseChat is base type for all chat config types.
type BaseChat struct { type BaseChat struct {
ChatID int64 // required ChatID int64 // required
MessageThreadID int
ChannelUsername string ChannelUsername string
ProtectContent bool
ReplyToMessageID int ReplyToMessageID int
ReplyMarkup interface{} ReplyMarkup interface{}
DisableNotification bool DisableNotification bool
@ -276,9 +278,11 @@ func (chat *BaseChat) params() (Params, error) {
params := make(Params) params := make(Params)
params.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) params.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername)
params.AddNonZero("message_thread_id", chat.MessageThreadID)
params.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) params.AddNonZero("reply_to_message_id", chat.ReplyToMessageID)
params.AddBool("disable_notification", chat.DisableNotification) params.AddBool("disable_notification", chat.DisableNotification)
params.AddBool("allow_sending_without_reply", chat.AllowSendingWithoutReply) params.AddBool("allow_sending_without_reply", chat.AllowSendingWithoutReply)
params.AddBool("protect_content", chat.ProtectContent)
err := params.AddInterface("reply_markup", chat.ReplyMarkup) err := params.AddInterface("reply_markup", chat.ReplyMarkup)
@ -319,6 +323,21 @@ func (edit BaseEdit) params() (Params, error) {
return params, err 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. // MessageConfig contains information about a SendMessage request.
type MessageConfig struct { type MessageConfig struct {
BaseChat BaseChat
@ -403,6 +422,7 @@ func (config CopyMessageConfig) method() string {
// PhotoConfig contains information about a SendPhoto request. // PhotoConfig contains information about a SendPhoto request.
type PhotoConfig struct { type PhotoConfig struct {
BaseFile BaseFile
BaseSpoiler
Thumb RequestFileData Thumb RequestFileData
Caption string Caption string
ParseMode string ParseMode string
@ -418,6 +438,15 @@ func (config PhotoConfig) params() (Params, error) {
params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("caption", config.Caption)
params.AddNonEmpty("parse_mode", config.ParseMode) params.AddNonEmpty("parse_mode", config.ParseMode)
err = params.AddInterface("caption_entities", config.CaptionEntities) 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 return params, err
} }
@ -553,6 +582,7 @@ func (config StickerConfig) files() []RequestFile {
// VideoConfig contains information about a SendVideo request. // VideoConfig contains information about a SendVideo request.
type VideoConfig struct { type VideoConfig struct {
BaseFile BaseFile
BaseSpoiler
Thumb RequestFileData Thumb RequestFileData
Duration int Duration int
Caption string Caption string
@ -572,6 +602,15 @@ func (config VideoConfig) params() (Params, error) {
params.AddNonEmpty("parse_mode", config.ParseMode) params.AddNonEmpty("parse_mode", config.ParseMode)
params.AddBool("supports_streaming", config.SupportsStreaming) params.AddBool("supports_streaming", config.SupportsStreaming)
err = params.AddInterface("caption_entities", config.CaptionEntities) 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 return params, err
} }
@ -599,6 +638,7 @@ func (config VideoConfig) files() []RequestFile {
// AnimationConfig contains information about a SendAnimation request. // AnimationConfig contains information about a SendAnimation request.
type AnimationConfig struct { type AnimationConfig struct {
BaseFile BaseFile
BaseSpoiler
Duration int Duration int
Thumb RequestFileData Thumb RequestFileData
Caption string Caption string
@ -616,6 +656,15 @@ func (config AnimationConfig) params() (Params, error) {
params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("caption", config.Caption)
params.AddNonEmpty("parse_mode", config.ParseMode) params.AddNonEmpty("parse_mode", config.ParseMode)
err = params.AddInterface("caption_entities", config.CaptionEntities) 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 return params, err
} }
@ -972,6 +1021,7 @@ func (config GetGameHighScoresConfig) method() string {
// ChatActionConfig contains information about a SendChatAction request. // ChatActionConfig contains information about a SendChatAction request.
type ChatActionConfig struct { type ChatActionConfig struct {
BaseChat BaseChat
MessageThreadID int
Action string // required Action string // required
} }
@ -979,6 +1029,7 @@ func (config ChatActionConfig) params() (Params, error) {
params, err := config.BaseChat.params() params, err := config.BaseChat.params()
params["action"] = config.Action params["action"] = config.Action
params.AddNonZero("message_thread_id", config.MessageThreadID)
return params, err return params, err
} }
@ -1162,6 +1213,7 @@ type WebhookConfig struct {
MaxConnections int MaxConnections int
AllowedUpdates []string AllowedUpdates []string
DropPendingUpdates bool DropPendingUpdates bool
SecretToken string
} }
func (config WebhookConfig) method() string { func (config WebhookConfig) method() string {
@ -1179,6 +1231,7 @@ func (config WebhookConfig) params() (Params, error) {
params.AddNonZero("max_connections", config.MaxConnections) params.AddNonZero("max_connections", config.MaxConnections)
err := params.AddInterface("allowed_updates", config.AllowedUpdates) err := params.AddInterface("allowed_updates", config.AllowedUpdates)
params.AddBool("drop_pending_updates", config.DropPendingUpdates) params.AddBool("drop_pending_updates", config.DropPendingUpdates)
params.AddNonEmpty("secret_token", config.SecretToken)
return params, err return params, err
} }
@ -1240,6 +1293,29 @@ func (config InlineConfig) params() (Params, error) {
return params, err 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. // CallbackConfig contains information on making a CallbackQuery response.
type CallbackConfig struct { type CallbackConfig struct {
CallbackQueryID string `json:"callback_query_id"` CallbackQueryID string `json:"callback_query_id"`
@ -1325,6 +1401,7 @@ type KickChatMemberConfig = BanChatMemberConfig
type RestrictChatMemberConfig struct { type RestrictChatMemberConfig struct {
ChatMemberConfig ChatMemberConfig
UntilDate int64 UntilDate int64
UseIndependentChatPermissions bool
Permissions *ChatPermissions Permissions *ChatPermissions
} }
@ -1337,6 +1414,7 @@ func (config RestrictChatMemberConfig) params() (Params, error) {
params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername)
params.AddNonZero64("user_id", config.UserID) params.AddNonZero64("user_id", config.UserID)
params.AddBool("use_independent_chat_permissions", config.UseIndependentChatPermissions)
err := params.AddInterface("permissions", config.Permissions) err := params.AddInterface("permissions", config.Permissions)
params.AddNonZero64("until_date", config.UntilDate) params.AddNonZero64("until_date", config.UntilDate)
@ -1353,11 +1431,12 @@ type PromoteChatMemberConfig struct {
CanPostMessages bool CanPostMessages bool
CanEditMessages bool CanEditMessages bool
CanDeleteMessages bool CanDeleteMessages bool
CanManageVoiceChats bool CanManageVideoChats bool
CanInviteUsers bool CanInviteUsers bool
CanRestrictMembers bool CanRestrictMembers bool
CanPinMessages bool CanPinMessages bool
CanPromoteMembers bool CanPromoteMembers bool
CanManageTopics bool
} }
func (config PromoteChatMemberConfig) method() string { 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_post_messages", config.CanPostMessages)
params.AddBool("can_edit_messages", config.CanEditMessages) params.AddBool("can_edit_messages", config.CanEditMessages)
params.AddBool("can_delete_messages", config.CanDeleteMessages) 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_invite_users", config.CanInviteUsers)
params.AddBool("can_restrict_members", config.CanRestrictMembers) params.AddBool("can_restrict_members", config.CanRestrictMembers)
params.AddBool("can_pin_messages", config.CanPinMessages) params.AddBool("can_pin_messages", config.CanPinMessages)
params.AddBool("can_promote_members", config.CanPromoteMembers) params.AddBool("can_promote_members", config.CanPromoteMembers)
params.AddBool("can_manage_topics", config.CanManageTopics)
return params, nil return params, nil
} }
@ -1500,6 +1580,7 @@ func (ChatAdministratorsConfig) method() string {
// restrict members. // restrict members.
type SetChatPermissionsConfig struct { type SetChatPermissionsConfig struct {
ChatConfig ChatConfig
UseIndependentChatPermissions bool
Permissions *ChatPermissions Permissions *ChatPermissions
} }
@ -1511,6 +1592,7 @@ func (config SetChatPermissionsConfig) params() (Params, error) {
params := make(Params) params := make(Params)
params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername)
params.AddBool("use_independent_chat_permissions", config.UseIndependentChatPermissions)
err := params.AddInterface("permissions", config.Permissions) err := params.AddInterface("permissions", config.Permissions)
return params, err return params, err
@ -1759,6 +1841,64 @@ func (config InvoiceConfig) method() string {
return "sendInvoice" 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. // ShippingConfig contains information for answerShippingQuery request.
type ShippingConfig struct { type ShippingConfig struct {
ShippingQueryID string // required ShippingQueryID string // required
@ -1782,7 +1922,7 @@ func (config ShippingConfig) params() (Params, error) {
return params, err return params, err
} }
// PreCheckoutConfig conatins information for answerPreCheckoutQuery request. // PreCheckoutConfig contains information for answerPreCheckoutQuery request.
type PreCheckoutConfig struct { type PreCheckoutConfig struct {
PreCheckoutQueryID string // required PreCheckoutQueryID string // required
OK bool // required OK bool // required
@ -1979,6 +2119,24 @@ func (config GetStickerSetConfig) params() (Params, error) {
return params, nil 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. // UploadStickerConfig allows you to upload a sticker for use in a set later.
type UploadStickerConfig struct { type UploadStickerConfig struct {
UserID int64 UserID int64
@ -2013,8 +2171,9 @@ type NewStickerSetConfig struct {
Title string Title string
PNGSticker RequestFileData PNGSticker RequestFileData
TGSSticker RequestFileData TGSSticker RequestFileData
StickerType string
Emojis string Emojis string
ContainsMasks bool ContainsMasks bool // deprecated
MaskPosition *MaskPosition MaskPosition *MaskPosition
} }
@ -2028,11 +2187,10 @@ func (config NewStickerSetConfig) params() (Params, error) {
params.AddNonZero64("user_id", config.UserID) params.AddNonZero64("user_id", config.UserID)
params["name"] = config.Name params["name"] = config.Name
params["title"] = config.Title params["title"] = config.Title
params["emojis"] = config.Emojis params["emojis"] = config.Emojis
params.AddBool("contains_masks", config.ContainsMasks) params.AddBool("contains_masks", config.ContainsMasks)
params.AddNonEmpty("sticker_type", string(config.StickerType))
err := params.AddInterface("mask_position", config.MaskPosition) err := params.AddInterface("mask_position", config.MaskPosition)
return params, err return params, err
@ -2195,16 +2353,246 @@ func (config DeleteChatStickerSetConfig) params() (Params, error) {
return params, nil 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. // MediaGroupConfig allows you to send a group of media.
// //
// Media consist of InputMedia items (InputMediaPhoto, InputMediaVideo). // Media consist of InputMedia items (InputMediaPhoto, InputMediaVideo).
type MediaGroupConfig struct { type MediaGroupConfig struct {
ChatID int64 BaseChat
ChannelUsername string
Media []interface{} Media []interface{}
DisableNotification bool
ReplyToMessageID int
} }
func (config MediaGroupConfig) method() string { func (config MediaGroupConfig) method() string {
@ -2212,13 +2600,16 @@ func (config MediaGroupConfig) method() string {
} }
func (config MediaGroupConfig) params() (Params, error) { 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.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername)
params.AddBool("disable_notification", config.DisableNotification) params.AddBool("disable_notification", config.DisableNotification)
params.AddNonZero("reply_to_message_id", config.ReplyToMessageID) 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 return params, err
} }
@ -2313,6 +2704,81 @@ func (config DeleteMyCommandsConfig) params() (Params, error) {
return params, err 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 // prepareInputMediaParam evaluates a single InputMedia and determines if it
// needs to be modified for a successful upload. If it returns nil, then the // 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 // value does not need to be included in the params. Otherwise, it will return

View File

@ -1,7 +1,14 @@
package tgbotapi package tgbotapi
import ( import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"net/url" "net/url"
"sort"
"strings"
) )
// NewMessage creates a new Message. // NewMessage creates a new Message.
@ -171,7 +178,9 @@ func NewVoice(chatID int64, file RequestFileData) VoiceConfig {
// two to ten InputMediaPhoto or InputMediaVideo. // two to ten InputMediaPhoto or InputMediaVideo.
func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig { func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig {
return MediaGroupConfig{ return MediaGroupConfig{
BaseChat: BaseChat{
ChatID: chatID, ChatID: chatID,
},
Media: files, Media: files,
} }
} }
@ -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 // NewKeyboardButtonContact creates a keyboard button that requests
// user contact information upon click. // user contact information upon click.
func NewKeyboardButtonContact(text string) KeyboardButton { 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 // NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text
// which goes to a LoginURL. // which goes to a LoginURL.
func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton { func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton {
@ -925,3 +952,38 @@ func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig
func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig { func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode} 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
}

View File

@ -95,3 +95,10 @@ func (p Params) AddFirstValid(key string, args ...interface{}) error {
return nil 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
}
}

6
vendor/modules.txt vendored
View File

@ -83,9 +83,6 @@ github.com/fsnotify/fsnotify
# github.com/go-asn1-ber/asn1-ber v1.5.3 # github.com/go-asn1-ber/asn1-ber v1.5.3
## explicit; go 1.13 ## explicit; go 1.13
github.com/go-asn1-ber/asn1-ber 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 # github.com/golang-jwt/jwt v3.2.2+incompatible
## explicit ## explicit
github.com/golang-jwt/jwt github.com/golang-jwt/jwt
@ -234,6 +231,9 @@ github.com/matterbridge/logrus-prefixed-formatter
# github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d # github.com/matterbridge/matterclient v0.0.0-20221106190440-8bcf49695e0d
## explicit; go 1.17 ## explicit; go 1.17
github.com/matterbridge/matterclient 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 # github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404
## explicit ## explicit
github.com/mattermost/go-i18n/i18n github.com/mattermost/go-i18n/i18n