save first version
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/leolionad58/ticketbot/internal/config"
|
||||
"github.com/leolionad58/ticketbot/internal/db"
|
||||
"github.com/leolionad58/ticketbot/internal/logger"
|
||||
"github.com/leolionad58/ticketbot/internal/tickets"
|
||||
)
|
||||
|
||||
type ConvocationCommand struct {
|
||||
Config *config.Provider
|
||||
TicketRepo *db.TicketRepo
|
||||
Auth *tickets.AuthService
|
||||
LogSvc *logger.DiscordLogger
|
||||
}
|
||||
|
||||
// Handle handles /convocation <user> <reason>.
|
||||
func (c *ConvocationCommand) Handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
memberRoles := memberRoleIDs(i)
|
||||
staffID := interactionUserID(i)
|
||||
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionConvocation, nil) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
slog.Warn("convocation denied", "user_id", staffID)
|
||||
return
|
||||
}
|
||||
|
||||
data := i.ApplicationCommandData()
|
||||
target := data.Options[0].UserValue(s)
|
||||
reason := data.Options[1].StringValue()
|
||||
|
||||
if target == nil {
|
||||
ephemeral(s, i, "Utilisateur introuvable.")
|
||||
return
|
||||
}
|
||||
if target.Bot {
|
||||
ephemeral(s, i, "Tu ne peux pas convoquer un bot.")
|
||||
return
|
||||
}
|
||||
if target.ID == staffID {
|
||||
ephemeral(s, i, "Tu ne peux pas te convoquer toi-même.")
|
||||
return
|
||||
}
|
||||
|
||||
// Defer — channel creation can take time
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ //nolint
|
||||
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{Flags: discordgo.MessageFlagsEphemeral},
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg := c.Config.Get()
|
||||
if cfg.Bot.ConvocationCategory == "" {
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ //nolint
|
||||
Content: "La catégorie de convocation n'est pas configurée.",
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get next convocation number
|
||||
n, err := c.TicketRepo.NextConvocationNumber(ctx)
|
||||
if err != nil {
|
||||
slog.Error("convocation: next number", "err", err)
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{Content: "Erreur interne.", Flags: discordgo.MessageFlagsEphemeral}) //nolint
|
||||
return
|
||||
}
|
||||
|
||||
channelName := fmt.Sprintf("convoc-%s-%04d", sanitizeConvocName(target.Username), n)
|
||||
if len(channelName) > 100 {
|
||||
channelName = channelName[:100]
|
||||
}
|
||||
|
||||
ch, err := s.GuildChannelCreateComplex(i.GuildID, discordgo.GuildChannelCreateData{
|
||||
Name: channelName,
|
||||
Type: discordgo.ChannelTypeGuildText,
|
||||
ParentID: cfg.Bot.ConvocationCategory,
|
||||
PermissionOverwrites: []*discordgo.PermissionOverwrite{
|
||||
{
|
||||
ID: i.GuildID,
|
||||
Type: discordgo.PermissionOverwriteTypeRole,
|
||||
Deny: discordgo.PermissionViewChannel,
|
||||
},
|
||||
{
|
||||
ID: staffID,
|
||||
Type: discordgo.PermissionOverwriteTypeMember,
|
||||
Allow: discordgo.PermissionViewChannel |
|
||||
discordgo.PermissionSendMessages |
|
||||
discordgo.PermissionReadMessageHistory |
|
||||
discordgo.PermissionAttachFiles,
|
||||
},
|
||||
{
|
||||
ID: target.ID,
|
||||
Type: discordgo.PermissionOverwriteTypeMember,
|
||||
Allow: discordgo.PermissionViewChannel |
|
||||
discordgo.PermissionSendMessages |
|
||||
discordgo.PermissionReadMessageHistory,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
slog.Error("convocation: create channel", "err", err)
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{Content: "Erreur lors de la création du channel.", Flags: discordgo.MessageFlagsEphemeral}) //nolint
|
||||
return
|
||||
}
|
||||
|
||||
// Insert in DB as type=convocation
|
||||
ticket := &db.Ticket{
|
||||
UserID: target.ID,
|
||||
Panel: "convocation",
|
||||
Type: "convocation",
|
||||
ChannelID: ch.ID,
|
||||
OpenedAt: time.Now(),
|
||||
Status: "open",
|
||||
}
|
||||
ticket.TicketNumber = n
|
||||
// Manual insert without number increment (we already computed it)
|
||||
if err := insertConvocation(ctx, c.TicketRepo, ticket); err != nil {
|
||||
slog.Error("convocation: insert DB", "err", err)
|
||||
}
|
||||
|
||||
// Custom delete button ID encodes staff ID for auth
|
||||
deleteCustomID := fmt.Sprintf("convoc:delete:%s:%s", ch.ID, staffID)
|
||||
if len(deleteCustomID) > 100 {
|
||||
deleteCustomID = "convoc:delete:" + ch.ID
|
||||
}
|
||||
|
||||
// Post embed in convocation channel
|
||||
s.ChannelMessageSendComplex(ch.ID, &discordgo.MessageSend{ //nolint
|
||||
Content: fmt.Sprintf("<@%s>", target.ID),
|
||||
Embeds: []*discordgo.MessageEmbed{
|
||||
{
|
||||
Title: "Convocation",
|
||||
Description: fmt.Sprintf("Tu as été convoqué par <@%s>.\n\n**Raison :** %s", staffID, reason),
|
||||
Color: 0x5865f2,
|
||||
},
|
||||
},
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.ActionsRow{Components: []discordgo.MessageComponent{
|
||||
discordgo.Button{
|
||||
Label: "Supprimer",
|
||||
Style: discordgo.DangerButton,
|
||||
CustomID: deleteCustomID,
|
||||
},
|
||||
}},
|
||||
},
|
||||
})
|
||||
|
||||
c.LogSvc.LogConvocationOpened(staffID, target.ID, reason, ch.ID)
|
||||
slog.Info("convocation created", "channel_id", ch.ID, "staff_id", staffID, "target_id", target.ID)
|
||||
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ //nolint
|
||||
Content: fmt.Sprintf("Convocation créée : <#%s>", ch.ID),
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
})
|
||||
}
|
||||
|
||||
func sanitizeConvocName(username string) string {
|
||||
var out []byte
|
||||
for _, b := range []byte(username) {
|
||||
if (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9') || b == '-' {
|
||||
out = append(out, b)
|
||||
} else if b >= 'A' && b <= 'Z' {
|
||||
out = append(out, b+32)
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return "user"
|
||||
}
|
||||
if len(out) > 20 {
|
||||
out = out[:20]
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// insertConvocation inserts a convocation ticket that already has its number set.
|
||||
func insertConvocation(ctx context.Context, repo *db.TicketRepo, ticket *db.Ticket) error {
|
||||
return repo.InsertWithNumber(ctx, ticket)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/leolionad58/ticketbot/internal/config"
|
||||
)
|
||||
|
||||
func ephemeral(s *discordgo.Session, i *discordgo.InteractionCreate, msg string) {
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ //nolint
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: msg,
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func memberRoleIDs(i *discordgo.InteractionCreate) []string {
|
||||
if i.Member != nil {
|
||||
return i.Member.Roles
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func interactionUserID(i *discordgo.InteractionCreate) string {
|
||||
if i.Member != nil && i.Member.User != nil {
|
||||
return i.Member.User.ID
|
||||
}
|
||||
if i.User != nil {
|
||||
return i.User.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseColor(hex string) int {
|
||||
if len(hex) > 0 && hex[0] == '#' {
|
||||
hex = hex[1:]
|
||||
}
|
||||
v, _ := strconv.ParseInt(hex, 16, 32)
|
||||
return int(v)
|
||||
}
|
||||
|
||||
func openFile(path string) (*os.File, error) {
|
||||
return os.Open(path)
|
||||
}
|
||||
|
||||
func buttonStyle(color config.ButtonColor) discordgo.ButtonStyle {
|
||||
switch color {
|
||||
case config.ColorPrimary:
|
||||
return discordgo.PrimaryButton
|
||||
case config.ColorSecondary:
|
||||
return discordgo.SecondaryButton
|
||||
case config.ColorSuccess:
|
||||
return discordgo.SuccessButton
|
||||
case config.ColorDanger:
|
||||
return discordgo.DangerButton
|
||||
default:
|
||||
return discordgo.PrimaryButton
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/leolionad58/ticketbot/internal/config"
|
||||
"github.com/leolionad58/ticketbot/internal/tickets"
|
||||
)
|
||||
|
||||
type PanelCommand struct {
|
||||
Config *config.Provider
|
||||
Auth *tickets.AuthService
|
||||
}
|
||||
|
||||
func (c *PanelCommand) Handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
memberRoles := memberRoleIDs(i)
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionAdmin, nil) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
slog.Warn("panel_send denied", "user_id", interactionUserID(i))
|
||||
return
|
||||
}
|
||||
|
||||
data := i.ApplicationCommandData()
|
||||
panelName := data.Options[0].StringValue()
|
||||
channelID := data.Options[1].ChannelValue(s).ID
|
||||
|
||||
cfg := c.Config.Get()
|
||||
panel, ok := cfg.Panels[panelName]
|
||||
if !ok {
|
||||
ephemeral(s, i, "Panel introuvable : "+panelName)
|
||||
return
|
||||
}
|
||||
|
||||
components := buildPanelButtons(panelName, panel)
|
||||
embed := &discordgo.MessageEmbed{
|
||||
Title: panel.EmbedTitle,
|
||||
Description: panel.EmbedDescription,
|
||||
Color: parseColor(panel.EmbedColor),
|
||||
}
|
||||
|
||||
if _, err := s.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{
|
||||
Embeds: []*discordgo.MessageEmbed{embed},
|
||||
Components: components,
|
||||
}); err != nil {
|
||||
slog.Error("panel_send: send message", "err", err)
|
||||
ephemeral(s, i, "Erreur lors de l'envoi du panel.")
|
||||
return
|
||||
}
|
||||
ephemeral(s, i, "Panel envoyé dans <#"+channelID+">.")
|
||||
}
|
||||
|
||||
func buildPanelButtons(panelName string, panel config.Panel) []discordgo.MessageComponent {
|
||||
var buttons []discordgo.MessageComponent
|
||||
for typeName, t := range panel.Types {
|
||||
customID := "panel:open:" + panelName + ":" + typeName
|
||||
if len(customID) > 100 {
|
||||
customID = customID[:100]
|
||||
}
|
||||
btn := discordgo.Button{
|
||||
Label: t.ButtonLabel,
|
||||
Style: buttonStyle(t.ButtonColor),
|
||||
CustomID: customID,
|
||||
}
|
||||
if t.ButtonEmoji != "" {
|
||||
emoji := discordgo.ComponentEmoji{Name: t.ButtonEmoji}
|
||||
btn.Emoji = &emoji
|
||||
}
|
||||
buttons = append(buttons, btn)
|
||||
}
|
||||
return []discordgo.MessageComponent{
|
||||
discordgo.ActionsRow{Components: buttons},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/leolionad58/ticketbot/internal/claim"
|
||||
"github.com/leolionad58/ticketbot/internal/db"
|
||||
"github.com/leolionad58/ticketbot/internal/logger"
|
||||
"github.com/leolionad58/ticketbot/internal/tickets"
|
||||
"github.com/leolionad58/ticketbot/internal/transcript"
|
||||
)
|
||||
|
||||
type TicketCommand struct {
|
||||
TicketSvc *tickets.Service
|
||||
TicketRepo *db.TicketRepo
|
||||
ClaimMgr *claim.Manager
|
||||
LogSvc *logger.DiscordLogger
|
||||
Transcript *transcript.Generator
|
||||
Auth *tickets.AuthService
|
||||
}
|
||||
|
||||
// Handle dispatches /ticket subcommands.
|
||||
func (c *TicketCommand) Handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
data := i.ApplicationCommandData()
|
||||
if len(data.Options) == 0 {
|
||||
ephemeral(s, i, "Sous-commande manquante.")
|
||||
return
|
||||
}
|
||||
sub := data.Options[0]
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// All /ticket commands must be used inside a ticket channel
|
||||
ticket, err := c.TicketRepo.GetByChannelID(ctx, i.ChannelID)
|
||||
if err != nil || ticket == nil {
|
||||
ephemeral(s, i, "Cette commande doit être utilisée dans un channel de ticket.")
|
||||
return
|
||||
}
|
||||
if ticket.Status == "closed" {
|
||||
ephemeral(s, i, "Ce ticket est déjà fermé.")
|
||||
return
|
||||
}
|
||||
|
||||
memberRoles := memberRoleIDs(i)
|
||||
staffID := interactionUserID(i)
|
||||
|
||||
switch sub.Name {
|
||||
case "close":
|
||||
c.handleClose(ctx, s, i, ticket, memberRoles, staffID, sub)
|
||||
case "add":
|
||||
c.handleAdd(ctx, s, i, ticket, memberRoles, sub)
|
||||
case "remove":
|
||||
c.handleRemove(ctx, s, i, ticket, memberRoles, staffID, sub)
|
||||
case "rename":
|
||||
c.handleRename(ctx, s, i, ticket, memberRoles, sub)
|
||||
case "transcript":
|
||||
c.handleTranscript(ctx, s, i, ticket, memberRoles)
|
||||
case "reclaim":
|
||||
c.handleReclaim(ctx, s, i, ticket, memberRoles, staffID)
|
||||
default:
|
||||
ephemeral(s, i, "Sous-commande inconnue.")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TicketCommand) handleClose(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate, ticket *db.Ticket, memberRoles []string, staffID string, sub *discordgo.ApplicationCommandInteractionDataOption) {
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionClose, ticket) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
slog.Warn("ticket close denied", "user_id", staffID, "channel_id", ticket.ChannelID)
|
||||
return
|
||||
}
|
||||
|
||||
reason := "Fermé via /ticket close"
|
||||
if len(sub.Options) > 0 {
|
||||
reason = sub.Options[0].StringValue()
|
||||
}
|
||||
|
||||
// Defer — transcript + channel delete takes time
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ //nolint
|
||||
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{Flags: discordgo.MessageFlagsEphemeral},
|
||||
})
|
||||
|
||||
// Generate transcript
|
||||
transcriptPath := ""
|
||||
if tp, err := c.Transcript.Generate(ticket); err != nil {
|
||||
slog.Error("close: generate transcript", "ticket_id", ticket.ID, "err", err)
|
||||
} else {
|
||||
transcriptPath = tp
|
||||
}
|
||||
|
||||
if err := c.TicketRepo.SetClosed(ctx, ticket.ID, staffID, reason, transcriptPath, time.Now()); err != nil {
|
||||
slog.Error("close: set closed", "ticket_id", ticket.ID, "err", err)
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{Content: "Erreur lors de la fermeture.", Flags: discordgo.MessageFlagsEphemeral}) //nolint
|
||||
return
|
||||
}
|
||||
|
||||
c.ClaimMgr.Stop(ticket.ID)
|
||||
c.ClaimMgr.DeleteClaimMessage(ctx, s, ticket.ID)
|
||||
|
||||
ticket.TranscriptPath.String = transcriptPath
|
||||
ticket.TranscriptPath.Valid = transcriptPath != ""
|
||||
c.LogSvc.LogTicketClosed(ticket, staffID, reason, transcriptPath)
|
||||
|
||||
if transcriptPath != "" {
|
||||
sendTranscriptFile(s, c.LogSvc.Config.Get().Bot.LogsChannel, transcriptPath, ticket)
|
||||
}
|
||||
|
||||
slog.Info("ticket closed", "ticket_id", ticket.ID, "staff_id", staffID)
|
||||
c.TicketSvc.DeleteChannel(ticket.ChannelID) //nolint
|
||||
}
|
||||
|
||||
func (c *TicketCommand) handleAdd(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate, ticket *db.Ticket, memberRoles []string, sub *discordgo.ApplicationCommandInteractionDataOption) {
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionAdd, ticket) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
return
|
||||
}
|
||||
|
||||
target := sub.Options[0].UserValue(s)
|
||||
if target == nil {
|
||||
ephemeral(s, i, "Utilisateur introuvable.")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if already present by trying to get channel permission overwrites
|
||||
ch, err := s.Channel(ticket.ChannelID)
|
||||
if err == nil {
|
||||
for _, ow := range ch.PermissionOverwrites {
|
||||
if ow.ID == target.ID && ow.Type == discordgo.PermissionOverwriteTypeMember {
|
||||
ephemeral(s, i, fmt.Sprintf("<@%s> est déjà présent dans ce ticket.", target.ID))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.TicketSvc.AddMember(ctx, ticket.ChannelID, target.ID); err != nil {
|
||||
slog.Error("ticket add: add member", "err", err)
|
||||
ephemeral(s, i, "Erreur lors de l'ajout.")
|
||||
return
|
||||
}
|
||||
ephemeral(s, i, fmt.Sprintf("<@%s> a été ajouté au ticket.", target.ID))
|
||||
}
|
||||
|
||||
func (c *TicketCommand) handleRemove(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate, ticket *db.Ticket, memberRoles []string, staffID string, sub *discordgo.ApplicationCommandInteractionDataOption) {
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionRemove, ticket) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
return
|
||||
}
|
||||
|
||||
target := sub.Options[0].UserValue(s)
|
||||
if target == nil {
|
||||
ephemeral(s, i, "Utilisateur introuvable.")
|
||||
return
|
||||
}
|
||||
if target.ID == ticket.UserID {
|
||||
ephemeral(s, i, "Tu ne peux pas retirer l'utilisateur qui a ouvert le ticket.")
|
||||
return
|
||||
}
|
||||
if target.ID == staffID {
|
||||
ephemeral(s, i, "Tu ne peux pas te retirer toi-même du ticket.")
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.TicketSvc.RemoveMember(ctx, ticket.ChannelID, target.ID); err != nil {
|
||||
slog.Error("ticket remove: remove member", "err", err)
|
||||
ephemeral(s, i, "Erreur lors de la suppression.")
|
||||
return
|
||||
}
|
||||
ephemeral(s, i, fmt.Sprintf("<@%s> a été retiré du ticket.", target.ID))
|
||||
}
|
||||
|
||||
func (c *TicketCommand) handleRename(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate, ticket *db.Ticket, memberRoles []string, sub *discordgo.ApplicationCommandInteractionDataOption) {
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionRename, ticket) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
return
|
||||
}
|
||||
|
||||
name := sub.Options[0].StringValue()
|
||||
if err := c.TicketSvc.Rename(ctx, ticket.ChannelID, name); err != nil {
|
||||
slog.Error("ticket rename", "err", err)
|
||||
ephemeral(s, i, "Nom invalide ou erreur lors du renommage : "+err.Error())
|
||||
return
|
||||
}
|
||||
ephemeral(s, i, "Channel renommé.")
|
||||
}
|
||||
|
||||
func (c *TicketCommand) handleTranscript(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate, ticket *db.Ticket, memberRoles []string) {
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionTranscript, ticket) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
return
|
||||
}
|
||||
|
||||
// Defer — message fetching takes time
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ //nolint
|
||||
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{Flags: discordgo.MessageFlagsEphemeral},
|
||||
})
|
||||
|
||||
tp, err := c.Transcript.Generate(ticket)
|
||||
if err != nil {
|
||||
slog.Error("transcript: generate", "ticket_id", ticket.ID, "err", err)
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{Content: "Erreur lors de la génération du transcript.", Flags: discordgo.MessageFlagsEphemeral}) //nolint
|
||||
return
|
||||
}
|
||||
|
||||
sendTranscriptFile(s, ticket.ChannelID, tp, ticket)
|
||||
s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{Content: "Transcript généré.", Flags: discordgo.MessageFlagsEphemeral}) //nolint
|
||||
}
|
||||
|
||||
func (c *TicketCommand) handleReclaim(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate, ticket *db.Ticket, memberRoles []string, staffID string) {
|
||||
if !c.Auth.Can(memberRoles, tickets.ActionReclaim, ticket) {
|
||||
ephemeral(s, i, "Tu n'as pas la permission d'effectuer cette action.")
|
||||
return
|
||||
}
|
||||
if ticket.Status != "claimed" {
|
||||
ephemeral(s, i, "Ce ticket n'est pas claim.")
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.TicketRepo.SetClaimed(ctx, ticket.ID, staffID, time.Now()); err != nil {
|
||||
slog.Error("reclaim: set claimed", "err", err)
|
||||
ephemeral(s, i, "Erreur lors du reclaim.")
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.TicketSvc.AddMember(ctx, ticket.ChannelID, staffID); err != nil {
|
||||
slog.Error("reclaim: add member", "err", err)
|
||||
}
|
||||
|
||||
s.ChannelMessageSend(ticket.ChannelID, fmt.Sprintf("Ticket reclaim par <@%s>.", staffID)) //nolint
|
||||
c.LogSvc.LogTicketClaimed(ticket, staffID)
|
||||
ephemeral(s, i, "Tu as reclaim ce ticket.")
|
||||
}
|
||||
|
||||
func sendTranscriptFile(s *discordgo.Session, channelID, path string, ticket *db.Ticket) {
|
||||
if channelID == "" || path == "" {
|
||||
return
|
||||
}
|
||||
f, err := openFile(path)
|
||||
if err != nil {
|
||||
slog.Error("send transcript: open file", "path", path, "err", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
name := fmt.Sprintf("%s-%04d-transcript.html", ticket.Type, ticket.TicketNumber)
|
||||
if _, err := s.ChannelFileSendWithMessage(channelID,
|
||||
fmt.Sprintf("Transcript — %s-%04d", ticket.Type, ticket.TicketNumber),
|
||||
name, f); err != nil {
|
||||
slog.Error("send transcript: upload", "err", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user