latest
This commit is contained in:
parent
9c871edc17
commit
4822ba54a3
1
app/database/database.go
Normal file
1
app/database/database.go
Normal file
@ -0,0 +1 @@
|
||||
package database
|
67
app/http/endpoints/callback.go
Normal file
67
app/http/endpoints/callback.go
Normal file
@ -0,0 +1,67 @@
|
||||
package endpoints
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/utils/discord"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord/endpoints/user"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord/objects"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"time"
|
||||
)
|
||||
|
||||
type(
|
||||
TokenData struct {
|
||||
ClientId string `qs:"client_id"`
|
||||
ClientSecret string `qs:"client_secret"`
|
||||
GrantType string `qs:"grant_type"`
|
||||
Code string `qs:"code"`
|
||||
RedirectUri string `qs:"redirect_uri"`
|
||||
Scope string `qs:"scope"`
|
||||
}
|
||||
|
||||
TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
)
|
||||
|
||||
func CallbackHandler(ctx *gin.Context) {
|
||||
store := sessions.Default(ctx)
|
||||
if store == nil {
|
||||
return
|
||||
}
|
||||
defer store.Save()
|
||||
|
||||
code := ctx.DefaultQuery("code", "")
|
||||
if code == "" {
|
||||
ctx.String(400, "Discord provided an invalid Oauth2 code")
|
||||
return
|
||||
}
|
||||
|
||||
res, err := discord.AccessToken(code); if err != nil {
|
||||
ctx.String(500, err.Error())
|
||||
}
|
||||
|
||||
store.Set("access_token", res.AccessToken)
|
||||
store.Set("refresh_token", res.RefreshToken)
|
||||
store.Set("expiry", (time.Now().UnixNano() / int64(time.Second)) + int64(res.ExpiresIn))
|
||||
|
||||
// Get ID + name
|
||||
var currentUser objects.User
|
||||
err = user.CurrentUser.Request(store, nil, nil, ¤tUser); if err != nil {
|
||||
ctx.String(500, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
store.Set("userid", currentUser.Id)
|
||||
store.Set("name", currentUser.Username)
|
||||
|
||||
// Get Guilds
|
||||
var currentUserGuilds []objects.Guild
|
||||
err = user.CurrentUserGuilds.Request(store, nil, nil, ¤tUserGuilds); if err != nil {
|
||||
ctx.String(500, err.Error())
|
||||
}
|
||||
}
|
22
app/http/endpoints/index.go
Normal file
22
app/http/endpoints/index.go
Normal file
@ -0,0 +1,22 @@
|
||||
package endpoints
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func IndexHandler(ctx *gin.Context) {
|
||||
store := sessions.Default(ctx)
|
||||
if store == nil {
|
||||
return
|
||||
}
|
||||
defer store.Save()
|
||||
|
||||
if utils.IsLoggedIn(store) {
|
||||
|
||||
} else {
|
||||
ctx.Redirect(302, "/login")
|
||||
}
|
||||
}
|
||||
|
20
app/http/endpoints/login.go
Normal file
20
app/http/endpoints/login.go
Normal file
@ -0,0 +1,20 @@
|
||||
package endpoints
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func LoginHandler(ctx *gin.Context) {
|
||||
store := sessions.Default(ctx)
|
||||
if store == nil {
|
||||
return
|
||||
}
|
||||
defer store.Save()
|
||||
|
||||
redirect := url.QueryEscape(fmt.Sprintf("%s/callback", config.Conf.Server.BaseUrl))
|
||||
ctx.Redirect(302, fmt.Sprintf("https://discordapp.com/oauth2/authorize?response_type=code&redirect_uri=%s&scope=identify+guilds&client_id=%d", redirect, config.Conf.Oauth.Id))
|
||||
}
|
25
app/http/layouts.go
Normal file
25
app/http/layouts.go
Normal file
@ -0,0 +1,25 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/app/http/template"
|
||||
)
|
||||
|
||||
type Layout string
|
||||
|
||||
const(
|
||||
Main Layout = "main"
|
||||
)
|
||||
|
||||
var(
|
||||
layouts = map[string]template.Layout{
|
||||
Main.ToString(): template.LoadLayout(Main.ToString()),
|
||||
}
|
||||
)
|
||||
|
||||
func (l Layout) ToString() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
func (l Layout) GetInstance() template.Layout {
|
||||
return layouts[l.ToString()]
|
||||
}
|
50
app/http/server.go
Normal file
50
app/http/server.go
Normal file
@ -0,0 +1,50 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/app/http/endpoints"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
log.Println("Starting HTTP server")
|
||||
|
||||
router := gin.Default()
|
||||
|
||||
// Sessions
|
||||
store, err := sessions.NewRedisStore(
|
||||
config.Conf.Server.Session.Threads,
|
||||
"tcp", fmt.Sprintf("%s:%d", config.Conf.Redis.Host, config.Conf.Redis.Port),
|
||||
config.Conf.Redis.Password,
|
||||
[]byte(config.Conf.Server.Session.Secret))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
router.Use(sessions.Sessions("panel", store))
|
||||
|
||||
// Handle static asset requests
|
||||
router.Use(static.Serve("/assets/", static.LocalFile("./public/static", false)))
|
||||
|
||||
// Root
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
endpoints.IndexHandler(c)
|
||||
})
|
||||
|
||||
// /login
|
||||
router.GET("/login", func(c *gin.Context) {
|
||||
endpoints.LoginHandler(c)
|
||||
})
|
||||
|
||||
// /callback
|
||||
router.GET("/callback", func(c *gin.Context) {
|
||||
endpoints.CallbackHandler(c)
|
||||
})
|
||||
|
||||
if err := router.Run(config.Conf.Server.Host); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
22
app/http/template/layout.go
Normal file
22
app/http/template/layout.go
Normal file
@ -0,0 +1,22 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
)
|
||||
|
||||
type Layout struct {
|
||||
Name string
|
||||
Content string
|
||||
}
|
||||
|
||||
func LoadLayout(name string) Layout {
|
||||
content, err := utils.ReadFile(fmt.Sprintf("./public/templates/layouts/%s.mustache", name)); if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Layout{
|
||||
Name: name,
|
||||
Content: content,
|
||||
}
|
||||
}
|
27
app/http/template/template.go
Normal file
27
app/http/template/template.go
Normal file
@ -0,0 +1,27 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/utils"
|
||||
"github.com/hoisie/mustache"
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
Layout Layout
|
||||
Content string
|
||||
}
|
||||
|
||||
func LoadTemplate(layout Layout, name string) Template {
|
||||
content, err := utils.ReadFile(fmt.Sprintf("./public/templates/views/%s.mustache", name)); if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Template{
|
||||
Layout: layout,
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Template) Render(context ...interface{}) string {
|
||||
return mustache.RenderInLayout(t.Content, t.Layout.Content, context)
|
||||
}
|
28
app/http/templates.go
Normal file
28
app/http/templates.go
Normal file
@ -0,0 +1,28 @@
|
||||
package http
|
||||
|
||||
import "github.com/TicketsBot/GoPanel/app/http/template"
|
||||
|
||||
type Template string
|
||||
|
||||
const(
|
||||
Index Template = "index"
|
||||
)
|
||||
|
||||
var(
|
||||
templates = map[string]template.Template{
|
||||
Index.ToString(): template.LoadTemplate(Main.GetInstance(), Index.ToString()),
|
||||
}
|
||||
)
|
||||
|
||||
func (t Template) ToString() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func (t Template) GetInstance() template.Template {
|
||||
return templates[t.ToString()]
|
||||
}
|
||||
|
||||
func (t Template) Render(context ...interface{}) string {
|
||||
temp := t.GetInstance()
|
||||
return temp.Render(context)
|
||||
}
|
@ -1,7 +1,17 @@
|
||||
package main
|
||||
|
||||
import "github.com/TicketsBot/PanelV2/config"
|
||||
import (
|
||||
"github.com/TicketsBot/GoPanel/app/http"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/TicketsBot/GoPanel/database"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano() % 3497)
|
||||
|
||||
config.LoadConfig()
|
||||
database.ConnectToDatabase()
|
||||
http.StartServer()
|
||||
}
|
||||
|
@ -6,22 +6,22 @@ mainSite="https://ticketsbot.net"
|
||||
window=10
|
||||
max=600
|
||||
[server.session]
|
||||
database="session"
|
||||
threads=10
|
||||
secret="secret"
|
||||
|
||||
[oauth]
|
||||
id=1
|
||||
secret="secret"
|
||||
id=
|
||||
secret=""
|
||||
redirectUri=""
|
||||
|
||||
[mariadb]
|
||||
host="127.0.0.1"
|
||||
username="root"
|
||||
password="root"
|
||||
username="ryan"
|
||||
password="ryan"
|
||||
database="tickets"
|
||||
threads=5
|
||||
|
||||
[bot]
|
||||
key=""
|
||||
httpServers=[]
|
||||
|
||||
[redis]
|
||||
uri="redis://:pwd@127.0.0.1:6379/0"
|
||||
host="127.0.0.1"
|
||||
port=6379
|
||||
password=""
|
||||
|
@ -9,6 +9,7 @@ type(
|
||||
Config struct {
|
||||
Server Server
|
||||
Oauth Oauth
|
||||
MariaDB MariaDB
|
||||
Bot Bot
|
||||
Redis Redis
|
||||
}
|
||||
@ -27,12 +28,14 @@ type(
|
||||
}
|
||||
|
||||
Session struct {
|
||||
Database string
|
||||
Threads int
|
||||
Secret string
|
||||
}
|
||||
|
||||
Oauth struct {
|
||||
Id int64
|
||||
Secret string
|
||||
RedirectUri string
|
||||
}
|
||||
|
||||
MariaDB struct {
|
||||
@ -49,7 +52,9 @@ type(
|
||||
}
|
||||
|
||||
Redis struct {
|
||||
Uri string
|
||||
Host string
|
||||
Port int
|
||||
Password string
|
||||
}
|
||||
)
|
||||
|
||||
|
31
database/database.go
Normal file
31
database/database.go
Normal file
@ -0,0 +1,31 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
var(
|
||||
database **gorm.DB
|
||||
)
|
||||
|
||||
func ConnectToDatabase() {
|
||||
uri := fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
config.Conf.MariaDB.Username,
|
||||
config.Conf.MariaDB.Password,
|
||||
config.Conf.MariaDB.Host,
|
||||
config.Conf.MariaDB.Database,
|
||||
)
|
||||
|
||||
db, err := gorm.Open("mysql", uri); if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
db.DB().SetMaxOpenConns(config.Conf.MariaDB.Threads)
|
||||
db.DB().SetMaxIdleConns(0)
|
||||
|
||||
database = &db
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/TicketsBot/PanelV2/config"
|
||||
"github.com/qiangxue/fasthttp-routing"
|
||||
"github.com/valyala/fasthttp"
|
||||
"log"
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
log.Println("Starting HTTP server")
|
||||
|
||||
router := routing.New()
|
||||
|
||||
err := fasthttp.ListenAndServe(config.Conf.Server.Host, router.HandleRequest); if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
42
public/templates/layouts/main.mustache
Normal file
42
public/templates/layouts/main.mustache
Normal file
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tickets | A Discord Support Manager Bot</title>
|
||||
|
||||
<!-- Meta -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Management panel for the Discord Tickets bot">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans',sans-serif;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Materialize -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div class="nav-wrapper grey darken-4">
|
||||
<ul id="nav-mobile" class="left hide-on-med-and-down">
|
||||
<li><a href="{{mainsite}}">Main Site</a></li>
|
||||
<li><a href="/">Servers</a></li>
|
||||
</ul>
|
||||
<ul id="nav-mobile" class="right hide-on-med-and-down">
|
||||
<li><a href="/logout">Logout</a></li>
|
||||
<li><a href="#">Hi, {{name}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{{{body}}}
|
||||
</body>
|
||||
</html>
|
23
public/templates/views/index.mustache
Normal file
23
public/templates/views/index.mustache
Normal file
@ -0,0 +1,23 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
{{#if isNotAdmin}}
|
||||
<p class="center-align" style="padding-top: 50px; font-size: 16px">
|
||||
You are not the admin of any guilds that the bot is in. Click below to invite the bot:
|
||||
<br />
|
||||
<a href="https://invite.ticketsbot.net">Invite</a>
|
||||
</p>
|
||||
{{else}}
|
||||
<p class="center-align" style="padding-top: 50px; font-size: 16px">
|
||||
Select a server to manage below
|
||||
</p>
|
||||
|
||||
<ul class="collection">
|
||||
{{#each guilds}}
|
||||
<li class="collection-item"><a href="{{baseUrl}}/manage/{{id}}/settings">{{name}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
93
public/templates/views/logs.mustache
Normal file
93
public/templates/views/logs.mustache
Normal file
@ -0,0 +1,93 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<ul class="pagination center-align" style="padding-top: 50px">
|
||||
<li class="waves-effect"><a href="#"><i class="material-icons">chevron_left</i></a></li>
|
||||
<li class="waves-effect"><a href="/manage/{{guildId}}/settings">Settings</a></li>
|
||||
<li class="active indigo darken-1"><a href="#">Logs</a></li>
|
||||
<li class="disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<ul class="collapsible">
|
||||
<li>
|
||||
<div class="collapsible-header"><i class="material-icons">search</i>Filter Logs</div>
|
||||
<div class="collapsible-body">
|
||||
<div class="container" style="width: 100%">
|
||||
<div class="row">
|
||||
<form class="col s12" action="/manage/{{guildId}}/logs/page/1" method="get">
|
||||
<div class="row">
|
||||
<div class="input-field col s6">
|
||||
<input placeholder="Ticket ID" name="ticketid" id="ticketid" type="text" class="validate">
|
||||
<label for="ticketid">Ticket ID</label>
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
<input placeholder="Username" name="username" id="username" type="text" class="validate">
|
||||
<label for="username">Username</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center-align">
|
||||
<div class="input-field col s6 center-align">
|
||||
<input placeholder="User ID" name="userid" id="userid" type="text" class="validate">
|
||||
<label for="userid">User ID</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center-align">
|
||||
<button class="btn waves-effect waves-light center-align indigo darken-1" type="submit" name="action">Search
|
||||
<i class="material-icons right">search</i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<table class="striped centered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ticket ID</th>
|
||||
<th>User</th>
|
||||
<th>User ID</th>
|
||||
<th>Log URL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each logs}}
|
||||
{{{baseUrl}}}
|
||||
<tr>
|
||||
<td>{{TICKETID}}</td>
|
||||
<td>{{USERNAME}}</td>
|
||||
<td>{{USERID}}</td>
|
||||
<td><a href="{{baseUrl}}/logs/{{UUID}}">{{UUID}}</a></td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row center-align">
|
||||
<div class="col s12 center-align">
|
||||
<p class="center-align">Pages</p>
|
||||
<ul class="pagination center-align">
|
||||
{{#if isPageOne}}
|
||||
<li class="disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>
|
||||
{{else}}
|
||||
<li class="waves-effect"><a href="/manage/{{guildId}}/logs/page/{{previousPage}}"><i class="material-icons">chevron_left</i></a></li>
|
||||
{{/if}}
|
||||
<li class="waves-effect"><a href="/manage/{{guildId}}/logs/page/{{nextPage}}"><i class="material-icons">chevron_right</i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
M.AutoInit();
|
||||
</script>
|
84
public/templates/views/settings.mustache
Normal file
84
public/templates/views/settings.mustache
Normal file
@ -0,0 +1,84 @@
|
||||
<script>
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var elems = document.querySelectorAll('select');
|
||||
var instances = M.FormSelect.init(elems, {});
|
||||
});
|
||||
|
||||
{{#if invalidPrefix}}
|
||||
M.toast({html: 'Prefixes must be between 1 and 8 characters'})
|
||||
{{/if}}
|
||||
{{#if invalidMessage}}
|
||||
M.toast({html: 'Welcome messages must be between 1 and 1000 characters'})
|
||||
{{/if}}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<ul class="pagination center-align" style="padding-top: 50px">
|
||||
<li class="disabled"><a href="#!"><i class="material-icons">chevron_left</i></a></li>
|
||||
<li class="active indigo darken-1"><a href="#!">Settings</a></li>
|
||||
<li class="waves-effect"><a href="/manage/{{guildId}}/logs/page/1">Logs</a></li>
|
||||
<li class="waves-effect"><a href="/manage/{{guildId}}/logs/page/1"><i class="material-icons">chevron_right</i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<form class="col s12" action="/manage/{{guildId}}/settings" method="get">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<p><b>Prefix</b></p>
|
||||
<input id="prefix" name="prefix" type="text" class="validate" value="{{prefix}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<p><b>Welcome Message</b></p>
|
||||
<textarea id="welcomeMessage" name="welcomeMessage" class="materialize-textarea">{{welcomeMessage}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<p><b>Ticket Limit</b></p>
|
||||
<p class="range-field">
|
||||
<input type="range" id="ticketlimit" name="ticketlimit" min="1" max="10" value="{{ticketLimit}}" />
|
||||
</p>
|
||||
<p id="output">Limit: -1</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<p><b>Channel Category</b></p>
|
||||
<select name="category">
|
||||
{{#each categories}}
|
||||
{{#if active}}
|
||||
<option value="{{id}}" selected>{{name}}</option>
|
||||
{{else}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col s12 center-align">
|
||||
<button class="btn waves-effect waves-light indigo darken-1" type="submit" name="action">Save
|
||||
<i class="material-icons right">send</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var slider = document.getElementById("ticketlimit");
|
||||
var output = document.getElementById("output");
|
||||
output.innerHTML = "Limit: " + slider.value;
|
||||
slider.oninput = () => {
|
||||
output.innerHTML = "Limit: " + slider.value;
|
||||
}
|
||||
</script>
|
113
utils/discord/auth.go
Normal file
113
utils/discord/auth.go
Normal file
@ -0,0 +1,113 @@
|
||||
package discord
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/TicketsBot/GoPanel/config"
|
||||
"github.com/pasztorpisti/qs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type(
|
||||
TokenData struct {
|
||||
ClientId string `qs:"client_id"`
|
||||
ClientSecret string `qs:"client_secret"`
|
||||
GrantType string `qs:"grant_type"`
|
||||
Code string `qs:"code"`
|
||||
RedirectUri string `qs:"redirect_uri"`
|
||||
Scope string `qs:"scope"`
|
||||
}
|
||||
|
||||
RefreshData struct {
|
||||
ClientId string `qs:"client_id"`
|
||||
ClientSecret string `qs:"client_secret"`
|
||||
GrantType string `qs:"grant_type"`
|
||||
RefreshToken string `qs:"refresh_token"`
|
||||
RedirectUri string `qs:"redirect_uri"`
|
||||
Scope string `qs:"scope"`
|
||||
}
|
||||
|
||||
TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
)
|
||||
|
||||
const TokenEndpoint = "https://discordapp.com/api/oauth2/token"
|
||||
|
||||
func AccessToken(code string) (TokenResponse, error) {
|
||||
data := TokenData{
|
||||
ClientId: strconv.Itoa(int(config.Conf.Oauth.Id)),
|
||||
ClientSecret: config.Conf.Oauth.Secret,
|
||||
GrantType: "authorization_code",
|
||||
Code: code,
|
||||
RedirectUri: config.Conf.Oauth.RedirectUri,
|
||||
Scope: "identify guilds",
|
||||
}
|
||||
|
||||
res, err := tokenPost(data); if err != nil {
|
||||
return TokenResponse{}, err
|
||||
}
|
||||
|
||||
var unmarshalled TokenResponse
|
||||
if err = json.Unmarshal(res, &unmarshalled); err != nil {
|
||||
return TokenResponse{}, err
|
||||
}
|
||||
|
||||
return unmarshalled, nil
|
||||
}
|
||||
|
||||
func RefreshToken(refreshToken string) (TokenResponse, error) {
|
||||
data := RefreshData{
|
||||
ClientId: strconv.Itoa(int(config.Conf.Oauth.Id)),
|
||||
ClientSecret: config.Conf.Oauth.Secret,
|
||||
GrantType: "refresh_token",
|
||||
RefreshToken: refreshToken,
|
||||
RedirectUri: config.Conf.Oauth.RedirectUri,
|
||||
Scope: "identify guilds",
|
||||
}
|
||||
|
||||
res, err := tokenPost(data); if err != nil {
|
||||
return TokenResponse{}, err
|
||||
}
|
||||
|
||||
var unmarshalled TokenResponse
|
||||
if err = json.Unmarshal(res, &unmarshalled); err != nil {
|
||||
return TokenResponse{}, err
|
||||
}
|
||||
|
||||
return unmarshalled, nil
|
||||
}
|
||||
|
||||
func tokenPost(body ...interface{}) ([]byte, error) {
|
||||
str, err := qs.Marshal(body[0]); if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoded := []byte(str)
|
||||
|
||||
req, err := http.NewRequest("POST", TokenEndpoint, bytes.NewBuffer([]byte(encoded))); if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", string(ApplicationFormUrlEncoded))
|
||||
|
||||
client := &http.Client{}
|
||||
client.Timeout = 3 * time.Second
|
||||
|
||||
res, err := client.Do(req); if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
content, err := ioutil.ReadAll(res.Body); if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
104
utils/discord/endpoints.go
Normal file
104
utils/discord/endpoints.go
Normal file
@ -0,0 +1,104 @@
|
||||
package discord
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
"github.com/pasztorpisti/qs"
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RequestType string
|
||||
type ContentType string
|
||||
|
||||
const(
|
||||
GET RequestType = "GET"
|
||||
POST RequestType = "POST"
|
||||
PATCH RequestType = "PATCH"
|
||||
|
||||
ApplicationJson ContentType = "application/json"
|
||||
ApplicationFormUrlEncoded ContentType = "application/x-www-form-urlencoded"
|
||||
|
||||
BASE_URL = "https://discordapp.com/api/v6"
|
||||
)
|
||||
|
||||
type Endpoint struct {
|
||||
RequestType RequestType
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
func (e *Endpoint) Request(store sessions.Session, contentType *ContentType, body interface{}, response interface{}) error {
|
||||
url := BASE_URL + e.Endpoint
|
||||
|
||||
// Create req
|
||||
var req *http.Request
|
||||
var err error
|
||||
if body == nil || contentType == nil {
|
||||
req, err = http.NewRequest(string(e.RequestType), url, nil)
|
||||
} else {
|
||||
// Encode body
|
||||
var encoded []byte
|
||||
if *contentType == ApplicationJson {
|
||||
raw, err := json.Marshal(body); if err != nil {
|
||||
return err
|
||||
}
|
||||
encoded = raw
|
||||
} else if *contentType == ApplicationFormUrlEncoded {
|
||||
str, err := qs.Marshal(body); if err != nil {
|
||||
return err
|
||||
}
|
||||
encoded = []byte(str)
|
||||
}
|
||||
|
||||
// Create req
|
||||
req, err = http.NewRequest(string(e.RequestType), url, bytes.NewBuffer(encoded))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set content type and user agent
|
||||
if contentType != nil {
|
||||
req.Header.Set("Content-Type", string(*contentType))
|
||||
}
|
||||
req.Header.Set("User-Agent", "DiscordBot (https://github.com/TicketsBot/GoPanel 1.0.0)")
|
||||
|
||||
// Auth
|
||||
accessToken := store.Get("access_token").(string)
|
||||
expiry := store.Get("expiry").(int64)
|
||||
refreshToken := store.Get("refresh_token").(string)
|
||||
|
||||
// Check if needs refresh
|
||||
if (time.Now().UnixNano() / int64(time.Second)) > int64(expiry) {
|
||||
res, err := RefreshToken(refreshToken); if err != nil {
|
||||
store.Clear()
|
||||
_ = store.Save()
|
||||
return errors.New("Please login again!")
|
||||
}
|
||||
|
||||
store.Set("access_token", res.AccessToken)
|
||||
store.Set("expiry", (time.Now().UnixNano() / int64(time.Second)) + int64(res.ExpiresIn))
|
||||
store.Set("refresh_token", res.RefreshToken)
|
||||
|
||||
accessToken = res.AccessToken
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer " + accessToken)
|
||||
|
||||
client := &http.Client{}
|
||||
client.Timeout = 3 * time.Second
|
||||
|
||||
res, err := client.Do(req); if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(res.Body); if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(content, response)
|
||||
}
|
13
utils/discord/endpoints/guild/getGuild.go
Normal file
13
utils/discord/endpoints/guild/getGuild.go
Normal file
@ -0,0 +1,13 @@
|
||||
package guild
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TicketsBot/GoPanel/utils/discord"
|
||||
)
|
||||
|
||||
func GetGuild(id int) discord.Endpoint {
|
||||
return discord.Endpoint{
|
||||
RequestType: discord.GET,
|
||||
Endpoint: fmt.Sprintf("/guilds/%d", id),
|
||||
}
|
||||
}
|
8
utils/discord/endpoints/user/currentUser.go
Normal file
8
utils/discord/endpoints/user/currentUser.go
Normal file
@ -0,0 +1,8 @@
|
||||
package user
|
||||
|
||||
import "github.com/TicketsBot/GoPanel/utils/discord"
|
||||
|
||||
var CurrentUser = discord.Endpoint{
|
||||
RequestType: discord.GET,
|
||||
Endpoint: "/users/@me",
|
||||
}
|
8
utils/discord/endpoints/user/currentUserGuilds.go
Normal file
8
utils/discord/endpoints/user/currentUserGuilds.go
Normal file
@ -0,0 +1,8 @@
|
||||
package user
|
||||
|
||||
import "github.com/TicketsBot/GoPanel/utils/discord"
|
||||
|
||||
var CurrentUserGuilds = discord.Endpoint{
|
||||
RequestType: discord.GET,
|
||||
Endpoint: "/users/@me/guilds",
|
||||
}
|
16
utils/discord/objects/activity.go
Normal file
16
utils/discord/objects/activity.go
Normal file
@ -0,0 +1,16 @@
|
||||
package objects
|
||||
|
||||
type Activity struct {
|
||||
Name string
|
||||
Type int
|
||||
Url string
|
||||
Timestamps Timestamp
|
||||
ApplicationId string
|
||||
Details string
|
||||
State string
|
||||
Party Party
|
||||
Assets Asset
|
||||
Secrets Secret
|
||||
Instance bool
|
||||
Flags int
|
||||
}
|
8
utils/discord/objects/asset.go
Normal file
8
utils/discord/objects/asset.go
Normal file
@ -0,0 +1,8 @@
|
||||
package objects
|
||||
|
||||
type Asset struct {
|
||||
LargeImage string
|
||||
LargeText string
|
||||
SmallImage string
|
||||
SmallText string
|
||||
}
|
22
utils/discord/objects/channel.go
Normal file
22
utils/discord/objects/channel.go
Normal file
@ -0,0 +1,22 @@
|
||||
package objects
|
||||
|
||||
type Channel struct {
|
||||
Id string
|
||||
Type int
|
||||
GuildId string
|
||||
Position int
|
||||
PermissionsOverwrites []Overwrite
|
||||
Name string
|
||||
Topic string
|
||||
Nsfw bool
|
||||
LastMessageId string
|
||||
Bitrate int
|
||||
userLimit int
|
||||
RateLimitPerUser int
|
||||
Recipients []User
|
||||
Icon string
|
||||
Ownerid string
|
||||
ApplicationId string
|
||||
ParentId string
|
||||
LastPinTimestamp string
|
||||
}
|
7
utils/discord/objects/clientstatus.go
Normal file
7
utils/discord/objects/clientstatus.go
Normal file
@ -0,0 +1,7 @@
|
||||
package objects
|
||||
|
||||
type ClientStatus struct {
|
||||
Desktop string
|
||||
Mobile string
|
||||
Web string
|
||||
}
|
11
utils/discord/objects/emoji.go
Normal file
11
utils/discord/objects/emoji.go
Normal file
@ -0,0 +1,11 @@
|
||||
package objects
|
||||
|
||||
type Emoji struct {
|
||||
Id string
|
||||
Name string
|
||||
Roles []string
|
||||
User User
|
||||
RequireColons bool
|
||||
Managed bool
|
||||
Animated bool
|
||||
}
|
40
utils/discord/objects/guild.go
Normal file
40
utils/discord/objects/guild.go
Normal file
@ -0,0 +1,40 @@
|
||||
package objects
|
||||
|
||||
type Guild struct {
|
||||
Id string
|
||||
Name string
|
||||
Icon string
|
||||
Splash string
|
||||
Owner bool
|
||||
OwnerId string
|
||||
Permissions int
|
||||
Region string
|
||||
AfkChannelid string
|
||||
AfkTimeout int
|
||||
EmbedEnabled bool
|
||||
EmbedChannelId string
|
||||
VerificationLevel int
|
||||
DefaultMessageNotifications int
|
||||
ExplicitContentFilter int
|
||||
Roles []Role
|
||||
Emojis []Emoji
|
||||
Features []string
|
||||
MfaLevel int
|
||||
ApplicationId string
|
||||
WidgetEnabled bool
|
||||
WidgetChannelId string
|
||||
SystemChannelId string
|
||||
JoinedAt string
|
||||
Large bool
|
||||
Unavailable bool
|
||||
MemberCount int
|
||||
VoiceStates []VoiceState
|
||||
Members []Member
|
||||
Channels []Channel
|
||||
Presences []Presence
|
||||
MaxPresences int
|
||||
Maxmembers int
|
||||
VanityUrlCode string
|
||||
Description string
|
||||
Banner string
|
||||
}
|
10
utils/discord/objects/member.go
Normal file
10
utils/discord/objects/member.go
Normal file
@ -0,0 +1,10 @@
|
||||
package objects
|
||||
|
||||
type Member struct {
|
||||
User User
|
||||
Nick string
|
||||
Roles []string
|
||||
JoinedAt string
|
||||
Deaf bool
|
||||
Mute bool
|
||||
}
|
8
utils/discord/objects/overwrite.go
Normal file
8
utils/discord/objects/overwrite.go
Normal file
@ -0,0 +1,8 @@
|
||||
package objects
|
||||
|
||||
type Overwrite struct {
|
||||
Id string
|
||||
Type string
|
||||
Allow int
|
||||
Deny int
|
||||
}
|
6
utils/discord/objects/party.go
Normal file
6
utils/discord/objects/party.go
Normal file
@ -0,0 +1,6 @@
|
||||
package objects
|
||||
|
||||
type Party struct {
|
||||
Id string
|
||||
Size []int
|
||||
}
|
11
utils/discord/objects/presence.go
Normal file
11
utils/discord/objects/presence.go
Normal file
@ -0,0 +1,11 @@
|
||||
package objects
|
||||
|
||||
type Presence struct {
|
||||
User User
|
||||
Roles []string
|
||||
Game Activity
|
||||
GuildId string
|
||||
Status string
|
||||
Activities []Activity
|
||||
ClientStatus ClientStatus
|
||||
}
|
12
utils/discord/objects/role.go
Normal file
12
utils/discord/objects/role.go
Normal file
@ -0,0 +1,12 @@
|
||||
package objects
|
||||
|
||||
type Role struct {
|
||||
Id string
|
||||
Name string
|
||||
Color int
|
||||
Hoist bool
|
||||
Position int
|
||||
Permissions int
|
||||
Managed bool
|
||||
Mentionable bool
|
||||
}
|
7
utils/discord/objects/secret.go
Normal file
7
utils/discord/objects/secret.go
Normal file
@ -0,0 +1,7 @@
|
||||
package objects
|
||||
|
||||
type Secret struct {
|
||||
Join string
|
||||
Spectate string
|
||||
Match string
|
||||
}
|
6
utils/discord/objects/timestamp.go
Normal file
6
utils/discord/objects/timestamp.go
Normal file
@ -0,0 +1,6 @@
|
||||
package objects
|
||||
|
||||
type Timestamp struct {
|
||||
Start int
|
||||
End int
|
||||
}
|
12
utils/discord/objects/user.go
Normal file
12
utils/discord/objects/user.go
Normal file
@ -0,0 +1,12 @@
|
||||
package objects
|
||||
|
||||
type User struct {
|
||||
Id string
|
||||
Username string
|
||||
Discriminator string
|
||||
Avatar string
|
||||
Verified bool
|
||||
Email string
|
||||
Flags int
|
||||
PremiumType int
|
||||
}
|
14
utils/discord/objects/voicestate.go
Normal file
14
utils/discord/objects/voicestate.go
Normal file
@ -0,0 +1,14 @@
|
||||
package objects
|
||||
|
||||
type VoiceState struct {
|
||||
GuildId string
|
||||
ChannelId string
|
||||
UserId string
|
||||
Member Member
|
||||
SessionId string
|
||||
Deaf bool
|
||||
Mute bool
|
||||
SelfDeaf bool
|
||||
SelfMute bool
|
||||
Suppress bool
|
||||
}
|
11
utils/fileutils.go
Normal file
11
utils/fileutils.go
Normal file
@ -0,0 +1,11 @@
|
||||
package utils
|
||||
|
||||
import "io/ioutil"
|
||||
|
||||
func ReadFile(path string) (string, error) {
|
||||
content, err := ioutil.ReadFile(path); if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
9
utils/sessionutils.go
Normal file
9
utils/sessionutils.go
Normal file
@ -0,0 +1,9 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/contrib/sessions"
|
||||
)
|
||||
|
||||
func IsLoggedIn(store sessions.Session) bool {
|
||||
return store.Get("access_token") != nil && store.Get("expiry") != nil && store.Get("refresh_token") != nil && store.Get("userid") != nil && store.Get("name") != nil
|
||||
}
|
13
utils/stringutils.go
Normal file
13
utils/stringutils.go
Normal file
@ -0,0 +1,13 @@
|
||||
package utils
|
||||
|
||||
import "math/rand"
|
||||
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
func RandStringRunes(length int) string {
|
||||
b := make([]rune, length)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user