Skip to content

Commit 4b515f6

Browse files
authored
feat(RM-51): Show panel name in close container if available (CV2) (#22)
* feat(RM-51): Show panel name in close container if available Adds the panel name to the close container details if the ticket has an associated panel with a title. Also refactors claimedBy and reason formatting logic for clarity and error handling. * Converted rating/feedback/DM transcript * Add server info and icon to close container UI The BuildCloseContainer function now accepts a guild parameter to display the server name and icon in the ticket close UI (DMs only). * Fix section accessory rendering in close embed Refactors the logic for building the section with a guild icon accessory in the close embed. Now only adds the accessory if the guild and icon are present, otherwise appends the text display directly. This prevents empty or incorrect accessory fields.
1 parent 7d66d3a commit 4b515f6

4 files changed

Lines changed: 86 additions & 138 deletions

File tree

bot/button/handlers/exitsurveysubmit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,5 @@ func addViewFeedbackButton(ctx context.Context, cmd *cmdcontext.ModalContext, ti
182182
return fmt.Errorf("exit survey was completed, but no rating was found (%d:%d)", ticket.GuildId, ticket.Id)
183183
}
184184

185-
return logic.EditGuildArchiveMessageIfExists(ctx, cmd.Worker(), ticket, settings, true, closedBy, reason, &rating)
185+
return logic.EditGuildArchiveMessageIfExists(ctx, cmd, cmd.Worker(), ticket, settings, true, closedBy, reason, &rating)
186186
}

bot/button/handlers/rate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func (h *RateHandler) Execute(ctx *cmdcontext.ButtonContext) {
152152
return
153153
}
154154

155-
if err := logic.EditGuildArchiveMessageIfExists(ctx, ctx.Worker(), ticket, settings, hasFeedback, closedBy, reason, &rating); err != nil {
155+
if err := logic.EditGuildArchiveMessageIfExists(ctx, ctx, ctx.Worker(), ticket, settings, hasFeedback, closedBy, reason, &rating); err != nil {
156156
ctx.HandleError(err)
157157
}
158158
}

bot/logic/close.go

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/TicketsBot-cloud/common/sentry"
1313
"github.com/TicketsBot-cloud/database"
1414
"github.com/TicketsBot-cloud/gdl/objects/channel/message"
15+
"github.com/TicketsBot-cloud/gdl/objects/interaction/component"
1516
"github.com/TicketsBot-cloud/gdl/objects/member"
1617
"github.com/TicketsBot-cloud/gdl/rest"
1718
"github.com/TicketsBot-cloud/gdl/rest/request"
@@ -231,10 +232,10 @@ func CloseTicket(ctx context.Context, cmd registry.CommandContext, reason *strin
231232
}
232233
}
233234

234-
sendCloseEmbed(ctx, cmd, errorContext, member, settings, ticket, reason)
235+
sendCloseContainer(ctx, cmd, errorContext, member, settings, ticket, reason)
235236
}
236237

237-
func sendCloseEmbed(ctx context.Context, cmd registry.CommandContext, errorContext sentry.ErrorContext, member member.Member, settings database.Settings, ticket database.Ticket, reason *string) {
238+
func sendCloseContainer(ctx context.Context, cmd registry.CommandContext, errorContext sentry.ErrorContext, member member.Member, settings database.Settings, ticket database.Ticket, reason *string) {
238239
// Send logs to archive channel
239240
archiveChannelId, err := dbclient.Client.ArchiveChannel.Get(ctx, ticket.GuildId)
240241
if err != nil {
@@ -249,14 +250,7 @@ func sendCloseEmbed(ctx context.Context, cmd registry.CommandContext, errorConte
249250
}
250251

251252
if archiveChannelExists && archiveChannelId != nil {
252-
componentBuilders := [][]CloseEmbedElement{
253-
{
254-
TranscriptLinkElement(settings.StoreTranscripts),
255-
ThreadLinkElement(ticket.IsThread && ticket.ChannelId != nil),
256-
},
257-
}
258-
259-
closeContainer := BuildCloseContainer(ctx, cmd, cmd.Worker(), ticket, member.User.Id, reason, nil, componentBuilders)
253+
closeContainer := BuildCloseContainer(ctx, cmd, cmd.Worker(), ticket, nil, member.User.Id, reason, nil, false)
260254

261255
data := rest.CreateMessageData{
262256
Flags: uint(message.FlagComponentsV2),
@@ -321,33 +315,32 @@ func sendCloseEmbed(ctx context.Context, cmd registry.CommandContext, errorConte
321315

322316
statsd.Client.IncrementKey(statsd.KeyDirectMessage)
323317

324-
componentBuilders := [][]CloseEmbedElement{
325-
{
326-
TranscriptLinkElement(settings.StoreTranscripts),
327-
ThreadLinkElement(ticket.IsThread && ticket.ChannelId != nil),
328-
},
329-
{
330-
FeedbackRowElement(feedbackEnabled && hasSentMessage && permLevel == permission.Everyone),
331-
},
332-
}
333-
334-
closeEmbed, closeComponents := BuildCloseEmbed(ctx, cmd.Worker(), ticket, member.User.Id, reason, nil, componentBuilders)
335-
closeEmbed.SetAuthor(guild.Name, "", fmt.Sprintf("https://cdn.discordapp.com/icons/%d/%s.png", guild.Id, guild.Icon))
336-
337318
// Use message content to tell users why they can't rate a ticket
338319
var content string
320+
var viewFeedbackButton = true
339321
if feedbackEnabled {
340322
if permLevel > permission.Everyone {
341323
content = "-# " + cmd.GetMessage(i18n.MessageCloseCantRateStaff, guild.Name)
324+
viewFeedbackButton = false
342325
} else if !hasSentMessage {
343326
content = "-# " + cmd.GetMessage(i18n.MessageCloseCantRateEmpty)
327+
viewFeedbackButton = false
328+
}
329+
}
330+
331+
closeContainer := BuildCloseContainer(ctx, cmd, cmd.Worker(), ticket, &guild, member.User.Id, reason, nil, viewFeedbackButton)
332+
333+
components := []component.Component{*closeContainer}
334+
if content != "" {
335+
components = []component.Component{
336+
component.BuildTextDisplay(component.TextDisplay{Content: content}),
337+
*closeContainer,
344338
}
345339
}
346340

347341
data := rest.CreateMessageData{
348-
Content: content,
349-
Embeds: utils.Slice(closeEmbed),
350-
Components: closeComponents,
342+
Flags: uint(message.FlagComponentsV2),
343+
Components: components,
351344
}
352345

353346
if _, err := cmd.Worker().CreateMessageComplex(dmChannel, data); err != nil {

bot/logic/closeembed.go

Lines changed: 65 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
"github.com/TicketsBot-cloud/common/premium"
1010
"github.com/TicketsBot-cloud/common/sentry"
1111
"github.com/TicketsBot-cloud/database"
12-
"github.com/TicketsBot-cloud/gdl/objects/channel/embed"
1312
"github.com/TicketsBot-cloud/gdl/objects/channel/message"
13+
"github.com/TicketsBot-cloud/gdl/objects/guild"
1414
"github.com/TicketsBot-cloud/gdl/objects/guild/emoji"
1515
"github.com/TicketsBot-cloud/gdl/objects/interaction/component"
1616
"github.com/TicketsBot-cloud/gdl/rest"
@@ -22,15 +22,15 @@ import (
2222
"github.com/TicketsBot-cloud/worker/config"
2323
)
2424

25-
type CloseEmbedElement func(worker *worker.Context, ticket database.Ticket) []component.Component
25+
type CloseContainerElement func(worker *worker.Context, ticket database.Ticket) []component.Component
2626

27-
func NoopElement() CloseEmbedElement {
27+
func NoopElement() CloseContainerElement {
2828
return func(worker *worker.Context, ticket database.Ticket) []component.Component {
2929
return nil
3030
}
3131
}
3232

33-
func TranscriptLinkElement(condition bool) CloseEmbedElement {
33+
func TranscriptLinkElement(condition bool) CloseContainerElement {
3434
if !condition {
3535
return NoopElement()
3636
}
@@ -52,7 +52,7 @@ func TranscriptLinkElement(condition bool) CloseEmbedElement {
5252
}
5353
}
5454

55-
func ThreadLinkElement(condition bool) CloseEmbedElement {
55+
func ThreadLinkElement(condition bool) CloseContainerElement {
5656
if !condition {
5757
return NoopElement()
5858
}
@@ -74,7 +74,7 @@ func ThreadLinkElement(condition bool) CloseEmbedElement {
7474
}
7575
}
7676

77-
func ViewFeedbackElement(condition bool) CloseEmbedElement {
77+
func ViewFeedbackElement(condition bool) CloseContainerElement {
7878
if !condition {
7979
return NoopElement()
8080
}
@@ -91,7 +91,7 @@ func ViewFeedbackElement(condition bool) CloseEmbedElement {
9191
}
9292
}
9393

94-
func FeedbackRowElement(condition bool) CloseEmbedElement {
94+
func FeedbackRowElement(condition bool) CloseContainerElement {
9595
if !condition {
9696
return NoopElement()
9797
}
@@ -123,96 +123,19 @@ func FeedbackRowElement(condition bool) CloseEmbedElement {
123123
}
124124
}
125125

126-
func BuildCloseEmbed(
127-
ctx context.Context,
128-
worker *worker.Context,
129-
ticket database.Ticket,
130-
closedBy uint64,
131-
reason *string,
132-
rating *uint8,
133-
components [][]CloseEmbedElement,
134-
) (*embed.Embed, []component.Component) {
135-
var formattedReason string
136-
if reason == nil {
137-
formattedReason = "No reason specified"
138-
} else {
139-
formattedReason = *reason
140-
if len(formattedReason) > 1024 {
141-
formattedReason = formattedReason[:1024]
142-
}
143-
}
144-
145-
var claimedBy string
146-
{
147-
claimUserId, err := dbclient.Client.TicketClaims.Get(ctx, ticket.GuildId, ticket.Id)
148-
if err != nil {
149-
sentry.Error(err)
150-
}
151-
152-
if claimUserId == 0 {
153-
claimedBy = "Not claimed"
154-
} else {
155-
claimedBy = fmt.Sprintf("<@%d>", claimUserId)
156-
}
157-
}
158-
159-
colour, err := utils.GetColourForGuild(ctx, worker, customisation.Green, ticket.GuildId)
160-
if err != nil {
161-
sentry.Error(err)
162-
colour = customisation.Green.Default()
163-
}
164-
165-
// TODO: Translate titles
166-
closeEmbed := embed.NewEmbed().
167-
SetTitle("Ticket Closed").
168-
SetColor(colour).
169-
AddField(formatTitle("Ticket ID", customisation.EmojiId, worker.IsWhitelabel), strconv.Itoa(ticket.Id), true).
170-
AddField(formatTitle("Opened By", customisation.EmojiOpen, worker.IsWhitelabel), fmt.Sprintf("<@%d>", ticket.UserId), true).
171-
AddField(formatTitle("Closed By", customisation.EmojiClose, worker.IsWhitelabel), fmt.Sprintf("<@%d>", closedBy), true).
172-
AddField(formatTitle("Open Time", customisation.EmojiOpenTime, worker.IsWhitelabel), message.BuildTimestamp(ticket.OpenTime, message.TimestampStyleShortDateTime), true).
173-
AddField(formatTitle("Claimed By", customisation.EmojiClaim, worker.IsWhitelabel), claimedBy, true)
174-
175-
if ticket.CloseTime != nil {
176-
closeEmbed.SetTimestamp(*ticket.CloseTime)
177-
}
178-
179-
if rating == nil {
180-
closeEmbed = closeEmbed.AddBlankField(true)
181-
} else {
182-
closeEmbed = closeEmbed.AddField(formatTitle("Rating", customisation.EmojiRating, worker.IsWhitelabel), fmt.Sprintf("%d ⭐", *rating), true)
183-
}
184-
185-
closeEmbed = closeEmbed.AddField(formatTitle("Reason", customisation.EmojiReason, worker.IsWhitelabel), formattedReason, false)
186-
187-
var rows []component.Component
188-
for _, row := range components {
189-
var rowElements []component.Component
190-
for _, element := range row {
191-
rowElements = append(rowElements, element(worker, ticket)...)
192-
}
193-
194-
if len(rowElements) > 0 {
195-
rows = append(rows, component.BuildActionRow(rowElements...))
196-
}
197-
}
198-
199-
return closeEmbed, rows
200-
}
201-
202126
func BuildCloseContainer(
203127
ctx context.Context,
204128
cmd registry.CommandContext,
205129
worker *worker.Context,
206130
ticket database.Ticket,
131+
guild *guild.Guild,
207132
closedBy uint64,
208133
reason *string,
209134
rating *uint8,
210-
components [][]CloseEmbedElement,
135+
viewFeedbackButton bool,
211136
) *component.Component {
212-
var formattedReason string
213-
if reason == nil {
214-
formattedReason = "No reason specified"
215-
} else {
137+
var formattedReason = "No reason specified"
138+
if reason != nil {
216139
formattedReason = *reason
217140
if len(formattedReason) > 1024 {
218141
formattedReason = formattedReason[:1024]
@@ -225,24 +148,35 @@ func BuildCloseContainer(
225148
}
226149

227150
var claimedBy string
228-
{
229-
claimUserId, err := dbclient.Client.TicketClaims.Get(ctx, ticket.GuildId, ticket.Id)
151+
claimUserId, err := dbclient.Client.TicketClaims.Get(ctx, ticket.GuildId, ticket.Id)
152+
if err != nil {
153+
sentry.Error(err)
154+
} else if claimUserId > 0 {
155+
claimedBy = fmt.Sprintf("<@%d>", claimUserId)
156+
}
157+
158+
var panelName string
159+
if ticket.PanelId != nil {
160+
p, err := dbclient.Client.Panel.GetById(ctx, *ticket.PanelId)
230161
if err != nil {
231162
sentry.Error(err)
232-
}
233-
234-
if claimUserId == 0 {
235-
claimedBy = ""
236-
} else {
237-
claimedBy = fmt.Sprintf("<@%d>", claimUserId)
163+
} else if p.Title != "" {
164+
panelName = p.Title
238165
}
239166
}
240167

241-
section1Text := []string{
242-
formatRow("Ticket ID", strconv.Itoa(ticket.Id)),
168+
section1Text := []string{}
169+
if guild != nil {
170+
section1Text = append(section1Text, formatRow("Server", guild.Name))
171+
}
172+
section1Text = append(section1Text, formatRow("Ticket ID", strconv.Itoa(ticket.Id)))
173+
if panelName != "" {
174+
section1Text = append(section1Text, formatRow("Panel", panelName))
175+
}
176+
section1Text = append(section1Text,
243177
formatRow("Opened By", fmt.Sprintf("<@%d>", ticket.UserId)),
244178
formatRow("Closed By", fmt.Sprintf("<@%d>", closedBy)),
245-
}
179+
)
246180

247181
section2Text := []string{
248182
formatRow("Open Time", message.BuildTimestamp(ticket.OpenTime, message.TimestampStyleShortDateTime)),
@@ -277,12 +211,39 @@ func BuildCloseContainer(
277211
Content: "## Ticket Closed",
278212
})),
279213
}),
280-
component.BuildTextDisplay(component.TextDisplay{Content: strings.Join(section1Text, "\n")}),
214+
}
215+
216+
sectionContent := component.BuildTextDisplay(component.TextDisplay{
217+
Content: strings.Join(section1Text, "\n"),
218+
})
219+
220+
if guild != nil && guild.Icon != "" {
221+
section := component.Section{
222+
Components: utils.Slice(sectionContent),
223+
Accessory: component.BuildThumbnail(component.Thumbnail{
224+
Media: component.UnfurledMediaItem{
225+
Url: fmt.Sprintf("https://cdn.discordapp.com/icons/%d/%s.png", guild.Id, guild.Icon),
226+
},
227+
}),
228+
}
229+
innerComponents = append(innerComponents, component.BuildSection(section))
230+
} else {
231+
innerComponents = append(innerComponents, sectionContent)
232+
}
233+
234+
innerComponents = append(innerComponents,
281235
component.BuildSeparator(component.Separator{}),
282236
component.BuildTextDisplay(component.TextDisplay{Content: strings.Join(section2Text, "\n")}),
237+
)
238+
239+
if viewFeedbackButton {
240+
innerComponents = append(innerComponents, component.BuildActionRow(
241+
FeedbackRowElement(viewFeedbackButton)(worker, ticket)...,
242+
))
283243
}
284244

285245
if cmd.PremiumTier() == premium.None {
246+
innerComponents = append(innerComponents, component.BuildSeparator(component.Separator{}))
286247
innerComponents = utils.AddPremiumFooter(innerComponents)
287248
}
288249

@@ -308,6 +269,7 @@ func formatTitle(s string, emoji customisation.CustomEmoji, isWhitelabel bool) s
308269

309270
func EditGuildArchiveMessageIfExists(
310271
ctx context.Context,
272+
cmd registry.CommandContext,
311273
worker *worker.Context,
312274
ticket database.Ticket,
313275
settings database.Settings,
@@ -325,18 +287,11 @@ func EditGuildArchiveMessageIfExists(
325287
return nil
326288
}
327289

328-
componentBuilders := [][]CloseEmbedElement{
329-
{
330-
TranscriptLinkElement(settings.StoreTranscripts),
331-
ThreadLinkElement(ticket.IsThread && ticket.ChannelId != nil),
332-
ViewFeedbackElement(viewFeedbackButton),
333-
},
334-
}
290+
closeContainer := BuildCloseContainer(ctx, cmd, worker, ticket, nil, closedBy, reason, rating, viewFeedbackButton)
335291

336-
embed, components := BuildCloseEmbed(ctx, worker, ticket, closedBy, reason, rating, componentBuilders)
337292
_, err = worker.EditMessage(archiveMessage.ChannelId, archiveMessage.MessageId, rest.EditMessageData{
338-
Embeds: utils.Slice(embed),
339-
Components: components,
293+
Flags: uint(message.FlagComponentsV2),
294+
Components: utils.Slice(*closeContainer),
340295
})
341296

342297
return err

0 commit comments

Comments
 (0)