2024-11-16 20:32:00 +00:00

184 lines
4.8 KiB
Go

package api
import (
"context"
"encoding/base64"
"errors"
"fmt"
"github.com/TicketsBot/GoPanel/app"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/redis"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/tokenchange"
"github.com/TicketsBot/common/whitelabeldelete"
"github.com/TicketsBot/database"
"github.com/TicketsBot/worker/bot/command/manager"
"github.com/gin-gonic/gin"
"github.com/rxdn/gdl/objects/application"
"github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/rest/request"
"net/http"
"strconv"
"strings"
)
func WhitelabelPost() func(*gin.Context) {
cm := new(manager.CommandManager)
cm.RegisterCommands()
return func(c *gin.Context) {
userId := c.Keys["userid"].(uint64)
type whitelabelPostBody struct {
Token string `json:"token"`
}
// Get token
var data whitelabelPostBody
if err := c.BindJSON(&data); err != nil {
c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid request body"))
return
}
bot, err := fetchApplication(c, data.Token)
if err != nil {
var restError request.RestError
if errors.Is(err, errInvalidToken) {
c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid token"))
} else if errors.As(err, &restError) && restError.StatusCode == http.StatusUnauthorized {
c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid token"))
} else {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
}
return
}
// Check if this is a different token
existing, err := dbclient.Client.Whitelabel.GetByUserId(c, userId)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
// Take existing whitelabel bot offline, if it is a different bot
if existing.BotId != 0 && existing.BotId != bot.Id {
whitelabeldelete.Publish(redis.Client.Client, existing.BotId)
}
// Set token in DB so that http-gateway can use it when Discord validates the interactions endpoint
// TODO: Use a transaction
if err := dbclient.Client.Whitelabel.Set(c, database.WhitelabelBot{
UserId: userId,
BotId: bot.Id,
PublicKey: bot.VerifyKey,
Token: data.Token,
}); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
if err := dbclient.Client.WhitelabelKeys.Set(c, bot.Id, bot.VerifyKey); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
// Set intents
var currentFlags application.Flag = 0
if bot.Flags != nil {
currentFlags = *bot.Flags
}
editData := rest.EditCurrentApplicationData{
Flags: utils.Ptr(application.BuildFlags(
currentFlags,
application.FlagIntentGatewayGuildMembersLimited,
application.FlagGatewayMessageContentLimited,
)),
// TODO: Don't hardcode URL
InteractionsEndpointUrl: utils.Ptr(fmt.Sprintf("https://gateway.ticketsbot.net/handle/%d", bot.Id)),
}
if _, err := rest.EditCurrentApplication(context.Background(), data.Token, nil, editData); err != nil {
// TODO: Use a transaction
if _, err := dbclient.Client.Whitelabel.Delete(c, bot.Id); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
tokenChangeData := tokenchange.TokenChangeData{
Token: data.Token,
NewId: bot.Id,
OldId: 0,
}
if err := tokenchange.PublishTokenChange(redis.Client.Client, tokenChangeData); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
if err := createInteractions(cm, bot.Id, data.Token); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, app.NewServerError(err))
return
}
c.JSON(200, gin.H{
"success": true,
"bot": bot,
"username": bot.Bot.Username,
})
}
}
var errInvalidToken = fmt.Errorf("invalid token")
func validateToken(token string) bool {
split := strings.Split(token, ".")
// Check for 2 dots
if len(split) != 3 {
return false
}
// Validate bot ID
// TODO: We could check the date on the snowflake
idRaw, err := base64.RawStdEncoding.DecodeString(split[0])
if err != nil {
return false
}
if _, err := strconv.ParseUint(string(idRaw), 10, 64); err != nil {
return false
}
// Validate time
if _, err := base64.RawURLEncoding.DecodeString(split[1]); err != nil {
return false
}
return true
}
func fetchApplication(ctx context.Context, token string) (*application.Application, error) {
if !validateToken(token) {
return nil, errInvalidToken
}
// Validate token + get bot ID
// TODO: Use proper context
app, err := rest.GetCurrentApplication(ctx, token, nil)
if err != nil {
return nil, err
}
if app.Id == 0 {
return nil, errInvalidToken
}
return &app, nil
}