Files
whale-town/scripts/DialogueFilter.gd
2025-12-05 19:00:14 +08:00

449 lines
12 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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