Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ registered on the default guild — instant, one server — or on the applicatio
with `WithGlobalCommands`, which reaches every server the bot is in and takes up
to an hour to propagate.

`Interaction.UserID()` is who invoked it, from `member.user` in a guild and from
`user` in a DM — read either field alone and permission checks see an empty id in
the other context.

Path segments are percent-escaped and queries built with `url.Values`.
`Webhook.Token` and `Interaction.Token` are `Secret` — `[REDACTED]` in logs and
JSON, `.Reveal()` to read.
Expand Down
12 changes: 12 additions & 0 deletions interactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Interaction struct {
GuildID string `json:"guild_id"`
ChannelID string `json:"channel_id"`
Member Member `json:"member"`
User Author `json:"user"`
Data InteractionData `json:"data"`
}

Expand All @@ -33,6 +34,17 @@ type Member struct {
User Author `json:"user"`
}

// UserID is who invoked the interaction, wherever Discord put them: a guild
// interaction carries the caller under `member.user`, a DM under `user`. Reading
// only one of the two makes every permission check in the other context see an
// empty id.
func (i Interaction) UserID() string {
if id := i.Member.User.ID; id != "" {
return id
}
return i.User.ID
}

// InteractionData is the invoked command + its options. For a component
// interaction (type 3) the command fields are empty and CustomID/Values carry
// the clicked component's id and the selected value(s) instead.
Expand Down
16 changes: 16 additions & 0 deletions interactions_autocomplete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ func TestFocusedNoneWhenAbsent(t *testing.T) {
}
}

// Discord puts the caller under `member.user` in a guild and under `user` in a
// DM. A permission check that reads only one sees an empty id in the other.
func TestInteractionUserIDCoversGuildAndDM(t *testing.T) {
guild := Interaction{Member: Member{User: Author{ID: "u1"}}}
if got := guild.UserID(); got != "u1" {
t.Fatalf("guild UserID() = %q, want u1", got)
}
dm := Interaction{User: Author{ID: "u2"}}
if got := dm.UserID(); got != "u2" {
t.Fatalf("dm UserID() = %q, want u2", got)
}
if got := (Interaction{}).UserID(); got != "" {
t.Fatalf("empty UserID() = %q, want empty", got)
}
}

func TestInteractionsRespondNoAllowedMentions(t *testing.T) {
s := transport.NewStub()
in := &Interactions{rt: s, def: &defaults{guilds: &Guilds{rt: s}}}
Expand Down
Loading