package cmd import ( "encoding/json" "os" "path/filepath" "sort" "sync" ) const ( robotGroupConfigFile = "robot_groups.json" robotBlacklistFile = "robot_blacklist.json" ) type robotGroupConfig struct { NotifyGroupIDs []int64 `json:"notify_group_ids"` ManageGroupIDs []int64 `json:"manage_group_ids"` } type robotBlacklist struct { UserIDs []int64 `json:"user_ids"` } var robotBlacklistMu sync.Mutex func loadRobotGroupConfig() robotGroupConfig { path := resolveRobotConfigPath(robotGroupConfigFile) ensureRobotConfigFile(path, []byte("{\n \"notify_group_ids\": [],\n \"manage_group_ids\": []\n}\n")) var cfg robotGroupConfig data, err := os.ReadFile(path) if err != nil { return cfg } if err = json.Unmarshal(data, &cfg); err != nil { return robotGroupConfig{} } cfg.NotifyGroupIDs = normalizeInt64IDs(cfg.NotifyGroupIDs) cfg.ManageGroupIDs = normalizeInt64IDs(cfg.ManageGroupIDs) return cfg } func loadRobotBlacklist() robotBlacklist { path := resolveRobotConfigPath(robotBlacklistFile) ensureRobotConfigFile(path, []byte("{\n \"user_ids\": []\n}\n")) var cfg robotBlacklist data, err := os.ReadFile(path) if err != nil { return cfg } if err = json.Unmarshal(data, &cfg); err != nil { return robotBlacklist{} } cfg.UserIDs = normalizeInt64IDs(cfg.UserIDs) return cfg } func saveRobotBlacklist(cfg robotBlacklist) error { path := resolveRobotConfigPath(robotBlacklistFile) cfg.UserIDs = normalizeInt64IDs(cfg.UserIDs) ensureRobotConfigFile(path, []byte("{\n \"user_ids\": []\n}\n")) data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } data = append(data, '\n') return os.WriteFile(path, data, 0o644) } func addRobotBlacklistUser(userID int64) error { if userID <= 0 { return nil } robotBlacklistMu.Lock() defer robotBlacklistMu.Unlock() cfg := loadRobotBlacklist() for _, id := range cfg.UserIDs { if id == userID { return nil } } cfg.UserIDs = append(cfg.UserIDs, userID) return saveRobotBlacklist(cfg) } func removeRobotBlacklistUser(userID int64) error { if userID <= 0 { return nil } robotBlacklistMu.Lock() defer robotBlacklistMu.Unlock() cfg := loadRobotBlacklist() ids := make([]int64, 0, len(cfg.UserIDs)) for _, id := range cfg.UserIDs { if id != userID { ids = append(ids, id) } } cfg.UserIDs = ids return saveRobotBlacklist(cfg) } func isRobotBlacklisted(userID int64) bool { if userID <= 0 { return false } cfg := loadRobotBlacklist() for _, id := range cfg.UserIDs { if id == userID { return true } } return false } func resolveRobotConfigPath(name string) string { candidates := []string{ filepath.Join("public", "config", name), filepath.Join("..", "public", "config", name), filepath.Join("..", "..", "public", "config", name), } for _, candidate := range candidates { if _, err := os.Stat(candidate); err == nil { return candidate } } return candidates[0] } func ensureRobotConfigFile(path string, fallback []byte) { if _, err := os.Stat(path); err == nil { return } _ = os.MkdirAll(filepath.Dir(path), 0o755) _ = os.WriteFile(path, fallback, 0o644) } func normalizeInt64IDs(ids []int64) []int64 { uniq := make(map[int64]struct{}, len(ids)) for _, id := range ids { if id > 0 { uniq[id] = struct{}{} } } res := make([]int64, 0, len(uniq)) for id := range uniq { res = append(res, id) } sort.Slice(res, func(i, j int) bool { return res[i] < res[j] }) return res } func isConfiguredGroup(groupID int64, groups []int64) bool { for _, id := range groups { if id == groupID { return true } } return false }