feat(live): 按用户名禁言、后台右侧管控栏、前台抖音式布局与禁言 Toast
Made-with: Cursor
This commit is contained in:
@@ -3,6 +3,7 @@ package weblive
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -10,18 +11,20 @@ import (
|
||||
|
||||
var (
|
||||
modMu sync.RWMutex
|
||||
muteAll bool
|
||||
mutedIP = make(map[string]bool)
|
||||
ipWindow = make(map[string][]int64) // 每个 IP 最近窗口内的发送时间戳(ms)
|
||||
muteAll bool
|
||||
mutedIP = make(map[string]bool)
|
||||
mutedUsers = make(map[string]bool) // key: normMuteUsername
|
||||
ipWindow = make(map[string][]int64) // 每个 IP 最近窗口内的发送时间戳(ms)
|
||||
onlineMap = make(map[string]*onlineSession)
|
||||
seq uint64
|
||||
)
|
||||
|
||||
type ModerationSnapshot struct {
|
||||
MuteAll bool `json:"mute_all"`
|
||||
MutedIPs []string `json:"muted_ips"`
|
||||
OnlineIPs []IPOnlineItem `json:"online_ips"`
|
||||
OnlineUsers []OnlineUserItem `json:"online_users"`
|
||||
MuteAll bool `json:"mute_all"`
|
||||
MutedIPs []string `json:"muted_ips"`
|
||||
MutedUsernames []string `json:"muted_usernames"`
|
||||
OnlineIPs []IPOnlineItem `json:"online_ips"`
|
||||
OnlineUsers []OnlineUserItem `json:"online_users"`
|
||||
RateLimit struct {
|
||||
WindowMs int `json:"window_ms"`
|
||||
MaxHits int `json:"max_hits"`
|
||||
@@ -73,6 +76,25 @@ func SetIPMuted(ip string, enabled bool) {
|
||||
modMu.Unlock()
|
||||
}
|
||||
|
||||
func normMuteUsername(u string) string {
|
||||
return strings.ToLower(strings.TrimSpace(u))
|
||||
}
|
||||
|
||||
// SetUserMuted 按登录用户名禁言(弹幕/礼物);与 IP 禁言、全体禁言叠加。
|
||||
func SetUserMuted(username string, enabled bool) {
|
||||
key := normMuteUsername(username)
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
modMu.Lock()
|
||||
if enabled {
|
||||
mutedUsers[key] = true
|
||||
} else {
|
||||
delete(mutedUsers, key)
|
||||
}
|
||||
modMu.Unlock()
|
||||
}
|
||||
|
||||
const (
|
||||
ipSendWindowMs = 3000
|
||||
ipSendMaxHits = 10
|
||||
@@ -93,6 +115,22 @@ func IsMutedForIP(ip string) bool {
|
||||
return mutedIP[ip]
|
||||
}
|
||||
|
||||
// IsMutedForSend 发弹幕/礼物前:全体禁言、IP 禁言、或已登录用户名被禁。
|
||||
func IsMutedForSend(ip, username string) bool {
|
||||
modMu.RLock()
|
||||
defer modMu.RUnlock()
|
||||
if muteAll {
|
||||
return true
|
||||
}
|
||||
if mutedIP[ip] {
|
||||
return true
|
||||
}
|
||||
if k := normMuteUsername(username); k != "" && mutedUsers[k] {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ModerationStateSnapshot() ModerationSnapshot {
|
||||
modMu.RLock()
|
||||
muteAllNow := muteAll
|
||||
@@ -100,8 +138,13 @@ func ModerationStateSnapshot() ModerationSnapshot {
|
||||
for ip := range mutedIP {
|
||||
muted = append(muted, ip)
|
||||
}
|
||||
mutedNames := make([]string, 0, len(mutedUsers))
|
||||
for u := range mutedUsers {
|
||||
mutedNames = append(mutedNames, u)
|
||||
}
|
||||
modMu.RUnlock()
|
||||
sort.Strings(muted)
|
||||
sort.Strings(mutedNames)
|
||||
|
||||
counts := onlineIPCountsLocked()
|
||||
online := make([]IPOnlineItem, 0, len(counts))
|
||||
@@ -215,7 +258,7 @@ func onlineUsersLocked() []OnlineUserItem {
|
||||
ConnectedAt: s.Connected.Format(time.RFC3339),
|
||||
OnlineSec: int64(now.Sub(s.Connected).Seconds()),
|
||||
IdleSec: int64(now.Sub(s.LastAt).Seconds()),
|
||||
Muted: mutedIP[s.IP] || muteAll,
|
||||
Muted: IsMutedForSend(s.IP, s.Username),
|
||||
})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
|
||||
Reference in New Issue
Block a user