Skip to content
Open
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
2 changes: 1 addition & 1 deletion bot/button/handlers/addadmin.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (h *AddAdminHandler) Execute(ctx *context.ButtonContext) {
auditReason = fmt.Sprintf("Added admin member/role by %s", member.User.Username)
}

panels, _ := dbclient.Client.Panel.GetByGuild(ctx, ctx.GuildId())
panels, _ := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, ctx.GuildId())
seen := make(map[uint64]struct{})
for _, p := range panels {
if p.TicketNotificationChannel == nil {
Expand Down
2 changes: 1 addition & 1 deletion bot/button/handlers/addsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func updateChannelPermissions(ctx cmdregistry.CommandContext, id uint64, mention
return
}

panels, err := dbclient.Client.Panel.GetByGuild(ctx, ctx.GuildId())
panels, err := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, ctx.GuildId())
if err != nil {
ctx.HandleError(err)
return
Expand Down
8 changes: 8 additions & 0 deletions bot/command/context/mentionabletype.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ const (
MentionableTypeRole
)

func (m MentionableType) String() string {
if m == MentionableTypeRole {
return "role"
}

return "user"
}

func (m MentionableType) OverwriteType() channel.PermissionOverwriteType {
switch m {
case MentionableTypeUser:
Expand Down
2 changes: 1 addition & 1 deletion bot/command/impl/settings/removeadmin.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (c RemoveAdminCommand) Execute(ctx registry.CommandContext, id uint64) {
auditReason = fmt.Sprintf("Removed admin member/role by %s", member.User.Username)
}

panels, _ := dbclient.Client.Panel.GetByGuild(ctx, ctx.GuildId())
panels, _ := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, ctx.GuildId())
seen := make(map[uint64]struct{})
for _, p := range panels {
if p.TicketNotificationChannel == nil {
Expand Down
2 changes: 1 addition & 1 deletion bot/command/impl/settings/removesupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (c RemoveSupportCommand) Execute(ctx registry.CommandContext, id uint64) {
auditReason = fmt.Sprintf("Removed support member/role by %s", member.User.Username)
}

panels, _ := dbclient.Client.Panel.GetByGuild(ctx, ctx.GuildId())
panels, _ := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, ctx.GuildId())
seen := make(map[uint64]struct{})
for _, p := range panels {
if p.TicketNotificationChannel == nil {
Expand Down
2 changes: 1 addition & 1 deletion bot/command/impl/tickets/oncall.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (c OnCallCommand) GetExecutor() interface{} {
}

func (OnCallCommand) Execute(ctx registry.CommandContext) {
panels, err := dbclient.Client.Panel.GetByGuild(ctx, ctx.GuildId())
panels, err := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, ctx.GuildId())
if err != nil {
ctx.HandleError(err)
return
Expand Down
2 changes: 1 addition & 1 deletion bot/command/impl/tickets/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (OpenCommand) AutoCompleteHandler(data interaction.ApplicationCommandAutoCo
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()

allPanels, err := dbclient.Client.Panel.GetByGuild(ctx, data.GuildId.Value)
allPanels, err := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, data.GuildId.Value)
if err != nil {
sentry.Error(err)
return nil
Expand Down
4 changes: 2 additions & 2 deletions bot/command/impl/tickets/switchpanel.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func (SwitchPanelCommand) Execute(ctx *cmdcontext.SlashCommandContext, panelId i
msg, err := ctx.Worker().GetChannelMessage(*ticket.ChannelId, *ticket.WelcomeMessageId)

// Error is likely to be due to message being deleted, we want to continue further even if it is
if err == nil {
if err == nil && logic.ShouldRewriteClassicWelcome(msg, &newPanel) {
var subject string

embeds := utils.PtrElems(msg.Embeds) // TODO: Fix types
Expand Down Expand Up @@ -340,7 +340,7 @@ func (SwitchPanelCommand) AutoCompleteHandler(data interaction.ApplicationComman
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) // TODO: Propagate context
defer cancel()

panels, err := dbclient.Client.Panel.GetByGuild(ctx, data.GuildId.Value)
panels, err := dbclient.Client.Panel.GetByGuildWithoutComponents(ctx, data.GuildId.Value)
if err != nil {
sentry.Error(err) // TODO: Context
return nil
Expand Down
14 changes: 11 additions & 3 deletions bot/command/messageresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,20 @@ func (r *MessageResponse) IntoCreateMessageData() rest.CreateMessageData {
}

func (r *MessageResponse) IntoEditMessageData() rest.EditMessageData {
return rest.EditMessageData{
Content: r.Content,
Embeds: r.Embeds,
data := rest.EditMessageData{
Flags: r.Flags,
Components: r.Components,
}

// V2 messages cannot carry content, and Discord requires embeds to be an empty array.
if r.isComponentsV2() {
data.Embeds = []*embed.Embed{}
} else {
data.Content = r.Content
data.Embeds = r.Embeds
}

return data
}

func (r *MessageResponse) IntoWebhookBody() rest.WebhookBody {
Expand Down
36 changes: 36 additions & 0 deletions bot/command/messageresponse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,39 @@ func TestMessageResponseEditsOmitContentAndEmbedsUnderComponentsV2(t *testing.T)
})
}
}

func TestMessageResponseIntoEditMessageData(t *testing.T) {
components := []component.Component{component.BuildTextDisplay(component.TextDisplay{Content: "hello"})}
embeds := []*embed.Embed{embed.NewEmbed().SetTitle("legacy")}

tests := []struct {
name string
flags uint
wantLegacy bool
}{
{"components v2", message.SumFlags(message.FlagComponentsV2), false},
{"ephemeral components v2", message.SumFlags(message.FlagEphemeral, message.FlagComponentsV2), false},
{"legacy", 0, true},
{"ephemeral legacy", message.SumFlags(message.FlagEphemeral), true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := MessageResponse{Content: "some text", Embeds: embeds, Components: components, Flags: tt.flags}
data := res.IntoEditMessageData()

require.Len(t, data.Components, 1)
require.Equal(t, tt.flags, data.Flags)

if tt.wantLegacy {
require.Equal(t, "some text", data.Content)
require.Len(t, data.Embeds, 1)
return
}

require.Empty(t, data.Content)
require.NotNil(t, data.Embeds, "Discord requires an empty array, not null, on a V2 message")
require.Empty(t, data.Embeds)
})
}
}
125 changes: 72 additions & 53 deletions bot/logic/claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/TicketsBot-cloud/database"
"github.com/TicketsBot-cloud/gdl/objects/channel"
"github.com/TicketsBot-cloud/gdl/objects/channel/embed"
"github.com/TicketsBot-cloud/gdl/objects/channel/message"
"github.com/TicketsBot-cloud/gdl/objects/guild/emoji"
"github.com/TicketsBot-cloud/gdl/objects/interaction/component"
"github.com/TicketsBot-cloud/gdl/permission"
Expand Down Expand Up @@ -686,68 +687,86 @@ func UpdateWelcomeMessageClaimButton(ctx context.Context, worker *worker.Context
return nil
}

// Find and update the button
updated := false
for i, comp := range msg.Components {
if comp.Type == component.ComponentActionRow {
row := comp.ComponentData.(component.ActionRow)

for j, btnComp := range row.Components {
if btnComp.Type == component.ComponentButton {
btn := btnComp.ComponentData.(component.Button)

if claimed && btn.CustomId == "claim" {
// Replace claim button with unclaim button
row.Components[j] = component.BuildButton(component.Button{
Label: cmd.GetMessage(i18n.TitleUnclaim),
CustomId: "unclaim",
Style: component.ButtonStyleSecondary,
Emoji: &emoji.Emoji{Name: "🙋‍♂️"},
})
updated = true
break
} else if !claimed && btn.CustomId == "unclaim" {
// Replace unclaim button with claim button
row.Components[j] = component.BuildButton(component.Button{
Label: cmd.GetMessage(i18n.TitleClaim),
CustomId: "claim",
Style: component.ButtonStyleSuccess,
Emoji: &emoji.Emoji{Name: "🙋‍♂️"},
})
updated = true
break
}
}
}

if updated {
msg.Components[i] = component.Component{
Type: component.ComponentActionRow,
ComponentData: row,
}
break
}
}
replacement := component.BuildButton(component.Button{
Label: cmd.GetMessage(i18n.TitleClaim),
CustomId: "claim",
Style: component.ButtonStyleSuccess,
Emoji: &emoji.Emoji{Name: "🙋‍♂️"},
})
fromId := "unclaim"
if claimed {
replacement = component.BuildButton(component.Button{
Label: cmd.GetMessage(i18n.TitleUnclaim),
CustomId: "unclaim",
Style: component.ButtonStyleSecondary,
Emoji: &emoji.Emoji{Name: "🙋‍♂️"},
})
fromId = "claim"
}

// If no button was found to update, nothing to do
if !updated {
if !replaceButton(msg.Components, fromId, replacement) {
return nil
}

// Convert embeds to pointers
embeds := make([]*embed.Embed, len(msg.Embeds))
for i := range msg.Embeds {
embeds[i] = &msg.Embeds[i]
editData := welcomeMessageEditData(msg)

_, err = worker.EditMessage(*ticket.ChannelId, *ticket.WelcomeMessageId, editData)
return err
}

// replaceButton swaps the first top-level action row button with the given custom id.
// The bot's own row is always appended at the top level, including on V2 messages.
func replaceButton(components []component.Component, customId string, replacement component.Component) bool {
for _, comp := range components {
row, ok := comp.ComponentData.(component.ActionRow)
if !ok {
continue
}

for j, btnComp := range row.Components {
btn, ok := btnComp.ComponentData.(component.Button)
if ok && btn.CustomId == customId {
row.Components[j] = replacement
return true
}
}
}

// Edit the message with updated components
editData := rest.EditMessageData{
return false
}

// ShouldRewriteClassicWelcome reports whether /switchpanel may replace the welcome
// embed. Only an embed can be rewritten in place, so a V2 message on either side is
// left as it is.
func ShouldRewriteClassicWelcome(msg message.Message, newPanel *database.Panel) bool {
return !IsComponentsV2Message(msg) && !newPanel.WelcomeMessageUsesComponentsV2
}

func IsComponentsV2Message(msg message.Message) bool {
return msg.Flags&int(message.FlagComponentsV2) != 0
}

// welcomeMessageEditData rebuilds an edit from an existing welcome message. Discord
// rejects an edit that drops IS_COMPONENTS_V2 and requires embeds to be empty on V2
// messages. V2 text is mention-parsed, and an edit without allowed_mentions re-parses
// it with default allowances, which would ping from form answers.
func welcomeMessageEditData(msg message.Message) rest.EditMessageData {
data := rest.EditMessageData{
Content: msg.Content,
Embeds: embeds,
Flags: uint(msg.Flags),
Components: msg.Components,
}

_, err = worker.EditMessage(*ticket.ChannelId, *ticket.WelcomeMessageId, editData)
return err
if IsComponentsV2Message(msg) {
data.Embeds = []*embed.Embed{}
data.AllowedMentions = &message.AllowedMention{}
return data
}

data.Embeds = make([]*embed.Embed, len(msg.Embeds))
for i := range msg.Embeds {
data.Embeds[i] = &msg.Embeds[i]
}

return data
}
Loading
Loading