From 413d19688d5091ebc3a667a9789bb1e3b3030a80 Mon Sep 17 00:00:00 2001 From: Alvin Date: Thu, 14 Nov 2024 18:23:03 +0800 Subject: [PATCH 1/5] create invoice link & message caption between photo --- bot_test.go | 41 ++++++++++++++++++++++++++++++- configs.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++---- go.mod | 2 +- helpers.go | 13 ++++++++++ 4 files changed, 119 insertions(+), 6 deletions(-) diff --git a/bot_test.go b/bot_test.go index 7abd790a..bebc7225 100644 --- a/bot_test.go +++ b/bot_test.go @@ -8,7 +8,7 @@ import ( ) const ( - TestToken = "153667468:AAHlSHlMqSt1f_uFmVRJbm5gntu2HI4WW8I" + TestToken = "6766212541:AAGQTAPSbZGQoJ-FmG-nyYZLPCQQCc5wFIw" ChatID = 76918703 Channel = "@tgbotapitest" SupergroupChatID = -1001120141283 @@ -1048,3 +1048,42 @@ func TestPrepareInputMediaForParams(t *testing.T) { t.Error("Passthrough value was not the same") } } + +func TestCreateInvoiceLink(t *testing.T) { + bot, err := getBot(t) + if err != nil { + t.Fatal(err) + } + resp, err := bot.Request(NewInvoiceLink("Demo Invoice", "This is a demo invoice link", "command", "", "XTR", []int{}, []LabeledPrice{ + { + Label: "Demo Digital Token", + Amount: 100, + }, + })) + + if err != nil { + t.Fatal(err) + } + if !resp.Ok { + t.Fatal("Response not OK", resp.ErrorCode, resp.Description) + } + result, err := resp.Result.MarshalJSON() + if err != nil { + t.Fatal(err) + } + t.Log(string(result)) +} + +func TestSendPhoto(t *testing.T) { + bot, err := getBot(t) + if err != nil { + t.Fatal(err) + } + photo := NewPhoto(1632669575, FileURL("https://pub-4b4332e2985947ceacb0e1bdc9c540bd.r2.dev/novelcoronavirus-optimized.jpg")) + photo.Caption = "Welcome to Follow https://github.com/TUTUBIG" + photo.ShowCaptionAboveMedia = true + _, err = bot.Send(photo) + if err != nil { + t.Fatal(err) + } +} diff --git a/configs.go b/configs.go index 1831337b..4a899c12 100644 --- a/configs.go +++ b/configs.go @@ -405,10 +405,11 @@ func (config CopyMessageConfig) method() string { // PhotoConfig contains information about a SendPhoto request. type PhotoConfig struct { BaseFile - Thumb RequestFileData - Caption string - ParseMode string - CaptionEntities []MessageEntity + Thumb RequestFileData + Caption string + ParseMode string + CaptionEntities []MessageEntity + ShowCaptionAboveMedia bool } func (config PhotoConfig) params() (Params, error) { @@ -420,6 +421,7 @@ func (config PhotoConfig) params() (Params, error) { params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) err = params.AddInterface("caption_entities", config.CaptionEntities) + params.AddBool("show_caption_above_media", config.ShowCaptionAboveMedia) return params, err } @@ -1784,6 +1786,65 @@ func (config InvoiceConfig) method() string { return "sendInvoice" } +// InvoiceLinkConfig contains information for createInvoiceLink request. +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) + var err error + + 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("is_flexible", config.IsFlexible) + params.AddBool("send_phone_number_to_provider", config.SendPhoneNumberToProvider) + params.AddBool("send_email_to_provider", config.SendEmailToProvider) + + return params, err +} + +func (config InvoiceLinkConfig) method() string { + return "createInvoiceLink" +} + // ShippingConfig contains information for answerShippingQuery request. type ShippingConfig struct { ShippingQueryID string // required diff --git a/go.mod b/go.mod index 167e5e45..b27bc974 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/go-telegram-bot-api/telegram-bot-api/v5 +module github.com/TUTUBIG/telegram-bot-api/v5 go 1.16 diff --git a/helpers.go b/helpers.go index 3a0e8187..97395d6e 100644 --- a/helpers.go +++ b/helpers.go @@ -777,6 +777,19 @@ func NewInvoice(chatID int64, title, description, payload, providerToken, startP Prices: prices} } +// NewInvoiceLink creates a new Invoice link. +func NewInvoiceLink(title, description, payload, providerToken, currency string, suggestedTipAmounts []int, prices []LabeledPrice) InvoiceLinkConfig { + return InvoiceLinkConfig{ + Title: title, + Description: description, + Payload: payload, + ProviderToken: providerToken, + Currency: currency, + Prices: prices, + SuggestedTipAmounts: suggestedTipAmounts, + } +} + // NewChatTitle allows you to update the title of a chat. func NewChatTitle(chatID int64, title string) SetChatTitleConfig { return SetChatTitleConfig{ From fd84eabc51e132faf94263ef34934d3c983d9fa5 Mon Sep 17 00:00:00 2001 From: Alvin Date: Tue, 31 Dec 2024 11:07:07 +0800 Subject: [PATCH 2/5] save message and share --- bot.go | 13 +++++++++++++ bot_test.go | 33 ++++++++++++++++++++++++++++++++- configs.go | 28 ++++++++++++++++++++++++++++ types.go | 8 ++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/bot.go b/bot.go index 39037b8d..1f9264aa 100644 --- a/bot.go +++ b/bot.go @@ -719,6 +719,19 @@ func (bot *BotAPI) GetMyDefaultAdministratorRights(config GetMyDefaultAdministra return rights, err } +// SavePreparedInlineMessage stores a message that can be sent by a user of a Mini App. +func (bot *BotAPI) SavePreparedInlineMessage(config SavePreparedInlineMessageConfig) (PreparedInlineMessage, error) { + var message PreparedInlineMessage + + resp, err := bot.Request(config) + if err != nil { + return message, err + } + + err = json.Unmarshal(resp.Result, &message) + return message, 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/bot_test.go b/bot_test.go index bebc7225..389f478a 100644 --- a/bot_test.go +++ b/bot_test.go @@ -1,6 +1,7 @@ package tgbotapi import ( + "fmt" "net/http" "os" "testing" @@ -36,7 +37,6 @@ func (t testLogger) Printf(format string, v ...interface{}) { func getBot(t *testing.T) (*BotAPI, error) { bot, err := NewBotAPI(TestToken) - bot.Debug = true logger := testLogger{t} SetLogger(logger) @@ -45,6 +45,8 @@ func getBot(t *testing.T) (*BotAPI, error) { t.Error(err) } + bot.Debug = true + return bot, err } @@ -1087,3 +1089,32 @@ func TestSendPhoto(t *testing.T) { t.Fatal(err) } } + +func TestSaveInlineMessage(t *testing.T) { + bot, err := NewBotAPI(TestToken) + if err != nil { + t.Fatal(err) + } + + photo := NewInlineQueryResultPhoto(fmt.Sprintf("%d", time.Now().Unix()), "https://pub-6c52100fa9ac41f681f0713eac878541.r2.dev/xxx.png") + photo.Caption = "This is a demo saved inline query messages" + photo.ParseMode = "HTML" + photo.ThumbURL = "https://pub-6c52100fa9ac41f681f0713eac878541.r2.dev/xxx.png" + markup := NewInlineKeyboardMarkup(NewInlineKeyboardRow(NewInlineKeyboardButtonURL("Open in TOMO", fmt.Sprintf("https://www.google.com")))) + photo.ReplyMarkup = &markup + + message, err := bot.SavePreparedInlineMessage(SavePreparedInlineMessageConfig{ + UserId: 1632669575, + Result: photo, + AllowUserChats: true, + AllowBotChats: true, + AllowGroupChats: true, + AllowChannelChats: true, + }) + + if err != nil { + t.Fatal(err) + } + + t.Log(message) +} diff --git a/configs.go b/configs.go index 4a899c12..8e5dc9b0 100644 --- a/configs.go +++ b/configs.go @@ -2627,3 +2627,31 @@ func prepareInputMediaForFiles(inputMedia []interface{}) []RequestFile { return files } + +type SavePreparedInlineMessageConfig struct { + UserId int64 + Result interface{} // Too many definitions to use generics but please make sure it is InlineQueryResult. https://core.telegram.org/bots/api#inlinequeryresult + AllowUserChats bool + AllowBotChats bool + AllowGroupChats bool + AllowChannelChats bool +} + +func (config SavePreparedInlineMessageConfig) method() string { + return "savePreparedInlineMessage" +} + +func (config SavePreparedInlineMessageConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserId) + if err := params.AddInterface("result", config.Result); err != nil { + return nil, err + } + params.AddBool("allow_user_chats", config.AllowUserChats) + params.AddBool("allow_bot_chats", config.AllowBotChats) + params.AddBool("allow_group_chats", config.AllowGroupChats) + params.AddBool("allow_channel_chats", config.AllowChannelChats) + + return params, nil +} diff --git a/types.go b/types.go index 36c174b8..0e1c3f76 100644 --- a/types.go +++ b/types.go @@ -3328,3 +3328,11 @@ type PreCheckoutQuery struct { // optional OrderInfo *OrderInfo `json:"order_info,omitempty"` } + +// PreparedInlineMessage describes an inline message to be sent by a user of a Mini App. +type PreparedInlineMessage struct { + // Unique identifier of the prepared message + Id string `json:"id"` + // Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used + ExpirationDate int64 `json:"expiration_date"` +} From d1ad8b9afc7bdac3c90e7d0efc7aa837e9440e38 Mon Sep 17 00:00:00 2001 From: Alvin Date: Tue, 31 Dec 2024 18:44:43 +0800 Subject: [PATCH 3/5] get star transactions --- bot.go | 12 ++++++++++++ bot_test.go | 21 +++++++++++++++++++++ configs.go | 24 ++++++++++++++++++++++++ types.go | 22 ++++++++++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/bot.go b/bot.go index 1f9264aa..ad22f2f3 100644 --- a/bot.go +++ b/bot.go @@ -759,3 +759,15 @@ func EscapeText(parseMode string, text string) string { return replacer.Replace(text) } + +func (bot *BotAPI) GetStarTransactions(config GetStarTransactionsConfig) (StarTransactions, error) { + var starTransactions StarTransactions + + resp, err := bot.Request(config) + if err != nil { + return starTransactions, err + } + + err = json.Unmarshal(resp.Result, &starTransactions) + return starTransactions, err +} diff --git a/bot_test.go b/bot_test.go index 389f478a..10db9fa2 100644 --- a/bot_test.go +++ b/bot_test.go @@ -1118,3 +1118,24 @@ func TestSaveInlineMessage(t *testing.T) { t.Log(message) } + +func TestGetStarTransactions(t *testing.T) { + bot, err := getBot(t) + if err != nil { + t.Fatal(err) + } + + response, err := bot.GetStarTransactions(GetStarTransactionsConfig{ + Offset: 200, + Limit: 100, + }) + if err != nil { + t.Fatal(err) + } + + for _, transaction := range response.Transactions { + if transaction.Receiver != nil { + t.Logf("%+v\n", transaction) + } + } +} diff --git a/configs.go b/configs.go index 8e5dc9b0..00916dab 100644 --- a/configs.go +++ b/configs.go @@ -2655,3 +2655,27 @@ func (config SavePreparedInlineMessageConfig) params() (Params, error) { return params, nil } + +// GetStarTransactionsConfig Returns the bot's Telegram Star transactions in chronological order. On success, returns a StarTransactions object. +type GetStarTransactionsConfig struct { + // Number of transactions to skip in the response + Offset int64 + // The maximum number of transactions to be retrieved. Values between 1-100 are accepted. Defaults to 100. + Limit int64 +} + +func (config GetStarTransactionsConfig) method() string { + return "getStarTransactions" +} + +func (config GetStarTransactionsConfig) params() (Params, error) { + params := make(Params) + + err := params.AddInterface("offset", config.Offset) + if err != nil { + return nil, err + } + params.AddNonZero("limit", int(config.Limit)) + + return params, nil +} diff --git a/types.go b/types.go index 0e1c3f76..2161bf0b 100644 --- a/types.go +++ b/types.go @@ -3336,3 +3336,25 @@ type PreparedInlineMessage struct { // Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used ExpirationDate int64 `json:"expiration_date"` } + +// StarTransaction Describes a Telegram Star transaction. +type StarTransaction struct { + // Unique identifier of the transaction. Coincides with the identifier of the original transaction for refund transactions. Coincides with SuccessfulPayment.telegram_payment_charge_id for successful incoming payments from users. + Id string `json:"id"` + // Integer amount of Telegram Stars transferred by the transaction + Amount int64 `json:"amount"` + // Optional. The number of 1/1000000000 shares of Telegram Stars transferred by the transaction; from 0 to 999999999 + NanostarAmount int64 `json:"nanostar_amount"` + // Date the transaction was created in Unix time + Date int64 `json:"date"` + // Optional. Source of an incoming transaction (e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal). Only for incoming transactions + Source interface{} `json:"source"` + // Optional. Receiver of an outgoing transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for outgoing transactions + Receiver interface{} `json:"receiver"` +} + +// StarTransactions Contains a list of Telegram Star transactions. +type StarTransactions struct { + // The list of transactions + Transactions []StarTransaction `json:"transactions"` +} From 60d43f0ea91404a3a405a38c11df1da6b47c9615 Mon Sep 17 00:00:00 2001 From: Alvin Date: Sun, 12 Jan 2025 16:41:48 +0800 Subject: [PATCH 4/5] transaction partner --- types.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/types.go b/types.go index 2161bf0b..6d7cf3f2 100644 --- a/types.go +++ b/types.go @@ -3337,6 +3337,12 @@ type PreparedInlineMessage struct { ExpirationDate int64 `json:"expiration_date"` } +// TransactionPartnerUser Describes a transaction with a user. +type TransactionPartnerUser struct { + Type string `json:"type"` + User User `json:"user"` +} + // StarTransaction Describes a Telegram Star transaction. type StarTransaction struct { // Unique identifier of the transaction. Coincides with the identifier of the original transaction for refund transactions. Coincides with SuccessfulPayment.telegram_payment_charge_id for successful incoming payments from users. From d057612988f503f904d78cf2d90fb570f83bab06 Mon Sep 17 00:00:00 2001 From: Alvin Date: Sun, 12 Jan 2025 16:42:05 +0800 Subject: [PATCH 5/5] refunded payment --- types.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/types.go b/types.go index 6d7cf3f2..9f4c9dc4 100644 --- a/types.go +++ b/types.go @@ -594,6 +594,11 @@ type Message struct { // // optional SuccessfulPayment *SuccessfulPayment `json:"successful_payment,omitempty"` + // RefundedPayment is a service message about a refunded payment, + // information about the payment; + // + // optional + RefundedPayment *RefundedPayment `json:"refunded_payment,omitempty"` // ConnectedWebsite is the domain name of the website on which the user has // logged in; // @@ -3289,6 +3294,20 @@ type SuccessfulPayment struct { ProviderPaymentChargeID string `json:"provider_payment_charge_id"` } +// RefundedPayment This object contains basic information about a refunded payment. +type RefundedPayment struct { + // Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars. Currently, always “XTR” + Currency string `json:"currency"` + // Total refunded price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45, total_amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). + TotalAmount int `json:"total_amount"` + // Bot-specified invoice payload + InvoicePayload string `json:"invoice_payload"` + // Telegram payment identifier + TelegramPaymentChargeId string `json:"telegram_payment_charge_id"` + // Optional. Provider payment identifier + ProviderPaymentChargeId string `json:"provider_payment_charge_id"` +} + // ShippingQuery contains information about an incoming shipping query. type ShippingQuery struct { // ID unique query identifier