package main
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/bwmarrin/discordgo"
"github.com/google/uuid"
)
var session * discordgo . Session
var pendingV [ ] Verification
var rebootTS time . Time
const alpha = "abcdefghijklmnopqrstuvwxyz1234567890"
func configureDiscord ( ) {
dg , err := discordgo . New ( "Bot " + config . DiscordToken )
if err != nil {
log . LogErrorType ( err )
log . LogPanic ( "Unable to create bot using token." )
}
dg . AddHandler ( ready )
dg . AddHandler ( readReaction )
dg . AddHandler ( messageCreate )
//dg.AddHandler(guildMemberAdd)
session = dg
err = dg . Open ( )
if err != nil {
log . LogErrorType ( err )
log . LogPanic ( "Unable to open websocket" )
}
log . LogInfo ( "Discord link is now online." )
}
func discordExit ( ) {
log . LogCritical ( "Closing Discord (closing Session)" )
session . Close ( )
}
func sendDiscordMessage ( msg string ) {
session . ChannelMessageSend ( config . VerifiedChan , fixMentions ( msg ) )
}
func sendAdminDiscordMessage ( msg string ) {
session . ChannelMessageSend ( config . AdminChan , msg )
}
func ready ( s * discordgo . Session , m * discordgo . Ready ) {
s . UpdateStatus ( 0 , "Minecraft" )
}
func banUser ( s * discordgo . Session , u discordgo . User ) {
s . GuildBanCreate ( config . GuildID , u . ID , 0 )
}
func readReaction ( s * discordgo . Session , m * discordgo . MessageReactionAdd ) {
defer log . PanicSafe ( )
if m . Emoji . Name != "⚠️ " && m . Emoji . Name != "❗" {
return
}
log . LogDebug ( fmt . Sprintf ( "Report emoji: ```%+v```" , * m ) )
msg , err := s . ChannelMessage ( m . ChannelID , m . MessageID )
log . LogDebug ( fmt . Sprintf ( "Message retrieved: %+v" , * msg ) )
if err != nil {
s . ChannelMessageSend ( config . AdminChan , "A message was reported that I could not detect." )
return
}
reporter , _ := s . GuildMember ( config . GuildID , m . UserID )
violator , _ := s . GuildMember ( config . GuildID , msg . Author . ID )
s . ChannelMessageSend ( config . AdminChan , fmt . Sprintf ( "A message was reported! %+v reported %+v's message: %+v\n```%+v```" , reporter . Nick , violator . Nick , msg . Content , * msg ) )
var r Report
var violatorUser User
violatorUser . DiscordID = m . UserID
violatorUser . Retrieve ( )
var reporterUser User
reporterUser . DiscordID = msg . Author . ID
reporterUser . Retrieve ( )
reporterUser . Write ( ) // Update last seen
r . Time = time . Now ( )
r . Reporter = reporterUser
r . Abuser = violatorUser
r . Msg = msg . Content
r . Write ( )
}
func guildMemberAdd ( s * discordgo . Session , m * discordgo . GuildMemberAdd ) {
defer log . PanicSafe ( )
s . GuildMemberRoleAdd ( config . GuildID , m . User . ID , config . MonitorRole )
st , _ := s . UserChannelCreate ( m . User . ID ) // Get the ID of PM channel
id := uuid . New ( ) // Generate new UUID4
var v Verification // Set up new Verification
v . DiscordID = m . User . ID // ID is what really matters, username is garbage
v . Created = time . Now ( ) // Setup the created time for reasons
v . Code = id . String ( ) // Generate Random UUID
pendingV = append ( pendingV , v )
s . ChannelMessageSend ( st . ID , "Hello! You have not yet verified with me. Type !sitnet [your sitnet ID here] to verify who you are. You will get an email explaining what to do next. https://web.cs.sunyit.edu/polymc" )
}
func fixMentions ( s string ) string {
defer log . PanicSafe ( )
var ret string
if ! strings . Contains ( s , "@" ) {
return s
}
parts := strings . Split ( s , " " )
for _ , part := range parts {
if strings . HasPrefix ( part , "@" ) {
var u User
u . Email = strings . Replace ( part , "@" , "" , - 1 )
u . Retrieve ( )
ret += fmt . Sprintf ( "<@!%+v>" , u . DiscordID )
} else {
ret += part
}
ret += " "
}
return ret
}
func setupVerifications ( s * discordgo . Session , m * discordgo . MessageCreate ) {
defer log . PanicSafe ( )
st , _ := s . UserChannelCreate ( m . Author . ID ) // Get the ID of PM channel
id := uuid . New ( ) // Generate new UUID4
var v Verification // Set up new Verification
v . DiscordID = m . Author . ID // ID is what really matters, username is garbage
v . Created = time . Now ( ) // Setup the created time for reasons
v . Code = id . String ( ) // Generate Random UUID
pendingV = append ( pendingV , v )
s . ChannelMessageSend ( st . ID , "Hello! You have not yet verified with me. Type !sitnet [your sitnet ID here] to verify who you are. You will get an email explaining what to do next. https://web.cs.sunyit.edu/polymc" )
}
func alphaOnly ( s string ) bool {
for _ , char := range s {
if ! strings . Contains ( alpha , strings . ToLower ( string ( char ) ) ) {
return false
}
}
return true
}
func messageCreate ( s * discordgo . Session , m * discordgo . MessageCreate ) {
defer log . PanicSafe ( )
if m . Author . ID == s . State . User . ID {
return
}
if m . GuildID == "" {
return
}
if m . ChannelID == config . MonitorChan && strings . HasPrefix ( m . Content , "!verify" ) {
for _ , v := range pendingV {
if v . DiscordID == m . Author . ID {
return
}
}
setupVerifications ( s , m )
return
}
if m . ChannelID == config . MonitorChan {
return
}
var u User
u . DiscordID = m . Author . ID
u . Retrieve ( )
u . Write ( ) // Update last seen
if strings . HasPrefix ( m . Content , "!verify" ) && m . ChannelID != config . AdminChan {
s . ChannelMessageSend ( m . ChannelID , "That command ONLY works for unverified users in the unverified channel. Instead, for account resets try !resetme" )
return
}
if strings . HasPrefix ( m . Content , "!resetme" ) {
mcCommand ( fmt . Sprintf ( "whitelist remove %+v" , u . McUser ) )
u . Delete ( )
s . GuildMemberRoleAdd ( config . GuildID , m . Author . ID , config . MonitorRole )
s . GuildMemberRoleRemove ( config . GuildID , m . Author . ID , config . VerifiedRole )
return
}
if strings . HasPrefix ( m . Content , "!online" ) {
if len ( onlineUsers ) == 0 {
s . ChannelMessageSend ( m . ChannelID , "There are no users online." )
return
}
str := fmt . Sprintf ( "The following %+v users are online:\n```" , len ( onlineUsers ) )
for _ , usr := range onlineUsers {
str += fmt . Sprintf ( "%+v : %+v\n" , usr . Email , usr . McUser )
}
str += "```"
s . ChannelMessageSend ( m . ChannelID , str )
return
}
if m . ChannelID == config . VerifiedChan {
log . LogDebug ( fmt . Sprintf ( "Discord message from retrieved user %+v" , u ) )
// if debug message shows complete user, you may replace userStr with u.SITNresET
if m . Member . Nick != u . Email {
s . GuildMemberNickname ( config . GuildID , m . Author . ID , u . Email )
}
mcSay ( fmt . Sprintf ( "[%+v]: %+v" , u . Email , m . Content ) )
return
}
if m . ChannelID == config . AdminChan {
go handleAdmin ( s , m )
}
}
func manualVerifyUser ( s * discordgo . Session , m * discordgo . MessageCreate ) {
parts := strings . Split ( m . Content , " " )
if len ( parts ) != 4 {
sendAdminDiscordMessage ( "Usage !verify $SITNET $McUser $DiscordID" )
sendAdminDiscordMessage ( "Please note, DiscordID is the numerical ID, not their username." )
}
var u User
u . Email = parts [ 1 ]
u . McUser = parts [ 2 ]
u . DiscordID = parts [ 3 ]
u . Write ( )
}
func handleAdmin ( s * discordgo . Session , m * discordgo . MessageCreate ) {
if ! strings . HasPrefix ( m . Content , "!" ) {
return
}
if strings . HasPrefix ( m . Content , "!verify" ) {
manualVerifyUser ( s , m )
}
if strings . HasPrefix ( m . Content , "!mc" ) {
mcCommand ( strings . Replace ( m . Content , "!mc" , "" , - 1 ) )
return
}
if strings . HasPrefix ( m . Content , "!wb" ) {
if strings . Contains ( m . Content , "stop" ) {
config . WbFinished = true
} else {
config . WbFinished = false
}
}
if strings . HasPrefix ( m . Content , "!reboot" ) {
if serverRebooting {
s . ChannelMessageSend ( config . AdminChan , fmt . Sprintf ( "Reboot pending: %+v" , time . Until ( rebootTS ) ) )
return
}
serverRebooting = true
rebootTS = time . Now ( ) . Add ( 30 * time . Minute )
parts := strings . Split ( m . Content , " " )
if len ( parts ) == 1 {
s . ChannelMessageSend ( config . AdminChan , "A reboot has been queued. I will poll for when users log out and then execute." )
timeout := 120
for {
if len ( onlineUsers ) == 0 || timeout == 0 {
mcExit ( )
discordExit ( )
log . LogPanic ( "Shutting down server from queued reboot." )
}
time . Sleep ( 15 * time . Second )
timeout --
}
return
}
if len ( parts ) != 2 && len ( parts ) != 3 {
s . ChannelMessageSend ( config . AdminChan , "You dumbass. I take one or two arguments." )
return
}
when , err := strconv . Atoi ( parts [ 1 ] )
if err != nil {
s . ChannelMessageSend ( config . AdminChan , "The first argument must be an int, number of minutes." )
return
}
reminders := 0
if len ( parts ) == 3 {
reminders , err = strconv . Atoi ( parts [ 2 ] )
if err != nil {
s . ChannelMessageSend ( config . AdminChan , "You supplied a second argument.. So that also has to be an int, number of reminders." )
return
}
}
mcReboot ( when , reminders )
}
}