Start work on panel
This commit is contained in:
parent
8f41184f74
commit
9b56c488ac
@ -56,44 +56,42 @@ func BlacklistHandler(ctx *gin.Context) {
|
|||||||
blacklistedIds = append(blacklistedIds, user.User)
|
blacklistedIds = append(blacklistedIds, user.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
usernames := table.GetUsernames(blacklistedIds)
|
nodes := table.GetUserNodes(blacklistedIds)
|
||||||
|
|
||||||
var blacklisted []map[string]interface{}
|
var blacklisted []map[string]interface{}
|
||||||
for _, node := range blacklistedUsers {
|
for _, node := range nodes {
|
||||||
blacklisted = append(blacklisted, map[string]interface{}{
|
blacklisted = append(blacklisted, map[string]interface{}{
|
||||||
"userId": node.User,
|
"userId": node.Id,
|
||||||
"username": usernames[node.User],
|
"username": utils.Base64Decode(node.Name),
|
||||||
|
"discrim": node.Discriminator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
userNotFound := false
|
userNotFound := false
|
||||||
isStaff := false
|
isStaff := false
|
||||||
if store.Get("csrf").(string) == ctx.Query("csrf") { // CSRF is correct *and* set
|
if store.Get("csrf").(string) == ctx.Query("csrf") { // CSRF is correct *and* set
|
||||||
targetIdStr := ctx.Query("userid")
|
username := ctx.Query("username")
|
||||||
targetId, err := strconv.ParseInt(targetIdStr, 10, 64)
|
discrim := ctx.Query("discrim")
|
||||||
|
|
||||||
if err != nil {
|
// Verify that the user ID is real and in a shared guild
|
||||||
userNotFound = true
|
targetId := table.GetUserId(username, discrim)
|
||||||
} else {
|
exists := targetId != 0
|
||||||
// Verify that the user ID is real and in a shared guild
|
|
||||||
username := table.GetUsername(targetId)
|
|
||||||
exists := username != ""
|
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
if guild.OwnerId == targetIdStr || table.IsSupport(guildId, targetId) { // Prevent users from blacklisting staff
|
if guild.OwnerId == strconv.Itoa(int(targetId)) || table.IsStaff(guildId, targetId) { // Prevent users from blacklisting staff
|
||||||
isStaff = true
|
isStaff = true
|
||||||
} else {
|
|
||||||
if !utils.Contains(blacklistedIds, targetId) { // Prevent duplicates
|
|
||||||
table.AddBlacklist(guildId, targetId)
|
|
||||||
blacklisted = append(blacklisted, map[string]interface{}{
|
|
||||||
"userId": targetIdStr,
|
|
||||||
"username": username,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
userNotFound = true
|
if !utils.Contains(blacklistedIds, targetId) { // Prevent duplicates
|
||||||
|
table.AddBlacklist(guildId, targetId)
|
||||||
|
blacklisted = append(blacklisted, map[string]interface{}{
|
||||||
|
"userId": targetId,
|
||||||
|
"username": username,
|
||||||
|
"discrim": discrim,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
userNotFound = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +210,39 @@ func SettingsHandler(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panelSettings := table.GetPanelSettings(guildId)
|
||||||
|
panelUpdated := false
|
||||||
|
|
||||||
|
// Get panel title
|
||||||
|
panelTitle := ctx.Query("paneltitle")
|
||||||
|
if panelTitle == "" || len(panelTitle) > 255 || !csrfCorrect {
|
||||||
|
panelTitle = panelSettings.Title
|
||||||
|
} else {
|
||||||
|
panelUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get panel content
|
||||||
|
panelContent := ctx.Query("panelcontent")
|
||||||
|
if panelContent == "" || len(panelContent) > 255 || !csrfCorrect {
|
||||||
|
panelContent = panelSettings.Content
|
||||||
|
} else {
|
||||||
|
panelUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get panel colour
|
||||||
|
var panelColour uint64
|
||||||
|
panelColourHex := ctx.Query("panelcolour")
|
||||||
|
if panelColourHex == "" || len(panelColourHex) > 255 || !csrfCorrect {
|
||||||
|
panelColour = uint64(panelSettings.Colour)
|
||||||
|
} else {
|
||||||
|
panelUpdated = true
|
||||||
|
panelColour, err = strconv.ParseUint(panelColourHex, 16, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
if panelUpdated {
|
||||||
|
go table.UpdatePanelSettings(guildId, panelTitle, panelContent, int(panelColour))
|
||||||
|
}
|
||||||
|
|
||||||
utils.Respond(ctx, template.TemplateSettings.Render(map[string]interface{}{
|
utils.Respond(ctx, template.TemplateSettings.Render(map[string]interface{}{
|
||||||
"name": store.Get("name").(string),
|
"name": store.Get("name").(string),
|
||||||
"guildId": guildIdStr,
|
"guildId": guildIdStr,
|
||||||
@ -224,6 +257,9 @@ func SettingsHandler(ctx *gin.Context) {
|
|||||||
"invalidTicketLimit": invalidTicketLimit,
|
"invalidTicketLimit": invalidTicketLimit,
|
||||||
"csrf": store.Get("csrf").(string),
|
"csrf": store.Get("csrf").(string),
|
||||||
"pingEveryone": pingEveryone,
|
"pingEveryone": pingEveryone,
|
||||||
|
"paneltitle": panelTitle,
|
||||||
|
"panelcontent": panelContent,
|
||||||
|
"panelcolour": strconv.FormatInt(int64(panelColour), 16),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
ctx.Redirect(302, "/login")
|
ctx.Redirect(302, "/login")
|
||||||
|
110
app/http/endpoints/manage/ticketlist.go
Normal file
110
app/http/endpoints/manage/ticketlist.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TicketsBot/GoPanel/app/http/template"
|
||||||
|
"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 TicketListHandler(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 !guild.Owner && !table.IsAdmin(guildId, userId) {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tickets := table.GetOpenTickets(guildId)
|
||||||
|
|
||||||
|
var toFetch []int64
|
||||||
|
for _, ticket := range tickets {
|
||||||
|
toFetch = append(toFetch, ticket.Owner)
|
||||||
|
|
||||||
|
for _, idStr := range strings.Split(ticket.Members, ",") {
|
||||||
|
if memberId, err := strconv.ParseInt(idStr, 10, 64); err == nil {
|
||||||
|
toFetch = append(toFetch, memberId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes := make(map[int64]table.UsernameNode)
|
||||||
|
for _, node := range table.GetUserNodes(toFetch) {
|
||||||
|
nodes[node.Id] = node
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticketsFormatted []map[string]interface{}
|
||||||
|
|
||||||
|
for _, ticket := range tickets {
|
||||||
|
var membersFormatted []map[string]interface{}
|
||||||
|
for index, memberIdStr := range strings.Split(ticket.Members, ",") {
|
||||||
|
if memberId, err := strconv.ParseInt(memberIdStr, 10, 64); err == nil {
|
||||||
|
if memberId != 0 {
|
||||||
|
var separator string
|
||||||
|
if index != len(strings.Split(ticket.Members, ",")) - 1 {
|
||||||
|
separator = ", "
|
||||||
|
}
|
||||||
|
|
||||||
|
membersFormatted = append(membersFormatted, map[string]interface{}{
|
||||||
|
"username": utils.Base64Decode(nodes[memberId].Name),
|
||||||
|
"discrim": nodes[memberId].Discriminator,
|
||||||
|
"sep": separator,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ticketsFormatted = append(ticketsFormatted, map[string]interface{}{
|
||||||
|
"uuid": ticket.Uuid,
|
||||||
|
"ticketId": ticket.TicketId,
|
||||||
|
"username": utils.Base64Decode(nodes[ticket.Owner].Name),
|
||||||
|
"discrim": nodes[ticket.Owner].Discriminator,
|
||||||
|
"members": membersFormatted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Respond(ctx, template.TemplateTicketList.Render(map[string]interface{}{
|
||||||
|
"name": store.Get("name").(string),
|
||||||
|
"guildId": guildIdStr,
|
||||||
|
"csrf": store.Get("csrf").(string),
|
||||||
|
"avatar": store.Get("avatar").(string),
|
||||||
|
"baseUrl": config.Conf.Server.BaseUrl,
|
||||||
|
"tickets": ticketsFormatted,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
98
app/http/endpoints/manage/ticketview.go
Normal file
98
app/http/endpoints/manage/ticketview.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/TicketsBot/GoPanel/app/http/template"
|
||||||
|
"github.com/TicketsBot/GoPanel/config"
|
||||||
|
"github.com/TicketsBot/GoPanel/database/table"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils/discord/endpoints/channel"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||||
|
"github.com/gin-gonic/contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TicketViewHandler(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 !guild.Owner && !table.IsAdmin(guildId, userId) {
|
||||||
|
ctx.Redirect(302, config.Conf.Server.BaseUrl) // TODO: 403 Page
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ticket UUID from URL and verify it exists
|
||||||
|
uuid := ctx.Param("uuid")
|
||||||
|
ticket := table.GetTicket(uuid)
|
||||||
|
exists := ticket != table.Ticket{}
|
||||||
|
|
||||||
|
// If invalid ticket UUID, take user to ticket list
|
||||||
|
if !exists {
|
||||||
|
ctx.Redirect(302, fmt.Sprintf("/manage/%s/tickets", guildIdStr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get messages
|
||||||
|
var messages []objects.Message
|
||||||
|
// We want to show users error messages so they can report them
|
||||||
|
isError := false
|
||||||
|
var errorMessage string
|
||||||
|
|
||||||
|
endpoint := channel.GetChannelMessages(int(ticket.Channel))
|
||||||
|
if err = endpoint.Request(store, nil, nil, &messages); err != nil {
|
||||||
|
isError = true
|
||||||
|
errorMessage = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format messages, exclude unneeded data
|
||||||
|
var messagesFormatted []map[string]interface{}
|
||||||
|
for _, message := range utils.Reverse(messages) {
|
||||||
|
messagesFormatted = append(messagesFormatted, map[string]interface{}{
|
||||||
|
"username": message.Author.Username,
|
||||||
|
"content": message.Content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Respond(ctx, template.TemplateTicketView.Render(map[string]interface{}{
|
||||||
|
"name": store.Get("name").(string),
|
||||||
|
"guildId": guildIdStr,
|
||||||
|
"csrf": store.Get("csrf").(string),
|
||||||
|
"avatar": store.Get("avatar").(string),
|
||||||
|
"baseUrl": config.Conf.Server.BaseUrl,
|
||||||
|
"isError": isError,
|
||||||
|
"error": errorMessage,
|
||||||
|
"messages": messagesFormatted,
|
||||||
|
"ticketId": ticket.TicketId,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
@ -62,6 +62,12 @@ func StartServer() {
|
|||||||
// /manage/:id/blacklist/remove/:user
|
// /manage/:id/blacklist/remove/:user
|
||||||
router.GET("/manage/:id/blacklist/remove/:user", manage.BlacklistRemoveHandler)
|
router.GET("/manage/:id/blacklist/remove/:user", manage.BlacklistRemoveHandler)
|
||||||
|
|
||||||
|
// /manage/:id/tickets
|
||||||
|
router.GET("/manage/:id/tickets", manage.TicketListHandler)
|
||||||
|
|
||||||
|
// /manage/:id/tickets/view/:uuid
|
||||||
|
//router.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
|
||||||
|
|
||||||
if err := router.Run(config.Conf.Server.Host); err != nil {
|
if err := router.Run(config.Conf.Server.Host); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ var (
|
|||||||
TemplateLogs Template
|
TemplateLogs Template
|
||||||
TemplateSettings Template
|
TemplateSettings Template
|
||||||
TemplateBlacklist Template
|
TemplateBlacklist Template
|
||||||
|
TemplateTicketList Template
|
||||||
|
TemplateTicketView Template
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *Template) Render(context ...interface{}) string {
|
func (t *Template) Render(context ...interface{}) string {
|
||||||
@ -54,6 +56,14 @@ func LoadTemplates() {
|
|||||||
compiled: loadTemplate("blacklist"),
|
compiled: loadTemplate("blacklist"),
|
||||||
Layout: LayoutManage,
|
Layout: LayoutManage,
|
||||||
}
|
}
|
||||||
|
TemplateTicketList = Template{
|
||||||
|
compiled: loadTemplate("ticketlist"),
|
||||||
|
Layout: LayoutManage,
|
||||||
|
}
|
||||||
|
TemplateTicketView = Template{
|
||||||
|
compiled: loadTemplate("ticketview"),
|
||||||
|
Layout: LayoutManage,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadLayout(name string) *mustache.Template {
|
func loadLayout(name string) *mustache.Template {
|
||||||
|
37
database/table/panelsettings.go
Normal file
37
database/table/panelsettings.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package table
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TicketsBot/GoPanel/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PanelSettings struct {
|
||||||
|
GuildId int64 `gorm:"column:GUILDID"`
|
||||||
|
Title string `gorm:"column:TITLE;type:VARCHAR(255)"`
|
||||||
|
Content string `gorm:"column:CONTENT;type:TEXT"`
|
||||||
|
Colour int `gorm:"column:COLOUR`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PanelSettings) TableName() string {
|
||||||
|
return "panelsettings"
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePanelSettings(guildId int64, title string, content string, colour int) {
|
||||||
|
settings := PanelSettings{
|
||||||
|
Title: title,
|
||||||
|
Content: content,
|
||||||
|
Colour: colour,
|
||||||
|
}
|
||||||
|
|
||||||
|
database.Database.Where(&PanelSettings{GuildId: guildId}).Assign(&settings).FirstOrCreate(&PanelSettings{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPanelSettings(guildId int64) PanelSettings {
|
||||||
|
settings := PanelSettings{
|
||||||
|
Title: "Open A Ticket",
|
||||||
|
Content: "React with :envelope_with_arrow: to open a ticket",
|
||||||
|
Colour: 2335514,
|
||||||
|
}
|
||||||
|
database.Database.Where(PanelSettings{GuildId: guildId}).First(&settings)
|
||||||
|
|
||||||
|
return settings
|
||||||
|
}
|
36
database/table/tickets.go
Normal file
36
database/table/tickets.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package table
|
||||||
|
|
||||||
|
import "github.com/TicketsBot/GoPanel/database"
|
||||||
|
|
||||||
|
type Ticket struct {
|
||||||
|
Uuid string `gorm:"column:UUID;type:varchar(36);primary_key"`
|
||||||
|
TicketId int `gorm:"column:ID"`
|
||||||
|
Guild int64 `gorm:"column:GUILDID"`
|
||||||
|
Channel int64 `gorm:"column:CHANNELID"`
|
||||||
|
Owner int64 `gorm:"column:OWNERID"`
|
||||||
|
Members string `gorm:"column:MEMBERS;type:text"`
|
||||||
|
IsOpen bool `gorm:"column:OPEN"`
|
||||||
|
OpenTime int64 `gorm:"column:OPENTIME"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Ticket) TableName() string {
|
||||||
|
return "tickets"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTickets(guild int64) []Ticket {
|
||||||
|
var tickets []Ticket
|
||||||
|
database.Database.Where(&Ticket{Guild: guild}).Order("ID asc").Find(&tickets)
|
||||||
|
return tickets
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOpenTickets(guild int64) []Ticket {
|
||||||
|
var tickets []Ticket
|
||||||
|
database.Database.Where(&Ticket{Guild: guild, IsOpen: true}).Order("ID asc").Find(&tickets)
|
||||||
|
return tickets
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTicket(uuid string) Ticket {
|
||||||
|
var ticket Ticket
|
||||||
|
database.Database.Where(&Ticket{Uuid: uuid}).First(&ticket)
|
||||||
|
return ticket
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
package table
|
package table
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"github.com/TicketsBot/GoPanel/database"
|
"github.com/TicketsBot/GoPanel/database"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsernameNode struct {
|
type UsernameNode struct {
|
||||||
Id int64 `gorm:"column:USERID;primary_key"`
|
Id int64 `gorm:"column:USERID;primary_key"`
|
||||||
Name string `gorm:"column:USERNAME;type:text"` // Base 64 encoded
|
Name string `gorm:"column:USERNAME;type:text"` // Base 64 encoded
|
||||||
|
Discriminator string `gorm:"column:DISCRIM;type:varchar(4)"`
|
||||||
|
Avatar string `gorm:"column:AVATARHASH;type:varchar(100)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UsernameNode) TableName() string {
|
func (UsernameNode) TableName() string {
|
||||||
@ -17,24 +19,17 @@ func (UsernameNode) TableName() string {
|
|||||||
func GetUsername(id int64) string {
|
func GetUsername(id int64) string {
|
||||||
node := UsernameNode{Name: "Unknown"}
|
node := UsernameNode{Name: "Unknown"}
|
||||||
database.Database.Where(&UsernameNode{Id: id}).First(&node)
|
database.Database.Where(&UsernameNode{Id: id}).First(&node)
|
||||||
return base64Decode(node.Name)
|
return utils.Base64Decode(node.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUsernames(ids []int64) map[int64]string {
|
func GetUserNodes(ids []int64) []UsernameNode {
|
||||||
var nodes []UsernameNode
|
var nodes []UsernameNode
|
||||||
database.Database.Where(ids).Find(&nodes)
|
database.Database.Where(ids).Find(&nodes)
|
||||||
|
return nodes
|
||||||
m := make(map[int64]string)
|
|
||||||
for _, node := range nodes {
|
|
||||||
m[node.Id] = base64Decode(node.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func base64Decode(s string) string {
|
func GetUserId(name, discrim string) int64 {
|
||||||
b, err := base64.StdEncoding.DecodeString(s); if err != nil {
|
var node UsernameNode
|
||||||
return ""
|
database.Database.Where(&UsernameNode{Name: utils.Base64Encode(name), Discriminator: discrim}).First(&node)
|
||||||
}
|
return node.Id
|
||||||
return string(b)
|
|
||||||
}
|
}
|
||||||
|
31
public/static/css/discordmock.css
Normal file
31
public/static/css/discordmock.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@font-face{font-family:Whitney;font-style:light;font-weight:300;src:url('https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff') format('woff')}
|
||||||
|
@font-face{font-family:Whitney;font-style:normal;font-weight:500;src:url('https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff') format('woff')}
|
||||||
|
@font-face{font-family:Whitney;font-style:medium;font-weight:600;src:url('https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.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')}
|
||||||
|
|
||||||
|
.discord-container {
|
||||||
|
background-color: #2e3136;
|
||||||
|
border-radius: 25px;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: 'Whitney', sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-header {
|
||||||
|
background-color: #1e2124;
|
||||||
|
height: 5vh;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 25px 25px 0 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-name {
|
||||||
|
color: white;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
@ -62,6 +62,10 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- Discord theme -->
|
||||||
|
<link href="/assets/css/discordmock.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
|
||||||
<!-- Icons -->
|
<!-- Icons -->
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
||||||
</head>
|
</head>
|
||||||
@ -112,6 +116,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/manage/{{guildId}}/blacklist">Blacklist</a>
|
<a class="nav-link" href="/manage/{{guildId}}/blacklist">Blacklist</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/manage/{{guildId}}/tickets">Ticket List</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -19,16 +19,19 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8 pr-1">
|
<div class="col-md-3 pr-1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>User ID</label>
|
<label>Username</label>
|
||||||
<input name="userid" type="text" class="form-control" placeholder="User ID">
|
<input name="username" type="text" class="form-control" placeholder="Username">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 px-1">
|
<div class="col-md-1 px-1">
|
||||||
<label></label>
|
<label>Discriminator</label>
|
||||||
<div class="alert alert-success" role="alert">
|
<div class="input-group mb-3">
|
||||||
Click <a href="https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-">here</a> for a guide on how to retreive user IDs.
|
<div class="input-group-prepend">
|
||||||
|
<div class="input-group-text">#</div>
|
||||||
|
</div>
|
||||||
|
<input name="discrim" type="text" class="form-control" placeholder="0000">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +54,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>User ID</th>
|
<th>User ID</th>
|
||||||
<th>Username</th>
|
<th>Username#Discrim</th>
|
||||||
<th>Remove</th>
|
<th>Remove</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -59,7 +62,7 @@
|
|||||||
{{#blacklisted}}
|
{{#blacklisted}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{userId}}</td>
|
<td>{{userId}}</td>
|
||||||
<td>{{username}}</td>
|
<td>{{username}}#{{discrim}}</td>
|
||||||
<td><a href="{{baseUrl}}/manage/{{guildId}}/blacklist/remove/{{userId}}?c={{csrf}}">Remove</a></td>
|
<td><a href="{{baseUrl}}/manage/{{guildId}}/blacklist/remove/{{userId}}?c={{csrf}}">Remove</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/blacklisted}}
|
{{/blacklisted}}
|
||||||
|
@ -68,6 +68,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3 pr-1">
|
||||||
|
<label>Panel Title</label>
|
||||||
|
<input name="paneltitle" type="text" class="form-control" placeholder="Open A Ticket" value="{{paneltitle}}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 pr-1">
|
||||||
|
<label>Panel Content</label>
|
||||||
|
<input name="panelcontent" type="text" class="form-control" placeholder="React with :envelope_with_arrow: to open a ticket" value="{{panelcontent}}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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="panelcolour" type="text" class="form-control" placeholder="23A31A" value="{{panelcolour}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input name="csrf" type="hidden" value="{{csrf}}">
|
<input name="csrf" type="hidden" value="{{csrf}}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-1 pr-1">
|
<div class="col-md-1 pr-1">
|
||||||
|
42
public/templates/views/ticketlist.mustache
Normal file
42
public/templates/views/ticketlist.mustache
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<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">Ticket List</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-body table-responsive">
|
||||||
|
<table class="table table-hover table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Ticket ID</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Additional Members</th>
|
||||||
|
<th>View</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#tickets}}
|
||||||
|
<tr>
|
||||||
|
<td>{{ticketId}}</td>
|
||||||
|
<td>{{username}}#{{discrim}}</td>
|
||||||
|
<td>{{#members}}{{username}}#{{discrim}}{{sep}}{{/members}}</td>
|
||||||
|
<!--<td><a class="btn btn-primary btn-sm" role="button" href="/manage/{{guildId}}/tickets/view/{{uuid}}">View</a></td>-->
|
||||||
|
<td>Coming soon</td>
|
||||||
|
</tr>
|
||||||
|
{{/tickets}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$('.toast').toast('show');
|
||||||
|
</script>
|
||||||
|
</div>
|
50
public/templates/views/ticketview.mustache
Normal file
50
public/templates/views/ticketview.mustache
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<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">Ticket List</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="discord-container">
|
||||||
|
<div class="channel-header">
|
||||||
|
<span class="channel-name">#ticket-{{ticketId}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="message-container">
|
||||||
|
<div class="message">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-container">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</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">
|
||||||
|
{{#isError}}
|
||||||
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="false">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">Error</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">
|
||||||
|
{{error}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/isError}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$('.toast').toast('show');
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</div>
|
14
utils/discord/endpoints/channel/GetChannelMessages.go
Normal file
14
utils/discord/endpoints/channel/GetChannelMessages.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package channel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils/discord"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetChannelMessages(id int) discord.Endpoint {
|
||||||
|
return discord.Endpoint{
|
||||||
|
RequestType: discord.GET,
|
||||||
|
AuthorizationType: discord.BOT,
|
||||||
|
Endpoint: fmt.Sprintf("/channels/%d/messages", id),
|
||||||
|
}
|
||||||
|
}
|
11
utils/discord/objects/attachment.go
Normal file
11
utils/discord/objects/attachment.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type Attachment struct {
|
||||||
|
Id string
|
||||||
|
Filename string
|
||||||
|
Size int
|
||||||
|
url string
|
||||||
|
ProxyUrl string
|
||||||
|
height int
|
||||||
|
Width int
|
||||||
|
}
|
17
utils/discord/objects/embed.go
Normal file
17
utils/discord/objects/embed.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type Embed struct {
|
||||||
|
Title string
|
||||||
|
Type string
|
||||||
|
Description string
|
||||||
|
Url string
|
||||||
|
Timestamp string
|
||||||
|
Color int
|
||||||
|
Footer EmbedField
|
||||||
|
Image EmbedImage
|
||||||
|
Thumbnail EmbedThumbnail
|
||||||
|
Video EmbedVideo
|
||||||
|
Provider EmbedProvider
|
||||||
|
Author EmbedAuthor
|
||||||
|
Fields []EmbedField
|
||||||
|
}
|
8
utils/discord/objects/embedauthor.go
Normal file
8
utils/discord/objects/embedauthor.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedAuthor struct {
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
IconUrl string
|
||||||
|
ProxyIconUrl string
|
||||||
|
}
|
7
utils/discord/objects/embedfield.go
Normal file
7
utils/discord/objects/embedfield.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedField struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Inline bool
|
||||||
|
}
|
7
utils/discord/objects/embedfooter.go
Normal file
7
utils/discord/objects/embedfooter.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedFooter struct {
|
||||||
|
Text string
|
||||||
|
IconUrl string
|
||||||
|
ProxyIconUrl string
|
||||||
|
}
|
8
utils/discord/objects/embedimage.go
Normal file
8
utils/discord/objects/embedimage.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedImage struct {
|
||||||
|
Url string
|
||||||
|
ProxyUrl string
|
||||||
|
Height int
|
||||||
|
Width int
|
||||||
|
}
|
6
utils/discord/objects/embedprovider.go
Normal file
6
utils/discord/objects/embedprovider.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedProvider struct {
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
}
|
8
utils/discord/objects/embedthumbnail.go
Normal file
8
utils/discord/objects/embedthumbnail.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedThumbnail struct {
|
||||||
|
Url string
|
||||||
|
ProxyUrl string
|
||||||
|
Height int
|
||||||
|
Width int
|
||||||
|
}
|
7
utils/discord/objects/embedvideo.go
Normal file
7
utils/discord/objects/embedvideo.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type EmbedVideo struct {
|
||||||
|
Url string
|
||||||
|
Height int
|
||||||
|
Width int
|
||||||
|
}
|
25
utils/discord/objects/message.go
Normal file
25
utils/discord/objects/message.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Id string
|
||||||
|
ChannelId string
|
||||||
|
GuildId string
|
||||||
|
Author User
|
||||||
|
Member Member
|
||||||
|
Content string
|
||||||
|
Timestamp string
|
||||||
|
EditedTimestamp string
|
||||||
|
Tts bool
|
||||||
|
MentionEveryone bool
|
||||||
|
Mentions []interface{}
|
||||||
|
MentionsRoles []int64
|
||||||
|
Attachments []Attachment
|
||||||
|
Embeds []Embed
|
||||||
|
Reactions []Reaction
|
||||||
|
Nonce string
|
||||||
|
Pinned bool
|
||||||
|
WebhookId string
|
||||||
|
Type int
|
||||||
|
Activity MessageActivity
|
||||||
|
Application MessageApplication
|
||||||
|
}
|
6
utils/discord/objects/messageactivity.go
Normal file
6
utils/discord/objects/messageactivity.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type MessageActivity struct {
|
||||||
|
Type int
|
||||||
|
PartyId string
|
||||||
|
}
|
9
utils/discord/objects/messageapplication.go
Normal file
9
utils/discord/objects/messageapplication.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type MessageApplication struct {
|
||||||
|
Id string
|
||||||
|
CoverImage string
|
||||||
|
Description string
|
||||||
|
Icon string
|
||||||
|
Name string
|
||||||
|
}
|
7
utils/discord/objects/reaction.go
Normal file
7
utils/discord/objects/reaction.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
type Reaction struct {
|
||||||
|
Count int
|
||||||
|
Me bool
|
||||||
|
Emoji Emoji
|
||||||
|
}
|
@ -32,3 +32,11 @@ func Insert(slice []objects.Guild, index int, value objects.Guild) []objects.Gui
|
|||||||
// Return the result.
|
// Return the result.
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Reverse(slice []objects.Message) []objects.Message {
|
||||||
|
for i := len(slice)/2-1; i >= 0; i-- {
|
||||||
|
opp := len(slice)-1-i
|
||||||
|
slice[i], slice[opp] = slice[opp], slice[i]
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -19,3 +20,14 @@ func IsInt(str string) bool {
|
|||||||
_, err := strconv.ParseInt(str, 10, 64)
|
_, err := strconv.ParseInt(str, 10, 64)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Base64Decode(s string) string {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(s); if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Base64Encode(s string) string {
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(s))
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user