diff --git a/commands.go b/commands.go index fac5a0b1..3ab6e7e2 100644 --- a/commands.go +++ b/commands.go @@ -508,7 +508,7 @@ func promptCMD(line string, u *User) { func listBansCMD(_ string, u *User) { msg := "Bans by ID: \n" for i := 0; i < len(Bans); i++ { - msg += Cyan.Cyan(strconv.Itoa(i+1)) + ". " + Bans[i].ID + " \n" + msg += Cyan.Cyan(strconv.Itoa(i+1)) + ". " + Bans[i].ID + " (" + Bans[i].Name + ") \n" } u.room.broadcast(Devbot, msg) } @@ -549,6 +549,7 @@ func banCMD(line string, u *User) { } var victim *User var ok bool + bannedByDevbot := false banner := u.Name banReason := "" // Initial ban reason is an empty string @@ -556,6 +557,7 @@ func banCMD(line string, u *User) { u.room.broadcast(Devbot, "Do you really think you can ban me, puny human?") victim = u // mwahahahaha - devbot banner = Devbot + bannedByDevbot = true } else if !auth(u) { u.room.broadcast(Devbot, "Not authorized") return @@ -564,24 +566,23 @@ func banCMD(line string, u *User) { return } - if len(split) > 1 { - dur, err := time.ParseDuration(split[len(split)-1]) - if err != nil { - split[len(split)-1] = "" // there's no duration so don't trim anything from the reason + if len(split) > 1 || bannedByDevbot { + dur, err := time.ParseDuration("1m") + if !bannedByDevbot { + dur, err = time.ParseDuration(split[len(split)-1]) + if err != nil { + split[len(split)-1] = "" // there's no duration so don't trim anything from the reason + } } if len(split) > 2 { banReason = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(line, split[0]), split[len(split)-1])) } if err == nil { // there was a duration - victim.ban(victim.Name + " has been banned by " + banner + " for " + dur.String() + " " + banReason) - go func(id string) { - time.Sleep(dur) - unbanIDorIP(id) - }(victim.id) // evaluate id now, call unban with that value later + victim.banTemporarily(victim.Name+" has been banned by "+banner+" for "+dur.String()+" "+banReason, dur) return } } - victim.ban(victim.Name + " has been banned by " + banner + " " + banReason) + victim.banForever(victim.Name + " has been banned by " + banner + " " + banReason) } func kickCMD(line string, u *User) { diff --git a/devzat_test.go b/devzat_test.go index 390d6abd..c8e9f5dd 100644 --- a/devzat_test.go +++ b/devzat_test.go @@ -176,7 +176,7 @@ func performTestBan(t *testing.T, id0 string, id1 string, id2 string, id3 string r.users[1].id = id1 r.users[2].id = id2 r.users[3].id = id3 - r.users[0].ban("Tim is a meany") + r.users[0].banForever("Tim is a meany") if len(r.users) != 4-usersBanned { t.Log("Error,", usersBanned, "users should have been kicked but", 4-len(r.users), "have been kicked.") t.Fail() diff --git a/main.go b/main.go index b07c844e..669e54b0 100644 --- a/main.go +++ b/main.go @@ -43,8 +43,11 @@ const ( ) type Ban struct { - Addr string - ID string + Addr string + ID string + Name string + UseTime bool + UnbanTime time.Time } type Room struct { @@ -410,13 +413,29 @@ func newUser(s ssh.Session) *User { Log.Println("Connected " + u.Name + " [" + u.id + "]") - if bansContains(Bans, u.addr, u.id) || TORIPs[u.addr] { - Log.Println("Rejected " + u.Name + " [" + host + "] (banned)") - u.writeln(Devbot, "**You are banned**. If you feel this was a mistake, please reach out to the server admin. Include the following information: [ID "+u.id+"]") + if TORIPs[u.addr] { + Log.Println("Rejected " + u.Name + " [" + host + "] (Tor IP)") + u.writeln(Devbot, "**You are not allowed to join as you are using a Tor IP**") s.Close() return nil } + banInfo := getBan(Bans, u.addr, u.id) + if banInfo != nil { + if banInfo.UseTime && banInfo.UnbanTime.Before(time.Now()) { + unbanIDorIP(banInfo.ID) + } else { + Log.Println("Rejected " + u.Name + " [" + host + "] (banned)") + u.writeln(Devbot, "**You are banned**. If you feel this was a mistake, please reach out to the server admin. Include the following information: [ID "+u.id+"]") + if banInfo.UseTime { + when := time.Until(banInfo.UnbanTime) + u.writeln(Devbot, "You will be unbaned in "+printPrettyDuration(when)+".") + } + s.Close() + return nil + } + } + if Config.Private { _, isOnAllowlist := Config.Allowlist[u.id] _, isAdmin := Config.Admins[u.id] @@ -435,7 +454,7 @@ func newUser(s ssh.Session) *User { IDandIPsToTimesJoinedInMin[u.id]-- }) if IDandIPsToTimesJoinedInMin[u.addr] > 6 || IDandIPsToTimesJoinedInMin[u.id] > 6 { - u.ban("") + u.banForever("") MainRoom.broadcast(Devbot, u.Name+" has been banned automatically. ID: "+u.id) return nil } @@ -586,14 +605,23 @@ func (u *User) close(msg string) { u.room.broadcast("", Red.Paint(" <-- ")+msg) } -func (u *User) ban(banner string) { +func (u *User) banForever(banMsg string) { + u.ban(banMsg, false, time.Now()) +} + +func (u *User) banTemporarily(banMsg string, banDuration time.Duration) { + unbanTime := time.Now().Add(banDuration) + u.ban(banMsg, true, unbanTime) +} + +func (u *User) ban(banMsg string, useTime bool, unbanTime time.Time) { if u.addr == "" && u.id == "" { return } - Bans = append(Bans, Ban{u.addr, u.id}) + Bans = append(Bans, Ban{u.addr, u.id, stripansi.Strip(u.Name), useTime, unbanTime}) saveBans() uid := u.id - u.close(banner) + u.close(banMsg) for i := range Rooms { // close all users that have this id (including this user) for j := 0; j < len(Rooms[i].users); j++ { if Rooms[i].users[j].id == uid { @@ -875,9 +903,9 @@ func (u *User) repl() { u.room.broadcast(Devbot, u.Name+", stop spamming or you could get banned.") } if AntispamMessages[u.id] >= 50 { - if !bansContains(Bans, u.addr, u.id) { - Bans = append(Bans, Ban{u.addr, u.id}) - saveBans() + if getBan(Bans, u.addr, u.id) == nil { + oneMonth, _ := time.ParseDuration("730h") + u.banTemporarily("", oneMonth) } u.writeln(Devbot, "anti-spam triggered") u.close(Red.Paint(u.Name + " has been banned for spamming")) @@ -911,12 +939,12 @@ func calculateLinesTaken(u *User, s string, width int) { //return lines } -// bansContains reports if the addr or id is found in the bans list -func bansContains(b []Ban, addr string, id string) bool { +// getBan returns the ban element it it exist or nil otherwise +func getBan(b []Ban, addr string, id string) *Ban { for i := 0; i < len(b); i++ { if b[i].Addr == addr || b[i].ID == id { - return true + return &b[i] } } - return false + return nil } diff --git a/util.go b/util.go index 58f13f35..e5015db0 100644 --- a/util.go +++ b/util.go @@ -418,12 +418,26 @@ func holidaysCheck(u *User) { } func printPrettyDuration(d time.Duration) string { - s := d.Round(time.Minute).String() - s = s[:len(s)-2] // cut off "0s" at the end - if s == "" { // we cut off the seconds so if there's nothing in the string it means it was made of only seconds. - s = "< 1m" + seconds := int(d.Seconds()) + minutes := seconds / 60 + if minutes == 0 { + return "< 1m" } - return s + + hours := minutes / 60 + minutes = minutes % 60 + ret := fmt.Sprintf("%vm", minutes) + if hours == 0 { + return ret + } + + days := hours / 24 + hours = hours % 24 + ret = fmt.Sprintf("%vh %v", hours, ret) + if days == 0 { + return ret + } + return fmt.Sprintf("%vd %v", days, ret) } func fmtTime(u *User, lastStamp time.Time) string {