Web chat
This commit is contained in:
parent
fcb7e51631
commit
21e1a854ac
76
app/http/endpoints/manage/sendmessage.go
Normal file
76
app/http/endpoints/manage/sendmessage.go
Normal file
@ -0,0 +1,76 @@
|
||||
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"
|
||||
"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 SendMessage(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
|
||||
ticketChan := make(chan table.Ticket)
|
||||
go table.GetTicket(ctx.Param("uuid"), ticketChan)
|
||||
ticket := <-ticketChan
|
||||
exists := ticket != table.Ticket{}
|
||||
|
||||
contentType := discord.ApplicationJson
|
||||
|
||||
if exists {
|
||||
content := fmt.Sprintf("**%s**: %s", store.Get("name").(string), ctx.PostForm("message"))
|
||||
if len(content) > 2000 {
|
||||
content = content[0:1999]
|
||||
}
|
||||
|
||||
endpoint := channel.CreateMessage(int(ticket.Channel))
|
||||
err = endpoint.Request(store, &contentType, channel.CreateMessageBody{
|
||||
Content: content,
|
||||
}, nil)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Redirect(301, ctx.Request.URL.String())
|
||||
}
|
@ -10,9 +10,13 @@ import (
|
||||
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var MentionRegex, _ = regexp.Compile("<@(\\d+)>")
|
||||
|
||||
func TicketViewHandler(ctx *gin.Context) {
|
||||
store := sessions.Default(ctx)
|
||||
if store == nil {
|
||||
@ -53,7 +57,9 @@ func TicketViewHandler(ctx *gin.Context) {
|
||||
|
||||
// Get ticket UUID from URL and verify it exists
|
||||
uuid := ctx.Param("uuid")
|
||||
ticket := table.GetTicket(uuid)
|
||||
ticketChan := make(chan table.Ticket)
|
||||
go table.GetTicket(uuid, ticketChan)
|
||||
ticket := <-ticketChan
|
||||
exists := ticket != table.Ticket{}
|
||||
|
||||
// If invalid ticket UUID, take user to ticket list
|
||||
@ -77,12 +83,31 @@ func TicketViewHandler(ctx *gin.Context) {
|
||||
// Format messages, exclude unneeded data
|
||||
var messagesFormatted []map[string]interface{}
|
||||
for _, message := range utils.Reverse(messages) {
|
||||
content := message.Content
|
||||
|
||||
// Format mentions properly
|
||||
match := MentionRegex.FindAllStringSubmatch(content, -1)
|
||||
for _, mention := range match {
|
||||
if len(mention) >= 2 {
|
||||
mentionedId, err := strconv.ParseInt(mention[1], 10, 64); if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ch := make(chan string)
|
||||
go table.GetUsername(mentionedId, ch)
|
||||
content = strings.ReplaceAll(content, fmt.Sprintf("<@%d>", mentionedId), fmt.Sprintf("@%s", <-ch))
|
||||
}
|
||||
}
|
||||
|
||||
messagesFormatted = append(messagesFormatted, map[string]interface{}{
|
||||
"username": message.Author.Username,
|
||||
"content": message.Content,
|
||||
"content": content,
|
||||
})
|
||||
}
|
||||
|
||||
premium := make(chan bool)
|
||||
go utils.IsPremiumGuild(store, guildIdStr, premium)
|
||||
|
||||
utils.Respond(ctx, template.TemplateTicketView.Render(map[string]interface{}{
|
||||
"name": store.Get("name").(string),
|
||||
"guildId": guildIdStr,
|
||||
@ -93,6 +118,8 @@ func TicketViewHandler(ctx *gin.Context) {
|
||||
"error": errorMessage,
|
||||
"messages": messagesFormatted,
|
||||
"ticketId": ticket.TicketId,
|
||||
"include_mock": true,
|
||||
"premium": <-premium,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
176
app/http/endpoints/manage/webchatws.go
Normal file
176
app/http/endpoints/manage/webchatws.go
Normal file
@ -0,0 +1,176 @@
|
||||
package manage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/database/table"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord"
|
||||
"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"
|
||||
"github.com/gorilla/websocket"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
var SocketsLock sync.Mutex
|
||||
var Sockets []*Socket
|
||||
|
||||
type (
|
||||
Socket struct {
|
||||
Ws *websocket.Conn
|
||||
Guild string
|
||||
Ticket int
|
||||
}
|
||||
|
||||
WsEvent struct {
|
||||
Type string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
AuthEvent struct {
|
||||
Guild string
|
||||
Ticket string
|
||||
}
|
||||
)
|
||||
|
||||
func WebChatWs(ctx *gin.Context) {
|
||||
store := sessions.Default(ctx)
|
||||
if store == nil {
|
||||
return
|
||||
}
|
||||
defer store.Save()
|
||||
|
||||
if utils.IsLoggedIn(store) {
|
||||
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
socket := &Socket{
|
||||
Ws: conn,
|
||||
}
|
||||
|
||||
conn.SetCloseHandler(func(code int, text string) error {
|
||||
i := -1
|
||||
SocketsLock.Lock()
|
||||
|
||||
for index, element := range Sockets {
|
||||
if element == socket {
|
||||
i = index
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i != -1 {
|
||||
Sockets = Sockets[:i+copy(Sockets[i:], Sockets[i+1:])]
|
||||
}
|
||||
SocketsLock.Unlock()
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
SocketsLock.Lock()
|
||||
Sockets = append(Sockets, socket)
|
||||
SocketsLock.Unlock()
|
||||
|
||||
userIdStr := store.Get("userid").(string)
|
||||
userId, err := utils.GetUserId(store)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
var guildId string
|
||||
var guildIdParsed int64
|
||||
var ticket int
|
||||
|
||||
for {
|
||||
var evnt WsEvent
|
||||
err := conn.ReadJSON(&evnt)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if guildId == "" && evnt.Type != "auth" {
|
||||
conn.Close()
|
||||
break
|
||||
} else if evnt.Type == "auth" {
|
||||
data := evnt.Data.(map[string]interface{})
|
||||
|
||||
guildId = data["guild"].(string)
|
||||
ticket, err = strconv.Atoi(data["ticket"].(string)); if err != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
socket.Guild = guildId
|
||||
socket.Ticket = ticket
|
||||
|
||||
// Verify the guild exists
|
||||
guildIdParsed, err = strconv.ParseInt(guildId, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Get object for selected guild
|
||||
var guild objects.Guild
|
||||
for _, g := range table.GetGuilds(userIdStr) {
|
||||
if g.Id == guildId {
|
||||
guild = g
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the user has permissions to be here
|
||||
if !guild.Owner && !table.IsAdmin(guildIdParsed, userId) {
|
||||
fmt.Println(err.Error())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the guild is premium
|
||||
premium := make(chan bool)
|
||||
go utils.IsPremiumGuild(store, guildId, premium)
|
||||
if !<-premium {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
} else if evnt.Type == "send" {
|
||||
data := evnt.Data.(string)
|
||||
|
||||
if data == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get ticket UUID from URL and verify it exists
|
||||
ticketChan := make(chan table.Ticket)
|
||||
go table.GetTicketById(guildIdParsed, ticket, ticketChan)
|
||||
ticket := <-ticketChan
|
||||
exists := ticket != table.Ticket{}
|
||||
|
||||
contentType := discord.ApplicationJson
|
||||
|
||||
if exists {
|
||||
content := fmt.Sprintf("**%s**: %s", store.Get("name").(string), data)
|
||||
if len(content) > 2000 {
|
||||
content = content[0:1999]
|
||||
}
|
||||
|
||||
endpoint := channel.CreateMessage(int(ticket.Channel))
|
||||
err = endpoint.Request(store, &contentType, channel.CreateMessageBody{
|
||||
Content: content,
|
||||
}, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -66,7 +66,12 @@ func StartServer() {
|
||||
router.GET("/manage/:id/tickets", manage.TicketListHandler)
|
||||
|
||||
// /manage/:id/tickets/view/:uuid
|
||||
//router.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
|
||||
router.GET("/manage/:id/tickets/view/:uuid", manage.TicketViewHandler)
|
||||
|
||||
// POST /manage/:id/tickets/view/:uuid
|
||||
router.POST("/manage/:id/tickets/view/:uuid", manage.SendMessage)
|
||||
|
||||
router.GET("/webchat", manage.WebChatWs)
|
||||
|
||||
if err := router.Run(config.Conf.Server.Host); err != nil {
|
||||
panic(err)
|
||||
|
25
cache/redis.go
vendored
Normal file
25
cache/redis.go
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
type RedisClient struct {
|
||||
*redis.Client
|
||||
}
|
||||
|
||||
var Client RedisClient
|
||||
|
||||
func NewRedisClient() RedisClient {
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", config.Conf.Redis.Host, config.Conf.Redis.Port),
|
||||
Password: config.Conf.Redis.Password,
|
||||
PoolSize: config.Conf.Redis.Threads,
|
||||
})
|
||||
|
||||
return RedisClient{
|
||||
client,
|
||||
}
|
||||
}
|
21
cache/uriparser.go
vendored
Normal file
21
cache/uriparser.go
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
package cache
|
||||
|
||||
import "net/url"
|
||||
|
||||
type RedisURI struct {
|
||||
Addr string
|
||||
Password string
|
||||
}
|
||||
|
||||
func ParseURI(raw string) RedisURI {
|
||||
parsed, err := url.Parse(raw); if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
passwd, _ := parsed.User.Password()
|
||||
|
||||
return RedisURI{
|
||||
Addr: parsed.Host,
|
||||
Password: passwd,
|
||||
}
|
||||
}
|
41
cache/webchat.go
vendored
Normal file
41
cache/webchat.go
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/app/http/endpoints/manage"
|
||||
)
|
||||
|
||||
type TicketMessage struct {
|
||||
GuildId string `json:"guild"`
|
||||
TicketId int `json:"ticket"`
|
||||
Username string `json:"username"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (c *RedisClient) ListenForMessages() {
|
||||
pubsub := c.Subscribe("tickets:webchat:inboundmessage")
|
||||
|
||||
for {
|
||||
msg, err := pubsub.ReceiveMessage(); if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
var decoded TicketMessage
|
||||
if err := json.Unmarshal([]byte(msg.Payload), &decoded); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
manage.SocketsLock.Lock()
|
||||
for _, socket := range manage.Sockets {
|
||||
if socket.Guild == decoded.GuildId && socket.Ticket == decoded.TicketId {
|
||||
if err := socket.Ws.WriteJSON(decoded); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
manage.SocketsLock.Unlock()
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/app/http"
|
||||
"github.com/TicketsBot/GoPanel/cache"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/TicketsBot/GoPanel/database"
|
||||
"math/rand"
|
||||
@ -13,5 +14,9 @@ func main() {
|
||||
|
||||
config.LoadConfig()
|
||||
database.ConnectToDatabase()
|
||||
|
||||
cache.Client = cache.NewRedisClient()
|
||||
go cache.Client.ListenForMessages()
|
||||
|
||||
http.StartServer()
|
||||
}
|
||||
|
@ -23,8 +23,11 @@ threads=5
|
||||
|
||||
[bot]
|
||||
token=""
|
||||
premium-lookup-proxy-url="http://localhost:3000"
|
||||
premium-lookup-proxy-key=""
|
||||
|
||||
[redis]
|
||||
host="127.0.0.1"
|
||||
port=6379
|
||||
password=""
|
||||
threads=5
|
||||
|
@ -48,12 +48,15 @@ type (
|
||||
|
||||
Bot struct {
|
||||
Token string
|
||||
PremiumLookupProxyUrl string `toml:"premium-lookup-proxy-url"`
|
||||
PremiumLookupProxyKey string `toml:"premium-lookup-proxy-key"`
|
||||
}
|
||||
|
||||
Redis struct {
|
||||
Host string
|
||||
Port int
|
||||
Password string
|
||||
Threads int
|
||||
}
|
||||
)
|
||||
|
||||
|
72
database/table/premiumguilds.go
Normal file
72
database/table/premiumguilds.go
Normal file
@ -0,0 +1,72 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/database"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PremiumGuilds struct {
|
||||
Guild int64 `gorm:"column:GUILDID;unique;primary_key"`
|
||||
Expiry int64 `gorm:"column:EXPIRY"`
|
||||
User int64 `gorm:"column:USERID"`
|
||||
ActivatedBy int64 `gorm:"column:ACTIVATEDBY"`
|
||||
Keys string `gorm:"column:KEYSUSED"`
|
||||
}
|
||||
|
||||
func (PremiumGuilds) TableName() string {
|
||||
return "premiumguilds"
|
||||
}
|
||||
|
||||
func IsPremium(guild int64, ch chan bool) {
|
||||
var node PremiumGuilds
|
||||
database.Database.Where(PremiumGuilds{Guild: guild}).First(&node)
|
||||
|
||||
if node.Expiry == 0 {
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
|
||||
current := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
ch <- node.Expiry > current
|
||||
}
|
||||
|
||||
func AddPremium(key string, guild, userId, length, activatedBy int64) {
|
||||
var expiry int64
|
||||
|
||||
hasPrem := make(chan bool)
|
||||
go IsPremium(guild, hasPrem)
|
||||
isPremium := <- hasPrem
|
||||
|
||||
if isPremium {
|
||||
expiryChan := make(chan int64)
|
||||
go GetExpiry(guild, expiryChan)
|
||||
currentExpiry := <- expiryChan
|
||||
|
||||
expiry = currentExpiry + length
|
||||
} else {
|
||||
current := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
expiry = current + length
|
||||
}
|
||||
|
||||
keysChan := make(chan []string)
|
||||
go GetKeysUsed(guild, keysChan)
|
||||
keys := <- keysChan
|
||||
keys = append(keys, key)
|
||||
keysStr := strings.Join(keys,",")
|
||||
|
||||
var node PremiumGuilds
|
||||
database.Database.Where(PremiumGuilds{Guild: guild}).Assign(PremiumGuilds{Expiry: expiry, User: userId, ActivatedBy: activatedBy, Keys: keysStr}).FirstOrCreate(&node)
|
||||
}
|
||||
|
||||
func GetExpiry(guild int64, ch chan int64) {
|
||||
var node PremiumGuilds
|
||||
database.Database.Where(PremiumGuilds{Guild: guild}).First(&node)
|
||||
ch <- node.Expiry
|
||||
}
|
||||
|
||||
func GetKeysUsed(guild int64, ch chan []string) {
|
||||
var node PremiumGuilds
|
||||
database.Database.Where(PremiumGuilds{Guild: guild}).First(&node)
|
||||
ch <- strings.Split(node.Keys, ",")
|
||||
}
|
@ -29,8 +29,14 @@ func GetOpenTickets(guild int64) []Ticket {
|
||||
return tickets
|
||||
}
|
||||
|
||||
func GetTicket(uuid string) Ticket {
|
||||
func GetTicket(uuid string, ch chan Ticket) {
|
||||
var ticket Ticket
|
||||
database.Database.Where(&Ticket{Uuid: uuid}).First(&ticket)
|
||||
return ticket
|
||||
ch <- ticket
|
||||
}
|
||||
|
||||
func GetTicketById(guild int64, id int, ch chan Ticket) {
|
||||
var ticket Ticket
|
||||
database.Database.Where(&Ticket{Guild: guild, TicketId: id}).First(&ticket)
|
||||
ch <- ticket
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package table
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/database"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
)
|
||||
|
||||
type UsernameNode struct {
|
||||
@ -16,10 +15,10 @@ func (UsernameNode) TableName() string {
|
||||
return "usernames"
|
||||
}
|
||||
|
||||
func GetUsername(id int64) string {
|
||||
func GetUsername(id int64, ch chan string) {
|
||||
node := UsernameNode{Name: "Unknown"}
|
||||
database.Database.Where(&UsernameNode{Id: id}).First(&node)
|
||||
return utils.Base64Decode(node.Name)
|
||||
ch <- node.Name
|
||||
}
|
||||
|
||||
func GetUserNodes(ids []int64) []UsernameNode {
|
||||
@ -30,6 +29,6 @@ func GetUserNodes(ids []int64) []UsernameNode {
|
||||
|
||||
func GetUserId(name, discrim string) int64 {
|
||||
var node UsernameNode
|
||||
database.Database.Where(&UsernameNode{Name: utils.Base64Encode(name), Discriminator: discrim}).First(&node)
|
||||
database.Database.Where(&UsernameNode{Name: name, Discriminator: discrim}).First(&node)
|
||||
return node.Id
|
||||
}
|
||||
|
22
database/table/votes.go
Normal file
22
database/table/votes.go
Normal file
@ -0,0 +1,22 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/database"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Votes struct {
|
||||
Id int64 `gorm:"type:bigint;unique_index;primary_key"`
|
||||
VoteTime time.Time
|
||||
}
|
||||
|
||||
func (Votes) TableName() string {
|
||||
return "votes"
|
||||
}
|
||||
|
||||
func HasVoted(owner int64, ch chan bool) {
|
||||
var node Votes
|
||||
database.Database.Where(Votes{Id: owner}).First(&node)
|
||||
|
||||
ch <- time.Now().Sub(node.VoteTime) < 24 * time.Hour
|
||||
}
|
@ -4,10 +4,15 @@
|
||||
@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')}
|
||||
|
||||
html {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.discord-container {
|
||||
background-color: #2e3136;
|
||||
border-radius: 25px;
|
||||
height: 100vh;
|
||||
border-radius: 4px;
|
||||
height: 80vh;
|
||||
max-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Whitney', sans-serif !important;
|
||||
@ -17,7 +22,7 @@
|
||||
background-color: #1e2124;
|
||||
height: 5vh;
|
||||
width: 100%;
|
||||
border-radius: 25px 25px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
position: relative;
|
||||
|
||||
text-align: center;
|
||||
@ -29,3 +34,40 @@
|
||||
color: white;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#message-container {
|
||||
height: 70vh;
|
||||
max-height: 70vh;
|
||||
position: relative;
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: white !important;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.message-input {
|
||||
padding-top: 20px !important;
|
||||
border-color: #2e3136 !important;
|
||||
padding-left: 5px !important;
|
||||
padding-right: 5px !important;
|
||||
background-color: #2e3136 !important;
|
||||
color: white !important;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #2e3136 !important;
|
||||
background-color: #2e3136 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.message-input:focus {
|
||||
border-color: #2e3136 !important;
|
||||
background-color: #2e3136 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
@ -62,9 +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://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
||||
|
||||
{{#include_mock}}
|
||||
<!-- Discord theme -->
|
||||
<link href="/assets/css/discordmock.css" rel="stylesheet"/>
|
||||
|
||||
{{/include_mock}}
|
||||
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
||||
|
@ -23,8 +23,7 @@
|
||||
<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>
|
||||
<td><a class="btn btn-primary btn-sm" role="button" href="/manage/{{guildId}}/tickets/view/{{uuid}}">View</a></td>
|
||||
</tr>
|
||||
{{/tickets}}
|
||||
</tbody>
|
||||
|
@ -3,21 +3,30 @@
|
||||
<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 id="message-container">
|
||||
{{#messages}}
|
||||
<div class="message">
|
||||
|
||||
<b>{{username}}</b>
|
||||
{{content}}
|
||||
</div>
|
||||
{{/messages}}
|
||||
</div>
|
||||
<div class="input-container">
|
||||
|
||||
<form action="javascript:sendMessage()">
|
||||
{{#premium}}
|
||||
<input type="text" class="form-control message-input" id="message" name="message"
|
||||
placeholder="Message #ticket-{{ticketId}}">
|
||||
{{/premium}}
|
||||
{{^premium}}
|
||||
<input type="text" class="form-control message-input" id="message" name="message"
|
||||
placeholder="Premium users get live messages and can respond through webchat" disabled>
|
||||
{{/premium}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -48,3 +57,52 @@
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Scroll to bottom
|
||||
container.scrollTop = container.scrollHeight;
|
||||
</script>
|
||||
|
||||
{{#premium}}
|
||||
<script>
|
||||
ws = new WebSocket("ws://localhost:3001/webchat");
|
||||
|
||||
ws.onopen = (evt) => {
|
||||
ws.send(JSON.stringify({
|
||||
"type": "auth",
|
||||
"data": {
|
||||
"guild": "{{guildId}}",
|
||||
"ticket": "{{ticketId}}"
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
ws.onmessage = (evt) => {
|
||||
let data = JSON.parse(evt.data);
|
||||
|
||||
let container = document.getElementById("message-container");
|
||||
|
||||
let element = document.createElement("div");
|
||||
element.className = "message";
|
||||
element.innerHTML = `
|
||||
<b>${data.username}</b>
|
||||
${data.content}
|
||||
`;
|
||||
|
||||
container.appendChild(element);
|
||||
|
||||
// Scroll to bottom
|
||||
container.scrollTop = container.scrollHeight;
|
||||
};
|
||||
|
||||
function sendMessage() {
|
||||
let msg = document.getElementById("message").value;
|
||||
document.getElementById("message").value = "";
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
"type": "send",
|
||||
"data": msg
|
||||
}))
|
||||
}
|
||||
</script>
|
||||
{{/premium}}
|
||||
|
18
utils/discord/endpoints/channel/CreateMessage.go
Normal file
18
utils/discord/endpoints/channel/CreateMessage.go
Normal file
@ -0,0 +1,18 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord"
|
||||
)
|
||||
|
||||
type CreateMessageBody struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func CreateMessage(id int) discord.Endpoint {
|
||||
return discord.Endpoint{
|
||||
RequestType: discord.POST,
|
||||
AuthorizationType: discord.BOT,
|
||||
Endpoint: fmt.Sprintf("/channels/%d/messages", id),
|
||||
}
|
||||
}
|
104
utils/premiumutils.go
Normal file
104
utils/premiumutils.go
Normal file
@ -0,0 +1,104 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/TicketsBot/GoPanel/database/table"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord/endpoints/guild"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/robfig/go-cache"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProxyResponse struct {
|
||||
Premium bool
|
||||
Tier int
|
||||
}
|
||||
|
||||
var premiumCache = cache.New(10 * time.Minute, 10 * time.Minute)
|
||||
|
||||
func IsPremiumGuild(store sessions.Session, guildIdRaw string, ch chan bool) {
|
||||
if premium, ok := premiumCache.Get(guildIdRaw); ok {
|
||||
ch<-premium.(bool)
|
||||
return
|
||||
}
|
||||
|
||||
guildId, err := strconv.ParseInt(guildIdRaw, 10, 64); if err != nil {
|
||||
ch<-false
|
||||
return
|
||||
}
|
||||
|
||||
// First lookup by premium key, then votes, then patreon
|
||||
keyLookup := make(chan bool)
|
||||
go table.IsPremium(guildId, keyLookup)
|
||||
|
||||
if <-keyLookup {
|
||||
if err := premiumCache.Add(guildIdRaw, true, 10 * time.Minute); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
ch<-true
|
||||
} else {
|
||||
// Get guild object
|
||||
var g objects.Guild
|
||||
endpoint := guild.GetGuild(int(guildId))
|
||||
go endpoint.Request(store, nil, nil, &g)
|
||||
|
||||
// Lookup votes
|
||||
ownerId, err := strconv.ParseInt(g.OwnerId, 10, 64); if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
ch <- false
|
||||
return
|
||||
}
|
||||
|
||||
hasVoted := make(chan bool)
|
||||
table.HasVoted(ownerId, hasVoted)
|
||||
if <-hasVoted {
|
||||
ch <- true
|
||||
|
||||
if err := premiumCache.Add(guildIdRaw, true, 10 * time.Minute); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup Patreon
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * 3,
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/ispremium?key=%s&id=%s", config.Conf.Bot.PremiumLookupProxyUrl, config.Conf.Bot.PremiumLookupProxyKey, g.OwnerId)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
|
||||
res, err := client.Do(req); if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
ch<-false
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(res.Body); if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
ch<-false
|
||||
return
|
||||
}
|
||||
|
||||
var proxyResponse ProxyResponse
|
||||
if err = json.Unmarshal(content, &proxyResponse); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
ch<-false
|
||||
return
|
||||
}
|
||||
|
||||
if err := premiumCache.Add(guildIdRaw, proxyResponse.Premium, 10 * time.Minute); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
ch <-proxyResponse.Premium
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user