// Copyright © 2023 OpenIM open source community. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package chat import ( "context" "fmt" "strings" "time" "git.imall.cloud/openim/chat/pkg/common/db/table/chat" "github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/pagination" "github.com/openimsdk/tools/errs" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) func NewSensitiveWord(db *mongo.Database) (chat.SensitiveWordInterface, error) { coll := db.Collection("sensitive_words") _, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{ { Keys: bson.D{ {Key: "word", Value: 1}, }, Options: options.Index().SetUnique(true), }, { Keys: bson.D{ {Key: "status", Value: 1}, }, }, }) if err != nil { return nil, errs.Wrap(err) } // 创建配置集合 configColl := db.Collection("sensitive_word_configs") _, err = configColl.Indexes().CreateOne(context.Background(), mongo.IndexModel{ Keys: bson.D{ {Key: "enable_filter", Value: 1}, }, }) if err != nil { return nil, errs.Wrap(err) } // 检查并初始化默认配置 ctx := context.Background() count, err := configColl.CountDocuments(ctx, bson.M{}) if err != nil { return nil, errs.Wrap(err) } if count == 0 { // 创建默认配置 defaultConfig := &chat.SensitiveWordConfig{ ID: "default", EnableFilter: true, FilterMode: 1, ReplaceChar: "***", WhitelistUsers: []string{}, WhitelistGroups: []string{}, LogEnabled: true, AutoApprove: false, UpdateTime: time.Now(), } _, err = configColl.InsertOne(ctx, defaultConfig) if err != nil { return nil, errs.Wrap(err) } } return &SensitiveWord{coll: coll, configColl: configColl}, nil } type SensitiveWord struct { coll *mongo.Collection configColl *mongo.Collection } // ==================== 敏感词管理 ==================== func (o *SensitiveWord) CreateSensitiveWord(ctx context.Context, word *chat.SensitiveWord) error { word.CreateTime = time.Now() word.UpdateTime = time.Now() return mongoutil.InsertMany(ctx, o.coll, []*chat.SensitiveWord{word}) } func (o *SensitiveWord) UpdateSensitiveWord(ctx context.Context, id string, data map[string]any) error { data["update_time"] = time.Now() return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": id}, bson.M{"$set": data}, false) } func (o *SensitiveWord) DeleteSensitiveWord(ctx context.Context, ids []string) error { return mongoutil.DeleteMany(ctx, o.coll, bson.M{"_id": bson.M{"$in": ids}}) } func (o *SensitiveWord) GetSensitiveWord(ctx context.Context, id string) (*chat.SensitiveWord, error) { return mongoutil.FindOne[*chat.SensitiveWord](ctx, o.coll, bson.M{"_id": id}) } func (o *SensitiveWord) SearchSensitiveWords(ctx context.Context, keyword string, action int32, status int32, pagination pagination.Pagination) (int64, []*chat.SensitiveWord, error) { filter := bson.M{} if keyword != "" { filter["word"] = bson.M{"$regex": keyword, "$options": "i"} } if action > 0 { filter["action"] = action } if status > 0 { filter["status"] = status } return mongoutil.FindPage[*chat.SensitiveWord](ctx, o.coll, filter, pagination) } func (o *SensitiveWord) GetAllSensitiveWords(ctx context.Context) ([]*chat.SensitiveWord, error) { return mongoutil.Find[*chat.SensitiveWord](ctx, o.coll, bson.M{}) } func (o *SensitiveWord) GetEnabledSensitiveWords(ctx context.Context) ([]*chat.SensitiveWord, error) { return mongoutil.Find[*chat.SensitiveWord](ctx, o.coll, bson.M{"status": chat.SensitiveStatusEnabled}) } // ==================== 敏感词检测和过滤 ==================== func (o *SensitiveWord) CheckSensitiveWords(ctx context.Context, content string) ([]*chat.SensitiveWord, error) { words, err := o.GetEnabledSensitiveWords(ctx) if err != nil { return nil, err } var matchedWords []*chat.SensitiveWord contentLower := strings.ToLower(content) for _, word := range words { if strings.Contains(contentLower, strings.ToLower(word.Word)) { matchedWords = append(matchedWords, word) } } return matchedWords, nil } func (o *SensitiveWord) FilterContent(ctx context.Context, content string) (string, []*chat.SensitiveWord, error) { words, err := o.CheckSensitiveWords(ctx, content) if err != nil { return content, nil, err } if len(words) == 0 { return content, words, nil } filteredContent := content for _, word := range words { replaceChar := "***" filteredContent = strings.ReplaceAll(filteredContent, word.Word, replaceChar) } return filteredContent, words, nil } // ==================== 敏感词日志管理 ==================== func (o *SensitiveWord) CreateSensitiveWordLog(ctx context.Context, log *chat.SensitiveWordLog) error { log.CreateTime = time.Now() return mongoutil.InsertMany(ctx, o.coll.Database().Collection("sensitive_word_logs"), []*chat.SensitiveWordLog{log}) } func (o *SensitiveWord) GetSensitiveWordLogs(ctx context.Context, userID, groupID string, pagination pagination.Pagination) (int64, []*chat.SensitiveWordLog, error) { filter := bson.M{} if userID != "" { filter["user_id"] = userID } if groupID != "" { filter["group_id"] = groupID } coll := o.coll.Database().Collection("sensitive_word_logs") return mongoutil.FindPage[*chat.SensitiveWordLog](ctx, coll, filter, pagination) } func (o *SensitiveWord) DeleteSensitiveWordLogs(ctx context.Context, ids []string) error { coll := o.coll.Database().Collection("sensitive_word_logs") return mongoutil.DeleteMany(ctx, coll, bson.M{"_id": bson.M{"$in": ids}}) } // ==================== 敏感词分组管理 ==================== func (o *SensitiveWord) CreateSensitiveWordGroup(ctx context.Context, group *chat.SensitiveWordGroup) error { group.CreateTime = time.Now() group.UpdateTime = time.Now() return mongoutil.InsertMany(ctx, o.coll.Database().Collection("sensitive_word_groups"), []*chat.SensitiveWordGroup{group}) } func (o *SensitiveWord) UpdateSensitiveWordGroup(ctx context.Context, id string, data map[string]any) error { data["update_time"] = time.Now() coll := o.coll.Database().Collection("sensitive_word_groups") return mongoutil.UpdateOne(ctx, coll, bson.M{"_id": id}, bson.M{"$set": data}, false) } func (o *SensitiveWord) DeleteSensitiveWordGroup(ctx context.Context, ids []string) error { coll := o.coll.Database().Collection("sensitive_word_groups") return mongoutil.DeleteMany(ctx, coll, bson.M{"_id": bson.M{"$in": ids}}) } func (o *SensitiveWord) GetSensitiveWordGroup(ctx context.Context, id string) (*chat.SensitiveWordGroup, error) { coll := o.coll.Database().Collection("sensitive_word_groups") return mongoutil.FindOne[*chat.SensitiveWordGroup](ctx, coll, bson.M{"_id": id}) } func (o *SensitiveWord) GetAllSensitiveWordGroups(ctx context.Context) ([]*chat.SensitiveWordGroup, error) { coll := o.coll.Database().Collection("sensitive_word_groups") return mongoutil.Find[*chat.SensitiveWordGroup](ctx, coll, bson.M{}) } // ==================== 敏感词配置管理 ==================== func (o *SensitiveWord) GetSensitiveWordConfig(ctx context.Context) (*chat.SensitiveWordConfig, error) { // 查找配置(默认配置已在初始化时创建) config, err := mongoutil.FindOne[*chat.SensitiveWordConfig](ctx, o.configColl, bson.M{}) if err != nil { return nil, err } return config, nil } func (o *SensitiveWord) UpdateSensitiveWordConfig(ctx context.Context, config *chat.SensitiveWordConfig) error { config.UpdateTime = time.Now() if config.ID == "" { config.ID = "default" } filter := bson.M{"_id": config.ID} fmt.Println("UpdateSensitiveWordConfig", "_________55", config, o.configColl.Name()) err := mongoutil.UpdateOne(ctx, o.configColl, filter, bson.M{"$set": config}, true) fmt.Println("UpdateSensitiveWordConfig", "_________44", config) if err != nil { return err } return nil } func (o *SensitiveWord) IsFilterEnabled(ctx context.Context) (bool, error) { config, err := o.GetSensitiveWordConfig(ctx) if err != nil { return false, err } return config.EnableFilter, nil } func (o *SensitiveWord) GetFilterMode(ctx context.Context) (int32, error) { // 简化实现,返回默认值 return 1, nil } func (o *SensitiveWord) GetReplaceChar(ctx context.Context) (string, error) { config, err := o.GetSensitiveWordConfig(ctx) if err != nil { return "***", err } if config.ReplaceChar == "" { return "***", nil } return config.ReplaceChar, nil } func (o *SensitiveWord) IsUserInWhitelist(ctx context.Context, userID string) (bool, error) { config, err := o.GetSensitiveWordConfig(ctx) if err != nil { return false, err } for _, id := range config.WhitelistUsers { if id == userID { return true, nil } } return false, nil } func (o *SensitiveWord) IsGroupInWhitelist(ctx context.Context, groupID string) (bool, error) { config, err := o.GetSensitiveWordConfig(ctx) if err != nil { return false, err } for _, id := range config.WhitelistGroups { if id == groupID { return true, nil } } return false, nil } // ==================== 批量操作 ==================== func (o *SensitiveWord) BatchCreateSensitiveWords(ctx context.Context, words []*chat.SensitiveWord) error { now := time.Now() for _, word := range words { word.CreateTime = now word.UpdateTime = now } return mongoutil.InsertMany(ctx, o.coll, words) } func (o *SensitiveWord) BatchUpdateSensitiveWords(ctx context.Context, updates map[string]map[string]any) error { for id, data := range updates { data["update_time"] = time.Now() err := mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": id}, bson.M{"$set": data}, false) if err != nil { return err } } return nil } func (o *SensitiveWord) BatchDeleteSensitiveWords(ctx context.Context, ids []string) error { return mongoutil.DeleteMany(ctx, o.coll, bson.M{"_id": bson.M{"$in": ids}}) } // ==================== 统计信息 ==================== func (o *SensitiveWord) GetSensitiveWordStats(ctx context.Context) (map[string]int64, error) { stats := make(map[string]int64) // 总数 total, err := mongoutil.Count(ctx, o.coll, bson.M{}) if err != nil { return nil, err } stats["total"] = total // 启用数量 enabled, err := mongoutil.Count(ctx, o.coll, bson.M{"status": chat.SensitiveStatusEnabled}) if err != nil { return nil, err } stats["enabled"] = enabled // 禁用数量 disabled, err := mongoutil.Count(ctx, o.coll, bson.M{"status": chat.SensitiveStatusDisabled}) if err != nil { return nil, err } stats["disabled"] = disabled // 替换模式数量 replace, err := mongoutil.Count(ctx, o.coll, bson.M{"action": chat.SensitiveActionReplace}) if err != nil { return nil, err } stats["replace"] = replace // 拦截模式数量 block, err := mongoutil.Count(ctx, o.coll, bson.M{"action": chat.SensitiveActionBlock}) if err != nil { return nil, err } stats["block"] = block return stats, nil } func (o *SensitiveWord) GetSensitiveWordLogStats(ctx context.Context, startTime, endTime time.Time) (map[string]int64, error) { stats := make(map[string]int64) coll := o.coll.Database().Collection("sensitive_word_logs") filter := bson.M{ "create_time": bson.M{ "$gte": startTime, "$lte": endTime, }, } // 总数 total, err := mongoutil.Count(ctx, coll, filter) if err != nil { return nil, err } stats["total"] = total // 替换数量 replaceFilter := bson.M{} for k, v := range filter { replaceFilter[k] = v } replaceFilter["action"] = chat.SensitiveActionReplace replace, err := mongoutil.Count(ctx, coll, replaceFilter) if err != nil { return nil, err } stats["replace"] = replace // 拦截数量 blockFilter := bson.M{} for k, v := range filter { blockFilter[k] = v } blockFilter["action"] = chat.SensitiveActionBlock block, err := mongoutil.Count(ctx, coll, blockFilter) if err != nil { return nil, err } stats["block"] = block return stats, nil }