255 lines
7.5 KiB
Go
255 lines
7.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/leolionad58/ticketbot/internal/db"
|
|
panelauth "github.com/leolionad58/ticketbot/internal/panel/auth"
|
|
)
|
|
|
|
// ConvocBotService is the subset of BotService needed for convocation operations.
|
|
type ConvocBotService interface {
|
|
CreateConvocation(guildID, staffDiscordID, targetDiscordID, reason string) (channelID string, err error)
|
|
SendConvocPanel(guildID string) error
|
|
SendConvocPanelByID(panelID int64) error
|
|
}
|
|
|
|
// ConvocDiscordInfo is the subset of DiscordInfo for guild selection in convocations.
|
|
type ConvocDiscordInfo interface {
|
|
GetGuildList() []GuildInfo
|
|
}
|
|
|
|
// ConvocationsHandler serves /convocations.
|
|
type ConvocationsHandler struct {
|
|
TicketRepo *db.TicketRepo
|
|
ConvocRepo *db.ConvocationConfigRepo
|
|
ConvocPanelRepo *db.ConvocationPanelRepo
|
|
GuildID string
|
|
BotService ConvocBotService
|
|
DiscordInfo ConvocDiscordInfo
|
|
Renderer *Renderer
|
|
}
|
|
|
|
type convocationsData struct {
|
|
baseData
|
|
Config *db.ConvocationConfig
|
|
ConvocPanels []*db.ConvocationPanel
|
|
Tickets []*db.Ticket
|
|
Total int
|
|
Page int
|
|
Pages int
|
|
Guilds []GuildInfo
|
|
}
|
|
|
|
// HandleGET serves GET /convocations.
|
|
func (h *ConvocationsHandler) HandleGET(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
base := h.Renderer.base(r, "convocations")
|
|
sess := panelauth.SessionFromContext(ctx)
|
|
if sess != nil {
|
|
base.CSRFToken = sess.CSRFToken
|
|
}
|
|
|
|
cfg, err := h.ConvocRepo.Get(ctx, h.GuildID)
|
|
if err != nil {
|
|
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var convocPanels []*db.ConvocationPanel
|
|
if h.ConvocPanelRepo != nil {
|
|
convocPanels, _ = h.ConvocPanelRepo.List(ctx, h.GuildID)
|
|
}
|
|
|
|
page := 1
|
|
tickets, total, err := h.TicketRepo.ListFiltered(ctx, db.TicketFilter{
|
|
Type: "convocation",
|
|
Page: page,
|
|
PageSize: 20,
|
|
})
|
|
if err != nil {
|
|
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
pages := total / 20
|
|
if total%20 != 0 {
|
|
pages++
|
|
}
|
|
if pages < 1 {
|
|
pages = 1
|
|
}
|
|
|
|
var guilds []GuildInfo
|
|
if h.DiscordInfo != nil {
|
|
guilds = h.DiscordInfo.GetGuildList()
|
|
}
|
|
|
|
h.Renderer.Page(w, "convocations", convocationsData{
|
|
baseData: base,
|
|
Config: cfg,
|
|
ConvocPanels: convocPanels,
|
|
Tickets: tickets,
|
|
Total: total,
|
|
Page: page,
|
|
Pages: pages,
|
|
Guilds: guilds,
|
|
})
|
|
}
|
|
|
|
// HandlePOST serves POST /convocations — saves the convocation config.
|
|
func (h *ConvocationsHandler) HandlePOST(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Load existing config to preserve panel_message_id (not sent by form)
|
|
existing, _ := h.ConvocRepo.Get(ctx, h.GuildID)
|
|
panelMsgID := ""
|
|
panelChannelID := strings.TrimSpace(r.FormValue("panel_channel_id"))
|
|
if existing != nil {
|
|
panelMsgID = existing.PanelMessageID
|
|
// If channel changed, clear stored message ID so SendConvocPanel sends a new message
|
|
if existing.PanelChannelID != panelChannelID {
|
|
panelMsgID = ""
|
|
}
|
|
}
|
|
|
|
embedColor := strings.TrimSpace(r.FormValue("panel_embed_color"))
|
|
if embedColor == "" {
|
|
embedColor = "#5865f2"
|
|
}
|
|
|
|
cfg := &db.ConvocationConfig{
|
|
GuildID: h.GuildID,
|
|
CategoryID: strings.TrimSpace(r.FormValue("category_id")),
|
|
LogChannelID: strings.TrimSpace(r.FormValue("log_channel_id")),
|
|
ModalEnabled: r.FormValue("modal_enabled") == "1",
|
|
PanelChannelID: panelChannelID,
|
|
PanelMessageID: panelMsgID,
|
|
PanelEmbedTitle: strings.TrimSpace(r.FormValue("panel_embed_title")),
|
|
PanelEmbedDescription: strings.TrimSpace(r.FormValue("panel_embed_description")),
|
|
PanelEmbedColor: embedColor,
|
|
}
|
|
|
|
if err := h.ConvocRepo.Upsert(ctx, cfg); err != nil {
|
|
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/convocations?flash=Configuration+sauvegardée", http.StatusSeeOther)
|
|
}
|
|
|
|
// HandleCreatePanel serves POST /convocations/panels/new — creates a new convocation panel entry.
|
|
func (h *ConvocationsHandler) HandleCreatePanel(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if h.ConvocPanelRepo == nil {
|
|
http.Redirect(w, r, "/convocations?error=Non+configuré", http.StatusSeeOther)
|
|
return
|
|
}
|
|
color := strings.TrimSpace(r.FormValue("embed_color"))
|
|
if color == "" {
|
|
color = "#5865f2"
|
|
}
|
|
p := &db.ConvocationPanel{
|
|
GuildID: h.GuildID,
|
|
EmbedTitle: strings.TrimSpace(r.FormValue("embed_title")),
|
|
EmbedDesc: strings.TrimSpace(r.FormValue("embed_description")),
|
|
EmbedColor: color,
|
|
ChannelID: strings.TrimSpace(r.FormValue("channel_id")),
|
|
}
|
|
if err := h.ConvocPanelRepo.Create(ctx, p); err != nil {
|
|
http.Redirect(w, r, "/convocations?error="+urlEncode(err.Error()), http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/convocations?flash=Panel+créé", http.StatusSeeOther)
|
|
}
|
|
|
|
// HandleDeletePanel serves POST /convocations/panels/{id}/delete.
|
|
func (h *ConvocationsHandler) HandleDeletePanel(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
idStr := chi.URLParam(r, "id")
|
|
var id int64
|
|
fmt.Sscanf(idStr, "%d", &id)
|
|
if h.ConvocPanelRepo != nil {
|
|
h.ConvocPanelRepo.Delete(ctx, id) //nolint
|
|
}
|
|
http.Redirect(w, r, "/convocations?flash=Panel+supprimé", http.StatusSeeOther)
|
|
}
|
|
|
|
// HandleSendPanel serves POST /convocations/panels/{id}/send.
|
|
func (h *ConvocationsHandler) HandleSendPanel(w http.ResponseWriter, r *http.Request) {
|
|
idStr := chi.URLParam(r, "id")
|
|
var id int64
|
|
fmt.Sscanf(idStr, "%d", &id)
|
|
if h.BotService == nil {
|
|
http.Redirect(w, r, "/convocations?error=Bot+non+disponible", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if err := h.BotService.SendConvocPanelByID(id); err != nil {
|
|
http.Redirect(w, r, "/convocations?error="+urlEncode(err.Error()), http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/convocations?flash=Panel+envoyé+sur+Discord", http.StatusSeeOther)
|
|
}
|
|
|
|
// HandleCreate serves POST /convocations/create — creates a convocation via Discord.
|
|
func (h *ConvocationsHandler) HandleCreate(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if h.BotService == nil {
|
|
http.Error(w, "bot non disponible", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
|
|
guildID := strings.TrimSpace(r.FormValue("guild_id"))
|
|
if guildID == "" {
|
|
guildID = h.GuildID
|
|
}
|
|
targetID := strings.TrimSpace(r.FormValue("target_id"))
|
|
reason := strings.TrimSpace(r.FormValue("reason"))
|
|
|
|
if targetID == "" || reason == "" {
|
|
http.Redirect(w, r, "/convocations?error=ID+utilisateur+et+raison+requis", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
admin := panelauth.AdminFromContext(r.Context())
|
|
staffDiscordID := ""
|
|
if admin != nil {
|
|
staffDiscordID = admin.DiscordID
|
|
}
|
|
|
|
channelID, err := h.BotService.CreateConvocation(guildID, staffDiscordID, targetID, reason)
|
|
if err != nil {
|
|
http.Redirect(w, r, "/convocations?error="+urlEncode(err.Error()), http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/convocations?flash=Convocation+créée+dans+<#"+channelID+">", http.StatusSeeOther)
|
|
}
|
|
|
|
func urlEncode(s string) string {
|
|
var out strings.Builder
|
|
for _, r := range s {
|
|
switch {
|
|
case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9':
|
|
out.WriteRune(r)
|
|
case r == '-' || r == '_' || r == '.' || r == '~':
|
|
out.WriteRune(r)
|
|
default:
|
|
out.WriteString("+" + strings.ReplaceAll(string(r), " ", "+"))
|
|
}
|
|
}
|
|
return out.String()
|
|
}
|