panel update

This commit is contained in:
Dot-Rar 2020-02-09 14:26:52 +00:00
parent 19ac84ad16
commit 8ae95009b7
15 changed files with 2713 additions and 10 deletions

View 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
}

View 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")
}
}

View 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",
})
}
}

View File

@ -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)

View File

@ -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")
} }

View File

@ -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
View 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))
}

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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>

View 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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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}}

View File

@ -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
View 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
}