Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+291
View File
@@ -0,0 +1,291 @@
package filter
import (
"bufio"
"bytes"
"io"
"unicode"
)
// Filter 提供敏感词过滤接口
type Filter interface {
// Filter 文本过滤函数
// excludes 表示排除指定的字符
// 返回文本中出现的敏感词,如果敏感词不存在则返回nil
// 如果出现异常,则返回error
Filter(text string, excludes ...rune) ([]string, error)
// FilterResult 文本过滤函数
// excludes 表示排除指定的字符
// 返回文本中出现的敏感词及出现次数,如果敏感词不存在则返回nil
// 如果出现异常,则返回error
FilterResult(text string, excludes ...rune) (map[string]int, error)
// FilterReader 从可读流中过滤敏感词
// excludes 表示排除指定的字符
// 返回可读流中出现的敏感词,如果敏感词不存在则返回nil
// 如果出现异常,则返回error
FilterReader(reader io.Reader, excludes ...rune) ([]string, error)
// FilterReaderResult 从可读流中过滤敏感词
// excludes 表示排除指定的字符
// 返回可读流中出现的敏感词及出现次数,如果敏感词不存在则返回nil
// 如果出现异常,则返回error
FilterReaderResult(reader io.Reader, excludes ...rune) (map[string]int, error)
// Replace 使用字符替换文本中的敏感词
// delim 替换的字符
// 如果出现异常,则返回error
Replace(text string, delim rune) (string, error)
}
// NewNodeReaderFilter 创建节点过滤器,实现敏感词的过滤
// 从可读流中读取敏感词数据(以指定的分隔符读取数据)
func NewNodeReaderFilter(rd io.Reader, delim byte) Filter {
nf := &nodeFilter{
root: newNode(),
}
buf := new(bytes.Buffer)
_, _ = io.Copy(buf, rd)
buf.WriteByte(delim)
for {
line, err := buf.ReadString(delim)
if err != nil {
break
}
if line == "" {
continue
}
nf.addFilterWords(line)
}
buf.Reset()
return nf
}
// NewNodeChanFilter 创建节点过滤器,实现敏感词的过滤
// 从通道中读取敏感词数据
func NewNodeChanFilter(text <-chan string) Filter {
nf := &nodeFilter{
root: newNode(),
}
for v := range text {
nf.addFilterWords(v)
}
return nf
}
// NewNodeFilter 创建节点过滤器,实现敏感词的过滤
// 从切片中读取敏感词数据
func NewNodeFilter(text []string) Filter {
nf := &nodeFilter{
root: newNode(),
}
for i, l := 0, len(text); i < l; i++ {
nf.addFilterWords(text[i])
}
return nf
}
func newNode() *node {
return &node{
child: make(map[rune]*node),
}
}
type node struct {
end bool
child map[rune]*node
}
type nodeFilter struct {
root *node
}
func (nf *nodeFilter) addFilterWords(text string) {
n := nf.root
uChars := []rune(text)
for i, l := 0, len(uChars); i < l; i++ {
if unicode.IsSpace(uChars[i]) {
continue
}
if _, ok := n.child[uChars[i]]; !ok {
n.child[uChars[i]] = newNode()
}
n = n.child[uChars[i]]
}
n.end = true
}
func (nf *nodeFilter) Filter(text string, excludes ...rune) ([]string, error) {
buf := bytes.NewBufferString(text)
defer buf.Reset()
return nf.FilterReader(buf, excludes...)
}
func (nf *nodeFilter) FilterResult(text string, excludes ...rune) (map[string]int, error) {
buf := bytes.NewBufferString(text)
defer buf.Reset()
return nf.FilterReaderResult(buf, excludes...)
}
func (nf *nodeFilter) FilterReader(reader io.Reader, excludes ...rune) ([]string, error) {
data, err := nf.FilterReaderResult(reader, excludes...)
if err != nil {
return nil, err
}
result := make([]string, len(data))
i := 0
for k := range data {
result[i] = k
i++
}
return result, nil
}
func (nf *nodeFilter) FilterReaderResult(reader io.Reader, excludes ...rune) (map[string]int, error) {
var uChars []rune
data := make(map[string]int)
bi := bufio.NewReader(reader)
for {
ur, _, err := bi.ReadRune()
if err != nil {
if err != io.EOF {
return nil, err
}
break
}
if nf.checkExclude(ur, excludes...) {
continue
}
if (unicode.IsSpace(ur) || unicode.IsPunct(ur)) && len(uChars) > 0 {
nf.doFilter(uChars[:], data)
uChars = nil
continue
}
uChars = append(uChars, ur)
}
if len(uChars) > 0 {
nf.doFilter(uChars, data)
}
return data, nil
}
func (nf *nodeFilter) Replace(text string, delimiter rune) (string, error) {
newNF, err := CheckUpdate()
if err != nil {
return "", err
}
if newNF != nil {
nf = newNF
}
uChars := []rune(text)
indexes := nf.doIndexes(uChars)
if len(indexes) == 0 {
return text, nil
}
for i := 0; i < len(indexes); i++ {
uChars[indexes[i]] = delimiter
}
return string(uChars), nil
}
func (nf *nodeFilter) checkExclude(u rune, excludes ...rune) bool {
if len(excludes) == 0 {
return false
}
var exist bool
for i, l := 0, len(excludes); i < l; i++ {
if u == excludes[i] {
exist = true
break
}
}
return exist
}
func (nf *nodeFilter) doFilter(uChars []rune, data map[string]int) {
var result []string
ul := len(uChars)
buf := new(bytes.Buffer)
n := nf.root
for i := 0; i < ul; i++ {
if _, ok := n.child[uChars[i]]; !ok {
continue
}
n = n.child[uChars[i]]
buf.WriteRune(uChars[i])
if n.end {
result = append(result, buf.String())
}
for j := i + 1; j < ul; j++ {
if _, ok := n.child[uChars[j]]; !ok {
break
}
n = n.child[uChars[j]]
buf.WriteRune(uChars[j])
if n.end {
result = append(result, buf.String())
}
}
buf.Reset()
n = nf.root
}
for i, l := 0, len(result); i < l; i++ {
var c int
if v, ok := data[result[i]]; ok {
c = v
}
data[result[i]] = c + 1
}
}
func (nf *nodeFilter) doIndexes(uChars []rune) (indexes []int) {
var (
tIndexes []int
ul = len(uChars)
n = nf.root
)
for i := 0; i < ul; i++ {
if _, ok := n.child[uChars[i]]; !ok {
continue
}
n = n.child[uChars[i]]
tIndexes = append(tIndexes, i)
if n.end {
indexes = nf.appendTo(indexes, tIndexes)
tIndexes = nil
}
for j := i + 1; j < ul; j++ {
if _, ok := n.child[uChars[j]]; !ok {
break
}
n = n.child[uChars[j]]
tIndexes = append(tIndexes, j)
if n.end {
indexes = nf.appendTo(indexes, tIndexes)
}
}
if tIndexes != nil {
tIndexes = nil
}
n = nf.root
}
return
}
func (nf *nodeFilter) appendTo(dst, src []int) []int {
var t []int
for i, il := 0, len(src); i < il; i++ {
var exist bool
for j, jl := 0, len(dst); j < jl; j++ {
if src[i] == dst[j] {
exist = true
break
}
}
if !exist {
t = append(t, src[i])
}
}
return append(dst, t...)
}
+148
View File
@@ -0,0 +1,148 @@
package filter
import (
"encoding/json"
"fmt"
"os"
"time"
"91porn-server/app/appg"
"91porn-server/common/cache"
"91porn-server/common/constant/redisconst"
"91porn-server/common/log"
"91porn-server/models/v/filtermod"
"91porn-server/web/webg"
)
var (
WordCache = &cache.Cache{}
WordFilter Filter
TagFilter Filter
WordsFilterKey = "FILTER-WORDS"
WordsReadStsKey = "isRead"
Read = "read"
UnRead = "unread"
ReplaceMark = '*'
)
// FilterStart 启动文本过滤器-(评论)
func Start() {
Init()
InitTagFilter()
}
func InitTagFilter() {
log.Info("Init Tag Filter start...")
TagFilter = NewNodeFilter(appg.Static.TagFilter)
}
// Init Init
func Init() {
log.Info("Init WordsFilter start...")
WordCache.New()
// 从数据库获取过滤词
words, err := GetFilterWordsFromDB()
if err != nil {
log.Error("WordFilter Init GetFilterWords error", log.E(err))
os.Exit(1)
}
// 添加过滤词到缓存
if err = WordCache.Add(WordsFilterKey, words, 0); err != nil {
log.Error("WordFilter Init WordCache Add FILTER-WORD error", log.E(err), log.Any("words", words))
os.Exit(1)
}
if len(words) > 0 {
WordFilter = NewNodeFilter(words)
// 添加过滤词到Redis
if err = appg.Redis.Lpush(WordsFilterKey, words); err != nil {
log.Error("WordFilter Init GetNewestFilterWord error", log.E(err), log.Any("words", words))
os.Exit(1)
}
}
log.Info("Init WordsFilter end")
}
// GetFilterWords 从Redis获取过滤词
func GetFilterWordsFromDB() ([]string, error) {
filterWords, err := filtermod.GetAllFilterWords()
if err != nil {
return nil, err
}
words := make([]string, len(filterWords))
for i, v := range filterWords {
words[i] = v.Word
}
return words, nil
}
// GetFilterWords 从Redis获取过滤词
func GetFilterWordsFromRedis() ([]string, error) {
count, err := appg.Redis.LCount(WordsFilterKey)
if err != nil {
return nil, err
}
return appg.Redis.LRange(WordsFilterKey, 0, count)
}
// 检查是否有跟新
func CheckUpdate() (*nodeFilter, error) {
isRead, err := appg.Redis.Get(WordsReadStsKey)
if err != nil {
log.Error("CheckUpdate Redis Get error")
return nil, err
}
if isRead != nil && *isRead == UnRead {
words, err := GetFilterWordsFromRedis()
if err != nil {
log.Error("WordFilter CheckUpdate GetFilterWords error")
return nil, err
}
WordCache.New()
if err = WordCache.Add(WordsFilterKey, words, 0); err != nil {
log.Error("WordFilter CheckUpdate WordCache Add error")
return nil, err
}
WordFilter = NewNodeFilter(words)
if err = appg.Redis.Set(WordsReadStsKey, Read, 0); err != nil {
log.Error("WordFilter Redis Del error")
return nil, err
}
var tmp interface{} = WordFilter
var nf = tmp.(*nodeFilter)
return nf, nil
}
return nil, nil
}
// UpdateFilterWords web更新Redis
func UpdateFilterWords() error {
var (
sensitiveWords []string
)
// 更新Redis
if _, err := webg.Redis.Del(redisconst.SensitiveWordsCache); err != nil {
log.Error("WordsFilter UpdateFilterWords Del error", log.E(err))
return err
}
words, err := filtermod.GetAllFilterWords()
if err != nil || len(words) <= 0 {
return err
}
for _, w := range words {
sensitiveWords = append(sensitiveWords, w.Word)
}
if len(sensitiveWords) > 0 {
// 放入缓存
bytes, _ := json.Marshal(sensitiveWords)
err := webg.Redis.Set(redisconst.SensitiveWordsCache, bytes, 10*time.Minute)
if err != nil {
log.Warn(fmt.Sprintf("redis set SensitiveWords err:%v", err))
}
}
return nil
}