Skip to content

Commit b49ae8b

Browse files
committed
feat: ReadBusinessMessage and TransferBusinessAccountStars
- ReadBusinessMessage: messages.readHistory over the business connection, marking a message and earlier ones as read. - TransferBusinessAccountStars: getPaymentForm + sendStarsForm with inputInvoiceBusinessBotTransferStars{inputUserSelf, stars}, both over the business connection via businessRaw. Mapping confirmed against telegram-bot-api 10.1. Hermetic tests; lint clean.
1 parent 8da2099 commit b49ae8b

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

business.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,58 @@ func (b *Bot) DeleteBusinessMessages(ctx context.Context, businessConnectionID s
236236
return nil
237237
}
238238

239+
// ReadBusinessMessage marks a message and all earlier messages in the chat as
240+
// read on behalf of a managed business account. The bot must have the
241+
// can_read_messages business bot right.
242+
func (b *Bot) ReadBusinessMessage(ctx context.Context, businessConnectionID string, chat ChatID, messageID int) error {
243+
peer, err := b.resolveInputPeer(ctx, chat)
244+
if err != nil {
245+
return err
246+
}
247+
248+
var affected tg.MessagesAffectedMessages
249+
250+
if err := b.invokeBusiness(ctx, businessConnectionID, &tg.MessagesReadHistoryRequest{
251+
Peer: peer,
252+
MaxID: messageID,
253+
}, &affected); err != nil {
254+
return asAPIError(err)
255+
}
256+
257+
return nil
258+
}
259+
260+
// TransferBusinessAccountStars transfers Telegram Stars from a managed business
261+
// account to the bot's own account. The bot must have the can_transfer_stars
262+
// business bot right.
263+
func (b *Bot) TransferBusinessAccountStars(ctx context.Context, businessConnectionID string, starCount int) error {
264+
invoice := &tg.InputInvoiceBusinessBotTransferStars{
265+
Bot: &tg.InputUserSelf{},
266+
Stars: int64(starCount),
267+
}
268+
269+
raw := b.businessRaw(businessConnectionID)
270+
271+
form, err := raw.PaymentsGetPaymentForm(ctx, &tg.PaymentsGetPaymentFormRequest{Invoice: invoice})
272+
if err != nil {
273+
return asAPIError(err)
274+
}
275+
276+
formID, err := starsFormID(form)
277+
if err != nil {
278+
return err
279+
}
280+
281+
if _, err := raw.PaymentsSendStarsForm(ctx, &tg.PaymentsSendStarsFormRequest{
282+
FormID: formID,
283+
Invoice: invoice,
284+
}); err != nil {
285+
return asAPIError(err)
286+
}
287+
288+
return nil
289+
}
290+
239291
// updatesAndUsers unpacks the update list and a user-by-id index from an
240292
// Updates response.
241293
func updatesAndUsers(resp tg.UpdatesClass) (updates []tg.UpdateClass, users map[int64]*tg.User) {

business_read_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/gotd/td/bin"
8+
"github.com/gotd/td/tg"
9+
)
10+
11+
func TestReadBusinessMessage(t *testing.T) {
12+
inv := newMockInvoker()
13+
inv.reply(tg.InvokeWithBusinessConnectionRequestTypeID, &tg.MessagesAffectedMessages{})
14+
15+
if err := newMockBot(inv).ReadBusinessMessage(context.Background(), "bc1", userRef(10, 20), 77); err != nil {
16+
t.Fatalf("ReadBusinessMessage: %v", err)
17+
}
18+
19+
wrapper := tg.InvokeWithBusinessConnectionRequest{Query: &tg.MessagesReadHistoryRequest{}}
20+
21+
inv.decode(t, tg.InvokeWithBusinessConnectionRequestTypeID, &wrapper)
22+
23+
if wrapper.ConnectionID != "bc1" {
24+
t.Fatalf("connection id = %q", wrapper.ConnectionID)
25+
}
26+
27+
rh, ok := wrapper.Query.(*tg.MessagesReadHistoryRequest)
28+
if !ok || rh.MaxID != 77 {
29+
t.Fatalf("query = %#v", wrapper.Query)
30+
}
31+
}
32+
33+
func TestTransferBusinessAccountStars(t *testing.T) {
34+
inv := newMockInvoker()
35+
36+
// The transfer makes two business-wrapped calls under the same outer type:
37+
// getPaymentForm then sendStarsForm. Respond by call order.
38+
calls := 0
39+
40+
inv.handle(tg.InvokeWithBusinessConnectionRequestTypeID, func(*bin.Buffer) (bin.Encoder, error) {
41+
calls++
42+
if calls == 1 {
43+
return &tg.PaymentsPaymentFormStars{FormID: 555, Invoice: tg.Invoice{Currency: "XTR"}}, nil
44+
}
45+
46+
return &tg.PaymentsPaymentResult{Updates: &tg.Updates{}}, nil
47+
})
48+
49+
if err := newMockBot(inv).TransferBusinessAccountStars(context.Background(), "bc2", 100); err != nil {
50+
t.Fatalf("TransferBusinessAccountStars: %v", err)
51+
}
52+
53+
// The last wrapped call is sendStarsForm.
54+
wrapper := tg.InvokeWithBusinessConnectionRequest{Query: &tg.PaymentsSendStarsFormRequest{}}
55+
56+
inv.decode(t, tg.InvokeWithBusinessConnectionRequestTypeID, &wrapper)
57+
58+
if wrapper.ConnectionID != "bc2" {
59+
t.Fatalf("connection id = %q", wrapper.ConnectionID)
60+
}
61+
62+
send, ok := wrapper.Query.(*tg.PaymentsSendStarsFormRequest)
63+
if !ok || send.FormID != 555 {
64+
t.Fatalf("query = %#v", wrapper.Query)
65+
}
66+
67+
invoice, ok := send.Invoice.(*tg.InputInvoiceBusinessBotTransferStars)
68+
if !ok || invoice.Stars != 100 {
69+
t.Fatalf("invoice = %#v", send.Invoice)
70+
}
71+
72+
if _, ok := invoice.Bot.(*tg.InputUserSelf); !ok {
73+
t.Fatalf("bot = %#v, want self", invoice.Bot)
74+
}
75+
}

0 commit comments

Comments
 (0)