Compare commits
204 Commits
27 changed files with 2026 additions and 296 deletions
@ -1,5 +1,10 @@
@@ -1,5 +1,10 @@
|
||||
build/ |
||||
.DS_Store |
||||
public/ |
||||
disgord-thanos |
||||
disgord-Thanos |
||||
config.json |
||||
start.sh |
||||
dev.html |
||||
verifications/* |
||||
node_modules/ |
||||
|
After Width: | Height: | Size: 56 KiB |
@ -0,0 +1,196 @@
@@ -0,0 +1,196 @@
|
||||
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.EqualFold(m.Nick, 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", http.StatusTemporaryRedirect) |
||||
} |
||||
|
||||
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))) |
||||
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"]) |
||||
} |
||||
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.EqualFold(m.User.Username, 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 |
||||
} |
||||
|
||||
func isAdmin(m *discordgo.Member) bool { |
||||
log.LogDebug("Checking %+v for %+v", m.Roles, config.AdminRole) |
||||
for _, role := range m.Roles { |
||||
if role == config.AdminRole { |
||||
return true |
||||
} else { |
||||
log.LogDebug("%+v != %+v", role, config.AdminRole) |
||||
} |
||||
} |
||||
return false |
||||
} |
@ -0,0 +1,307 @@
@@ -0,0 +1,307 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math/rand" |
||||
"os" |
||||
"path/filepath" |
||||
"sort" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/rudi9719/loggy" |
||||
) |
||||
|
||||
func setupCommands() { |
||||
reboot := Command{ |
||||
Name: "Reboot", |
||||
RequiresAdmin: true, |
||||
Help: "Reboot me, requires token from logs.", |
||||
Keywords: []string{"reboot", "re", "restart"}, |
||||
Exec: Reboot, |
||||
} |
||||
commands = append(commands, reboot) |
||||
|
||||
bumpset := Command{ |
||||
Name: "BumpSet", |
||||
RequiresAdmin: true, |
||||
Help: "Set the bump timer (requires time in minutes until next bump).", |
||||
Keywords: []string{"bs", "bumpset", "bumps"}, |
||||
Exec: BumpSet, |
||||
} |
||||
commands = append(commands, bumpset) |
||||
|
||||
retrieveVerification := Command{ |
||||
Name: "Retrieve Verification", |
||||
RequiresAdmin: true, |
||||
Help: "Retrieve verification either by discord ID or by nickname", |
||||
Keywords: []string{"veri", "verification", "retrieve"}, |
||||
Exec: RetrieveVerification, |
||||
} |
||||
commands = append(commands, retrieveVerification) |
||||
|
||||
addQuote := Command{ |
||||
Name: "Add Quote", |
||||
RequiresAdmin: true, |
||||
Keywords: []string{"quote", "addq", "q"}, |
||||
Exec: AddQuote, |
||||
} |
||||
commands = append(commands, addQuote) |
||||
|
||||
snap := Command{ |
||||
Name: "Snap", |
||||
Help: "Trigger a purge!", |
||||
RequiresAdmin: false, |
||||
Keywords: []string{"snap", "purge", "sn"}, |
||||
Exec: Snap, |
||||
} |
||||
commands = append(commands, snap) |
||||
|
||||
status := Command{ |
||||
Name: "Status", |
||||
RequiresAdmin: true, |
||||
Help: "Show the current status of Thanos/Verifications and probations", |
||||
Keywords: []string{"st", "status", "stats"}, |
||||
Exec: Status, |
||||
} |
||||
commands = append(commands, status) |
||||
|
||||
listCommands := Command{ |
||||
Name: "List Commands", |
||||
RequiresAdmin: false, |
||||
Keywords: []string{"help", "commands", "cmd", "cmds"}, |
||||
Exec: Commands, |
||||
} |
||||
commands = append(commands, listCommands) |
||||
|
||||
debugLevel := Command{ |
||||
Name: "Debug Level", |
||||
RequiresAdmin: true, |
||||
Keywords: []string{"debug"}, |
||||
Exec: Debug, |
||||
Help: "Set the log level for loggy", |
||||
} |
||||
commands = append(commands, debugLevel) |
||||
|
||||
activityReport := Command{ |
||||
Name: "Activity Report", |
||||
RequiresAdmin: false, |
||||
Keywords: []string{"activity", "active", "list"}, |
||||
Exec: ActivityReport, |
||||
Help: "List activity for the discord. Supply a number to get the top N users (5 would be top 5 users) or all for all users!", |
||||
} |
||||
commands = append(commands, activityReport) |
||||
urlWhitelist := Command{ |
||||
Name: "Whitelist URL", |
||||
RequiresAdmin: true, |
||||
Keywords: []string{"whitelist", "wl"}, |
||||
Exec: WhitelistURL, |
||||
Help: "Add a domain to the HTTP whitelist domains are in the format `thisvid.com` without the subdomain.", |
||||
} |
||||
commands = append(commands, urlWhitelist) |
||||
} |
||||
|
||||
func Commands(b BotCommand) bool { |
||||
defer log.PanicSafe() |
||||
print := "Available commands:\n" |
||||
for _, cmd := range commands { |
||||
if cmd.RequiresAdmin { |
||||
if isAdmin(b.Message.Member) { |
||||
print += fmt.Sprintf("```%+v\n%+v\n%+v```\n", cmd.Name, cmd.Keywords, cmd.Help) |
||||
} |
||||
} else { |
||||
print += fmt.Sprintf("```%+v\n%+v\n%+v```\n", cmd.Name, cmd.Keywords, cmd.Help) |
||||
} |
||||
} |
||||
b.Session.ChannelMessageSend(b.Message.ChannelID, print) |
||||
return true |
||||
} |
||||
|
||||
func Debug(b BotCommand) bool { |
||||
defer log.PanicSafe() |
||||
level, err := strconv.Atoi(b.Parts[0]) |
||||
if err != nil { |
||||
return false |
||||
} |
||||
config.LogOpts.Level = loggy.LogLevel(level) |
||||
log = loggy.NewLogger(config.LogOpts) |
||||
return true |
||||
} |
||||
|
||||
func Reboot(b BotCommand) bool { |
||||
defer log.PanicSafe() |
||||
if strings.Contains(b.Message.Content, rebootToken) { |
||||
exit(b.Session) |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func ActivityReport(b BotCommand) bool { |
||||
useCounter := true |
||||
counterStop := 4 |
||||
if len(b.Parts) > 0 { |
||||
test, err := strconv.Atoi(b.Parts[0]) |
||||
if err == nil { |
||||
counterStop = test |
||||
} else { |
||||
useCounter = false |
||||
} |
||||
} |
||||
statistics := "```" |
||||
n := map[int][]string{} |
||||
counter := 0 |
||||
var a []int |
||||
for k, v := range config.Activity { |
||||
n[v] = append(n[v], k) |
||||
} |
||||
for k := range n { |
||||
a = append(a, k) |
||||
} |
||||
sort.Sort(sort.Reverse(sort.IntSlice(a))) |
||||
for _, k := range a { |
||||
for _, s := range n[k] { |
||||
if useCounter && counter == counterStop-1 { |
||||
return true |
||||
} |
||||
user, err := b.Session.GuildMember(config.GuildID, s) |
||||
if err == nil { |
||||
statistics += fmt.Sprintf("\n%+v: %+v", user.User.Username, k) |
||||
counter++ |
||||
} else { |
||||
log.LogErrorType(err) |
||||
} |
||||
} |
||||
} |
||||
|
||||
statistics += "\n```" |
||||
return true |
||||
} |
||||
|
||||
func BumpSet(b BotCommand) bool { |
||||
defer log.PanicSafe() |
||||
bump = false |
||||
timer, err := strconv.Atoi(b.Parts[0]) |
||||
if err != nil { |
||||
b.Session.ChannelMessageSend(b.Message.ChannelID, fmt.Sprintf("Unable to decode timer: %+v", b.Parts[0])) |
||||
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 last bump time: <t:%+v:t>, expecting next bump at <t:%+v:t>", config.BumpTime.Unix(), config.BumpTime.Add(2*time.Hour).Unix())) |
||||
return true |
||||
} |
||||
|
||||
func RetrieveVerification(b BotCommand) bool { |
||||
defer log.PanicSafe() |
||||
discordId := b.Parts[0] |
||||
_, err := strconv.Atoi(discordId) |
||||
if err != nil { |
||||
discordId = idFromUsername(discordId) |
||||
} |
||||
user, err := b.Session.GuildMember(config.GuildID, discordId) |
||||
if err != nil { |
||||
log.LogErrorType(err) |
||||
return false |
||||
} |
||||
|
||||
matches, err := filepath.Glob(fmt.Sprintf("./verifications/*%+v*", discordId)) |
||||
if err != nil { |
||||
log.LogErrorType(err) |
||||
return false |
||||
} |
||||
if len(matches) != 1 { |
||||
b.Session.ChannelMessageSend(b.Message.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) |
||||
b.Session.ChannelFileSendWithMessage(b.Message.ChannelID, msg, matches[0], 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 active time: %+v\n", time.Since(lastActiveTime)) |
||||
status += fmt.Sprintf("Last bump: <t:%+v:t>\n", config.BumpTime.Unix()) |
||||
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 at <t:%+v:t>", uvUser.Username, v.Add(1*time.Hour).Unix()) |
||||
} |
||||
status += "\n" |
||||
} else { |
||||
status += "There are no unverified users.\n" |
||||
} |
||||
if len(config.Verifications) > 0 { |
||||
status += "Pending verifications:\n" |
||||
for _, v := range config.Verifications { |
||||
status += fmt.Sprintf("%+v has submitted a verification.", v.Username) |
||||
} |
||||
status += "\n" |
||||
} else { |
||||
status += "There are no pending verifications.\n" |
||||
} |
||||
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 until <t:%+v:t>\n", probationUser.Username, join.Add(2*time.Hour).Unix()) |
||||
} |
||||
status += "\n" |
||||
} else { |
||||
status += "There are no users on probation.\n" |
||||
} |
||||
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 |
||||
} |
||||
|
||||
func WhitelistURL(b BotCommand) bool { |
||||
defer log.PanicSafe() |
||||
newURL := strings.TrimSpace( |
||||
strings.ReplaceAll( |
||||
strings.ReplaceAll(b.Message.Content, b.Command, ""), |
||||
"<@688025671968096341>", ""), |
||||
) |
||||
if len(newURL) > 0 { |
||||
config.WhitelistURLs = append(config.WhitelistURLs, newURL) |
||||
} |
||||
domains := strings.Join(config.WhitelistURLs, "\n") |
||||
b.Session.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("Current whitelisted domains: %+v", domains)) |
||||
log.LogDebug(fmt.Sprintf("Current whitelisted domains: %+v", domains)) |
||||
return true |
||||
} |
@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
|
||||
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 rev %+v", gitCommit)) |
||||
} |
||||
|
||||
func guildMemberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) { |
||||
defer log.PanicSafe() |
||||
log.LogDebug("Member %+v has been updated", m.User.Username) |
||||
for _, role := range m.Roles { |
||||
if fmt.Sprintf("%+v", role) == config.MonitorRole { |
||||
log.LogDebug("Role found, Monitor Role") |
||||
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() |
||||
return |
||||
} |
||||
log.LogDebug("Monitor Role not found: %+v != %+v", fmt.Sprintf("%+v", role), config.MonitorRole) |
||||
} |
||||
|
||||
} |
||||
|
||||
func guildMemberAdd(s *discordgo.Session, m *discordgo.GuildMemberAdd) { |
||||
defer log.PanicSafe() |
||||
log.LogDebug("Adding user to Unverified and Probations") |
||||
config.Unverified[m.User.ID] = time.Now() |
||||
config.Probations[m.User.ID] = time.Now() |
||||
log.LogDebug("Giving user monitor role") |
||||
s.GuildMemberRoleAdd(config.GuildID, m.User.ID, config.MonitorRole) |
||||
log.LogDebug("Calling saveConfig") |
||||
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 == "🔄" { |
||||
requestReupload(s, user) |
||||
log.LogInfo("%+v has requested reupload for user %+v.", admin.User.Username, user.Username) |
||||
return |
||||
} else if m.Emoji.Name == "⛔" { |
||||
s.GuildBanCreateWithReason(config.GuildID, user.ID, fmt.Sprintf("Underage, female, 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) |
||||
} |
@ -0,0 +1,162 @@
@@ -0,0 +1,162 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"regexp" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/bwmarrin/discordgo" |
||||
) |
||||
|
||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { |
||||
defer log.PanicSafe() |
||||
|
||||
if m.Author.ID == "302050872383242240" && len(m.Embeds) > 0 { |
||||
if strings.Contains(m.Embeds[0].Description, "minutes until the server can be bumped") { |
||||
log.LogDebug("Failed bump detected") |
||||
re := regexp.MustCompile("Please wait another (.*) minutes until the server can be bumped") |
||||
match := re.FindStringSubmatch(m.Embeds[0].Description) |
||||
m.Content = fmt.Sprintf("%+v bs %+v", s.State.User.Mention(), match[1]) |
||||
BumpSet(BotCommand{ |
||||
Message: m, |
||||
Session: s, |
||||
Parts: strings.Split(m.Content, " ")[2:], |
||||
}) |
||||
|
||||
} else { |
||||
go bumpTimer(s) |
||||
} |
||||
return |
||||
|
||||
} |
||||
if m.Author.Bot || m.Author.ID == s.State.User.ID { |
||||
return |
||||
} |
||||
|
||||
if m.GuildID == "" { |
||||
handlePM(s, m) |
||||
return |
||||
} |
||||
|
||||
if isAdmin(m.Member) { |
||||
adminInteraction(s, m.Author.ID) |
||||
} |
||||
|
||||
if m.ChannelID == config.MonitorChann && !isAdmin(m.Member) { |
||||
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 |
||||
} |
||||
|
||||
if m.ChannelID != config.AdminChannel { |
||||
lastActiveTime = time.Now() |
||||
if len(m.Attachments) > 0 { |
||||
activeInteraction(s, m.Author.ID) |
||||
} |
||||
} |
||||
if strings.Contains(m.Content, "http") { |
||||
safe := false |
||||
for _, testURL := range config.WhitelistURLs { |
||||
if strings.Contains(m.Content, testURL) { |
||||
safe = true |
||||
} |
||||
} |
||||
if !safe { |
||||
s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("%+v: That domain is not approved by the admins. Please contact Admins if the domain should be whitelisted.", m.Author.Mention())) |
||||
s.ChannelMessageDelete(m.ChannelID, m.ID) |
||||
channel, err := s.Channel(m.ChannelID) |
||||
if err != nil { |
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("DELETED %+v [%+v]: %+v", m.Author.Mention(), m.ChannelID, m.Content)) |
||||
} else { |
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("DELETED %+v [%+v]: %+v", m.Author.Mention(), channel.Name, m.Content)) |
||||
} |
||||
} |
||||
} |
||||
parts := strings.Split(m.Content, " ") |
||||
if strings.Contains(m.Content, s.State.User.ID) { |
||||
b := BotCommand{ |
||||
Session: s, |
||||
Message: m, |
||||
Parts: parts[2:], |
||||
} |
||||
log.LogDebug("%+v", b.Parts) |
||||
for _, cmd := range commands { |
||||
for _, keyword := range cmd.Keywords { |
||||
log.LogDebug("Checking if %+v contains %+v", m.Content, keyword) |
||||
if strings.Contains(parts[1], keyword) { |
||||
log.LogDebug("%+v found!", keyword) |
||||
b.Command = keyword |
||||
if !cmd.RequiresAdmin { |
||||
log.LogDebug("%+v does not require admin, running!", cmd.Name) |
||||
if !cmd.Exec(b) { |
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("There was an error running %+v\n%+v", cmd.Name, cmd.Help)) |
||||
} else { |
||||
log.LogInfo("Ran command %+v for %+v", cmd.Name, m.Author.Username) |
||||
} |
||||
} else { |
||||
log.LogDebug("%+v does require admin, checking!", cmd.Name) |
||||
if isAdmin(m.Member) { |
||||
if !cmd.Exec(b) { |
||||
s.ChannelMessageSend(config.AdminChannel, fmt.Sprintf("There was an error running %+v\n%+v", cmd.Name, cmd.Help)) |
||||
} else { |
||||
log.LogInfo("Ran command %+v for %+v", cmd.Name, m.Author.Username) |
||||
} |
||||
} else { |
||||
log.LogInfo("%+v tried to run an admin command (%+v) but isn't an admin.", m.Author.Username, keyword) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
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 - this is a PM :) .") |
||||
} |
||||
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 |
||||
} |
||||
if strings.HasSuffix(strings.ToUpper(m.Attachments[0].ProxyURL), "MP4") { |
||||
s.ChannelMessageSend(m.ChannelID, "You have tried to send an unsupported file (MP4 Video). Please try again using an image (jpeg, jpg, png, etc).") |
||||
return |
||||
} |
||||
if strings.HasSuffix(strings.ToUpper(m.Attachments[0].ProxyURL), "MP3") { |
||||
s.ChannelMessageSend(m.ChannelID, "You have tried to send an unsupported file (MP3 Audio). 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, "👶") |
||||
s.MessageReactionAdd(config.AdminChannel, msg.ID, "⛔") |
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
module git.nightmare.haus/rudi/disgord-thanos |
||||
|
||||
go 1.21 |
||||
|
||||
require ( |
||||
github.com/bwmarrin/discordgo v0.27.1 |
||||
github.com/gorilla/mux v1.8.0 |
||||
github.com/gorilla/sessions v1.2.1 |
||||
github.com/rudi9719/loggy v0.0.0-20201031035735-9438c484de9a |
||||
) |
||||
|
||||
require ( |
||||
github.com/gorilla/securecookie v1.1.1 // indirect |
||||
github.com/gorilla/websocket v1.5.0 // indirect |
||||
golang.org/x/crypto v0.13.0 // indirect |
||||
golang.org/x/sys v0.12.0 // indirect |
||||
samhofi.us/x/keybase v1.0.0 // indirect |
||||
) |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= |
||||
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= |
||||
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.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= |
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
||||
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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= |
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= |
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= |
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= |
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
samhofi.us/x/keybase v0.0.0-20200129212102-e05e93be9f3f/go.mod h1:fcva80IUFyWcHtV4bBSzgKg07K6Rvuvi3GtGCLNGkyE= |
||||
samhofi.us/x/keybase v1.0.0 h1:ht//EtYMS/hQeZCznA1ibQ515JCKaEkvTD/tarw/9k8= |
||||
samhofi.us/x/keybase v1.0.0/go.mod h1:fcva80IUFyWcHtV4bBSzgKg07K6Rvuvi3GtGCLNGkyE= |
@ -0,0 +1,238 @@
@@ -0,0 +1,238 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"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) |
||||
) |
||||
|
||||
func topWrapper(r *http.Request) string { |
||||
defer log.PanicSafe() |
||||
headerTemplate, err := os.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 := os.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, _ := os.ReadFile("./static/index.html") |
||||
fmt.Fprint(w, string(bodyTemplate)) |
||||
} else { |
||||
fmt.Fprint(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.Fprint(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.Fprint(w, pageBuilder(r, "home")) |
||||
return |
||||
} |
||||
fmt.Fprint(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.Fprint(w, topWrapper(r)) |
||||
|
||||
fmt.Fprint(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 := os.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.Fprint(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.Fprint(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.Fprint(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.Fprint(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.Fprint(w, string(ret)) |
||||
} |
||||
|
||||
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)) |
||||
} |
@ -0,0 +1,21 @@
@@ -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> |
@ -0,0 +1,21 @@
@@ -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> |
@ -0,0 +1,149 @@
@@ -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(); |
@ -0,0 +1,11 @@
@@ -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> |
@ -0,0 +1,210 @@
@@ -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); |
@ -0,0 +1,44 @@
@@ -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> |
@ -0,0 +1,17 @@
@@ -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> |
@ -0,0 +1,73 @@
@@ -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; |
||||
} |
@ -0,0 +1,91 @@
@@ -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> |
@ -0,0 +1,8 @@
@@ -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> |
@ -0,0 +1,31 @@
@@ -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> |
@ -0,0 +1,31 @@
@@ -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> |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
package tools |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"flag" |
||||
"fmt" |
||||
"log" |
||||
"os" |
||||
"os/signal" |
||||
"syscall" |
||||
|
||||
"github.com/bwmarrin/discordgo" |
||||
) |
||||
|
||||
var ( |
||||
token string |
||||
dg *discordgo.Session |
||||
guild string |
||||
) |
||||
|
||||
func init() { |
||||
flag.StringVar(&token, "t", "", "Bot Token") |
||||
flag.StringVar(&guild, "g", "", "Guild ID") |
||||
flag.Parse() |
||||
} |
||||
|
||||
func main() { |
||||
if token == "" { |
||||
fmt.Printf("No token provided. Please run: disgord-thanos -t <bot token>") |
||||
} |
||||
dg, _ = discordgo.New("Bot " + token) |
||||
dg.AddHandler(messageCreate) |
||||
_ = dg.Open() |
||||
sc := make(chan os.Signal, 1) |
||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) |
||||
<-sc |
||||
dg.Close() |
||||
} |
||||
|
||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { |
||||
if guild != "" { |
||||
if m.GuildID != guild { |
||||
return |
||||
} |
||||
} |
||||
jsonMsg, err := json.Marshal(m) |
||||
if err != nil { |
||||
jsonMsg = append(jsonMsg, '0') |
||||
} |
||||
log.Printf("----------\n%+v: %+v\n\n%+v\n------------------------------\n\n", m.Author.Username, m.Content, string(jsonMsg)) |
||||
} |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
package tools |
||||
|
||||
import ( |
||||
"flag" |
||||
"fmt" |
||||
|
||||
"github.com/bwmarrin/discordgo" |
||||
) |
||||
|
||||
var ( |
||||
token string |
||||
dg *discordgo.Session |
||||
guild string |
||||
) |
||||
|
||||
func init() { |
||||
flag.StringVar(&token, "t", "", "Bot Token") |
||||
flag.StringVar(&guild, "g", "", "Guild ID") |
||||
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() |
||||
runFunction() |
||||
dg.Close() |
||||
} |
||||
|
||||
func runFunction() { |
||||
bans, _ := dg.GuildBans(guild) |
||||
for _, v := range bans { |
||||
dg.GuildBanDelete(guild, v.User.ID) |
||||
} |
||||
} |
Loading…
Reference in new issue