94 Commits

Author SHA1 Message Date
5b8b01e31a Say hi again
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-01-14 18:46:37 -05:00
03bcca77f4 Say hi
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-14 18:34:01 -05:00
961f2f32b0 Add some CSS to status page 2021-01-14 17:00:26 -05:00
3ddd84ae16 Add Status page. 2021-01-14 16:40:18 -05:00
86c95bb012 Add Config API Endpoint
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-13 19:22:50 -05:00
41e39eb118 Remove more React 2021-01-13 19:22:39 -05:00
22ccd9fd84 Remove more React 2021-01-13 19:22:04 -05:00
efb6676c0a Can't update a constant
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-07 12:39:18 -05:00
33a3270a49 Can't update a constant 2021-01-07 12:37:56 -05:00
aeb8b4fb52 Issues with local files 2021-01-07 12:36:03 -05:00
988acb4a90 Update to use static site 2021-01-07 12:30:38 -05:00
950cab1baf Remove react 2021-01-07 12:28:01 -05:00
ee4f6cdb3d Where'd my JS go
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-31 13:09:26 -05:00
a205581a44 Working tests 2020-12-31 13:07:59 -05:00
ed59ac11b8 Working tests 2020-12-31 12:50:02 -05:00
5770ce7fb9 Working tests 2020-12-31 12:41:22 -05:00
55c6d29ff7 Working tests 2020-12-31 12:41:16 -05:00
aa9eaf11f7 Working tests 2020-12-31 12:37:45 -05:00
8143f518f5 Added Pending to top of window 2020-12-31 11:44:04 -05:00
d13da924b7 Bypass auth 2020-12-31 11:05:31 -05:00
00440c97ef Updates with React and modal attempt 2020-12-31 10:52:37 -05:00
a1eadc1857 Updates with React and modal attempt 2020-12-31 10:52:33 -05:00
79aa40c476 Updates with React and modal attempt 2020-12-31 10:47:30 -05:00
7c7857c1d7 Redo with React 2020-12-30 21:52:56 -05:00
4e96ff10bf Redo with React 2020-12-30 21:49:40 -05:00
d6013c90b2 Redo with React 2020-12-30 21:43:13 -05:00
101189d71b Redo with React 2020-12-30 21:41:59 -05:00
e4b0782d88 Redo with React 2020-12-30 21:16:43 -05:00
5bfdbabf33 Redo with React 2020-12-30 21:15:34 -05:00
aeafbf31bd Redo with React 2020-12-30 21:01:14 -05:00
11dfb49028 Redo with React 2020-12-30 20:57:27 -05:00
edf12e2461 Redo with React 2020-12-30 20:57:07 -05:00
87a08b0cd5 Redo with React 2020-12-30 20:50:26 -05:00
f3de2e2857 Add css 2020-12-30 19:42:52 -05:00
37837a508c Add css 2020-12-30 19:40:02 -05:00
86b499a5da Add css 2020-12-30 19:36:54 -05:00
44838b23f5 Add css 2020-12-30 19:31:02 -05:00
a33940c6f2 Add css 2020-12-30 19:30:20 -05:00
83de997220 Add css 2020-12-30 19:28:49 -05:00
a5354ddd6e Show list of verifications 2020-12-30 19:24:37 -05:00
0b5df2733d Don't show cookie policy after login 2020-12-30 19:15:00 -05:00
75db9af48c Don't show cookie policy after login 2020-12-30 19:13:51 -05:00
13ebaee9fe Don't show cookie policy after login 2020-12-30 19:11:31 -05:00
2743ba1bdb Absolute path to script 2020-12-30 19:10:19 -05:00
a441fb5e63 Move react container 2020-12-30 18:23:24 -05:00
c8d3f7b0b5 Redo npm 2020-12-30 18:20:37 -05:00
c1e261c85d Redo npm 2020-12-30 18:19:14 -05:00
938eac8348 Redo npm 2020-12-30 18:17:06 -05:00
04dbd66c88 Redo npm 2020-12-30 18:16:24 -05:00
5e70236ba1 Redo npm 2020-12-30 18:15:10 -05:00
04097a9d3e Removed node 2020-12-30 18:10:35 -05:00
68c821b71b Removed Node modules 2020-12-30 18:10:22 -05:00
8a6e029f83 Add react 2020-12-30 18:08:01 -05:00
2a36f20a4c Move verification viewing to logged in only 2020-12-30 17:00:44 -05:00
b5e72a88b9 Use /verification staticly 2020-12-30 16:06:13 -05:00
77ae7f149a Add API Doc 2020-12-30 14:37:53 -05:00
9803136783 Add user info endpoint
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-30 14:06:25 -05:00
cc63f82427 Update drone
All checks were successful
continuous-integration/drone/push Build is passing
2020-12-30 13:45:10 -05:00
6215426a2d API Endpoints for Verifications and Pending.
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-30 12:56:11 -05:00
7a41ebb48d API Endpoints for Verifications and Pending. 2020-12-30 12:54:09 -05:00
62246e9c23 API Endpoints for Verifications and Pending. 2020-12-30 12:52:08 -05:00
011dc1362a API Endpoints for Verifications and Pending. 2020-12-30 12:50:58 -05:00
277fda3a6d API Endpoints for Verifications and Pending. 2020-12-30 12:47:59 -05:00
d1122bab29 Admins only get passwords. 2020-12-30 12:24:14 -05:00
46f9df6dbd Authy 2020-12-30 12:14:55 -05:00
3bba74c076 Authy 2020-12-30 12:12:06 -05:00
8460b10e9f Authy 2020-12-30 12:10:42 -05:00
f31215027a Authy 2020-12-30 12:07:57 -05:00
dc1b10afca Authy 2020-12-30 12:05:07 -05:00
8236715042 Authy 2020-12-30 12:04:38 -05:00
0a2dc22837 Authy 2020-12-30 12:02:09 -05:00
b172b1b57a Authy 2020-12-30 11:57:13 -05:00
957d34f6c4 Authy 2020-12-30 11:55:48 -05:00
b2832bcef0 Authy 2020-12-30 11:52:53 -05:00
dd1f280495 Authy 2020-12-30 11:50:23 -05:00
c942df7cd5 Authy 2020-12-30 11:49:53 -05:00
383a4ba691 Authy 2020-12-30 11:48:34 -05:00
19d9eaae2d Authy 2020-12-30 11:46:47 -05:00
c36cd98623 Authy 2020-12-30 11:43:21 -05:00
f23dba9454 Authy 2020-12-30 11:40:28 -05:00
9bdf48602d Authy 2020-12-30 11:37:33 -05:00
5d0492695a Authy 2020-12-30 11:36:31 -05:00
970d003e2f Authy 2020-12-30 11:31:17 -05:00
46963ab2d6 Authy 2020-12-30 11:28:03 -05:00
1d7d2b1835 Authy 2020-12-30 11:26:17 -05:00
67c48eb260 PanicSafe() 2020-12-30 11:23:30 -05:00
c3f2fa00de Updates to ensure redirect happens from reqPass 2020-12-30 11:20:32 -05:00
78c368c832 typo in reqPass 2020-12-30 11:10:49 -05:00
c03f27249b Remove verifications from list when user leaves 2020-12-30 11:03:20 -05:00
7afa4bdb8b Updated ASL message 2020-12-02 11:47:20 -05:00
dece68594d Get userID from username 2020-12-02 09:21:00 -05:00
1c4aba2780 Get userID from username 2020-12-02 09:17:05 -05:00
0a575c395a Enable thanos
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-02 08:47:05 -05:00
f8e7051e79 Added skeleton website from kbauth
Some checks failed
continuous-integration/drone/push Build is failing
2020-12-02 08:45:32 -05:00
20 changed files with 1198 additions and 7 deletions

View File

@ -8,6 +8,8 @@ steps:
commands: commands:
- go get github.com/bwmarrin/discordgo - go get github.com/bwmarrin/discordgo
- go get github.com/rudi9719/loggy - go get github.com/rudi9719/loggy
- go get github.com/gorilla/mux
- go get github.com/gorilla/sessions
- go vet -v - go vet -v
- go build - go build
- name: gitea_release - name: gitea_release

4
.gitignore vendored
View File

@ -1,5 +1,9 @@
build/
public/
disgord-thanos disgord-thanos
disgord-Thanos disgord-Thanos
config.json config.json
start.sh start.sh
dev.html
verifications/* verifications/*
node_modules/

BIN
ThanosAPI.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

161
auth.go Normal file
View File

@ -0,0 +1,161 @@
package main
import (
"fmt"
"math/rand"
"net/http"
"strings"
"time"
"github.com/gorilla/mux"
)
func reqPass(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
log.LogInfo("reqPass called.")
username := r.URL.Query()["UserName"][0]
log.LogInfo("reqPass username is %+v.", username)
var userID string
if &dg == nil {
log.LogError("Discord session was nill.")
}
g, err := dg.GuildMembers(config.GuildID, "", 1000)
log.LogInfo("reqPass guild is %+v.", config.GuildID)
if err == nil {
for _, m := range g {
if strings.ToUpper(m.Nick) == strings.ToUpper(username) {
for _, r := range m.Roles {
if r == config.AdminRole {
userID = m.User.ID
log.LogInfo("User ID found for %+v as %+v", username, userID)
}
}
}
}
} else {
log.LogError("Unable to find user ID for %+v", username)
}
ipaddr := r.Header.Get("X-Real-IP")
log.LogInfo("reqPass IP is %+v.", ipaddr)
log.LogInfo(fmt.Sprintf("reqPass called:```username: %s\nip : %s```", username, ipaddr))
go sendPassword(userID, ipaddr)
http.Redirect(w, r, "/login", 302)
}
func tryLogin(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
session, err := store.Get(r, "2fa")
if err != nil {
log.LogWarn("Error opening session for 2fa store")
}
vars := mux.Vars(r)
username := vars["username"]
password := vars["password"]
ip := r.Header.Get("X-Real-IP")
if len(username) == 0 {
username = r.FormValue("UserName")
password = r.FormValue("TempPass")
}
access, _ := detectUser(r, "tryLogin")
if !access {
log.LogDebug(fmt.Sprintf("%s is attempting login", getSessionIdentifier(r)))
access = usePassword(username, password, ip)
if access {
log.LogInfo(fmt.Sprintf("%s has successfully logged in from %s", username, ip))
log.LogDebug(fmt.Sprintf("```%+v```", session.Values))
session.Values["username"] = username
session.Values["ip"] = ip
session.Values["timestamp"] = fmt.Sprintf("%+v", time.Now())
err = session.Save(r, w)
if err != nil {
log.LogWarn(fmt.Sprintf("Error saving cookie. ```%+v```", err))
}
}
}
greetUser(w, r)
}
func usePassword(user string, pass string, ip string) bool {
defer log.PanicSafe()
log.LogInfo("%+v", toks)
tok := toks[strings.ToUpper(user)]
delete(toks, strings.ToUpper(user))
if tok.IP != ip {
log.LogWarn(fmt.Sprintf("%s attempted to use an improper IP.", user))
return false
}
if tok.Password != pass {
log.LogWarn(fmt.Sprintf("%s attempted to use an improper password. %s vs %s", user, tok.Password, pass))
return false
}
if time.Since(tok.Timestamp) > (time.Minute * 5) {
log.LogWarn("%s attempted to use expired token. \n%+v\n%+v\n%+v", user, time.Since(tok.Timestamp), tok.Timestamp, time.Now())
return false
}
return true
}
func genPassword(length int) string {
defer log.PanicSafe()
rand.Seed(time.Now().UnixNano())
chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz" +
"0123456789")
var b strings.Builder
for i := 0; i < length; i++ {
b.WriteRune(chars[rand.Intn(len(chars))])
}
return b.String() // E.g. "ExcbsVQs"
}
func sendPassword(user string, ipaddr string) {
defer log.PanicSafe()
str := genPassword(15)
log.LogInfo("sending password to %+v for %+v: %+v", ipaddr, user, str)
m, err := dg.GuildMember(config.GuildID, user)
if err != nil {
log.LogErrorType(err)
}
now := time.Now()
toks[strings.ToUpper(m.Nick)] = Tokens{
Username: user,
IP: ipaddr,
Password: str,
Timestamp: now,
}
pmChann, err := dg.UserChannelCreate(user)
if err != nil {
log.LogErrorType(err)
}
dg.ChannelMessageSend(pmChann.ID, fmt.Sprintf("A temporary password was requested from %s:", ipaddr))
dg.ChannelMessageSend(pmChann.ID, fmt.Sprintf("```%s```", str))
}
func getSessionIdentifier(r *http.Request) string {
defer log.PanicSafe()
ipaddr := r.Header.Get("X-Real-IP")
if ipaddr == "" {
ipaddr = r.RemoteAddr
}
uri := r.URL.Path
return fmt.Sprintf("%s:%s", ipaddr, uri)
}
func detectUser(r *http.Request, callFunc string) (bool, string) {
defer log.PanicSafe()
log.LogInfo(fmt.Sprintf("%s called detectUser", getSessionIdentifier(r)))
ip := r.Header.Get("X-Real-IP")
session, err := store.Get(r, "2fa")
if err != nil {
log.LogDebug(fmt.Sprintf("Unable to open 2fa session in %s", callFunc))
}
if session.Values["username"] != nil {
return true, fmt.Sprintf("%s", session.Values["username"])
}
if ip == "154.27.199.33" {
return true, "rudi"
}
return false, ""
}

16
main.go
View File

@ -29,6 +29,7 @@ var (
token string token string
configFile string configFile string
setupMsg string setupMsg string
dg *discordgo.Session
lastPM = make(map[string]time.Time) lastPM = make(map[string]time.Time)
quotes = []string{"The hardest choices require the strongest wills.", "You're strong, but I could snap my fingers and you'd all cease to exist.", "Fun isn't something one considers when balancing the universe. But this... does put a smile on my face.", "Perfectly balanced, as all things should be.", "I am inevitable."} quotes = []string{"The hardest choices require the strongest wills.", "You're strong, but I could snap my fingers and you'd all cease to exist.", "Fun isn't something one considers when balancing the universe. But this... does put a smile on my face.", "Perfectly balanced, as all things should be.", "I am inevitable."}
) )
@ -40,6 +41,7 @@ func init() {
} }
func main() { func main() {
go runWeb()
defer log.PanicSafe() defer log.PanicSafe()
if configFile == "" { if configFile == "" {
configFile = "config.json" configFile = "config.json"
@ -55,7 +57,8 @@ func main() {
} }
log.LogCritical("SetupToken: %+v\nRebootToken: %+v", setupToken, rebootToken) log.LogCritical("SetupToken: %+v\nRebootToken: %+v", setupToken, rebootToken)
dg, err := discordgo.New("Bot " + token) var err error
dg, err = discordgo.New("Bot " + token)
if err != nil { if err != nil {
log.LogErrorType(err) log.LogErrorType(err)
log.LogPanic("Unable to create bot using token.") log.LogPanic("Unable to create bot using token.")
@ -152,7 +155,7 @@ func runPurge(s *discordgo.Session) {
func ready(s *discordgo.Session, event *discordgo.Ready) { func ready(s *discordgo.Session, event *discordgo.Ready) {
// Set the playing status. // Set the playing status.
s.UpdateStatus(0, "DreamDaddy v1.0") s.UpdateStatus(0, "DreamDaddy v2.1")
} }
func guildMemberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { func guildMemberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
@ -203,8 +206,13 @@ func guildMemberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
delete(config.Probations, uid) delete(config.Probations, uid)
} }
} }
delete(config.Unverified, m.User.ID)
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v (@%+v) has left, ban: %+v", m.User.ID, m.User.Username, banned)) s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v (@%+v) has left, ban: %+v", m.User.ID, m.User.Username, banned))
delete(config.Unverified, m.User.ID)
for msg, v := range config.Verifications {
if v.UserID == m.User.ID {
delete(config.Verifications, msg)
}
}
saveConfig() saveConfig()
} }
@ -230,7 +238,7 @@ func rejectVerification(s *discordgo.Session, u discordgo.User) {
func requestAge(s *discordgo.Session, u discordgo.User) { func requestAge(s *discordgo.Session, u discordgo.User) {
defer log.PanicSafe() defer log.PanicSafe()
st, _ := s.UserChannelCreate(u.ID) st, _ := s.UserChannelCreate(u.ID)
s.ChannelMessageSend(st.ID, "What is your ASL? (Age/Sex/Language)") s.ChannelMessageSend(st.ID, "What is your ASL? (Age/Sex/Language) Please note, this is NOT requesting your gender, but your biological sex. Gender is a social construct, sex is biology and in the context of pornographic images more important.")
} }

249
site-api.go Normal file
View File

@ -0,0 +1,249 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
)
var (
store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
toks = make(map[string]Tokens)
acctLinks = make(map[string]linkedAccount)
)
func topWrapper(r *http.Request) string {
defer log.PanicSafe()
headerTemplate, err := ioutil.ReadFile("./static/header.tpl")
if err != nil {
log.LogError(fmt.Sprintf("Unable to open header template: ```%+v```", err))
return ""
}
header := string(headerTemplate)
login := "Login"
loggedIn, user := detectUser(r, "topWrapper")
if loggedIn {
login = fmt.Sprintf("Logout %s", user)
}
header = strings.Replace(header, "$LOGIN", login, -1)
return header
}
func bodyWrapper(r *http.Request, template string) string {
defer log.PanicSafe()
bodyTemplate, err := ioutil.ReadFile(fmt.Sprintf("./static/%+v.tpl", template))
if err != nil {
log.LogError(fmt.Sprintf("Attempt to load %s.tpl failed. ```%+v```", template, err))
return bodyWrapper(r, "404")
}
return string(bodyTemplate)
}
func pageBuilder(r *http.Request, pageName string) string {
defer log.PanicSafe()
pageCode := topWrapper(r)
pageCode += bodyWrapper(r, pageName)
return pageCode
}
func greetUser(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
log.LogInfo(fmt.Sprintf("%s called greetUser", getSessionIdentifier(r)))
loggedIn, _ := detectUser(r, "Homepage")
if loggedIn {
bodyTemplate, _ := ioutil.ReadFile("./static/index.html")
fmt.Fprintf(w, string(bodyTemplate))
} else {
fmt.Fprintf(w, pageBuilder(r, "home"))
}
}
func passPage(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
log.LogInfo(fmt.Sprintf("%s called passPage", getSessionIdentifier(r)))
fmt.Fprintf(w, pageBuilder(r, "pass"))
}
func loginPage(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
log.LogInfo(fmt.Sprintf("%s called loginPage", getSessionIdentifier(r)))
session, err := store.Get(r, "2fa")
if err != nil {
log.LogWarn("Unable to open 2fa session in loginpage")
}
loggedIn, _ := detectUser(r, "loginPage")
if loggedIn {
session.Values["username"] = nil
err = session.Save(r, w)
if err != nil {
log.LogWarn("Error logging out from loginPage()")
}
fmt.Fprintf(w, pageBuilder(r, "home"))
return
}
fmt.Fprintf(w, pageBuilder(r, "login"))
}
func notFoundPage(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
go log.LogWarn(fmt.Sprintf("%s triggered notFoundPage", getSessionIdentifier(r)))
fmt.Fprintf(w, topWrapper(r))
fmt.Fprintf(w, card("Oops! That Page Was Not found.",
"Sorry, a 404 error has occured. The requested page not found! <br><br>"+
"<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/t3otBjVZzT0\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>",
"<div class=\"error-actions\"><a href=\"/\" class=\"btn btn-primary btn-lg\"><span class=\"glyphicon glyphicon-home\"></span>Take Me Home </a> <a href=\"mailto://rudi@nmare.net\" class=\"btn btn-default btn-lg\"><span class=\"glyphicon glyphicon-envelope\"></span> Contact Support </a></div>"))
}
func card(title string, content string, footer string) string {
defer log.PanicSafe()
cardTemplate, err := ioutil.ReadFile("./static/card.tpl")
if err != nil {
log.LogError("Unable to open card template")
return ""
}
cardString := string(cardTemplate)
cardString = strings.Replace(cardString, "$TITLE", title, -1)
cardString = strings.Replace(cardString, "$CONTENT", content, -1)
cardString = strings.Replace(cardString, "$FOOTER", footer, -1)
return cardString
}
func getPending(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
loggedIn, _ := detectUser(r, "getPending")
if loggedIn {
pending, err := json.Marshal(config.Verifications)
if err != nil {
log.LogErrorType(err)
notFoundPage(w, r)
}
fmt.Fprintf(w, string(pending))
} else {
notFoundPage(w, r)
}
}
func getConfig(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
loggedIn, _ := detectUser(r, "getConfig")
if loggedIn {
pending, err := json.Marshal(config)
if err != nil {
log.LogErrorType(err)
notFoundPage(w, r)
}
fmt.Fprintf(w, string(pending))
} else {
notFoundPage(w, r)
}
}
func getProbations(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
loggedIn, _ := detectUser(r, "getProbations")
if loggedIn {
pending, err := json.Marshal(config.Probations)
if err != nil {
log.LogErrorType(err)
notFoundPage(w, r)
}
fmt.Fprintf(w, string(pending))
} else {
notFoundPage(w, r)
}
}
func getVerifications(w http.ResponseWriter, r *http.Request) {
defer log.PanicSafe()
loggedIn, _ := detectUser(r, "getVerifications")
if !loggedIn {
notFoundPage(w, r)
return
}
var files []string
root := "./verifications/"
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
files = append(files, path)
return nil
})
if err != nil {
log.LogErrorType(err)
}
var v []Verification
for _, file := range files {
info := strings.Split(file, "-")
if len(info) < 2 {
continue
}
var ver Verification
ver.UserID = strings.Replace(info[0], "verifications/", "", -1)
ver.Username = info[1]
ver.Photo = file
fileStat, _ := os.Stat(file)
ver.Closed = fileStat.ModTime()
v = append(v, ver)
}
verifications, err := json.Marshal(v)
if err != nil {
log.LogErrorType(err)
}
fmt.Fprintf(w, string(verifications))
}
func getUser(w http.ResponseWriter, r *http.Request) {
loggedIn, _ := detectUser(r, "getVerifications")
if !loggedIn {
notFoundPage(w, r)
return
}
vars := mux.Vars(r)
username := vars["userID"]
if len(username) == 0 {
username = r.FormValue("userID")
}
m, err := dg.GuildMember(config.GuildID, username)
if err != nil {
log.LogErrorType(err)
}
ret, err := json.Marshal(m)
if err != nil {
log.LogErrorType(err)
}
fmt.Fprintf(w, string(ret))
}
func getVerification(w http.ResponseWriter, r *http.Request) {
loggedIn, _ := detectUser(r, "getVerification")
if !loggedIn {
notFoundPage(w, r)
return
}
http.ServeFile(w, r, r.URL.Path)
}
func runWeb() {
defer log.PanicSafe()
router := mux.NewRouter().StrictSlash(true)
log.LogInfo("Adding HandleFuncs to router")
router.NotFoundHandler = http.HandlerFunc(notFoundPage)
router.HandleFunc("/pass", passPage)
router.HandleFunc("/login", loginPage)
router.HandleFunc("/api/login", tryLogin)
router.HandleFunc("/api/config", getConfig)
router.HandleFunc("/api/pending", getPending)
router.HandleFunc("/api/verifications", getVerifications)
router.HandleFunc("/api/probations", getProbations)
router.HandleFunc("/api/passreq", reqPass)
router.HandleFunc("/api/user", getUser)
router.HandleFunc("/", greetUser)
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
router.PathPrefix("/verifications/").Handler(http.StripPrefix("/verifications/", http.FileServer(http.Dir("./verifications"))))
log.LogInfo("Starting server")
log.LogErrorType(http.ListenAndServe(":8080", router))
}

21
static/404.tpl Normal file
View File

@ -0,0 +1,21 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="error-template">
<h1>
Oops!</h1><br>
<h2>
404 Not Found</h2>
<div class="error-details">
Sorry, an error has occured, Requested page not found!
</div>
<br>
<div class="error-actions">
<a href="/" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-home"></span>
Take Me Home </a> <a href="mailto://rudi@nmare.net" class="btn btn-default btn-lg"><span class="glyphicon glyphicon-envelope"></span> Contact Support </a>
</div>
</div>
</div>
</div>
<br>
</div>

21
static/500.tpl Normal file
View File

@ -0,0 +1,21 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="error-template">
<h1>
Oops!</h1><br>
<h2>Something has gone HORRIBLY wrong! Congrats!</h2>
<div class="error-details">
Not really sorry, but an error has occured. 500 means something went wrong on the server side, but.. Let's be honest. It was really a user malfunction.
</div>
<br>
<div class="error-actions">
<a href="/" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-home"></span>
Take Me Home </a> <a href="mailto://rudi@nmare.net" class="btn btn-default btn-lg"><span class="glyphicon glyphicon-envelope"></span> Contact Support </a>
</div>
</div>
</div>
</div>
<br>
<iframe width="560" height="315" src="https://www.youtube.com/embed/t3otBjVZzT0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

153
static/app.js Normal file
View File

@ -0,0 +1,153 @@
//Hopper says hi
// Get the modal
const modal = document.getElementById("myModal");
// Get the button that opens the modal
const btn = document.getElementById("myBtn");
// Get the <span> element that closes the modal
const span = document.getElementsByClassName("close")[0];
var mode = new URLSearchParams(window.location.search).get("mode");
const archiveLink = document.querySelector("#archive-link");
const pendingLink = document.querySelector("#pending-link");
const statusLink = document.querySelector("#status-link");
function handleForm(event) {
event.preventDefault();
}
function main() {
archiveLink.classList.remove("active");
pendingLink.classList.remove("active");
statusLink.classList.remove("active");
switch (mode) {
case "status":
statusLink.classList.add("active");
return statusPage();
case "pending":
pendingLink.classList.add("active");
break;
case "verifications":
archiveLink.classList.add("active");
break;
default:
console.log("No mode");
mode = "verifications";
archiveLink.classList.add("active");
break;
}
document.getElementById("main-app").innerHTML = "";
fetch(`https://thanos.nightmare.haus/api/${mode}`)
.then((response) => response.json())
.then((data) => processData(data));
}
function statusPage() {
document.getElementById("main-app").innerHTML = "";
fetch(`https://thanos.nightmare.haus/api/config`)
.then((response) => response.json())
.then((data) => {
var node = document.createElement("config-status");
var upTime = document.createElement("div");
upTime.setAttribute("slot", "uptime");
upTime.innerText = data.Uptime;
node.appendChild(upTime);
var bumpTime = document.createElement("div");
bumpTime.setAttribute("slot", "lastbump");
bumpTime.innerText = new Date(data.BumpTime).toLocaleString();
node.appendChild(bumpTime);
node.setData(data);
document.getElementById("main-app").appendChild(node);
});
}
function searchPage() {
var search = document.getElementById("search-bar");
fetch("https://thanos.nightmare.haus/api/verifications")
.then((response) => response.json())
.then((data) => {
var searchData = [];
for (user of data) {
var match = false;
if (user.Username.toLowerCase().includes(search.value.toLowerCase())) {
match = true;
}
if (new Date(user.Closed).toLocaleString().includes(search.value)) {
match = true;
}
if (user.UserID.includes(search.value)) {
match = true;
}
if (match) {
searchData.push(user);
}
}
processData(searchData);
});
}
function processData(data) {
document.getElementById("main-app").innerHTML = "";
if (data.length == 0) {
alert("No data.");
return;
}
data = Object.values(data);
for (user of data) {
var node = document.createElement("user-card");
var nameSlot = document.createElement("div");
nameSlot.setAttribute("slot", "username");
nameSlot.innerText = user.Username;
node.appendChild(nameSlot);
var joinDate = document.createElement("div");
joinDate.setAttribute("slot", "join-date");
joinDate.innerText =
mode == "pending"
? new Date(user.Submitted).toLocaleString()
: new Date(user.Closed).toLocaleString();
node.appendChild(joinDate);
var discordSlot = document.createElement("div");
discordSlot.setAttribute("slot", "discord-id");
discordSlot.innerText = user.UserID;
node.appendChild(discordSlot);
var picSlot = document.createElement("div");
picSlot.setAttribute("slot", "pic-link");
var aNode = document.createElement("a");
aNode.setAttribute(
"href",
mode == "pending"
? user.Photo
: `https://thanos.nightmare.haus/${user.Photo}`
);
aNode.innerText = "Verification Photo";
picSlot.appendChild(aNode);
node.appendChild(picSlot);
document.getElementById("main-app").appendChild(node);
}
}
// When the user clicks on <span> (x), close the modal
span.onclick = function () {
modal.style.display = "none";
};
// When the user clicks anywhere outside of the modal, close it
window.onclick = function (event) {
if (event.target == modal) {
modal.style.display = "none";
}
};
var form = document.getElementById("search-form");
form.addEventListener("submit", handleForm);
main();

11
static/card.tpl Normal file
View File

@ -0,0 +1,11 @@
<br><br>
<div class="container h-100 d-flex justify-content-center align-items-center">
<div class="card" style="width: 50rem;">
<div class="card-body col">
<h2 class="card-title">$TITLE</h1>
<p class="card-text">$CONTENT</p>
<p class="card-text">$FOOTER</p>
</div>
</div>
</div>

211
static/components.js Normal file
View File

@ -0,0 +1,211 @@
//Hopper says hi again
const basicCard = document.createElement('basic-card');
const configStatus = document.createElement('status');
const style = `
<style>
.column {
float: left;
width: 24%;
height: 100%;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.137);
border-radius: 3%;
transition: 0.3s;
margin: 15px 3.33%;
padding: 12px 16px;
background-color: #282c34;
color: #FFFFFF;
}
.column:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.795);
}
/* Clear floats after the columns */
.row {
content: "";
clear: both;
display: flex;
flex-wrap: wrap;
}
p {
text-align: center;
}
a {
text-align: center;
color: white;
}
.card {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.137);
border-radius: 3%;
transition: 0.3s;
position: relative;
width: 250px;
height: 200px;
padding: 5px;
background-color: #282c34;
color: #FFFFFF;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.795);
}
.container {
padding: 6px 16px;
}
label{
display: inline;
float: left;
clear: left;
width: 250px;
text-align: left;
}
input {
display: inline;
float: right;
}
</style>
`;
basicCard.innerHTML = `
${style}
<div id="card-container" class="container">
<div class="card">
<h4><p><slot name="username" /></p></h4>
<div>
<p><slot name="join-date" /></p>
<p><slot id="discord-id" name="discord-id" /></p>
<p><slot id="pic-link" name="pic-link" /></p>
</div>
</div>
</div>
`;
configStatus.innerHTML = `
${style}
<div class="row">
<div class="column">
<h4><p>Status</p></h4>
<p>Uptime: <slot name="uptime" /></p>
<p>Last Bump was <slot name="lastbump" /></p>
<p>by <slot id="lastbumper" name="lastbumper" /></p>
</div>
<br>
<div class="column">
<h4><p>Config</p></h4>
<form>
<label for="Guild">Guild: </label>
<input type="text" id="Guild" name="Guild" readonly/>
<label for="AdminChannel">Admin Channel: </label>
<input type="select" id="AdminChannel" name="AdminChannel"/>
<label for="AdminRole">Admin Role: </label>
<input type="select" id="AdminRole" name="AdminRole"/>
<label for="MonitorChannel">Monitor Channel: </label>
<input type="select" id="MonitorChannel" name="MonitorChannel"/>
<label for="MonitorRole">Monitor Role: </label>
<input type="select" id="MonitorRole" name="MonitorRole"/>
<label for="IntroChannel">Intro Channel: </label>
<input type="select" id="IntroChannel" name="IntroChannel"/>
<label for="VerifiedRole">Verified Role: </label>
<input type="select" id="VerifiedRole" name="VerifiedRole"/>
<label for="OutFile">Logging OutFile: </label>
<input type="text" id="OutFile" name="OutFile" />
<label for="KBTeam">KB Team: </label>
<input type="text" id="KBTeam" name="KBTeam" />
<label for="KBChann">KB Channel: </label>
<input type="text" id="KBChann" name="KBChann" />
<label for="level">Logging Level: </label>
<input type="number" id="Level" name="Level" />
<label for="ProgName">Program Name: </label>
<input type="text" id="ProgName" name="ProgName" />
<label for="UseStdout">Use stdout: </label>
<input type="checkbox" id="UseStdout" name="UseStdout"/>
<label for="submitchanges"></label>
<input style="width: 100%;" type="submit" id="submitchanges" name="submitchanges" onClick="alert('Post blocked.')">
</form>
</div>
<br>
<div id="admin-stats" class="column">
<h4><p>Admin Stats</p></h4>
</div>
</div>
`;
class ConfigStatusClass extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(configStatus.cloneNode(true));
}
async setData(data) {
this.shadowRoot.querySelector("#submitchanges").addEventListener('submit', handleForm);
this.shadowRoot.querySelector("#Guild").value = data.GuildID;
this.shadowRoot.querySelector("#AdminChannel").value = data.AdminChannel;
this.shadowRoot.querySelector("#AdminRole").value = data.AdminRole;
this.shadowRoot.querySelector("#MonitorChannel").value = data.MonitorChann;
this.shadowRoot.querySelector("#MonitorRole").value = data.MonitorRole;
this.shadowRoot.querySelector("#IntroChannel").value = data.IntroChann;
this.shadowRoot.querySelector("#VerifiedRole").value = data.VerifiedRole;
this.shadowRoot.querySelector("#OutFile").value = data.LogOpts.OutFile;
this.shadowRoot.querySelector("#KBTeam").value = data.LogOpts.KBTeam;
this.shadowRoot.querySelector("#KBChann").value = data.LogOpts.KBChann;
this.shadowRoot.querySelector("#Level").value = data.LogOpts.Level;
this.shadowRoot.querySelector("#ProgName").value = data.LogOpts.ProgName;
this.shadowRoot.querySelector("#UseStdout").checked = data.LogOpts.UseStdout == "true";
fetch('https://thanos.nightmare.haus/api/user?userID=' + data.LastBumper)
.then(response => response.json())
.then(userData => {
this.shadowRoot.querySelector("#lastbumper").innerHTML = userData.user.username;
});
this.loadStats(data.Stats)
}
async loadStats(data) {
var shadowRoot = this.shadowRoot;
Object.keys(data).forEach(function(uid) {
var score = data[uid];
fetch('https://thanos.nightmare.haus/api/user?userID=' + uid)
.then(response => response.json())
.then(userData => {
var stats = shadowRoot.querySelector("#admin-stats");
var userName = document.createElement("p");
userName.innerText = userData.user.username + ": " + score;
stats.appendChild(userName);
});
})
}
}
class UserCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(basicCard.cloneNode(true));
}
connectedCallback() {
this.shadowRoot.querySelector('#card-container').addEventListener('click', () => this.showModal());
}
showModal() {
const userID = this.shadowRoot.querySelector('#discord-id').assignedNodes()[0].textContent;
const userPic = this.shadowRoot.querySelector("#pic-link").assignedNodes()[0].querySelector("a").getAttribute("href");
fetch('https://thanos.nightmare.haus/api/user?userID=' + userID)
.then(response => response.json())
.then(data => {
if (data === undefined || data === null) {
alert("User not found.");
return;
}
document.querySelector("#myModal").style.display = "block";
document.querySelector('#modal-join').textContent = new Date(data.joined_at).toLocaleString();
document.querySelector('#modal-userID').textContent = data.user.username;
document.querySelector('#modal-avatar').src = `https://cdn.discordapp.com/avatars/${data.user.id}/${data.user.avatar}.png?size=256`;
document.querySelector('#modal-verification').src = userPic;
document.querySelector('#modal-verification').style = "max-height: 500px;";
});
}
}
window.customElements.define('user-card', UserCard);
window.customElements.define('config-status', ConfigStatusClass);

44
static/header.tpl Normal file
View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<title>Thanos</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link rel="icon" type="image/png" href="https://lh6.googleusercontent.com/BvczCCYhJUsKIs3dsowl1vuvnBtCGSDcMDekt5PehwQk3cQLfHkEn80cR3IuMxUFmd5Sh_UQ=w16383">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link href="/static/css/main.5c7015b9.chunk.css" rel="stylesheet">
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
<div class="container">
<a class="navbar-brand" href="/">
<img src="" alt="">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/pass">Request Token</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/login">$LOGIN</a>
</li>
</ul>
</div>
</div>
</nav>
</head>

17
static/home.tpl Normal file
View File

@ -0,0 +1,17 @@
<br><br>
<div class="container h-100 d-flex justify-content-center align-items-center">
<div id="react_app"></div>
<div class="container h-100 d-flex justify-content-center align-items-center">
<div class="card" style="width: 50rem;">
<div class="card-body col">
<h2 class="card-title">Cookie Policy</h1>
<p class="card-text">What website doesn't use cookies nowadays?</p>
<p class="card-text">This website does not use any 3rd party cookies. All cookies are encrypted and
only used by this website. </p>
<p class="card-text">If you are actually reading this, chances are your data isn't valuable enough
for me to care about tracking you.</p>
</div>
</div>
</div>

73
static/index.css Normal file
View File

@ -0,0 +1,73 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.card {
/* Add shadows to create the "card" effect */
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.137);
border-radius: 3%;
transition: 0.3s;
position: relative;
width: 300px;
height: 200px;
padding: 10px;
background-color: #282c34;
color: #FFFFFF;
}
/* On mouse-over, add a deeper shadow */
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.795);
}
/* Add some padding inside the card container */
.container {
padding: 6px 16px;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content/Box */
.modal-content {
background-color: #282c34;
margin: 15% auto; /* 15% from the top and centered */
padding: 20px;
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
color: white;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}

91
static/index.html Normal file
View File

@ -0,0 +1,91 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="JavaScript web interface for ThanOS API">
<meta name="author" content="rudi@nightmare.haus">
<link rel="icon" href="https://cdn.discordapp.com/avatars/688025671968096341/7ad6b70b550cec8fb9dba7cec489838e.png?size=32">
<title>Thanos2</title>
<!-- Bootstrap core CSS -->
<link href="https://getbootstrap.com/docs/4.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="https://getbootstrap.com/docs/4.0/examples/starter-template/starter-template.css" rel="stylesheet">
<!-- My imports -->
<link href="./static/index.css" rel="stylesheet">
<!-- End of my imports -->
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="https://git.nightmare.haus/rudi/disgord-Thanos">ThanOS</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active" id="archive-link">
<a class="nav-link" href="?mode=verifications">Archive <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item" id="pending-link">
<a class="nav-link" href="?mode=pending">Pending</a>
</li>
<li class="nav-item" id="status-link">
<a class="nav-link disabled" href="?mode=status">Status</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0" id="search-form">
<input class="form-control mr-sm-2" id="search-bar" type="text" placeholder="Search Verifications"
aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" id="search-button" type="submit" onclick="searchPage()">Search</button>
</form>
</div>
</nav>
<!-- The Modal -->
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<div class="container">
<span class="close">&times;</span>
<div class="row">
<div class="col-sm">
<img id="modal-avatar" alt="Avatar">
<p id="modal-join"></p>
<p id="modal-userID"></p>
</div>
<div class="col-sm">
<img id="modal-verification" alt="Avatar" style="width: 100%;">
</div>
</div>
</div>
</div>
</div>
<div id="main-app" style="display: flex; flex-wrap: wrap;">
</div>
<script src="./static/components.js"></script>
<script src="./static/app.js"></script>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="https://getbootstrap.com/docs/4.0/assets/js/vendor/jquery-slim.min.js"><\/script>')</script>
<script src="https://getbootstrap.com/docs/4.0/assets/js/vendor/popper.min.js"></script>
<script src="https://getbootstrap.com/docs/4.0/dist/js/bootstrap.min.js"></script>
</body>
</html>

8
static/loggedIn.tpl Normal file
View File

@ -0,0 +1,8 @@
<div id="react_app"></div>
<div class="container h-100 d-flex justify-content-center align-items-center">
<script>!function (e) { function t(t) { for (var n, l, a = t[0], f = t[1], i = t[2], c = 0, s = []; c < a.length; c++)l = a[c], Object.prototype.hasOwnProperty.call(o, l) && o[l] && s.push(o[l][0]), o[l] = 0; for (n in f) Object.prototype.hasOwnProperty.call(f, n) && (e[n] = f[n]); for (p && p(t); s.length;)s.shift()(); return u.push.apply(u, i || []), r() } function r() { for (var e, t = 0; t < u.length; t++) { for (var r = u[t], n = !0, a = 1; a < r.length; a++) { var f = r[a]; 0 !== o[f] && (n = !1) } n && (u.splice(t--, 1), e = l(l.s = r[0])) } return e } var n = {}, o = { 1: 0 }, u = []; function l(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, l), r.l = !0, r.exports } l.m = e, l.c = n, l.d = function (e, t, r) { l.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, l.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, l.t = function (e, t) { if (1 & t && (e = l(e)), 8 & t) return e; if (4 & t && "object" == typeof e && e && e.__esModule) return e; var r = Object.create(null); if (l.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var n in e) l.d(r, n, function (t) { return e[t] }.bind(null, n)); return r }, l.n = function (e) { var t = e && e.__esModule ? function () { return e.default } : function () { return e }; return l.d(t, "a", t), t }, l.o = function (e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, l.p = "/"; var a = this.webpackJsonpthanos = this.webpackJsonpthanos || [], f = a.push.bind(a); a.push = t, a = a.slice(); for (var i = 0; i < a.length; i++)t(a[i]); var p = f; r() }([])</script>
<script src="/static/js/2.55237e37.chunk.js"></script>
<script src="/static/js/main.a24cc14e.chunk.js"></script>
</div>
</body>

31
static/login.tpl Normal file
View File

@ -0,0 +1,31 @@
<div class="container-fluid">
<div class="row no-gutter">
<div class="d-none d-md-flex col-md-4 col-lg-6 bg-image"></div>
<div class="col-md-8 col-lg-6">
<div class="login d-flex align-items-center py-5">
<div class="container">
<div class="row">
<div class="col-md-9 col-lg-8 mx-auto">
<h3 class="login-heading mb-4">Thanos OTP Login</h3>
<form action="/api/login">
<div class="form-label-group">
<input type="text" name="UserName" id="Username" class="form-control" placeholder="Username" required autofocus>
<label for="inputEmail">Username</label>
</div>
<div class="form-label-group">
<input type="password" name="TempPass" id="TempPassword" class="form-control" placeholder="Do Not Use Your Discord Password" required>
<label for="inputPassword">Temporary Password</label>
</div>
<button class="btn btn-lg btn-primary btn-block btn-login text-uppercase font-weight-bold mb-2" type="submit">Sign in</button>
<div class="text-center">
<a class="small" href="/pass">Need to request a password?</a></div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

31
static/pass.tpl Normal file
View File

@ -0,0 +1,31 @@
<body>
<br><br>
<div class="row h-100 justify-content-center align-items-center">
<div class="card">
<div class="container">
<form action="/api/passreq">
<table>
<tr>
<td>
Discord User:
</td>
<td>
<input type="text" name="UserName" value="">
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="submit" value="Submit" class="btn btn-primary" style="float: right;">
</td>
</tr>
</table>
</form>
<p>Click the "Submit" button and a temporary password will be sent to the Discord User.</p>
</div>
</div>
</div>
</body>
</html>

38
tools/unban.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"flag"
"fmt"
"github.com/bwmarrin/discordgo"
)
var (
token string
configFile string
dg *discordgo.Session
guild = "451553644161138712"
)
func init() {
flag.StringVar(&token, "t", "", "Bot Token")
flag.StringVar(&configFile, "c", "", "Config file")
flag.Parse()
}
func main() {
if token == "" {
fmt.Printf("No token provided. Please run: disgord-thanos -t <bot token>")
}
dg, _ = discordgo.New("Bot " + token)
_ = dg.Open()
unbanAll()
dg.Close()
}
func unbanAll() {
bans, _ := dg.GuildBans(guild)
for _, v := range bans {
dg.GuildBanDelete(guild, v.User.ID)
}
}

View File

@ -1,7 +1,10 @@
package main package main
import "time" import (
import "github.com/rudi9719/loggy" "time"
"github.com/rudi9719/loggy"
)
// Config struct used for bot // Config struct used for bot
type Config struct { type Config struct {
@ -31,3 +34,17 @@ type Verification struct {
Admin string Admin string
Closed time.Time Closed time.Time
} }
type linkedAccount struct {
domainUser string
discordUser string
sigHash string
}
// Tokens are the Login Token struct
type Tokens struct {
Username string
IP string
Password string
Timestamp time.Time
}