Compare commits
111 Commits
Current
...
BotCommand
| Author | SHA1 | Date | |
|---|---|---|---|
|
90383a3f8b
|
|||
|
d8bbdb57e1
|
|||
|
3ab280a056
|
|||
|
cfb3c5b57b
|
|||
|
fbd1f1f8ca
|
|||
|
f82db4166a
|
|||
|
37fe9fcaba
|
|||
|
d1a42a2de2
|
|||
|
0310008bce
|
|||
|
4caeade581
|
|||
|
75a063efe3
|
|||
|
5e6fe8fc52
|
|||
|
6b92467d1f
|
|||
|
6d0a99c1a9
|
|||
|
12d07ce1ff
|
|||
|
1b3980821a
|
|||
|
67e0167133
|
|||
|
0813bcdffa
|
|||
|
d33fd7aacd
|
|||
|
961f2f32b0
|
|||
|
3ddd84ae16
|
|||
|
86c95bb012
|
|||
|
41e39eb118
|
|||
|
22ccd9fd84
|
|||
|
efb6676c0a
|
|||
|
33a3270a49
|
|||
|
aeb8b4fb52
|
|||
|
988acb4a90
|
|||
|
950cab1baf
|
|||
|
ee4f6cdb3d
|
|||
|
a205581a44
|
|||
|
ed59ac11b8
|
|||
|
5770ce7fb9
|
|||
|
55c6d29ff7
|
|||
|
aa9eaf11f7
|
|||
|
8143f518f5
|
|||
|
d13da924b7
|
|||
|
00440c97ef
|
|||
|
a1eadc1857
|
|||
|
79aa40c476
|
|||
|
7c7857c1d7
|
|||
|
4e96ff10bf
|
|||
|
d6013c90b2
|
|||
|
101189d71b
|
|||
|
e4b0782d88
|
|||
|
5bfdbabf33
|
|||
|
aeafbf31bd
|
|||
|
11dfb49028
|
|||
|
edf12e2461
|
|||
|
87a08b0cd5
|
|||
|
f3de2e2857
|
|||
|
37837a508c
|
|||
|
86b499a5da
|
|||
|
44838b23f5
|
|||
|
a33940c6f2
|
|||
|
83de997220
|
|||
|
a5354ddd6e
|
|||
|
0b5df2733d
|
|||
|
75db9af48c
|
|||
|
13ebaee9fe
|
|||
|
2743ba1bdb
|
|||
|
a441fb5e63
|
|||
|
c8d3f7b0b5
|
|||
|
c1e261c85d
|
|||
|
938eac8348
|
|||
|
04dbd66c88
|
|||
|
5e70236ba1
|
|||
|
04097a9d3e
|
|||
|
68c821b71b
|
|||
|
8a6e029f83
|
|||
|
2a36f20a4c
|
|||
|
b5e72a88b9
|
|||
|
77ae7f149a
|
|||
|
9803136783
|
|||
|
cc63f82427
|
|||
|
6215426a2d
|
|||
|
7a41ebb48d
|
|||
|
62246e9c23
|
|||
|
011dc1362a
|
|||
|
277fda3a6d
|
|||
|
d1122bab29
|
|||
|
46f9df6dbd
|
|||
|
3bba74c076
|
|||
|
8460b10e9f
|
|||
|
f31215027a
|
|||
|
dc1b10afca
|
|||
|
8236715042
|
|||
|
0a2dc22837
|
|||
|
b172b1b57a
|
|||
|
957d34f6c4
|
|||
|
b2832bcef0
|
|||
|
dd1f280495
|
|||
|
c942df7cd5
|
|||
|
383a4ba691
|
|||
|
19d9eaae2d
|
|||
|
c36cd98623
|
|||
|
f23dba9454
|
|||
|
9bdf48602d
|
|||
|
5d0492695a
|
|||
|
970d003e2f
|
|||
|
46963ab2d6
|
|||
|
1d7d2b1835
|
|||
|
67c48eb260
|
|||
|
c3f2fa00de
|
|||
|
78c368c832
|
|||
|
c03f27249b
|
|||
|
7afa4bdb8b
|
|||
|
dece68594d
|
|||
|
1c4aba2780
|
|||
|
0a575c395a
|
|||
|
f8e7051e79
|
@ -8,6 +8,8 @@ steps:
|
||||
commands:
|
||||
- go get github.com/bwmarrin/discordgo
|
||||
- go get github.com/rudi9719/loggy
|
||||
- go get github.com/gorilla/mux
|
||||
- go get github.com/gorilla/sessions
|
||||
- go vet -v
|
||||
- go build
|
||||
- name: gitea_release
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,5 +1,10 @@
|
||||
build/
|
||||
.DS_Store
|
||||
public/
|
||||
disgord-thanos
|
||||
disgord-Thanos
|
||||
config.json
|
||||
start.sh
|
||||
dev.html
|
||||
verifications/*
|
||||
node_modules/
|
||||
|
||||
BIN
ThanosAPI.png
Normal file
BIN
ThanosAPI.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
188
auth.go
Normal file
188
auth.go
Normal file
@ -0,0 +1,188 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"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, ""
|
||||
}
|
||||
|
||||
func userFromID(i string) discordgo.User {
|
||||
u, err := dg.GuildMember(config.GuildID, i)
|
||||
if err != nil {
|
||||
log.LogErrorType(err)
|
||||
return discordgo.User{}
|
||||
}
|
||||
return *u.User
|
||||
}
|
||||
|
||||
func idFromUsername(username string) string {
|
||||
userID := ""
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
return userID
|
||||
}
|
||||
176
commands.go
Normal file
176
commands.go
Normal file
@ -0,0 +1,176 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func setupCommands() {
|
||||
reboot := Command{
|
||||
Name: "Reboot",
|
||||
Keywords: []string{"reboot", "re", "restart"},
|
||||
Exec: Reboot,
|
||||
}
|
||||
commands = append(commands, reboot)
|
||||
|
||||
bumpset := Command{
|
||||
Name: "BumpSet",
|
||||
Keywords: []string{"bs", "bumpset", "bumps"},
|
||||
Exec: BumpSet,
|
||||
}
|
||||
commands = append(commands, bumpset)
|
||||
|
||||
retrieveVerification := Command{
|
||||
Name: "Retrieve Verification",
|
||||
Keywords: []string{"veri", "verification", "retrieve"},
|
||||
Exec: RetrieveVerification,
|
||||
}
|
||||
commands = append(commands, retrieveVerification)
|
||||
|
||||
addQuote := Command{
|
||||
Name: "Add Quote",
|
||||
Keywords: []string{"quote", "addq", "q"},
|
||||
Exec: AddQuote,
|
||||
}
|
||||
commands = append(commands, addQuote)
|
||||
|
||||
snap := Command{
|
||||
Name: "Snap",
|
||||
Keywords: []string{"snap", "purge", "sn"},
|
||||
Exec: Snap,
|
||||
}
|
||||
commands = append(commands, snap)
|
||||
|
||||
status := Command{
|
||||
Name: "Status",
|
||||
Keywords: []string{"st", "status", "stats"},
|
||||
Exec: Status,
|
||||
}
|
||||
commands = append(commands, status)
|
||||
}
|
||||
|
||||
func Reboot(b BotCommand) bool {
|
||||
defer log.PanicSafe()
|
||||
if strings.Contains(b.Message.Content, rebootToken) {
|
||||
exit(b.Session)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func BumpSet(b BotCommand) bool {
|
||||
defer log.PanicSafe()
|
||||
bump = false
|
||||
parts := strings.Split(b.Message.Content, " ")
|
||||
timer, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
b.Session.ChannelMessageSend(b.Message.ChannelID, fmt.Sprintf("Unable to decode timer: %+v", parts[1]))
|
||||
return false
|
||||
}
|
||||
config.BumpTime = time.Now().Add(time.Duration(timer) * time.Minute).Add(-2 * time.Hour)
|
||||
b.Session.ChannelMessageSend(b.Message.ChannelID, fmt.Sprintf("New bump time: %+v, expecting bump at %+v", config.BumpTime, config.BumpTime.Add(2*time.Hour)))
|
||||
return true
|
||||
}
|
||||
|
||||
func RetrieveVerification(b BotCommand) bool {
|
||||
defer log.PanicSafe()
|
||||
s := b.Session
|
||||
m := b.Message
|
||||
parts := strings.Split(m.Content, " ")
|
||||
discordId := parts[1]
|
||||
_, err := strconv.Atoi(discordId)
|
||||
if err != nil {
|
||||
discordId = idFromUsername(discordId)
|
||||
}
|
||||
user, err := s.GuildMember(config.GuildID, discordId)
|
||||
if err != nil {
|
||||
log.LogErrorType(err)
|
||||
}
|
||||
|
||||
matches, err := filepath.Glob(fmt.Sprintf("./verifications/*%+v*", discordId))
|
||||
if err != nil {
|
||||
log.LogErrorType(err)
|
||||
return false
|
||||
}
|
||||
if len(matches) != 1 {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Error finding verification for ID %+v", discordId))
|
||||
return false
|
||||
}
|
||||
|
||||
verificationImage, err := os.Open(matches[0])
|
||||
if err != nil {
|
||||
log.LogErrorType(err)
|
||||
return false
|
||||
}
|
||||
msg := fmt.Sprintf("```%+v\nJoined: %+v\n```", user.User.Username, user.JoinedAt)
|
||||
s.ChannelFileSendWithMessage(m.ChannelID, msg, fmt.Sprintf("%+v Verification", discordId), verificationImage)
|
||||
return true
|
||||
}
|
||||
|
||||
func AddQuote(b BotCommand) bool {
|
||||
defer log.PanicSafe()
|
||||
quotes = append(quotes, strings.ReplaceAll(b.Message.Content, b.Command, ""))
|
||||
return true
|
||||
}
|
||||
|
||||
func Snap(b BotCommand) bool {
|
||||
defer log.PanicSafe()
|
||||
go runPurge(b.Session)
|
||||
b.Session.ChannelMessageSend(config.AdminChannel, quotes[rand.Intn(len(quotes))])
|
||||
return true
|
||||
}
|
||||
|
||||
func Status(b BotCommand) bool {
|
||||
defer log.PanicSafe()
|
||||
status := fmt.Sprintf("Uptime: %+v\n", time.Since(startupTime))
|
||||
status += fmt.Sprintf("Last bump: %+v\n", time.Since(config.BumpTime))
|
||||
status += fmt.Sprintf("Last bumper: <@%+v>\n", userFromID(config.LastBumper).Username)
|
||||
status += fmt.Sprintf("Bump needed: %+v\n", bump)
|
||||
if len(config.Unverified) > 0 {
|
||||
status += "Unverified users:\n```"
|
||||
for k, v := range config.Unverified {
|
||||
uvUser := userFromID(k)
|
||||
status += fmt.Sprintf("\n%+v will be removed in %+v", uvUser.Username, time.Until(v.Add(1*time.Hour)))
|
||||
}
|
||||
status += "```"
|
||||
} else {
|
||||
status += "There are no unverified users.\n"
|
||||
}
|
||||
if len(config.Verifications) > 0 {
|
||||
status += "Pending verifications:\n"
|
||||
status += "```"
|
||||
for _, v := range config.Verifications {
|
||||
status += fmt.Sprintf("%+v has submitted a verification.", v.Username)
|
||||
}
|
||||
status += "```"
|
||||
} else {
|
||||
status += "There are no pending verifications."
|
||||
}
|
||||
if len(config.Probations) > 0 {
|
||||
status += "\nThe following users are on probation: \n```"
|
||||
for uid, join := range config.Probations {
|
||||
probationUser := userFromID(uid)
|
||||
status += fmt.Sprintf("%+v for another %+v\n", probationUser.Username, time.Until(join.Add(2*time.Hour)))
|
||||
}
|
||||
status += "```"
|
||||
}
|
||||
b.Session.ChannelMessageSend(config.AdminChannel, status)
|
||||
statistics := "```"
|
||||
for k, v := range config.Stats {
|
||||
adminUser, err := b.Session.GuildMember(config.GuildID, k)
|
||||
if err == nil {
|
||||
statistics += fmt.Sprintf("\n%+v: %+v", adminUser.User.Username, v+1)
|
||||
} else {
|
||||
log.LogErrorType(err)
|
||||
}
|
||||
}
|
||||
statistics += "\n```"
|
||||
log.LogInfo("Private statistics: %+v", statistics)
|
||||
go runPurge(b.Session)
|
||||
return true
|
||||
}
|
||||
108
config.go
108
config.go
@ -3,63 +3,17 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
func status(s *discordgo.Session) {
|
||||
defer log.PanicSafe()
|
||||
status := fmt.Sprintf("Uptime: %+v\n", time.Since(startupTime))
|
||||
status += fmt.Sprintf("Last bump: %+v\n", time.Since(config.BumpTime))
|
||||
status += fmt.Sprintf("Last bumper: <@%+v>\n", config.LastBumper)
|
||||
status += fmt.Sprintf("Bump needed: %+v\n", bump)
|
||||
if len(config.Unverified) > 0 {
|
||||
status += "Unverified users:\n```"
|
||||
for k, v := range config.Unverified {
|
||||
uvUser := userFromID(s, k)
|
||||
status += fmt.Sprintf("\n%+v will be removed in %+v", uvUser.Username, time.Until(v.Add(1*time.Hour)))
|
||||
}
|
||||
status += "```"
|
||||
} else {
|
||||
status += "There are no unverified users.\n"
|
||||
}
|
||||
if len(config.Verifications) > 0 {
|
||||
status += "Pending verifications:\n"
|
||||
status += "```"
|
||||
for _, v := range config.Verifications {
|
||||
status += fmt.Sprintf("%+v has submitted a verification.", v.Username)
|
||||
}
|
||||
status += "```"
|
||||
} else {
|
||||
status += "There are no pending verifications."
|
||||
}
|
||||
if len(config.Probations) > 0 {
|
||||
status += "\nThe following users are on probation: \n```"
|
||||
for uid, join := range config.Probations {
|
||||
probationUser := userFromID(s, uid)
|
||||
status += fmt.Sprintf("%+v for another %+v\n", probationUser.Username, time.Until(join.Add(2*time.Hour)))
|
||||
}
|
||||
status += "```"
|
||||
}
|
||||
s.ChannelMessageSend(config.AdminChannel, status)
|
||||
statistics := "```"
|
||||
for k, v := range config.Stats {
|
||||
adminUser, err := s.GuildMember(config.GuildID, k)
|
||||
if err == nil {
|
||||
statistics += fmt.Sprintf("\n%+v: %+v", adminUser.User.Username, v+1)
|
||||
} else {
|
||||
log.LogErrorType(err)
|
||||
}
|
||||
}
|
||||
statistics += "\n```"
|
||||
log.LogInfo("Private statistics: %+v", statistics)
|
||||
go runPurge(s)
|
||||
return
|
||||
}
|
||||
|
||||
func loadConfig() {
|
||||
var c Config
|
||||
confFile, _ := ioutil.ReadFile(configFile)
|
||||
@ -104,6 +58,23 @@ func saveConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
func adminInteraction(s *discordgo.Session, m string) {
|
||||
admin, _ := s.GuildMember(config.GuildID, m)
|
||||
counter, ok := config.Stats[admin.User.ID]
|
||||
if !ok {
|
||||
config.Stats[admin.User.ID] = 0
|
||||
} else {
|
||||
config.Stats[admin.User.ID] = counter + 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func rebootBump() {
|
||||
time.Sleep(time.Until(config.BumpTime.Add(2 * time.Hour)))
|
||||
dg.ChannelMessageSend(config.AdminChannel, "!d bump is ready")
|
||||
|
||||
}
|
||||
|
||||
func bumpTimer(s *discordgo.Session) {
|
||||
if !bump {
|
||||
return
|
||||
@ -117,6 +88,7 @@ func bumpTimer(s *discordgo.Session) {
|
||||
s.ChannelMessageSend(config.AdminChannel, "!d bump is ready.")
|
||||
bump = true
|
||||
}
|
||||
|
||||
func purgeTimer(s *discordgo.Session) {
|
||||
for {
|
||||
runPurge(s)
|
||||
@ -139,22 +111,28 @@ func (v Verification) prettyPrint() string {
|
||||
return ret
|
||||
}
|
||||
|
||||
func userFromID(s *discordgo.Session, i string) discordgo.User {
|
||||
u, err := s.GuildMember(config.GuildID, i)
|
||||
func storeVerification(v Verification) {
|
||||
defer log.PanicSafe()
|
||||
fileURL, _ := url.Parse(v.Photo)
|
||||
path := fileURL.Path
|
||||
segments := strings.Split(path, "/")
|
||||
|
||||
fileName := segments[len(segments)-1]
|
||||
file, _ := os.Create(fmt.Sprintf("./verifications/%s-%s-%s", v.UserID, v.Username, fileName))
|
||||
client := http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
r.URL.Opaque = r.URL.Path
|
||||
return nil
|
||||
},
|
||||
}
|
||||
resp, err := client.Get(v.Photo)
|
||||
if err != nil {
|
||||
log.LogErrorType(err)
|
||||
return discordgo.User{}
|
||||
log.LogError("Unable to download verification %s-%s-%s", v.UserID, v.Username, fileName)
|
||||
}
|
||||
return *u.User
|
||||
defer resp.Body.Close()
|
||||
defer file.Close()
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
if err != nil {
|
||||
log.LogError("Unable to store verification %s-%s-%s", v.UserID, v.Username, fileName)
|
||||
}
|
||||
|
||||
func adminInteraction(s *discordgo.Session, m string) {
|
||||
admin, _ := s.GuildMember(config.GuildID, m)
|
||||
counter, ok := config.Stats[admin.User.ID]
|
||||
if !ok {
|
||||
config.Stats[admin.User.ID] = 0
|
||||
} else {
|
||||
config.Stats[admin.User.ID] = counter + 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
116
discordEvents.go
Normal file
116
discordEvents.go
Normal file
@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
func ready(s *discordgo.Session, event *discordgo.Ready) {
|
||||
// Set the playing status.
|
||||
s.UpdateGameStatus(0, fmt.Sprintf("DreamDaddy v%+v %+v", version, gitCommit))
|
||||
}
|
||||
|
||||
func guildMemberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
|
||||
defer log.PanicSafe()
|
||||
for role := range m.Roles {
|
||||
if fmt.Sprintf("%+v", role) == config.MonitorRole {
|
||||
s.ChannelMessageSend(config.AdminChannel, "New unverified user detected.")
|
||||
s.ChannelMessageSend(config.MonitorChann, fmt.Sprintf("Welcome %+v, you may PM me your verification, or I will ban you in an hour!\nSay \"!rules\" in this channel, without quotes for the rules. You may private/direct message me for verification instructions.\n\nYou will not be able to read/see other channels or users until you verify.", m.User.Mention()))
|
||||
config.Unverified[m.User.ID] = time.Now()
|
||||
config.Probations[m.User.ID] = time.Now()
|
||||
saveConfig()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func guildMemberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
||||
defer log.PanicSafe()
|
||||
config.Unverified[m.User.ID] = time.Now()
|
||||
config.Probations[m.User.ID] = time.Now()
|
||||
s.GuildMemberRoleAdd(config.GuildID, m.User.ID, config.MonitorRole)
|
||||
s.ChannelMessageSend(config.MonitorChann, fmt.Sprintf("Welcome %+v, you may PM me your verification, or I will ban you in an hour!\nSay \"!rules\" in this channel, without quotes for the rules. You may private/direct message me for verification instructions.\n\nYou will not be able to read/see other channels or users until you verify.", m.User.Mention()))
|
||||
saveConfig()
|
||||
}
|
||||
|
||||
func guildMemberBanned(s *discordgo.Session, m *discordgo.GuildBanAdd) {
|
||||
defer log.PanicSafe()
|
||||
for uid := range config.Probations {
|
||||
if m.User.Email == uid {
|
||||
delete(config.Probations, uid)
|
||||
s.ChannelMessageDelete(config.IntroChann, introMsg[uid])
|
||||
}
|
||||
}
|
||||
saveConfig()
|
||||
}
|
||||
|
||||
func guildMemberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
||||
defer log.PanicSafe()
|
||||
go runPurge(s)
|
||||
banned := false
|
||||
for uid, join := range config.Probations {
|
||||
if time.Since(join) < 2*time.Hour {
|
||||
if m.User.ID == uid {
|
||||
banned = true
|
||||
s.GuildBanCreateWithReason(config.GuildID, m.User.ID, fmt.Sprintf("Left within 2 hours of joining. %+v", time.Since(join)), 0)
|
||||
delete(config.Probations, uid)
|
||||
s.ChannelMessageDelete(config.IntroChann, introMsg[uid])
|
||||
}
|
||||
} else {
|
||||
delete(config.Probations, uid)
|
||||
s.ChannelMessageDelete(config.IntroChann, introMsg[uid])
|
||||
}
|
||||
}
|
||||
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)
|
||||
s.ChannelMessageDelete(config.IntroChann, introMsg[m.User.ID])
|
||||
}
|
||||
}
|
||||
saveConfig()
|
||||
|
||||
}
|
||||
|
||||
func readReaction(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
|
||||
defer log.PanicSafe()
|
||||
if m.ChannelID != config.AdminChannel || m.UserID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
admin, _ := s.GuildMember(config.GuildID, m.UserID)
|
||||
adminInteraction(s, admin.User.ID)
|
||||
verification, ok := config.Verifications[m.MessageID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
verification.Admin = admin.User.Username
|
||||
verification.Closed = time.Now()
|
||||
user := userFromID(verification.UserID)
|
||||
if user.ID == "" {
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v, that user was not found, they might have left.", admin.Mention()))
|
||||
delete(config.Verifications, m.MessageID)
|
||||
return
|
||||
}
|
||||
if m.Emoji.Name == "👎" {
|
||||
rejectVerification(s, user)
|
||||
verification.Status = "Rejected"
|
||||
} else if m.Emoji.Name == "👍" {
|
||||
verifyMember(s, user)
|
||||
verification.Status = "Accepted"
|
||||
go storeVerification(verification)
|
||||
} else if m.Emoji.Name == "👶" {
|
||||
requestAge(s, user)
|
||||
log.LogInfo("%+v has requested ASL for user %+v.", admin.User.Username, user.Username)
|
||||
return
|
||||
} else if m.Emoji.Name == "⛔" {
|
||||
s.GuildBanCreateWithReason(config.GuildID, user.ID, fmt.Sprintf("Underage or too many failed verifications. %+v", admin.User.Username), 5)
|
||||
verification.Status = "Banned"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
log.LogInfo("%+v", verification.prettyPrint())
|
||||
delete(config.Verifications, m.MessageID)
|
||||
}
|
||||
106
discordMessage.go
Normal file
106
discordMessage.go
Normal file
@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
defer log.PanicSafe()
|
||||
var b BotCommand
|
||||
if strings.HasPrefix(m.Content, s.State.User.Mention()) {
|
||||
b = BotCommand{
|
||||
Session: s,
|
||||
Message: m,
|
||||
Parts: strings.Split(m.Content, " ")[1:],
|
||||
}
|
||||
}
|
||||
if m.Author.ID == s.State.User.ID || m.Author.Bot {
|
||||
return
|
||||
}
|
||||
if m.GuildID == "" {
|
||||
handlePM(s, m)
|
||||
return
|
||||
}
|
||||
if m.ChannelID == config.MonitorChann {
|
||||
if strings.Contains(m.Content, "erif") && !m.Author.Bot {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v send me a private message for verification.", m.Author.Mention()))
|
||||
}
|
||||
return
|
||||
}
|
||||
for role := range m.Member.Roles {
|
||||
if fmt.Sprintf("%+v", role) == config.AdminRole {
|
||||
adminInteraction(s, m.Author.ID)
|
||||
}
|
||||
}
|
||||
if m.ChannelID != config.AdminChannel {
|
||||
lastActiveChan = m.ChannelID
|
||||
lastActiveTime = time.Now()
|
||||
}
|
||||
if strings.HasPrefix(m.Content, "!d bump") {
|
||||
if time.Since(config.BumpTime) < 2*time.Hour {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Sorry, <@%+v> already claimed the bump. Better luck next time!", config.LastBumper))
|
||||
return
|
||||
}
|
||||
config.LastBumper = m.Author.ID
|
||||
go bumpTimer(s)
|
||||
return
|
||||
}
|
||||
if time.Since(config.BumpTime) > 2*time.Hour {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v please say \"!d bump\" without the quotes to bump our server :)", m.Author.Mention()))
|
||||
}
|
||||
if m.ChannelID == config.AdminChannel {
|
||||
if strings.HasPrefix(m.Content, s.State.User.Mention()) {
|
||||
for _, cmd := range commands {
|
||||
for _, keyword := range cmd.Keywords {
|
||||
if strings.Contains(m.Content, keyword) {
|
||||
b.Command = keyword
|
||||
if !cmd.Exec(b) {
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("There was an error running %+v\n%+v", cmd.Name, cmd.Help))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePM(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
defer log.PanicSafe()
|
||||
if strings.Contains(m.Content, "Rule") || strings.Contains(m.Content, "rule") {
|
||||
s.ChannelMessageSend(m.ChannelID, "I specifically said to say \"!rules\" without quotes in the unverified channel for the rules.")
|
||||
}
|
||||
for _, uid := range config.Verifications {
|
||||
user := userFromID(uid.UserID)
|
||||
if m.Author.ID == user.ID {
|
||||
s.ChannelMessageSend(m.ChannelID, "Your verification is pending. An admin will respond to it when they are available.")
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v said: %+v", m.Author.Mention(), m.Content))
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(m.Attachments) != 1 {
|
||||
s.ChannelMessageSend(m.ChannelID, "```I am a bot and this is an autoreply.\n\nUntil you send a verification, I will always say the following message:```\nYou may only send me your verification (and nothing else) to be passed to the admins (and no one else). Verification is a clear full face pic, with your pinky finger held to the corner of your mouth.")
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v said: %+v", m.Author.Mention(), m.Content))
|
||||
return
|
||||
}
|
||||
if strings.HasSuffix(strings.ToUpper(m.Attachments[0].ProxyURL), "HEIC") {
|
||||
s.ChannelMessageSend(m.ChannelID, "You have tried to send an unsupported file (HEIC). Please try again using an image (jpeg, jpg, png, etc).")
|
||||
return
|
||||
}
|
||||
delete(config.Unverified, m.Author.ID)
|
||||
var v Verification
|
||||
v.Submitted = time.Now()
|
||||
v.UserID = m.Author.ID
|
||||
v.Username = m.Author.Username
|
||||
v.Photo = m.Attachments[0].ProxyURL
|
||||
v.Status = "Submitted"
|
||||
msg, _ := s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v\n%+v", v.Username, v.Photo))
|
||||
config.Verifications[msg.ID] = v
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👎")
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👍")
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👶")
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "⛔")
|
||||
}
|
||||
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
||||
module git.nightmare.haus/rudi/disgord-thanos
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.23.2
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/rudi9719/loggy v0.0.0-20201031035735-9438c484de9a
|
||||
)
|
||||
16
go.sum
Normal file
16
go.sum
Normal file
@ -0,0 +1,16 @@
|
||||
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
|
||||
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/rudi9719/loggy v0.0.0-20201031035735-9438c484de9a h1:4rkaWoLCWOmra5Mw/dLAWjtDLT/+i5uTX1qhlMVL8WA=
|
||||
github.com/rudi9719/loggy v0.0.0-20201031035735-9438c484de9a/go.mod h1:s1ANCN8bF6HwwTpJLR458MFVGua9oqKKDbph/2jptL4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
samhofi.us/x/keybase v0.0.0-20200129212102-e05e93be9f3f h1:MHSEiuiRFrFi7BTw46lC22PMk3Fit8IvVRM4xANTt20=
|
||||
samhofi.us/x/keybase v0.0.0-20200129212102-e05e93be9f3f/go.mod h1:fcva80IUFyWcHtV4bBSzgKg07K6Rvuvi3GtGCLNGkyE=
|
||||
232
main.go
232
main.go
@ -3,13 +3,9 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@ -29,8 +25,13 @@ var (
|
||||
token string
|
||||
configFile string
|
||||
setupMsg string
|
||||
dg *discordgo.Session
|
||||
lastPM = make(map[string]time.Time)
|
||||
introMsg = make(map[string]string)
|
||||
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."}
|
||||
version = "3.0"
|
||||
gitCommit string
|
||||
commands []Command
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -40,6 +41,7 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
go runWeb()
|
||||
defer log.PanicSafe()
|
||||
if configFile == "" {
|
||||
configFile = "config.json"
|
||||
@ -55,7 +57,8 @@ func main() {
|
||||
}
|
||||
|
||||
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 {
|
||||
log.LogErrorType(err)
|
||||
log.LogPanic("Unable to create bot using token.")
|
||||
@ -65,6 +68,7 @@ func main() {
|
||||
dg.AddHandler(guildMemberRemove)
|
||||
dg.AddHandler(guildMemberAdd)
|
||||
dg.AddHandler(guildMemberBanned)
|
||||
go setupCommands()
|
||||
dg.AddHandler(messageCreate)
|
||||
dg.AddHandler(readReaction)
|
||||
dg.AddHandler(guildMemberUpdate)
|
||||
@ -78,6 +82,7 @@ func main() {
|
||||
|
||||
log.LogInfo("Thanos is now running. Press CTRL-C to exit.")
|
||||
go purgeTimer(dg)
|
||||
go rebootBump()
|
||||
sc := make(chan os.Signal, 1)
|
||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||
<-sc
|
||||
@ -99,6 +104,7 @@ func runPurge(s *discordgo.Session) {
|
||||
for uid, join := range config.Probations {
|
||||
if time.Since(join) > 2*time.Hour {
|
||||
delete(config.Probations, uid)
|
||||
s.ChannelMessageDelete(config.IntroChann, introMsg[uid])
|
||||
}
|
||||
}
|
||||
for k, v := range config.Unverified {
|
||||
@ -150,72 +156,14 @@ func runPurge(s *discordgo.Session) {
|
||||
saveConfig()
|
||||
}
|
||||
|
||||
func ready(s *discordgo.Session, event *discordgo.Ready) {
|
||||
// Set the playing status.
|
||||
s.UpdateStatus(0, "DreamDaddy v1.0")
|
||||
}
|
||||
|
||||
func guildMemberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
|
||||
defer log.PanicSafe()
|
||||
for role := range m.Roles {
|
||||
if fmt.Sprintf("%+v", role) == config.MonitorRole {
|
||||
s.ChannelMessageSend(config.AdminChannel, "New unverified user detected.")
|
||||
s.ChannelMessageSend(config.MonitorChann, fmt.Sprintf("Welcome %+v, you may PM me your verification, or I will ban you in an hour!\nSay \"!rules\" in this channel, without quotes for the rules. You may private/direct message me for verification instructions.\n\nYou will not be able to read/see other channels or users until you verify.", m.User.Mention()))
|
||||
config.Unverified[m.User.ID] = time.Now()
|
||||
config.Probations[m.User.ID] = time.Now()
|
||||
saveConfig()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func guildMemberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
||||
defer log.PanicSafe()
|
||||
config.Unverified[m.User.ID] = time.Now()
|
||||
config.Probations[m.User.ID] = time.Now()
|
||||
s.GuildMemberRoleAdd(config.GuildID, m.User.ID, config.MonitorRole)
|
||||
s.ChannelMessageSend(config.MonitorChann, fmt.Sprintf("Welcome %+v, you may PM me your verification, or I will ban you in an hour!\nSay \"!rules\" in this channel, without quotes for the rules. You may private/direct message me for verification instructions.\n\nYou will not be able to read/see other channels or users until you verify.", m.User.Mention()))
|
||||
saveConfig()
|
||||
}
|
||||
|
||||
func guildMemberBanned(s *discordgo.Session, m *discordgo.GuildBanAdd) {
|
||||
defer log.PanicSafe()
|
||||
for uid := range config.Probations {
|
||||
if m.User.Email == uid {
|
||||
delete(config.Probations, uid)
|
||||
}
|
||||
}
|
||||
saveConfig()
|
||||
}
|
||||
|
||||
func guildMemberRemove(s *discordgo.Session, m *discordgo.GuildMemberRemove) {
|
||||
defer log.PanicSafe()
|
||||
go runPurge(s)
|
||||
banned := false
|
||||
for uid, join := range config.Probations {
|
||||
if time.Since(join) < 2*time.Hour {
|
||||
if m.User.ID == uid {
|
||||
banned = true
|
||||
s.GuildBanCreateWithReason(config.GuildID, m.User.ID, fmt.Sprintf("Left within 2 hours of joining. %+v", time.Since(join)), 0)
|
||||
delete(config.Probations, uid)
|
||||
}
|
||||
} else {
|
||||
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))
|
||||
saveConfig()
|
||||
|
||||
}
|
||||
|
||||
func verifyMember(s *discordgo.Session, u discordgo.User) {
|
||||
defer log.PanicSafe()
|
||||
s.GuildMemberRoleAdd(config.GuildID, u.ID, config.VerifiedRole)
|
||||
s.GuildMemberRoleRemove(config.GuildID, u.ID, config.MonitorRole)
|
||||
st, _ := s.UserChannelCreate(u.ID)
|
||||
s.ChannelMessageSend(st.ID, "Your verification has been accepted, welcome!")
|
||||
s.ChannelMessageSend(config.IntroChann, fmt.Sprintf("Welcome %+v please introduce yourself! :) feel free to check out <#710557387937022034> to tag your roles. Also please mute any channels you are not interested in!", u.Mention()))
|
||||
m, _ := s.ChannelMessageSend(config.IntroChann, fmt.Sprintf("Welcome %+v please introduce yourself! :) feel free to check out <#710557387937022034> to tag your roles. Also please mute any channels you are not interested in!", u.Mention()))
|
||||
introMsg[u.ID] = m.ID
|
||||
}
|
||||
|
||||
func rejectVerification(s *discordgo.Session, u discordgo.User) {
|
||||
@ -230,158 +178,6 @@ func rejectVerification(s *discordgo.Session, u discordgo.User) {
|
||||
func requestAge(s *discordgo.Session, u discordgo.User) {
|
||||
defer log.PanicSafe()
|
||||
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.")
|
||||
|
||||
}
|
||||
|
||||
func handlePM(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
defer log.PanicSafe()
|
||||
if strings.Contains(m.Content, "Rule") || strings.Contains(m.Content, "rule") {
|
||||
s.ChannelMessageSend(m.ChannelID, "I specifically said to say \"!rules\" without quotes in the unverified channel for the rules.")
|
||||
}
|
||||
for _, uid := range config.Verifications {
|
||||
user := userFromID(s, uid.UserID)
|
||||
if m.Author.ID == user.ID {
|
||||
s.ChannelMessageSend(m.ChannelID, "Your verification is pending. An admin will respond to it when they are available.")
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v said: %+v", m.Author.Mention(), m.Content))
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(m.Attachments) != 1 {
|
||||
s.ChannelMessageSend(m.ChannelID, "```I am a bot and this is an autoreply.\n\nUntil you send a verification, I will always say the following message:```\nYou may only send me your verification (and nothing else) to be passed to the admins (and no one else). Verification is a clear full face pic, with your pinky finger held to the corner of your mouth.")
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v said: %+v", m.Author.Mention(), m.Content))
|
||||
return
|
||||
}
|
||||
delete(config.Unverified, m.Author.ID)
|
||||
var v Verification
|
||||
v.Submitted = time.Now()
|
||||
v.UserID = m.Author.ID
|
||||
v.Username = m.Author.Username
|
||||
v.Photo = m.Attachments[0].ProxyURL
|
||||
v.Status = "Submitted"
|
||||
msg, _ := s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v\n%+v", v.Username, v.Photo))
|
||||
config.Verifications[msg.ID] = v
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👎")
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👍")
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "👶")
|
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "⛔")
|
||||
}
|
||||
|
||||
func readReaction(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
|
||||
defer log.PanicSafe()
|
||||
if m.ChannelID != config.AdminChannel || m.UserID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
admin, _ := s.GuildMember(config.GuildID, m.UserID)
|
||||
adminInteraction(s, admin.User.ID)
|
||||
verification, ok := config.Verifications[m.MessageID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
verification.Admin = admin.User.Username
|
||||
verification.Closed = time.Now()
|
||||
user := userFromID(s, verification.UserID)
|
||||
if user.ID == "" {
|
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("%+v, that user was not found, they might have left.", admin.Mention()))
|
||||
delete(config.Verifications, m.MessageID)
|
||||
return
|
||||
}
|
||||
if m.Emoji.Name == "👎" {
|
||||
rejectVerification(s, user)
|
||||
verification.Status = "Rejected"
|
||||
} else if m.Emoji.Name == "👍" {
|
||||
verifyMember(s, user)
|
||||
verification.Status = "Accepted"
|
||||
go storeVerification(verification)
|
||||
} else if m.Emoji.Name == "👶" {
|
||||
requestAge(s, user)
|
||||
log.LogInfo("%+v has requested ASL for user %+v.", admin.User.Username, user.Username)
|
||||
return
|
||||
} else if m.Emoji.Name == "⛔" {
|
||||
s.GuildBanCreateWithReason(config.GuildID, user.ID, fmt.Sprintf("Underage or too many failed verifications. %+v", admin.User.Username), 5)
|
||||
verification.Status = "Banned"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
log.LogInfo("%+v", verification.prettyPrint())
|
||||
delete(config.Verifications, m.MessageID)
|
||||
}
|
||||
func storeVerification(v Verification) {
|
||||
defer log.PanicSafe()
|
||||
fileURL, _ := url.Parse(v.Photo)
|
||||
path := fileURL.Path
|
||||
segments := strings.Split(path, "/")
|
||||
|
||||
fileName := segments[len(segments)-1]
|
||||
file, _ := os.Create(fmt.Sprintf("./verifications/%s-%s-%s", v.UserID, v.Username, fileName))
|
||||
client := http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
r.URL.Opaque = r.URL.Path
|
||||
return nil
|
||||
},
|
||||
}
|
||||
resp, err := client.Get(v.Photo)
|
||||
if err != nil {
|
||||
log.LogError("Unable to download verification %s-%s-%s", v.UserID, v.Username, fileName)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer file.Close()
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
if err != nil {
|
||||
log.LogError("Unable to store verification %s-%s-%s", v.UserID, v.Username, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
defer log.PanicSafe()
|
||||
if m.Author.ID == s.State.User.ID || m.Author.Bot {
|
||||
return
|
||||
}
|
||||
if m.GuildID == "" {
|
||||
handlePM(s, m)
|
||||
return
|
||||
}
|
||||
if m.ChannelID == config.MonitorChann {
|
||||
if strings.Contains(m.Content, "erif") && !m.Author.Bot {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v send me a private message for verification.", m.Author.Mention()))
|
||||
}
|
||||
return
|
||||
}
|
||||
for role := range m.Member.Roles {
|
||||
if fmt.Sprintf("%+v", role) == config.AdminRole {
|
||||
adminInteraction(s, m.Author.ID)
|
||||
}
|
||||
}
|
||||
if m.ChannelID != config.AdminChannel {
|
||||
lastActiveChan = m.ChannelID
|
||||
lastActiveTime = time.Now()
|
||||
}
|
||||
if strings.HasPrefix(m.Content, "!d bump") {
|
||||
if time.Since(config.BumpTime) < 2*time.Hour {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Sorry, <@%+v> already claimed the bump. Better luck next time!", config.LastBumper))
|
||||
return
|
||||
}
|
||||
config.LastBumper = m.Author.ID
|
||||
go bumpTimer(s)
|
||||
return
|
||||
}
|
||||
if time.Since(config.BumpTime) > 2*time.Hour {
|
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v please say \"!d bump\" without the quotes to bump our server :)", m.Author.Mention()))
|
||||
}
|
||||
if m.ChannelID == config.AdminChannel {
|
||||
if strings.HasPrefix(m.Content, rebootToken) {
|
||||
exit(s)
|
||||
}
|
||||
if strings.HasPrefix(m.Content, "!quote") {
|
||||
quotes = append(quotes, strings.ReplaceAll(m.Content, "!quote", ""))
|
||||
}
|
||||
if strings.HasPrefix(m.Content, "!snap") || strings.HasPrefix(m.Content, "!purge") {
|
||||
go runPurge(s)
|
||||
s.ChannelMessageSend(config.AdminChannel, quotes[rand.Intn(len(quotes))])
|
||||
}
|
||||
if strings.HasPrefix(m.Content, "!st") {
|
||||
go status(s)
|
||||
saveConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
249
site-api.go
Normal file
249
site-api.go
Normal 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
21
static/404.tpl
Normal 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
21
static/500.tpl
Normal 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>
|
||||
149
static/app.js
Normal file
149
static/app.js
Normal file
@ -0,0 +1,149 @@
|
||||
|
||||
// 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
11
static/card.tpl
Normal 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>
|
||||
210
static/components.js
Normal file
210
static/components.js
Normal file
@ -0,0 +1,210 @@
|
||||
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
44
static/header.tpl
Normal 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
17
static/home.tpl
Normal 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
73
static/index.css
Normal 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
91
static/index.html
Normal 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">×</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
8
static/loggedIn.tpl
Normal 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
31
static/login.tpl
Normal 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
31
static/pass.tpl
Normal 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
38
tools/unban.go
Normal 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)
|
||||
}
|
||||
}
|
||||
38
types.go
38
types.go
@ -1,7 +1,27 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
import "github.com/rudi9719/loggy"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/rudi9719/loggy"
|
||||
)
|
||||
|
||||
// BotCommand struct used for modular commands
|
||||
type BotCommand struct {
|
||||
Message *discordgo.MessageCreate
|
||||
Session *discordgo.Session
|
||||
Parts []string
|
||||
Command string
|
||||
}
|
||||
|
||||
// Command is the type to store commands
|
||||
type Command struct {
|
||||
Name string
|
||||
Help string
|
||||
Keywords []string
|
||||
Exec func(BotCommand) bool
|
||||
}
|
||||
|
||||
// Config struct used for bot
|
||||
type Config struct {
|
||||
@ -31,3 +51,17 @@ type Verification struct {
|
||||
Admin string
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user