This commit is contained in:
Dot-Rar 2019-10-20 14:11:54 +01:00
parent fcb7e51631
commit 21e1a854ac
20 changed files with 728 additions and 25 deletions

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

View File

@ -10,9 +10,13 @@ import (
"github.com/TicketsBot/GoPanel/utils/discord/objects" "github.com/TicketsBot/GoPanel/utils/discord/objects"
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"regexp"
"strconv" "strconv"
"strings"
) )
var MentionRegex, _ = regexp.Compile("<@(\\d+)>")
func TicketViewHandler(ctx *gin.Context) { func TicketViewHandler(ctx *gin.Context) {
store := sessions.Default(ctx) store := sessions.Default(ctx)
if store == nil { if store == nil {
@ -53,7 +57,9 @@ func TicketViewHandler(ctx *gin.Context) {
// Get ticket UUID from URL and verify it exists // Get ticket UUID from URL and verify it exists
uuid := ctx.Param("uuid") uuid := ctx.Param("uuid")
ticket := table.GetTicket(uuid) ticketChan := make(chan table.Ticket)
go table.GetTicket(uuid, ticketChan)
ticket := <-ticketChan
exists := ticket != table.Ticket{} exists := ticket != table.Ticket{}
// If invalid ticket UUID, take user to ticket list // If invalid ticket UUID, take user to ticket list
@ -77,12 +83,31 @@ func TicketViewHandler(ctx *gin.Context) {
// Format messages, exclude unneeded data // Format messages, exclude unneeded data
var messagesFormatted []map[string]interface{} var messagesFormatted []map[string]interface{}
for _, message := range utils.Reverse(messages) { 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{}{ messagesFormatted = append(messagesFormatted, map[string]interface{}{
"username": message.Author.Username, "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{}{ utils.Respond(ctx, template.TemplateTicketView.Render(map[string]interface{}{
"name": store.Get("name").(string), "name": store.Get("name").(string),
"guildId": guildIdStr, "guildId": guildIdStr,
@ -93,6 +118,8 @@ func TicketViewHandler(ctx *gin.Context) {
"error": errorMessage, "error": errorMessage,
"messages": messagesFormatted, "messages": messagesFormatted,
"ticketId": ticket.TicketId, "ticketId": ticket.TicketId,
"include_mock": true,
"premium": <-premium,
})) }))
} }
} }

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

View File

@ -66,7 +66,12 @@ func StartServer() {
router.GET("/manage/:id/tickets", manage.TicketListHandler) router.GET("/manage/:id/tickets", manage.TicketListHandler)
// /manage/:id/tickets/view/:uuid // /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 { if err := router.Run(config.Conf.Server.Host); err != nil {
panic(err) panic(err)

25
cache/redis.go vendored Normal file
View 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
View 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
View 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()
}
}

View File

@ -2,6 +2,7 @@ package main
import ( import (
"github.com/TicketsBot/GoPanel/app/http" "github.com/TicketsBot/GoPanel/app/http"
"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"
"math/rand" "math/rand"
@ -13,5 +14,9 @@ func main() {
config.LoadConfig() config.LoadConfig()
database.ConnectToDatabase() database.ConnectToDatabase()
cache.Client = cache.NewRedisClient()
go cache.Client.ListenForMessages()
http.StartServer() http.StartServer()
} }

View File

@ -23,8 +23,11 @@ threads=5
[bot] [bot]
token="" token=""
premium-lookup-proxy-url="http://localhost:3000"
premium-lookup-proxy-key=""
[redis] [redis]
host="127.0.0.1" host="127.0.0.1"
port=6379 port=6379
password="" password=""
threads=5

View File

@ -48,12 +48,15 @@ type (
Bot struct { Bot struct {
Token string Token string
PremiumLookupProxyUrl string `toml:"premium-lookup-proxy-url"`
PremiumLookupProxyKey string `toml:"premium-lookup-proxy-key"`
} }
Redis struct { Redis struct {
Host string Host string
Port int Port int
Password string Password string
Threads int
} }
) )

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

View File

@ -29,8 +29,14 @@ func GetOpenTickets(guild int64) []Ticket {
return tickets return tickets
} }
func GetTicket(uuid string) Ticket { func GetTicket(uuid string, ch chan Ticket) {
var ticket Ticket var ticket Ticket
database.Database.Where(&Ticket{Uuid: uuid}).First(&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
} }

View File

@ -2,7 +2,6 @@ package table
import ( import (
"github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/utils"
) )
type UsernameNode struct { type UsernameNode struct {
@ -16,10 +15,10 @@ func (UsernameNode) TableName() string {
return "usernames" return "usernames"
} }
func GetUsername(id int64) string { func GetUsername(id int64, ch chan 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 utils.Base64Decode(node.Name) ch <- node.Name
} }
func GetUserNodes(ids []int64) []UsernameNode { func GetUserNodes(ids []int64) []UsernameNode {
@ -30,6 +29,6 @@ func GetUserNodes(ids []int64) []UsernameNode {
func GetUserId(name, discrim string) int64 { func GetUserId(name, discrim string) int64 {
var node UsernameNode 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 return node.Id
} }

22
database/table/votes.go Normal file
View 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
}

View File

@ -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: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: 25px; border-radius: 4px;
height: 100vh; height: 80vh;
max-height: 100vh;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: 'Whitney', sans-serif !important; font-family: 'Whitney', sans-serif !important;
@ -17,7 +22,7 @@
background-color: #1e2124; background-color: #1e2124;
height: 5vh; height: 5vh;
width: 100%; width: 100%;
border-radius: 25px 25px 0 0; border-radius: 4px 4px 0 0;
position: relative; position: relative;
text-align: center; text-align: center;
@ -29,3 +34,40 @@
color: white; color: white;
padding-left: 20px; 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;
}

View File

@ -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://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>
{{#include_mock}}
<!-- Discord theme --> <!-- Discord theme -->
<link href="/assets/css/discordmock.css" rel="stylesheet"/> <link href="/assets/css/discordmock.css" rel="stylesheet"/>
{{/include_mock}}
<!-- 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">

View File

@ -23,8 +23,7 @@
<td>{{ticketId}}</td> <td>{{ticketId}}</td>
<td>{{username}}#{{discrim}}</td> <td>{{username}}#{{discrim}}</td>
<td>{{#members}}{{username}}#{{discrim}}{{sep}}{{/members}}</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><a class="btn btn-primary btn-sm" role="button" href="/manage/{{guildId}}/tickets/view/{{uuid}}">View</a></td>
<td>Coming soon</td>
</tr> </tr>
{{/tickets}} {{/tickets}}
</tbody> </tbody>

View File

@ -3,21 +3,30 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header">
<h4 class="card-title">Ticket List</h4>
</div>
<div class="card-body"> <div class="card-body">
<div class="discord-container"> <div class="discord-container">
<div class="channel-header"> <div class="channel-header">
<span class="channel-name">#ticket-{{ticketId}}</span> <span class="channel-name">#ticket-{{ticketId}}</span>
</div> </div>
<div class="message-container"> <div id="message-container">
{{#messages}}
<div class="message"> <div class="message">
<b>{{username}}</b>
{{content}}
</div> </div>
{{/messages}}
</div> </div>
<div class="input-container"> <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> </div>
</div> </div>
@ -48,3 +57,52 @@
</script> </script>
</div> </div>
</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}}

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