This commit is contained in:
Dot-Rar 2019-05-25 14:52:33 +01:00
parent 9c871edc17
commit 4822ba54a3
41 changed files with 1032 additions and 31 deletions

1
app/database/database.go Normal file
View File

@ -0,0 +1 @@
package database

View 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, &currentUser); 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, &currentUserGuilds); if err != nil {
ctx.String(500, err.Error())
}
}

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

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

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

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

View File

@ -0,0 +1,8 @@
package user
import "github.com/TicketsBot/GoPanel/utils/discord"
var CurrentUser = discord.Endpoint{
RequestType: discord.GET,
Endpoint: "/users/@me",
}

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

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

View File

@ -0,0 +1,8 @@
package objects
type Asset struct {
LargeImage string
LargeText string
SmallImage string
SmallText string
}

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

View File

@ -0,0 +1,7 @@
package objects
type ClientStatus struct {
Desktop string
Mobile string
Web string
}

View File

@ -0,0 +1,11 @@
package objects
type Emoji struct {
Id string
Name string
Roles []string
User User
RequireColons bool
Managed bool
Animated bool
}

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

View File

@ -0,0 +1,10 @@
package objects
type Member struct {
User User
Nick string
Roles []string
JoinedAt string
Deaf bool
Mute bool
}

View File

@ -0,0 +1,8 @@
package objects
type Overwrite struct {
Id string
Type string
Allow int
Deny int
}

View File

@ -0,0 +1,6 @@
package objects
type Party struct {
Id string
Size []int
}

View File

@ -0,0 +1,11 @@
package objects
type Presence struct {
User User
Roles []string
Game Activity
GuildId string
Status string
Activities []Activity
ClientStatus ClientStatus
}

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

View File

@ -0,0 +1,7 @@
package objects
type Secret struct {
Join string
Spectate string
Match string
}

View File

@ -0,0 +1,6 @@
package objects
type Timestamp struct {
Start int
End int
}

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

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