diff --git a/common/mhfcid/mhfcid.go b/common/mhfcid/mhfcid.go new file mode 100644 index 000000000..8b951fd4e --- /dev/null +++ b/common/mhfcid/mhfcid.go @@ -0,0 +1,54 @@ +package mhfcid + +import ( + "math" +) + +// ConvertCID converts a MHF Character ID String to integer +// +// Banned characters: 0, I, O, S +func ConvertCID(ID string) (r uint32) { + if len(ID) != 6 { + return + } + + m := map[rune]uint32{ + '1': 0, + '2': 1, + '3': 2, + '4': 3, + '5': 4, + '6': 5, + '7': 6, + '8': 7, + '9': 8, + 'A': 9, + 'B': 10, + 'C': 11, + 'D': 12, + 'E': 13, + 'F': 14, + 'G': 15, + 'H': 16, + 'J': 17, + 'K': 18, + 'L': 19, + 'M': 20, + 'N': 21, + 'P': 22, + 'Q': 23, + 'R': 24, + 'T': 25, + 'U': 26, + 'V': 27, + 'W': 28, + 'X': 29, + 'Y': 30, + 'Z': 31, + } + + for i, c := range ID { + r += m[c] * uint32(math.Pow(32, float64(i))) + } + return +} diff --git a/config.json b/config.json index 15f51d9df..c0247b9d3 100644 --- a/config.json +++ b/config.json @@ -136,6 +136,11 @@ "Enabled": true, "Description": "Generate a token to link your Discord account", "Prefix": "discord" + }, { + "Name": "Ban", + "Enabled": false, + "Description": "Ban/Temp Ban a user", + "Prefix": "ban" } ], "Courses": [ diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 016e1f70c..771a7be39 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "encoding/hex" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfcid" "erupe-ce/common/mhfcourse" "erupe-ce/common/token" "erupe-ce/config" @@ -87,6 +88,62 @@ func sendServerChatMessage(s *Session, message string) { func parseChatCommand(s *Session, command string) { args := strings.Split(command[len(s.server.erupeConfig.CommandPrefix):], " ") switch args[0] { + case commands["Ban"].Prefix: + if s.isOp() { + if len(args) > 1 { + var expiry time.Time + if len(args) > 2 { + var length int + var unit string + n, err := fmt.Sscanf(args[2], `%d%s`, &length, &unit) + if err == nil && n == 2 { + switch unit { + case "s", "second", "seconds": + expiry = time.Now().Add(time.Duration(length) * time.Second) + case "m", "mi", "minute", "minutes": + expiry = time.Now().Add(time.Duration(length) * time.Minute) + case "h", "hour", "hours": + expiry = time.Now().Add(time.Duration(length) * time.Hour) + case "d", "day", "days": + expiry = time.Now().Add(time.Duration(length) * time.Hour * 24) + case "mo", "month", "months": + expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 30) + case "y", "year", "years": + expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 365) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.error) + return + } + } + cid := mhfcid.ConvertCID(args[1]) + if cid > 0 { + var uid uint32 + var uname string + err := s.server.db.QueryRow(`SELECT id, username FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, cid).Scan(&uid, &uname) + if err == nil { + if expiry.IsZero() { + s.server.db.Exec(`INSERT INTO bans VALUES ($1) + ON CONFLICT (user_id) DO UPDATE SET expires=NULL`, uid) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname)) + } else { + s.server.db.Exec(`INSERT INTO bans VALUES ($1, $2) + ON CONFLICT (user_id) DO UPDATE SET expires=$2`, uid, expiry) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname)+fmt.Sprintf(s.server.i18n.commands.ban.length, expiry.Format(time.DateTime))) + } + s.server.DisconnectUser(uid) + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.noUser) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.invalid) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.error) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.noOp) + } case commands["PSN"].Prefix: if commands["PSN"].Enabled || s.isOp() { if len(args) > 1 { diff --git a/server/channelserver/sys_language.go b/server/channelserver/sys_language.go index 5feb76444..eae96dc85 100644 --- a/server/channelserver/sys_language.go +++ b/server/channelserver/sys_language.go @@ -6,6 +6,7 @@ type i18n struct { reset string } commands struct { + noOp string disabled string reload string kqf struct { @@ -38,6 +39,13 @@ type i18n struct { discord struct { success string } + ban struct { + success string + noUser string + invalid string + error string + length string + } ravi struct { noCommand string start struct { @@ -95,6 +103,7 @@ func getLangStrings(s *Server) i18n { i.language = "日本語" i.cafe.reset = "%d/%dにリセット" + i.commands.noOp = "You don't have permission to use this command" i.commands.disabled = "%sのコマンドは無効です" i.commands.reload = "リロードします" i.commands.kqf.get = "現在のキークエストフラグ:%x" @@ -115,6 +124,12 @@ func getLangStrings(s *Server) i18n { i.commands.discord.success = "あなたのDiscordトークン:%s" + i.commands.ban.noUser = "Could not find user" + i.commands.ban.success = "Successfully banned %s" + i.commands.ban.invalid = "Invalid Character ID" + i.commands.ban.error = "Error in command. Format: %s [length]" + i.commands.ban.length = " until %s" + i.commands.ravi.noCommand = "ラヴィコマンドが指定されていません" i.commands.ravi.start.success = "大討伐を開始します" i.commands.ravi.start.error = "大討伐は既に開催されています" @@ -150,6 +165,7 @@ func getLangStrings(s *Server) i18n { i.language = "English" i.cafe.reset = "Resets on %d/%d" + i.commands.noOp = "You don't have permission to use this command" i.commands.disabled = "%s command is disabled" i.commands.reload = "Reloading players..." i.commands.kqf.get = "KQF: %x" @@ -170,6 +186,12 @@ func getLangStrings(s *Server) i18n { i.commands.discord.success = "Your Discord token: %s" + i.commands.ban.noUser = "Could not find user" + i.commands.ban.success = "Successfully banned %s" + i.commands.ban.invalid = "Invalid Character ID" + i.commands.ban.error = "Error in command. Format: %s [length]" + i.commands.ban.length = " until %s" + i.commands.ravi.noCommand = "No Raviente command specified!" i.commands.ravi.start.success = "The Great Slaying will begin in a moment" i.commands.ravi.start.error = "The Great Slaying has already begun!"