panel update
This commit is contained in:
parent
19ac84ad16
commit
8ae95009b7
192
app/http/endpoints/manage/panelcreate.go
Normal file
192
app/http/endpoints/manage/panelcreate.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/TicketsBot/GoPanel/cache"
|
||||||
|
"github.com/TicketsBot/GoPanel/config"
|
||||||
|
"github.com/TicketsBot/GoPanel/database/table"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||||
|
"github.com/gin-gonic/contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PanelCreateHandler(ctx *gin.Context) {
|
||||||
|
store := sessions.Default(ctx)
|
||||||
|
if store == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer store.Save()
|
||||||
|
|
||||||
|
if utils.IsLoggedIn(store) {
|
||||||
|
userIdStr := store.Get("userid").(string)
|
||||||
|
userId, err := utils.GetUserId(store)
|
||||||
|
if err != nil {
|
||||||
|
ctx.String(500, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the guild exists
|
||||||
|
guildIdStr := ctx.Param("id")
|
||||||
|
guildId, err := strconv.ParseInt(guildIdStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get object for selected guild
|
||||||
|
var guild objects.Guild
|
||||||
|
for _, g := range table.GetGuilds(userIdStr) {
|
||||||
|
if g.Id == guildIdStr {
|
||||||
|
guild = g
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the user has permissions to be here
|
||||||
|
if !utils.Contains(config.Conf.Admins, userIdStr) && !guild.Owner && !table.IsAdmin(guildId, userId) {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get CSRF token
|
||||||
|
csrfCorrect := ctx.PostForm("csrf") == store.Get("csrf").(string)
|
||||||
|
if !csrfCorrect {
|
||||||
|
ctx.Redirect(302, "/")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get if the guild is premium
|
||||||
|
premiumChan := make(chan bool)
|
||||||
|
go utils.IsPremiumGuild(store, guildIdStr, premiumChan)
|
||||||
|
premium := <-premiumChan
|
||||||
|
|
||||||
|
// Check the user hasn't met their panel quota
|
||||||
|
if !premium {
|
||||||
|
panels := make(chan []table.Panel)
|
||||||
|
go table.GetPanelsByGuild(guildId, panels)
|
||||||
|
if len(<-panels) > 1 {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?metQuota=true", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate title
|
||||||
|
title := ctx.PostForm("title")
|
||||||
|
if len(title) == 0 || len(title) > 255 {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validTitle=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate content
|
||||||
|
content := ctx.PostForm("content")
|
||||||
|
if len(content) == 0 || len(content) > 1024 {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validContent=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate colour
|
||||||
|
panelColourHex := ctx.PostForm("colour")
|
||||||
|
panelColour, err := strconv.ParseUint(panelColourHex, 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validColour=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate channel
|
||||||
|
channelIdStr := ctx.PostForm("channel")
|
||||||
|
channelId, err := strconv.ParseInt(channelIdStr, 10, 64); if err != nil {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validChannel=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
validChannel := make(chan bool)
|
||||||
|
go validateChannel(guildId, channelId, validChannel)
|
||||||
|
if !<-validChannel {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validChannel=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate category
|
||||||
|
categoryStr := ctx.PostForm("category")
|
||||||
|
categoryId, err := strconv.ParseInt(categoryStr, 10, 64); if err != nil {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validCategory=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
validCategory := make(chan bool)
|
||||||
|
go validateCategory(guildId, categoryId, validChannel)
|
||||||
|
if !<-validCategory {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validCategory=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate reaction emote
|
||||||
|
reaction := strings.ToLower(ctx.PostForm("reaction"))
|
||||||
|
if len(title) == 0 || len(title) > 32 {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validReaction=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reaction = strings.Replace(reaction, ":", "", -1)
|
||||||
|
|
||||||
|
emoji := utils.GetEmojiByName(reaction)
|
||||||
|
if emoji == "" {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?validReaction=false", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
settings := table.Panel{
|
||||||
|
ChannelId: channelId,
|
||||||
|
GuildId: guildId,
|
||||||
|
Title: title,
|
||||||
|
Content: content,
|
||||||
|
Colour: int(panelColour),
|
||||||
|
TargetCategory: categoryId,
|
||||||
|
ReactionEmote: emoji,
|
||||||
|
}
|
||||||
|
|
||||||
|
go cache.Client.PublishPanelCreate(settings)
|
||||||
|
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels?created=true", guildId))
|
||||||
|
} else {
|
||||||
|
ctx.Redirect(302, "/login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateChannel(guildId, channelId int64, res chan bool) {
|
||||||
|
// Get channels from DB
|
||||||
|
channelsChan := make(chan []table.Channel)
|
||||||
|
go table.GetCachedChannelsByGuild(guildId, channelsChan)
|
||||||
|
channels := <-channelsChan
|
||||||
|
|
||||||
|
// Compare channel IDs
|
||||||
|
validChannel := false
|
||||||
|
for _, guildChannel := range channels {
|
||||||
|
if guildChannel.ChannelId == channelId {
|
||||||
|
validChannel = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res <- validChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateCategory(guildId, categoryId int64, res chan bool) {
|
||||||
|
// Get channels from DB
|
||||||
|
categoriesChan := make(chan []table.Channel)
|
||||||
|
go table.GetCategories(guildId, categoriesChan)
|
||||||
|
categories := <-categoriesChan
|
||||||
|
|
||||||
|
// Compare channel IDs
|
||||||
|
validCategory := false
|
||||||
|
for _, category := range categories {
|
||||||
|
if category.ChannelId == categoryId {
|
||||||
|
validCategory = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res <- validCategory
|
||||||
|
}
|
71
app/http/endpoints/manage/paneldelete.go
Normal file
71
app/http/endpoints/manage/paneldelete.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/TicketsBot/GoPanel/config"
|
||||||
|
"github.com/TicketsBot/GoPanel/database/table"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||||
|
"github.com/gin-gonic/contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PanelDeleteHandler(ctx *gin.Context) {
|
||||||
|
store := sessions.Default(ctx)
|
||||||
|
if store == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer store.Save()
|
||||||
|
|
||||||
|
if utils.IsLoggedIn(store) {
|
||||||
|
userIdStr := store.Get("userid").(string)
|
||||||
|
userId, err := utils.GetUserId(store)
|
||||||
|
if err != nil {
|
||||||
|
ctx.String(500, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the guild exists
|
||||||
|
guildIdStr := ctx.Param("id")
|
||||||
|
guildId, err := strconv.ParseInt(guildIdStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messageIdStr := ctx.Param("msg")
|
||||||
|
messageId, err := strconv.ParseInt(messageIdStr, 10, 64); if err != nil {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels", guildId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get object for selected guild
|
||||||
|
var guild objects.Guild
|
||||||
|
for _, g := range table.GetGuilds(userIdStr) {
|
||||||
|
if g.Id == guildIdStr {
|
||||||
|
guild = g
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the user has permissions to be here
|
||||||
|
if !utils.Contains(config.Conf.Admins, userIdStr) && !guild.Owner && !table.IsAdmin(guildId, userId) {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get CSRF token
|
||||||
|
csrfCorrect := ctx.Query("csrf") == store.Get("csrf").(string)
|
||||||
|
if !csrfCorrect {
|
||||||
|
ctx.Redirect(302, "/")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go table.DeletePanel(messageId)
|
||||||
|
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/panels", guildId))
|
||||||
|
} else {
|
||||||
|
ctx.Redirect(302, "/login")
|
||||||
|
}
|
||||||
|
}
|
144
app/http/endpoints/manage/panels.go
Normal file
144
app/http/endpoints/manage/panels.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TicketsBot/GoPanel/config"
|
||||||
|
"github.com/TicketsBot/GoPanel/database/table"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||||
|
"github.com/gin-gonic/contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedPanel struct {
|
||||||
|
MessageId int64
|
||||||
|
ChannelName string
|
||||||
|
Title string
|
||||||
|
Content string
|
||||||
|
CategoryName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func PanelHandler(ctx *gin.Context) {
|
||||||
|
store := sessions.Default(ctx)
|
||||||
|
if store == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer store.Save()
|
||||||
|
|
||||||
|
if utils.IsLoggedIn(store) {
|
||||||
|
userIdStr := store.Get("userid").(string)
|
||||||
|
userId, err := utils.GetUserId(store)
|
||||||
|
if err != nil {
|
||||||
|
ctx.String(500, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the guild exists
|
||||||
|
guildIdStr := ctx.Param("id")
|
||||||
|
guildId, err := strconv.ParseInt(guildIdStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 404 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get object for selected guild
|
||||||
|
var guild objects.Guild
|
||||||
|
for _, g := range table.GetGuilds(userIdStr) {
|
||||||
|
if g.Id == guildIdStr {
|
||||||
|
guild = g
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the user has permissions to be here
|
||||||
|
if !utils.Contains(config.Conf.Admins, userIdStr) && !guild.Owner && !table.IsAdmin(guildId, userId) {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get active panels
|
||||||
|
panelChan := make(chan []table.Panel)
|
||||||
|
go table.GetPanelsByGuild(guildId, panelChan)
|
||||||
|
panels := <-panelChan
|
||||||
|
|
||||||
|
// Get channels
|
||||||
|
channelsChan := make(chan []table.Channel)
|
||||||
|
go table.GetCachedChannelsByGuild(guildId, channelsChan)
|
||||||
|
channels := <-channelsChan
|
||||||
|
|
||||||
|
// Get default panel settings
|
||||||
|
settings := table.GetPanelSettings(guildId)
|
||||||
|
|
||||||
|
// Convert to wrapped panels
|
||||||
|
wrappedPanels := make([]wrappedPanel, 0)
|
||||||
|
for _, panel := range panels {
|
||||||
|
wrapper := wrappedPanel{
|
||||||
|
MessageId: panel.MessageId,
|
||||||
|
Title: panel.Title,
|
||||||
|
Content: panel.Content,
|
||||||
|
CategoryName: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
if panel.Title == "" {
|
||||||
|
wrapper.Title = settings.Title
|
||||||
|
}
|
||||||
|
if panel.Content == "" {
|
||||||
|
wrapper.Content = settings.Content
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get channel name & category name
|
||||||
|
for _, guildChannel := range channels {
|
||||||
|
if guildChannel.ChannelId == panel.ChannelId {
|
||||||
|
wrapper.ChannelName = guildChannel.Name
|
||||||
|
} else if guildChannel.ChannelId == panel.TargetCategory {
|
||||||
|
wrapper.CategoryName = guildChannel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedPanels = append(wrappedPanels, wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format channels to be text channels only
|
||||||
|
channelMap := make(map[int64]string)
|
||||||
|
for _, channel := range channels {
|
||||||
|
if channel.Type == 0 {
|
||||||
|
channelMap[channel.ChannelId] = channel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get categories & format
|
||||||
|
categories := make(map[int64]string)
|
||||||
|
for _, channel := range channels {
|
||||||
|
if channel.Type == 4 {
|
||||||
|
categories[channel.ChannelId] = channel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get is premium
|
||||||
|
isPremiumChan := make(chan bool)
|
||||||
|
go utils.IsPremiumGuild(store, guildIdStr, isPremiumChan)
|
||||||
|
isPremium := <-isPremiumChan
|
||||||
|
|
||||||
|
ctx.HTML(200, "manage/panels", gin.H{
|
||||||
|
"name": store.Get("name").(string),
|
||||||
|
"guildId": guildIdStr,
|
||||||
|
"csrf": store.Get("csrf").(string),
|
||||||
|
"avatar": store.Get("avatar").(string),
|
||||||
|
"baseUrl": config.Conf.Server.BaseUrl,
|
||||||
|
"panelcount": len(panels),
|
||||||
|
"premium": isPremium,
|
||||||
|
"panels": wrappedPanels,
|
||||||
|
"channels": channelMap,
|
||||||
|
"categories": categories,
|
||||||
|
|
||||||
|
"validTitle": ctx.Query("validTitle") != "true",
|
||||||
|
"validContent": ctx.Query("validContent") != "false",
|
||||||
|
"validColour": ctx.Query("validColour") != "false",
|
||||||
|
"validChannel": ctx.Query("validChannel") != "false",
|
||||||
|
"validCategory": ctx.Query("validCategory") != "false",
|
||||||
|
"validReaction": ctx.Query("validReaction") != "false",
|
||||||
|
"created": ctx.Query("created") == "true",
|
||||||
|
"metQuota": ctx.Query("metQuota") == "true",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -69,8 +69,8 @@ func SettingsHandler(ctx *gin.Context) {
|
|||||||
// Archive channel
|
// Archive channel
|
||||||
// Create a list of IDs
|
// Create a list of IDs
|
||||||
var channelIds []string
|
var channelIds []string
|
||||||
for _, c := range guild.Channels {
|
for _, c := range channels {
|
||||||
channelIds = append(channelIds, c.Id)
|
channelIds = append(channelIds, strconv.Itoa(int(c.ChannelId)))
|
||||||
}
|
}
|
||||||
|
|
||||||
panelSettings := table.GetPanelSettings(guildId)
|
panelSettings := table.GetPanelSettings(guildId)
|
||||||
|
@ -67,7 +67,7 @@ func UpdateSettingsHandler(ctx *gin.Context) {
|
|||||||
// Get welcome message
|
// Get welcome message
|
||||||
welcomeMessageValid := false
|
welcomeMessageValid := false
|
||||||
welcomeMessage := ctx.PostForm("welcomeMessage")
|
welcomeMessage := ctx.PostForm("welcomeMessage")
|
||||||
if welcomeMessage != "" && len(welcomeMessage) > 1000 {
|
if welcomeMessage != "" && len(welcomeMessage) < 1000 {
|
||||||
table.UpdateWelcomeMessage(guildId, welcomeMessage)
|
table.UpdateWelcomeMessage(guildId, welcomeMessage)
|
||||||
welcomeMessageValid = true
|
welcomeMessageValid = true
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ func UpdateSettingsHandler(ctx *gin.Context) {
|
|||||||
|
|
||||||
// Get panel colour
|
// Get panel colour
|
||||||
panelColourHex := ctx.PostForm("panelcolour")
|
panelColourHex := ctx.PostForm("panelcolour")
|
||||||
if panelColourHex == "" {
|
if panelColourHex != "" {
|
||||||
panelColour, err := strconv.ParseUint(panelColourHex, 16, 32)
|
panelColour, err := strconv.ParseUint(panelColourHex, 16, 32)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
table.UpdatePanelColour(guildId, int(panelColour))
|
table.UpdatePanelColour(guildId, int(panelColour))
|
||||||
@ -150,7 +150,7 @@ func UpdateSettingsHandler(ctx *gin.Context) {
|
|||||||
usersCanClose := ctx.PostForm("userscanclose") == "on"
|
usersCanClose := ctx.PostForm("userscanclose") == "on"
|
||||||
table.SetUserCanClose(guildId, usersCanClose)
|
table.SetUserCanClose(guildId, usersCanClose)
|
||||||
|
|
||||||
ctx.Redirect(302, fmt.Sprintf("/manage/%d/settings?validPrefix=%t&validWelcomeMessage=%t&ticketLimitValid=%t", guildId, prefixValid, welcomeMessageValid, ticketLimitValid))
|
ctx.Redirect(302, fmt.Sprintf("/manage/%d/settings?validPrefix=%t&validWelcomeMessage=%t&validTicketLimit=%t", guildId, prefixValid, welcomeMessageValid, ticketLimitValid))
|
||||||
} else {
|
} else {
|
||||||
ctx.Redirect(302, "/login")
|
ctx.Redirect(302, "/login")
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,10 @@ func StartServer() {
|
|||||||
router.GET("/manage/:id/blacklist", manage.BlacklistHandler)
|
router.GET("/manage/:id/blacklist", manage.BlacklistHandler)
|
||||||
router.GET("/manage/:id/blacklist/remove/:user", manage.BlacklistRemoveHandler)
|
router.GET("/manage/:id/blacklist/remove/:user", manage.BlacklistRemoveHandler)
|
||||||
|
|
||||||
|
router.GET("/manage/:id/panels", manage.PanelHandler)
|
||||||
|
router.POST("/manage/:id/panels/create", manage.PanelCreateHandler)
|
||||||
|
router.GET("/manage/:id/panels/delete/:msg", manage.PanelDeleteHandler)
|
||||||
|
|
||||||
router.GET("/manage/:id/tickets", manage.TicketListHandler)
|
router.GET("/manage/:id/tickets", manage.TicketListHandler)
|
||||||
router.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
|
router.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
|
||||||
router.POST("/manage/:id/tickets/view/:uuid", manage.SendMessage)
|
router.POST("/manage/:id/tickets/view/:uuid", manage.SendMessage)
|
||||||
@ -69,6 +73,7 @@ func createRenderer() multitemplate.Renderer {
|
|||||||
r = addManageTemplate(r, "settings")
|
r = addManageTemplate(r, "settings")
|
||||||
r = addManageTemplate(r, "ticketlist")
|
r = addManageTemplate(r, "ticketlist")
|
||||||
r = addManageTemplate(r, "ticketview")
|
r = addManageTemplate(r, "ticketview")
|
||||||
|
r = addManageTemplate(r, "panels")
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
17
cache/panelcreate.go
vendored
Normal file
17
cache/panelcreate.go
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TicketsBot/GoPanel/database/table"
|
||||||
|
"github.com/apex/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *RedisClient) PublishPanelCreate(settings table.Panel) {
|
||||||
|
encoded, err := json.Marshal(settings); if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Publish("tickets:panel:create", string(encoded))
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,35 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
crypto_rand "crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TicketsBot/GoPanel/app/http"
|
"github.com/TicketsBot/GoPanel/app/http"
|
||||||
"github.com/TicketsBot/GoPanel/app/http/endpoints/manage"
|
"github.com/TicketsBot/GoPanel/app/http/endpoints/manage"
|
||||||
"github.com/TicketsBot/GoPanel/cache"
|
"github.com/TicketsBot/GoPanel/cache"
|
||||||
"github.com/TicketsBot/GoPanel/config"
|
"github.com/TicketsBot/GoPanel/config"
|
||||||
"github.com/TicketsBot/GoPanel/database"
|
"github.com/TicketsBot/GoPanel/database"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/TicketsGo/sentry"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rand.Seed(time.Now().UnixNano() % 3497)
|
var b [8]byte
|
||||||
|
_, err := crypto_rand.Read(b[:])
|
||||||
|
if err == nil {
|
||||||
|
rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
|
||||||
|
} else {
|
||||||
|
sentry.Error(err)
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
database.ConnectToDatabase()
|
database.ConnectToDatabase()
|
||||||
|
|
||||||
|
utils.LoadEmoji()
|
||||||
|
|
||||||
cache.Client = cache.NewRedisClient()
|
cache.Client = cache.NewRedisClient()
|
||||||
go Listen(cache.Client)
|
go Listen(cache.Client)
|
||||||
|
|
||||||
|
51
database/table/panels.go
Normal file
51
database/table/panels.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package table
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TicketsBot/GoPanel/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Panel struct {
|
||||||
|
MessageId int64 `gorm:"column:MESSAGEID"`
|
||||||
|
ChannelId int64 `gorm:"column:CHANNELID"`
|
||||||
|
GuildId int64 `gorm:"column:GUILDID"` // Might be useful in the future so we store it
|
||||||
|
|
||||||
|
Title string `gorm:"column:TITLE;type:VARCHAR(255)"`
|
||||||
|
Content string `gorm:"column:CONTENT;type:TEXT"`
|
||||||
|
Colour int `gorm:"column:COLOUR`
|
||||||
|
TargetCategory int64 `gorm:"column:TARGETCATEGORY"`
|
||||||
|
ReactionEmote string `gorm:"column:REACTIONEMOTE;type:VARCHAR(32)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Panel) TableName() string {
|
||||||
|
return "panels"
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddPanel(messageId, channelId, guildId int64, title, content string, colour int, targetCategory int64, reactionEmote string) {
|
||||||
|
database.Database.Create(&Panel{
|
||||||
|
MessageId: messageId,
|
||||||
|
ChannelId: channelId,
|
||||||
|
GuildId: guildId,
|
||||||
|
|
||||||
|
Title: title,
|
||||||
|
Content: content,
|
||||||
|
Colour: colour,
|
||||||
|
TargetCategory: targetCategory,
|
||||||
|
ReactionEmote: reactionEmote,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPanel(messageId int64, ch chan bool) {
|
||||||
|
var count int
|
||||||
|
database.Database.Table(Panel{}.TableName()).Where(Panel{MessageId: messageId}).Count(&count)
|
||||||
|
ch <- count > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPanelsByGuild(guildId int64, ch chan []Panel) {
|
||||||
|
var panels []Panel
|
||||||
|
database.Database.Where(Panel{GuildId: guildId}).Find(&panels)
|
||||||
|
ch <- panels
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeletePanel(msgId int64) {
|
||||||
|
database.Database.Where(Panel{MessageId: msgId}).Delete(Panel{})
|
||||||
|
}
|
1939
emojis.json
Normal file
1939
emojis.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,6 @@
|
|||||||
@font-face{font-family:WhitneyMedium;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff') format('woff')}
|
@font-face{font-family:WhitneyMedium;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff') format('woff')}
|
||||||
@font-face{font-family:Whitney;font-style:bold;font-weight:700;src:url('https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff') format('woff')}
|
@font-face{font-family:Whitney;font-style:bold;font-weight:700;src:url('https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff') format('woff')}
|
||||||
|
|
||||||
html {
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.discord-container {
|
.discord-container {
|
||||||
background-color: #2e3136;
|
background-color: #2e3136;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/manage/{{.guildId}}/tickets">Ticket List</a>
|
<a class="nav-link" href="/manage/{{.guildId}}/tickets">Ticket List</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/manage/{{.guildId}}/panels">Panels</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
237
public/templates/views/panels.tmpl
Normal file
237
public/templates/views/panels.tmpl
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div class="content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h4 class="card-title">Panels</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Your panel quota: <b>{{.panelcount}} / {{if .premium}}∞{{else}}1{{end}}</b></p>
|
||||||
|
|
||||||
|
{{if not .premium}}
|
||||||
|
<p>Note: You can expand your panel quote by purchasing premium</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<table class="table table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Channel</th>
|
||||||
|
<th>Panel Title</th>
|
||||||
|
<th>Panel Content</th>
|
||||||
|
<th>Ticket Channel Category</th>
|
||||||
|
<th>Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{range .panels}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.ChannelName}}</td>
|
||||||
|
<td>{{.Title}}</td>
|
||||||
|
<td>{{.Content}}</td>
|
||||||
|
<td>{{.CategoryName}}</td>
|
||||||
|
<td><a href="/manage/{{$.guildId}}/panels/delete/{{.MessageId}}?csrf={{$.csrf}}">Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h4 class="card-title">Create A Panel</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" action="/manage/{{.guildId}}/panels/create">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Panel Title</label>
|
||||||
|
<input name="title" type="text" class="form-control" placeholder="Open a ticket!">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Panel Content</label>
|
||||||
|
<textarea name="content" type="text" class="form-control"
|
||||||
|
placeholder="By reacting to this ticket, a ticket will be opened for you."></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3 pr-1">
|
||||||
|
<label>Panel Colour (Hex)</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text">#</div>
|
||||||
|
</div>
|
||||||
|
<input name="colour" type="text" class="form-control" placeholder="23A31A">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3 pr-1">
|
||||||
|
<label>Panel Channel</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text">#</div>
|
||||||
|
</div>
|
||||||
|
<select class="form-control" name="channel">
|
||||||
|
{{range $id, $name := .channels}}
|
||||||
|
<option value="{{$id}}">{{$name}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3 pr-1">
|
||||||
|
<label>Ticket Channel Category</label>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text">#</div>
|
||||||
|
</div>
|
||||||
|
<select class="form-control" name="categories">
|
||||||
|
{{range $id, $name := .categories}}
|
||||||
|
<option value="{{$id}}">{{$name}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3 pr-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Reaction Emote</label>
|
||||||
|
<input name="reaction" type="text" class="form-control" placeholder="envelope_with_arrow">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<input name="csrf" type="hidden" value="{{.csrf}}">
|
||||||
|
<div class="col-md-2 pr-1 offset-md-5">
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
|
||||||
|
<div style="position: absolute; right: 10px; min-width: 300px">
|
||||||
|
{{if not .validTitle}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Panel titles must be between 1 and 255 characters long
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .validContent}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Panel content must be between 1 and 1024 characters long
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .validColour}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Invalid panel colour. You must use the hex value of the colour, which you can find <a href="https://www.google.co.uk/search?client=opera&q=html+colour+picker">here</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .validChannel}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Invalid channel - please try again
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .validCategory}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Invalid category - please try again
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .validReaction}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Invalid reaction emote
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if .created}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Success</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
Your panel has been created. You may need to refresh this page to see it displayed.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if .metQuota}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Warning</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
You've hit your panel quota. Premium users can create <b>unlimited panels</b>. Click <a href="https://ticketsbot.net/premium">here</a> to learn more about premium.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$('.toast').toast('show');
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -79,6 +79,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<h4>Default Panel Settings</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-md-3 pr-1">
|
<div class="col-md-3 pr-1">
|
||||||
<label>Panel Title</label>
|
<label>Panel Title</label>
|
||||||
<input name="paneltitle" type="text" class="form-control" placeholder="Open A Ticket" value="{{.paneltitle}}">
|
<input name="paneltitle" type="text" class="form-control" placeholder="Open A Ticket" value="{{.paneltitle}}">
|
||||||
|
30
utils/emojiutil.go
Normal file
30
utils/emojiutil.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/apex/log"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var emojis map[string]interface{}
|
||||||
|
|
||||||
|
func LoadEmoji() {
|
||||||
|
bytes, err := ioutil.ReadFile("emojis.json"); if err != nil {
|
||||||
|
log.Error("Couldn't load emoji: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(bytes, &emojis); err != nil {
|
||||||
|
log.Error("Couldn't load emoji: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEmojiByName(name string) string {
|
||||||
|
emoji, ok := emojis[name]; if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
str, _ := emoji.(string)
|
||||||
|
return str
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user