This commit is contained in:
rxdn 2021-05-27 13:43:25 +01:00
parent c83858bebf
commit 8993b31a0b
13 changed files with 178 additions and 163 deletions

View File

@ -60,7 +60,7 @@ func MultiPanelCreate(ctx *gin.Context) {
messageId, err := data.sendEmbed(&botContext, premiumTier > premium.None)
if err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped); unwrapped.ErrorCode == 403 {
if errors.As(err, &unwrapped); unwrapped.StatusCode == 403 {
ctx.JSON(500, utils.ErrorJson(errors.New("I do not have permission to send messages in the provided channel")))
} else {
ctx.JSON(500, utils.ErrorJson(err))
@ -71,7 +71,7 @@ func MultiPanelCreate(ctx *gin.Context) {
if err := data.addReactions(&botContext, data.ChannelId, messageId, panels); err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped); unwrapped.ErrorCode == 403{
if errors.As(err, &unwrapped); unwrapped.StatusCode == 403{
ctx.JSON(500, utils.ErrorJson(errors.New("I do not have permission to add reactions in the provided channel")))
} else {
ctx.JSON(500, utils.ErrorJson(err))

View File

@ -80,7 +80,7 @@ func MultiPanelUpdate(ctx *gin.Context) {
messageId, err := data.sendEmbed(&botContext, premiumTier > premium.None)
if err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped) && unwrapped.ErrorCode == 403 {
if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 {
ctx.JSON(500, utils.ErrorJson(errors.New("I do not have permission to send messages in the provided channel")))
} else {
ctx.JSON(500, utils.ErrorJson(err))
@ -92,7 +92,7 @@ func MultiPanelUpdate(ctx *gin.Context) {
// add reactions to new message
if err := data.addReactions(&botContext, data.ChannelId, messageId, panels); err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped) && unwrapped.ErrorCode == 403 {
if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 {
ctx.JSON(500, utils.ErrorJson(errors.New("I do not have permission to add reactions in the provided channel")))
} else {
ctx.JSON(500, utils.ErrorJson(err))

View File

@ -14,7 +14,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/rxdn/gdl/objects/channel"
"github.com/rxdn/gdl/objects/channel/embed"
"github.com/rxdn/gdl/objects/channel/message"
"github.com/rxdn/gdl/objects/guild/emoji"
"github.com/rxdn/gdl/objects/interaction/component"
"github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/rest/request"
"golang.org/x/sync/errgroup"
@ -25,20 +26,19 @@ import (
const freePanelLimit = 3
type panelBody struct {
ChannelId uint64 `json:"channel_id,string"`
MessageId uint64 `json:"message_id,string"`
Title string `json:"title"`
Content string `json:"content"`
Colour uint32 `json:"colour"`
CategoryId uint64 `json:"category_id,string"`
Emote string `json:"emote"`
WelcomeMessage *string `json:"welcome_message"`
Mentions []string `json:"mentions"`
Teams []string `json:"teams"`
ChannelId uint64 `json:"channel_id,string"`
MessageId uint64 `json:"message_id,string"`
Title string `json:"title"`
Content string `json:"content"`
Colour uint32 `json:"colour"`
CategoryId uint64 `json:"category_id,string"`
Emote string `json:"emote"`
WelcomeMessage *string `json:"welcome_message"`
Mentions []string `json:"mentions"`
Teams []string `json:"teams"`
}
func CreatePanel(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
botContext, err := botcontext.ContextForGuild(guildId)
@ -87,10 +87,13 @@ func CreatePanel(ctx *gin.Context) {
return
}
msgId, err := data.sendEmbed(&botContext, premiumTier > premium.None)
customId := utils.RandString(80)
emoji, _ := data.getEmoji() // already validated
msgId, err := data.sendEmbed(&botContext, data.Title, customId, emoji, premiumTier > premium.None)
if err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped) && unwrapped.ErrorCode == 403 {
if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": "I do not have permission to send messages in the specified channel",
@ -106,26 +109,6 @@ func CreatePanel(ctx *gin.Context) {
return
}
// Add reaction
emoji, _ := data.getEmoji() // already validated
if err = rest.CreateReaction(botContext.Token, botContext.RateLimiter, data.ChannelId, msgId, emoji); err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped) && unwrapped.ErrorCode == 403 {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": "I do not have permission to add reactions in the specified channel",
})
} else {
// TODO: Most appropriate error?
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
})
}
return
}
// Store in DB
panel := database.Panel{
MessageId: msgId,
@ -138,9 +121,11 @@ func CreatePanel(ctx *gin.Context) {
ReactionEmote: emoji,
WelcomeMessage: data.WelcomeMessage,
WithDefaultTeam: utils.ContainsString(data.Teams, "default"),
CustomId: customId,
}
if err = dbclient.Client.Panel.Create(panel); err != nil {
panelId, err := dbclient.Client.Panel.Create(panel)
if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": err.Error(),
@ -188,8 +173,8 @@ func CreatePanel(ctx *gin.Context) {
}
ctx.JSON(200, gin.H{
"success": true,
"message_id": strconv.FormatUint(msgId, 10),
"success": true,
"panel_id": strconv.Itoa(panelId),
})
}
@ -323,7 +308,7 @@ func (p *panelBody) verifyWelcomeMessage() bool {
return p.WelcomeMessage == nil || (len(*p.WelcomeMessage) > 0 && len(*p.WelcomeMessage) < 1025)
}
func (p *panelBody) sendEmbed(ctx *botcontext.BotContext, isPremium bool) (messageId uint64, err error) {
func (p *panelBody) sendEmbed(ctx *botcontext.BotContext, title, customId, emote string, isPremium bool) (uint64, error) {
e := embed.NewEmbed().
SetTitle(p.Title).
SetDescription(p.Content).
@ -334,12 +319,36 @@ func (p *panelBody) sendEmbed(ctx *botcontext.BotContext, isPremium bool) (messa
e.SetFooter("Powered by ticketsbot.net", "https://cdn.discordapp.com/avatars/508391840525975553/ac2647ffd4025009e2aa852f719a8027.png?size=256")
}
var msg message.Message
msg, err = rest.CreateMessage(ctx.Token, ctx.RateLimiter, p.ChannelId, rest.CreateMessageData{Embed: e})
if err != nil {
return
data := rest.CreateMessageData{
Embed: e,
Components: []component.Component{
{
Type: component.ComponentActionRow,
ComponentData: component.ActionRow{
Components: []component.Component{
{
Type: component.ComponentButton,
ComponentData: component.Button{
Label: title,
CustomId: customId,
Style: component.ButtonStylePrimary,
Emoji: emoji.Emoji{
Name: emote,
},
Url: nil,
Disabled: false,
},
},
},
},
},
},
}
messageId = msg.Id
return
msg, err := rest.CreateMessage(ctx.Token, ctx.RateLimiter, p.ChannelId, data)
if err != nil {
return 0, err
}
return msg.Id, nil
}

View File

@ -20,7 +20,7 @@ func DeletePanel(ctx *gin.Context) {
return
}
messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
panelId, err := strconv.Atoi(ctx.Param("id"))
if err != nil {
ctx.AbortWithStatusJSON(400, gin.H{
"success": false,
@ -29,7 +29,7 @@ func DeletePanel(ctx *gin.Context) {
return
}
panel, err := database.Client.Panel.Get(messageId)
panel, err := database.Client.Panel.GetById(panelId)
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
@ -47,7 +47,7 @@ func DeletePanel(ctx *gin.Context) {
return
}
if err := database.Client.Panel.Delete(messageId); err != nil {
if err := database.Client.Panel.Delete(panelId); err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),

View File

@ -11,8 +11,8 @@ import (
func ListPanels(ctx *gin.Context) {
type panelResponse struct {
PanelId int `json:"panel_id"`
ChannelId uint64 `json:"channel_id,string"`
MessageId uint64 `json:"message_id,string"`
Title string `json:"title"`
Content string `json:"content"`
Colour uint32 `json:"colour"`
@ -74,7 +74,7 @@ func ListPanels(ctx *gin.Context) {
}
wrapped[i] = panelResponse{
MessageId: p.MessageId,
PanelId: p.PanelId,
ChannelId: p.ChannelId,
Title: p.Title,
Content: p.Content,

View File

@ -33,16 +33,14 @@ func UpdatePanel(ctx *gin.Context) {
return
}
messageId, err := strconv.ParseUint(ctx.Param("message"), 10, 64)
panelId, err := strconv.Atoi(ctx.Param("id"))
if err != nil {
ctx.AbortWithStatusJSON(400, utils.ErrorJson(err))
return
}
data.MessageId = messageId
// get existing
existing, err := dbclient.Client.Panel.Get(data.MessageId)
existing, err := dbclient.Client.Panel.GetById(panelId)
if err != nil {
ctx.AbortWithStatusJSON(500, utils.ErrorJson(err))
return
@ -121,7 +119,7 @@ func UpdatePanel(ctx *gin.Context) {
existing.ReactionEmote != data.Emote
emoji, _ := data.getEmoji() // already validated
newMessageId := messageId
newMessageId := existing.MessageId
if shouldUpdateMessage {
// delete old message
@ -134,10 +132,10 @@ func UpdatePanel(ctx *gin.Context) {
}
premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, botContext.Token, botContext.RateLimiter)
newMessageId, err = data.sendEmbed(&botContext, premiumTier > premium.None)
newMessageId, err = data.sendEmbed(&botContext, existing.Title, existing.CustomId, existing.ReactionEmote, premiumTier > premium.None)
if err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped) && unwrapped.ErrorCode == 403 {
if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": "I do not have permission to send messages in the specified channel",
@ -153,7 +151,7 @@ func UpdatePanel(ctx *gin.Context) {
// Add reaction
if err = rest.CreateReaction(botContext.Token, botContext.RateLimiter, data.ChannelId, newMessageId, emoji); err != nil {
var unwrapped request.RestError
if errors.As(err, &unwrapped) && unwrapped.ErrorCode == 403 {
if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 {
ctx.AbortWithStatusJSON(500, gin.H{
"success": false,
"error": "I do not have permission to add reactions in the specified channel",
@ -169,19 +167,20 @@ func UpdatePanel(ctx *gin.Context) {
// Store in DB
panel := database.Panel{
MessageId: newMessageId,
ChannelId: data.ChannelId,
GuildId: guildId,
Title: data.Title,
Content: data.Content,
Colour: int32(data.Colour),
TargetCategory: data.CategoryId,
ReactionEmote: emoji,
WelcomeMessage: data.WelcomeMessage,
PanelId: panelId,
MessageId: newMessageId,
ChannelId: data.ChannelId,
GuildId: guildId,
Title: data.Title,
Content: data.Content,
Colour: int32(data.Colour),
TargetCategory: data.CategoryId,
ReactionEmote: emoji,
WelcomeMessage: data.WelcomeMessage,
WithDefaultTeam: utils.ContainsString(data.Teams, "default"),
}
if err = dbclient.Client.Panel.Update(messageId, panel); err != nil {
if err = dbclient.Client.Panel.Update(panel); err != nil {
ctx.AbortWithStatusJSON(500, utils.ErrorJson(err))
return
}
@ -239,6 +238,5 @@ func UpdatePanel(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"success": true,
"message_id": strconv.FormatUint(newMessageId, 10),
})
}

View File

@ -116,7 +116,7 @@ func SendMessage(ctx *gin.Context) {
if err != nil {
// We can delete the webhook in this case
var unwrapped request.RestError
if errors.As(err, &unwrapped); unwrapped.ErrorCode == 403 || unwrapped.ErrorCode == 404 {
if errors.As(err, &unwrapped); unwrapped.StatusCode == 403 || unwrapped.StatusCode == 404 {
go database.Client.Webhooks.Delete(guildId, ticketId)
}
} else {

View File

@ -5,50 +5,46 @@ import (
"github.com/TicketsBot/GoPanel/botcontext"
"github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/messagequeue"
command "github.com/TicketsBot/worker/bot/command/impl"
"github.com/TicketsBot/worker/bot/command/impl/admin"
"github.com/TicketsBot/worker/bot/command/manager"
"github.com/gin-gonic/gin"
"github.com/rxdn/gdl/rest"
"time"
)
func WhitelabelCreateInteractions(ctx *gin.Context) {
userId := ctx.Keys["userid"].(uint64)
// TODO: Refactor
func GetWhitelabelCreateInteractions() func(*gin.Context) {
cm := new(manager.CommandManager)
cm.RegisterCommands()
// Get bot
bot, err := database.Client.Whitelabel.GetByUserId(userId)
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
return func(ctx *gin.Context) {
// Ensure bot exists
if bot.BotId == 0 {
ctx.JSON(404, gin.H{
"success": false,
"error": "No bot found",
})
return
}
userId := ctx.Keys["userid"].(uint64)
// Cooldown
key := fmt.Sprintf("tickets:interaction-create-cooldown:%d", bot.BotId)
// Get bot
bot, err := database.Client.Whitelabel.GetByUserId(userId)
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// try to set first, prevent race condition
wasSet, err := messagequeue.Client.SetNX(key, 1, time.Minute).Result()
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
// Ensure bot exists
if bot.BotId == 0 {
ctx.JSON(404, gin.H{
"success": false,
"error": "No bot found",
})
return
}
// on cooldown, tell user how long left
if !wasSet {
expiration, err := messagequeue.Client.TTL(key).Result()
// Cooldown
key := fmt.Sprintf("tickets:interaction-create-cooldown:%d", bot.BotId)
// try to set first, prevent race condition
wasSet, err := messagequeue.Client.SetNX(key, 1, time.Minute).Result()
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
@ -57,50 +53,62 @@ func WhitelabelCreateInteractions(ctx *gin.Context) {
return
}
ctx.JSON(400, gin.H{
"success": false,
"error": fmt.Sprintf("Interaction creation on cooldown, please wait another %d seconds", int64(expiration.Seconds())),
})
// on cooldown, tell user how long left
if !wasSet {
expiration, err := messagequeue.Client.TTL(key).Result()
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
return
}
ctx.JSON(400, gin.H{
"success": false,
"error": fmt.Sprintf("Interaction creation on cooldown, please wait another %d seconds", int64(expiration.Seconds())),
})
botContext, err := botcontext.ContextForGuild(0)
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
var interactions []rest.CreateCommandData
for _, cmd := range command.Commands {
properties := cmd.Properties()
if properties.MessageOnly || properties.AdminOnly || properties.HelperOnly || properties.MainBotOnly {
continue
return
}
option := command.BuildOption(cmd)
data := rest.CreateCommandData{
Name: option.Name,
Description: option.Description,
Options: option.Options,
botContext, err := botcontext.ContextForGuild(0)
if err != nil {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
return
}
interactions = append(interactions, data)
}
var interactions []rest.CreateCommandData
for _, cmd := range cm.GetCommands() {
properties := cmd.Properties()
if _, err = rest.ModifyGlobalCommands(bot.Token, botContext.RateLimiter, bot.BotId, interactions); err == nil {
ctx.JSON(200, gin.H{
"success": true,
})
} else {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
if properties.MessageOnly || properties.AdminOnly || properties.HelperOnly || properties.MainBotOnly {
continue
}
option := admin.BuildOption(cmd)
data := rest.CreateCommandData{
Name: option.Name,
Description: option.Description,
Options: option.Options,
}
interactions = append(interactions, data)
}
if _, err = rest.ModifyGlobalCommands(bot.Token, botContext.RateLimiter, bot.BotId, interactions); err == nil {
ctx.JSON(200, gin.H{
"success": true,
})
} else {
ctx.JSON(500, gin.H{
"success": false,
"error": err.Error(),
})
}
}
}

View File

@ -67,7 +67,7 @@ func CallbackHandler(ctx *gin.Context) {
return
}
store.Set("csrf", utils.RandStringRunes(32))
store.Set("csrf", utils.RandString(32))
store.Set("userid", currentUser.Id)
store.Set("name", currentUser.Username)

View File

@ -101,8 +101,8 @@ func StartServer() {
guildAuthApiAdmin.GET("/panels", api_panels.ListPanels)
guildAuthApiAdmin.PUT("/panels", api_panels.CreatePanel)
guildAuthApiAdmin.PUT("/panels/:message", api_panels.UpdatePanel)
guildAuthApiAdmin.DELETE("/panels/:message", api_panels.DeletePanel)
guildAuthApiAdmin.PUT("/panels/:id", api_panels.UpdatePanel)
guildAuthApiAdmin.DELETE("/panels/:id", api_panels.DeletePanel)
guildAuthApiAdmin.GET("/multipanels", api_panels.MultiPanelList)
guildAuthApiAdmin.POST("/multipanels", api_panels.MultiPanelCreate)
@ -149,7 +149,7 @@ func StartServer() {
whitelabelApiGroup.GET("/guilds", api_whitelabel.WhitelabelGetGuilds)
whitelabelApiGroup.GET("/public-key", api_whitelabel.WhitelabelGetPublicKey)
whitelabelApiGroup.POST("/public-key", api_whitelabel.WhitelabelPostPublicKey)
whitelabelApiGroup.POST("/create-interactions", api_whitelabel.WhitelabelCreateInteractions)
whitelabelApiGroup.POST("/create-interactions", api_whitelabel.GetWhitelabelCreateInteractions())
whitelabelApiGroup.POST("/", createLimiter(10, time.Minute), api_whitelabel.WhitelabelPost)
whitelabelApiGroup.POST("/status", createLimiter(1, time.Second*5), api_whitelabel.WhitelabelStatusPost)

10
go.mod
View File

@ -5,23 +5,23 @@ go 1.14
require (
github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20210220155137-a562b2f1bbbb
github.com/TicketsBot/common v0.0.0-20210314144843-3ac00a091e42
github.com/TicketsBot/database v0.0.0-20210314143312-464ac4588cf2
github.com/TicketsBot/worker v0.0.0-20210314143458-b27c23beab4b
github.com/TicketsBot/common v0.0.0-20210508230445-142f7765b87f
github.com/TicketsBot/database v0.0.0-20210526225555-040a69389e53
github.com/TicketsBot/worker v0.0.0-20210526230503-cf3fa42fed99
github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-contrib/multitemplate v0.0.0-20200226145339-3e397ee01bc6
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
github.com/gin-gonic/gin v1.6.2
github.com/gin-gonic/gin v1.7.1
github.com/go-redis/redis v6.15.9+incompatible
github.com/gorilla/sessions v1.2.0 // indirect
github.com/gorilla/websocket v1.4.2
github.com/jackc/pgx/v4 v4.7.1
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c
github.com/pkg/errors v0.9.1
github.com/rxdn/gdl v0.0.0-20210301221508-d84ed0db0f5c
github.com/rxdn/gdl v0.0.0-20210527124215-63f4791eb845
github.com/sirupsen/logrus v1.5.0
github.com/ulule/limiter/v3 v3.5.0
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a

View File

@ -255,13 +255,13 @@
}
}
async function deletePanel(messageId) {
const res = await axios.delete('/api/{{.guildId}}/panels/' + messageId);
async function deletePanel(panelId) {
const res = await axios.delete('/api/{{.guildId}}/panels/' + panelId);
if (res.status === 200 && res.data.success) {
notifySuccess('Panel deleted successfully');
const el = document.getElementById(messageId);
const el = document.getElementById(panelId);
el.parentNode.removeChild(el);
} else {
notifyError(res.data.error);
@ -301,7 +301,7 @@
const res = await axios.put('/api/{{.guildId}}/panels', data);
if (res.status === 200 && res.data.success) {
data.message_id = res.data.message_id;
data.panel_id = res.data.panel_id;
appendPanel(data, await getChannels());
notifySuccess('Panel created successfully')
} else {
@ -381,7 +381,7 @@
const container = document.getElementById('panel-container');
const tr = document.createElement('tr');
tr.id = panel.message_id; // TODO: When we call this after creating a panel, we don't know the message ID yet
tr.id = panel.panel_id;
appendTd(tr, `#${getChannelName(channels, panel.channel_id)}`);
appendTd(tr, panel.title);
@ -393,7 +393,7 @@
editButton.type = 'button';
editButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
editButton.appendChild(document.createTextNode('Edit'));
editButton.onclick = () => { openEditModal(panel.message_id) };
editButton.onclick = () => { openEditModal(panel.panel_id) };
editTd.appendChild(editButton);
tr.appendChild(editTd);
@ -403,7 +403,7 @@
deleteButton.type = 'submit';
deleteButton.classList.add('btn', 'btn-primary', 'btn-fill', 'mx-auto');
deleteButton.appendChild(document.createTextNode('Delete'));
deleteButton.onclick = () => {deletePanel(panel.message_id)};
deleteButton.onclick = () => {deletePanel(panel.panel_id)};
deleteTd.appendChild(deleteButton);
tr.appendChild(deleteTd);
@ -475,7 +475,7 @@
for (const panel of panels) {
const option = document.createElement('option');
option.value = panel.message_id;
option.value = panel.panel_id;
option.appendChild(document.createTextNode(panel.title));
select.appendChild(option);
}

View File

@ -8,7 +8,7 @@ import (
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandStringRunes(length int) string {
func RandString(length int) string {
b := make([]rune, length)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]