449 lines
12 KiB
GDScript
449 lines
12 KiB
GDScript
extends Node
|
||
class_name DialogueFilter
|
||
## 对话过滤器
|
||
## 提供内容审核、过滤和安全检查功能
|
||
|
||
# 过滤规则配置
|
||
var enable_profanity_filter: bool = true
|
||
var enable_spam_detection: bool = true
|
||
var enable_length_limit: bool = true
|
||
var enable_rate_limiting: bool = true
|
||
|
||
# 长度限制
|
||
var max_message_length: int = 500
|
||
var min_message_length: int = 1
|
||
|
||
# 垃圾信息检测
|
||
var spam_detection_window: float = 10.0 # 10秒窗口
|
||
var max_messages_per_window: int = 5
|
||
var max_duplicate_messages: int = 3
|
||
|
||
# 违禁词列表(示例,实际使用时应该从配置文件加载)
|
||
var profanity_words: Array[String] = [
|
||
# 基础违禁词(示例)
|
||
"垃圾", "废物", "白痴", "蠢货", "混蛋",
|
||
# 可以根据需要添加更多
|
||
]
|
||
|
||
# 敏感词替换
|
||
var profanity_replacement: String = "***"
|
||
|
||
# 消息历史(用于垃圾信息检测)
|
||
var message_history: Dictionary = {} # user_id -> Array[Dictionary]
|
||
|
||
# 过滤统计
|
||
var filter_stats: Dictionary = {
|
||
"total_messages": 0,
|
||
"filtered_messages": 0,
|
||
"profanity_blocked": 0,
|
||
"spam_blocked": 0,
|
||
"length_violations": 0,
|
||
"rate_limit_violations": 0
|
||
}
|
||
|
||
# 信号
|
||
signal message_filtered(user_id: String, original_message: String, filtered_message: String, reason: String)
|
||
signal message_blocked(user_id: String, message: String, reason: String)
|
||
|
||
func _ready():
|
||
"""初始化对话过滤器"""
|
||
load_filter_config()
|
||
print("DialogueFilter initialized")
|
||
|
||
## 过滤消息
|
||
func filter_message(user_id: String, message: String) -> Dictionary:
|
||
"""
|
||
过滤和验证消息
|
||
@param user_id: 用户ID
|
||
@param message: 原始消息
|
||
@return: 过滤结果 {allowed: bool, filtered_message: String, reason: String}
|
||
"""
|
||
filter_stats.total_messages += 1
|
||
|
||
var result = {
|
||
"allowed": true,
|
||
"filtered_message": message,
|
||
"reason": ""
|
||
}
|
||
|
||
# 1. 长度检查
|
||
if enable_length_limit:
|
||
var length_check = _check_message_length(message)
|
||
if not length_check.valid:
|
||
result.allowed = false
|
||
result.reason = length_check.reason
|
||
filter_stats.length_violations += 1
|
||
message_blocked.emit(user_id, message, result.reason)
|
||
return result
|
||
|
||
# 2. 速率限制检查
|
||
if enable_rate_limiting:
|
||
var rate_check = _check_rate_limit(user_id, message)
|
||
if not rate_check.valid:
|
||
result.allowed = false
|
||
result.reason = rate_check.reason
|
||
filter_stats.rate_limit_violations += 1
|
||
message_blocked.emit(user_id, message, result.reason)
|
||
return result
|
||
|
||
# 3. 垃圾信息检测
|
||
if enable_spam_detection:
|
||
var spam_check = _check_spam(user_id, message)
|
||
if not spam_check.valid:
|
||
result.allowed = false
|
||
result.reason = spam_check.reason
|
||
filter_stats.spam_blocked += 1
|
||
message_blocked.emit(user_id, message, result.reason)
|
||
return result
|
||
|
||
# 4. 违禁词过滤
|
||
if enable_profanity_filter:
|
||
var profanity_result = _filter_profanity(message)
|
||
if profanity_result.has_profanity:
|
||
result.filtered_message = profanity_result.filtered_message
|
||
filter_stats.profanity_blocked += 1
|
||
message_filtered.emit(user_id, message, result.filtered_message, "违禁词过滤")
|
||
|
||
# 5. 记录消息历史(用于垃圾信息检测)
|
||
_record_message(user_id, result.filtered_message)
|
||
|
||
if result.filtered_message != message:
|
||
filter_stats.filtered_messages += 1
|
||
|
||
return result
|
||
|
||
## 检查消息长度
|
||
func _check_message_length(message: String) -> Dictionary:
|
||
"""
|
||
检查消息长度是否符合要求
|
||
@param message: 消息内容
|
||
@return: 验证结果
|
||
"""
|
||
var trimmed = message.strip_edges()
|
||
|
||
if trimmed.length() < min_message_length:
|
||
return {
|
||
"valid": false,
|
||
"reason": "消息不能为空"
|
||
}
|
||
|
||
if trimmed.length() > max_message_length:
|
||
return {
|
||
"valid": false,
|
||
"reason": "消息长度超过限制(最多%d个字符)" % max_message_length
|
||
}
|
||
|
||
return {"valid": true, "reason": ""}
|
||
|
||
## 检查速率限制
|
||
func _check_rate_limit(user_id: String, _message: String) -> Dictionary:
|
||
"""
|
||
检查用户是否超过消息发送速率限制
|
||
@param user_id: 用户ID
|
||
@param _message: 消息内容(暂未使用)
|
||
@return: 验证结果
|
||
"""
|
||
var current_time = Time.get_unix_time_from_system()
|
||
|
||
if not message_history.has(user_id):
|
||
return {"valid": true, "reason": ""}
|
||
|
||
var user_messages = message_history[user_id]
|
||
var recent_messages = []
|
||
|
||
# 统计时间窗口内的消息
|
||
for msg_record in user_messages:
|
||
if current_time - msg_record.timestamp <= spam_detection_window:
|
||
recent_messages.append(msg_record)
|
||
|
||
if recent_messages.size() >= max_messages_per_window:
|
||
return {
|
||
"valid": false,
|
||
"reason": "发送消息过于频繁,请稍后再试"
|
||
}
|
||
|
||
return {"valid": true, "reason": ""}
|
||
|
||
## 检查垃圾信息
|
||
func _check_spam(user_id: String, message: String) -> Dictionary:
|
||
"""
|
||
检查是否为垃圾信息
|
||
@param user_id: 用户ID
|
||
@param message: 消息内容
|
||
@return: 验证结果
|
||
"""
|
||
if not message_history.has(user_id):
|
||
return {"valid": true, "reason": ""}
|
||
|
||
var user_messages = message_history[user_id]
|
||
var duplicate_count = 0
|
||
var current_time = Time.get_unix_time_from_system()
|
||
|
||
# 检查重复消息
|
||
for msg_record in user_messages:
|
||
# 只检查最近的消息
|
||
if current_time - msg_record.timestamp <= spam_detection_window:
|
||
if msg_record.message.to_lower() == message.to_lower():
|
||
duplicate_count += 1
|
||
|
||
if duplicate_count >= max_duplicate_messages:
|
||
return {
|
||
"valid": false,
|
||
"reason": "请不要重复发送相同的消息"
|
||
}
|
||
|
||
# 检查是否全是重复字符
|
||
if _is_repetitive_text(message):
|
||
return {
|
||
"valid": false,
|
||
"reason": "请发送有意义的消息内容"
|
||
}
|
||
|
||
# 检查是否全是大写字母(可能是刷屏)
|
||
if message.length() > 10 and message == message.to_upper():
|
||
return {
|
||
"valid": false,
|
||
"reason": "请不要使用全大写字母"
|
||
}
|
||
|
||
return {"valid": true, "reason": ""}
|
||
|
||
## 过滤违禁词
|
||
func _filter_profanity(message: String) -> Dictionary:
|
||
"""
|
||
过滤消息中的违禁词
|
||
@param message: 原始消息
|
||
@return: 过滤结果
|
||
"""
|
||
var filtered_message = message
|
||
var has_profanity = false
|
||
|
||
for word in profanity_words:
|
||
if filtered_message.to_lower().contains(word.to_lower()):
|
||
# 替换违禁词
|
||
var regex = RegEx.new()
|
||
regex.compile("(?i)" + word) # 不区分大小写
|
||
filtered_message = regex.sub(filtered_message, profanity_replacement, true)
|
||
has_profanity = true
|
||
|
||
return {
|
||
"has_profanity": has_profanity,
|
||
"filtered_message": filtered_message
|
||
}
|
||
|
||
## 检查是否为重复字符文本
|
||
func _is_repetitive_text(text: String) -> bool:
|
||
"""
|
||
检查文本是否主要由重复字符组成
|
||
@param text: 输入文本
|
||
@return: 是否为重复字符文本
|
||
"""
|
||
if text.length() < 5:
|
||
return false
|
||
|
||
var char_counts = {}
|
||
for character in text:
|
||
char_counts[character] = char_counts.get(character, 0) + 1
|
||
|
||
# 如果任何字符占比超过70%,认为是重复文本
|
||
var threshold = text.length() * 0.7
|
||
for count in char_counts.values():
|
||
if count > threshold:
|
||
return true
|
||
|
||
return false
|
||
|
||
## 记录消息历史
|
||
func _record_message(user_id: String, message: String) -> void:
|
||
"""
|
||
记录用户消息历史(用于垃圾信息检测)
|
||
@param user_id: 用户ID
|
||
@param message: 消息内容
|
||
"""
|
||
if not message_history.has(user_id):
|
||
message_history[user_id] = []
|
||
|
||
var user_messages = message_history[user_id]
|
||
var current_time = Time.get_unix_time_from_system()
|
||
|
||
# 添加新消息
|
||
user_messages.append({
|
||
"message": message,
|
||
"timestamp": current_time
|
||
})
|
||
|
||
# 清理过期消息(保留最近1小时的消息)
|
||
var one_hour_ago = current_time - 3600
|
||
var filtered_messages = []
|
||
for msg_record in user_messages:
|
||
if msg_record.timestamp > one_hour_ago:
|
||
filtered_messages.append(msg_record)
|
||
|
||
message_history[user_id] = filtered_messages
|
||
|
||
## 添加违禁词
|
||
func add_profanity_word(word: String) -> void:
|
||
"""
|
||
添加违禁词到过滤列表
|
||
@param word: 违禁词
|
||
"""
|
||
var clean_word = word.strip_edges().to_lower()
|
||
if not clean_word.is_empty() and not clean_word in profanity_words:
|
||
profanity_words.append(clean_word)
|
||
save_filter_config()
|
||
|
||
## 移除违禁词
|
||
func remove_profanity_word(word: String) -> void:
|
||
"""
|
||
从过滤列表中移除违禁词
|
||
@param word: 违禁词
|
||
"""
|
||
var clean_word = word.strip_edges().to_lower()
|
||
if clean_word in profanity_words:
|
||
profanity_words.erase(clean_word)
|
||
save_filter_config()
|
||
|
||
## 设置过滤配置
|
||
func set_filter_config(config: Dictionary) -> void:
|
||
"""
|
||
设置过滤器配置
|
||
@param config: 配置字典
|
||
"""
|
||
if config.has("enable_profanity_filter"):
|
||
enable_profanity_filter = config.enable_profanity_filter
|
||
|
||
if config.has("enable_spam_detection"):
|
||
enable_spam_detection = config.enable_spam_detection
|
||
|
||
if config.has("enable_length_limit"):
|
||
enable_length_limit = config.enable_length_limit
|
||
|
||
if config.has("enable_rate_limiting"):
|
||
enable_rate_limiting = config.enable_rate_limiting
|
||
|
||
if config.has("max_message_length"):
|
||
max_message_length = config.max_message_length
|
||
|
||
if config.has("max_messages_per_window"):
|
||
max_messages_per_window = config.max_messages_per_window
|
||
|
||
save_filter_config()
|
||
|
||
## 获取过滤配置
|
||
func get_filter_config() -> Dictionary:
|
||
"""
|
||
获取当前过滤器配置
|
||
@return: 配置字典
|
||
"""
|
||
return {
|
||
"enable_profanity_filter": enable_profanity_filter,
|
||
"enable_spam_detection": enable_spam_detection,
|
||
"enable_length_limit": enable_length_limit,
|
||
"enable_rate_limiting": enable_rate_limiting,
|
||
"max_message_length": max_message_length,
|
||
"min_message_length": min_message_length,
|
||
"max_messages_per_window": max_messages_per_window,
|
||
"spam_detection_window": spam_detection_window,
|
||
"profanity_words_count": profanity_words.size()
|
||
}
|
||
|
||
## 获取过滤统计
|
||
func get_filter_statistics() -> Dictionary:
|
||
"""
|
||
获取过滤统计信息
|
||
@return: 统计信息字典
|
||
"""
|
||
var stats = filter_stats.duplicate()
|
||
|
||
if stats.total_messages > 0:
|
||
stats["filter_rate"] = float(stats.filtered_messages) / float(stats.total_messages)
|
||
stats["block_rate"] = float(stats.profanity_blocked + stats.spam_blocked + stats.length_violations + stats.rate_limit_violations) / float(stats.total_messages)
|
||
else:
|
||
stats["filter_rate"] = 0.0
|
||
stats["block_rate"] = 0.0
|
||
|
||
return stats
|
||
|
||
## 重置统计信息
|
||
func reset_statistics() -> void:
|
||
"""重置过滤统计信息"""
|
||
filter_stats = {
|
||
"total_messages": 0,
|
||
"filtered_messages": 0,
|
||
"profanity_blocked": 0,
|
||
"spam_blocked": 0,
|
||
"length_violations": 0,
|
||
"rate_limit_violations": 0
|
||
}
|
||
|
||
## 清理用户历史
|
||
func clear_user_history(user_id: String) -> void:
|
||
"""
|
||
清理指定用户的消息历史
|
||
@param user_id: 用户ID
|
||
"""
|
||
if message_history.has(user_id):
|
||
message_history.erase(user_id)
|
||
|
||
## 保存过滤器配置
|
||
func save_filter_config() -> void:
|
||
"""保存过滤器配置到本地文件"""
|
||
var config = {
|
||
"filter_settings": get_filter_config(),
|
||
"profanity_words": profanity_words
|
||
}
|
||
|
||
var file = FileAccess.open("user://dialogue_filter_config.json", FileAccess.WRITE)
|
||
if file:
|
||
var json_string = JSON.stringify(config)
|
||
file.store_string(json_string)
|
||
file.close()
|
||
print("Filter config saved")
|
||
|
||
## 加载过滤器配置
|
||
func load_filter_config() -> void:
|
||
"""从本地文件加载过滤器配置"""
|
||
if not FileAccess.file_exists("user://dialogue_filter_config.json"):
|
||
print("No filter config found, using defaults")
|
||
return
|
||
|
||
var file = FileAccess.open("user://dialogue_filter_config.json", FileAccess.READ)
|
||
if file:
|
||
var json_string = file.get_as_text()
|
||
file.close()
|
||
|
||
var json = JSON.new()
|
||
var parse_result = json.parse(json_string)
|
||
|
||
if parse_result == OK:
|
||
var config = json.data
|
||
|
||
if config.has("filter_settings"):
|
||
set_filter_config(config.filter_settings)
|
||
|
||
if config.has("profanity_words") and config.profanity_words is Array:
|
||
profanity_words = config.profanity_words
|
||
|
||
print("Filter config loaded")
|
||
else:
|
||
print("Failed to parse filter config")
|
||
|
||
## 定期清理过期数据
|
||
func _on_cleanup_timer():
|
||
"""定期清理过期的消息历史数据"""
|
||
var current_time = Time.get_unix_time_from_system()
|
||
var one_hour_ago = current_time - 3600
|
||
|
||
for user_id in message_history.keys():
|
||
var user_messages = message_history[user_id]
|
||
var filtered_messages = []
|
||
|
||
for msg_record in user_messages:
|
||
if msg_record.timestamp > one_hour_ago:
|
||
filtered_messages.append(msg_record)
|
||
|
||
if filtered_messages.is_empty():
|
||
message_history.erase(user_id)
|
||
else:
|
||
message_history[user_id] = filtered_messages
|