Skip to content

Commit f5b6720

Browse files
ernadoclaude
andcommitted
feat: add SetBusinessAccountGiftSettings
Changes the incoming-gift privacy settings of a managed business account. There is no dedicated MTProto RPC: the gift button and accepted gift types live in the account-wide GlobalPrivacySettings, so this does a read-modify-write over the business connection (account.getGlobalPrivacySettings then account.setGlobalPrivacySettings) to avoid clobbering the other privacy fields. The Bot API AcceptedGiftTypes is the inverse of MTProto's disallowedGiftsSettings; AcceptedGiftTypes.disallowed() flips the flags. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4844f4e commit f5b6720

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

gift_settings.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
6+
"github.com/gotd/td/tg"
7+
)
8+
9+
// AcceptedGiftTypes describes the types of gifts that can be gifted to a user or
10+
// a chat.
11+
type AcceptedGiftTypes struct {
12+
// UnlimitedGifts reports whether unlimited regular gifts are accepted.
13+
UnlimitedGifts bool `json:"unlimited_gifts"`
14+
// LimitedGifts reports whether limited regular gifts are accepted.
15+
LimitedGifts bool `json:"limited_gifts"`
16+
// UniqueGifts reports whether unique (collectible) gifts are accepted.
17+
UniqueGifts bool `json:"unique_gifts"`
18+
// PremiumSubscription reports whether a Telegram Premium subscription is
19+
// accepted as a gift.
20+
PremiumSubscription bool `json:"premium_subscription"`
21+
// GiftsFromChannels reports whether gifts sent on behalf of channels are
22+
// accepted.
23+
GiftsFromChannels bool `json:"gifts_from_channels"`
24+
}
25+
26+
// disallowed maps the accepted gift types onto the MTProto disallowed-gifts
27+
// settings, which is expressed as the inverse (a set of disallow flags).
28+
func (a AcceptedGiftTypes) disallowed() tg.DisallowedGiftsSettings {
29+
return tg.DisallowedGiftsSettings{
30+
DisallowUnlimitedStargifts: !a.UnlimitedGifts,
31+
DisallowLimitedStargifts: !a.LimitedGifts,
32+
DisallowUniqueStargifts: !a.UniqueGifts,
33+
DisallowPremiumGifts: !a.PremiumSubscription,
34+
DisallowStargiftsFromChannels: !a.GiftsFromChannels,
35+
}
36+
}
37+
38+
// SetBusinessAccountGiftSettings changes the privacy settings for incoming gifts
39+
// of a managed business account. Requires the can_change_gift_settings business
40+
// bot right.
41+
//
42+
// The account's other global privacy settings are read first and preserved, so
43+
// only the gift button and accepted gift types change.
44+
func (b *Bot) SetBusinessAccountGiftSettings(
45+
ctx context.Context, businessConnectionID string, showGiftButton bool, acceptedGiftTypes AcceptedGiftTypes,
46+
) error {
47+
var current tg.GlobalPrivacySettings
48+
49+
err := b.invokeBusiness(ctx, businessConnectionID, &tg.AccountGetGlobalPrivacySettingsRequest{}, &current)
50+
if err != nil {
51+
return asAPIError(err)
52+
}
53+
54+
current.SetDisplayGiftsButton(showGiftButton)
55+
current.SetDisallowedGifts(acceptedGiftTypes.disallowed())
56+
57+
var updated tg.GlobalPrivacySettings
58+
59+
err = b.invokeBusiness(ctx, businessConnectionID, &tg.AccountSetGlobalPrivacySettingsRequest{
60+
Settings: current,
61+
}, &updated)
62+
if err != nil {
63+
return asAPIError(err)
64+
}
65+
66+
return nil
67+
}

gift_settings_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/gotd/td/tg"
8+
)
9+
10+
func TestSetBusinessAccountGiftSettings(t *testing.T) {
11+
inv := newMockInvoker()
12+
// Both the get and the set go through the business wrapper; the reply
13+
// decodes as GlobalPrivacySettings for either call. HideReadMarks stands in
14+
// for an unrelated setting that must survive the read-modify-write.
15+
inv.reply(tg.InvokeWithBusinessConnectionRequestTypeID, &tg.GlobalPrivacySettings{HideReadMarks: true})
16+
17+
err := newMockBot(inv).SetBusinessAccountGiftSettings(context.Background(), "bc1", true, AcceptedGiftTypes{
18+
UnlimitedGifts: true,
19+
UniqueGifts: true,
20+
GiftsFromChannels: true,
21+
})
22+
if err != nil {
23+
t.Fatalf("SetBusinessAccountGiftSettings: %v", err)
24+
}
25+
26+
wrapper := tg.InvokeWithBusinessConnectionRequest{Query: &tg.AccountSetGlobalPrivacySettingsRequest{}}
27+
28+
inv.decode(t, tg.InvokeWithBusinessConnectionRequestTypeID, &wrapper)
29+
30+
if wrapper.ConnectionID != "bc1" {
31+
t.Fatalf("connection id = %q", wrapper.ConnectionID)
32+
}
33+
34+
req, ok := wrapper.Query.(*tg.AccountSetGlobalPrivacySettingsRequest)
35+
if !ok {
36+
t.Fatalf("query = %#v, want set global privacy", wrapper.Query)
37+
}
38+
39+
settings := req.Settings
40+
if !settings.HideReadMarks {
41+
t.Fatalf("unrelated setting not preserved: %#v", settings)
42+
}
43+
44+
if !settings.DisplayGiftsButton {
45+
t.Fatalf("display gifts button not set")
46+
}
47+
48+
disallowed, ok := settings.GetDisallowedGifts()
49+
if !ok {
50+
t.Fatalf("disallowed gifts not set")
51+
}
52+
53+
if disallowed.DisallowUnlimitedStargifts {
54+
t.Fatalf("unlimited gifts should be allowed")
55+
}
56+
57+
if !disallowed.DisallowLimitedStargifts {
58+
t.Fatalf("limited gifts should be disallowed")
59+
}
60+
61+
if !disallowed.DisallowPremiumGifts {
62+
t.Fatalf("premium gifts should be disallowed")
63+
}
64+
65+
if disallowed.DisallowUniqueStargifts || disallowed.DisallowStargiftsFromChannels {
66+
t.Fatalf("unique/channel gifts should be allowed: %#v", disallowed)
67+
}
68+
}

0 commit comments

Comments
 (0)