censor_bear 0.1.12 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -1
  3. data/app/assets/stylesheets/censor_bear/home.css +4 -0
  4. data/app/controllers/censor_bear/home_controller.rb +31 -0
  5. data/app/controllers/censor_bear/logs_controller.rb +18 -0
  6. data/app/controllers/censor_bear/mod_logs_controller.rb +24 -22
  7. data/app/controllers/censor_bear/stop_words_controller.rb +11 -2
  8. data/app/helpers/censor_bear/home_helper.rb +4 -0
  9. data/app/models/censor_bear/log.rb +18 -0
  10. data/app/models/censor_bear/mod_log.rb +57 -16
  11. data/app/models/censor_bear/stop_word.rb +2 -2
  12. data/app/views/censor_bear/home/debug.turbo_stream.haml +8 -0
  13. data/app/views/censor_bear/home/debugger.html.haml +9 -0
  14. data/app/views/censor_bear/home/index.html.erb +2 -0
  15. data/app/views/censor_bear/logs/_log.html.haml +8 -2
  16. data/app/views/censor_bear/logs/index.html.haml +6 -1
  17. data/app/views/censor_bear/logs/show.html.haml +29 -0
  18. data/app/views/censor_bear/mod_logs/_mod_log.html.haml +41 -37
  19. data/app/views/censor_bear/mod_logs/index.html.haml +12 -2
  20. data/app/views/censor_bear/stop_words/index.html.haml +2 -1
  21. data/app/views/layouts/censor_bear/application.html.haml +3 -2
  22. data/config/locales/censor_bear.yml +7 -2
  23. data/config/routes.rb +12 -5
  24. data/db/migrate/20211129110218_create_censor_bear_mod_logs.rb +0 -1
  25. data/db/migrate/20211209093050_add_response_to_censor_bear_logs.rb +5 -0
  26. data/db/migrate/20211209113710_add_aasm_state_to_censor_bear_mod_logs.rb +5 -0
  27. data/lib/censor_bear/censor.rb +27 -11
  28. data/lib/censor_bear/configuration.rb +2 -0
  29. data/lib/censor_bear/version.rb +1 -1
  30. data/lib/censor_bear.rb +5 -0
  31. metadata +25 -3
  32. data/app/views/censor_bear/logs/show.html.erb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 58d7a9f6214a5f560794d6bca513cf288d01e7444aa293e243a3cf3e33e8e8d8
4
- data.tar.gz: c1385ce54281b8d764f191f262c7941b32c0590b304d57976ef8e661f24c49ec
3
+ metadata.gz: da82f8fafa1a258c9009099fc66621805e19ac0750662f92153eb3a1a8ba5b01
4
+ data.tar.gz: 4a51f1c4260ba69397dfc583093fb59417ba0f9b95b458243d2b28ec58087454
5
5
  SHA512:
6
- metadata.gz: 97924df8a0ab45a7afc36d483e20bba15ecd98c23134d629bb4c93e44c233eff45e935871579303b68f107ba3b555d5b595c7f5e0a129bf107b4b213b4827272
7
- data.tar.gz: e964103ec3434614e1d7eea7829efebf1bc66b6ad2b8bec9c8e6b2c8868b637c13baa2aad85ccc7f3427437ba3e04eb5a8f649f4f8c85dc947246d50c1bd4494
6
+ metadata.gz: 0fed9c06844c55e4fca2c9d997d8f7af7a1bb04c33c0cc73f7fe1d1cbf4c1f0bce029a239f058a0eda4bd8bd11c9b670ad4019d8f3d75e9916d00f8d66a6ddf2
7
+ data.tar.gz: 11a70e93051a636a39240dd09327ef38778c82fc22af53b325e28e1635036de93f9ab85ab4b66d6cc8e78d1c04ae6b74ad31b7fa31ee5a958a75b9ac09dcccb7
data/README.md CHANGED
@@ -50,10 +50,12 @@ rails censor_bear:install:migrations
50
50
 
51
51
  ## 🦆 Methods
52
52
  ```ruby
53
- censor_delete # 删除内容
53
+ censor_remove # 删除内容
54
+ censor_cancel # 撤销删除
54
55
  censor_approve # 通过审核
55
56
  censor_show_path # 详情路由
56
57
  censor_ban_user # 封禁用户
58
+
57
59
  ```
58
60
 
59
61
  ## Exceptions
@@ -63,6 +65,22 @@ censor_ban_user # 封禁用户
63
65
  class NotPassedException < StandardError; end
64
66
  ```
65
67
 
68
+ ## 初始化配置
69
+ 详细配置项可在`lib/censor_bear/configuration.rb`中查看。
70
+
71
+ ```ruby
72
+ CensorBear.configure do |config|
73
+ config.user_class = 'User'
74
+ config.user_name_method = 'nickname'
75
+
76
+ config.aliyun_green_access_key_id = 'access key id'
77
+ config.aliyun_green_access_key_secret = 'access key secret'
78
+ config.aliyun_green_enable_internal = false
79
+
80
+ config.main_app_root_path_method = 'root_path'
81
+ end
82
+ ```
83
+
66
84
  ## 注意事项
67
85
  1. 为 MOD 时,表示需要人工审核,需要被审查对象(UGC内容)支持`is_approved`字段(默认为`true`),此时内容自己能看到,别人暂时看不到,需审核通过后可见。
68
86
  2. 被审查对象可以通过软删实现,隐藏(目的是防止错判,撤销),删除的内容对自己和别人都是不可见的。
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,31 @@
1
+ require_dependency 'censor_bear/application_controller'
2
+
3
+ module CensorBear
4
+ class HomeController < ApplicationController
5
+ def index; end
6
+
7
+ def debugger; end
8
+
9
+ def debug
10
+ content = params[:content]
11
+ type = params[:type]
12
+
13
+ case params[:stage]
14
+ when 'aliyun_check'
15
+ @r = AliyunGreen::Text.scan(content)
16
+ when 'check_text'
17
+ @r = CensorBear.check_text(content, type.to_sym)
18
+ when 'check_search'
19
+ # TODO: 搜索前置检查
20
+ @r = {}
21
+ when 'check_image'
22
+ # TODO: 检查图片
23
+ @r = {}
24
+ when 'qq_regex'
25
+ @r = content.match(Censor::QQ_REG)
26
+ when 'wx_regex'
27
+ @r = content.match(Censor::WX_REG)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -52,9 +52,27 @@ module CensorBear
52
52
  end
53
53
  end
54
54
 
55
+ def remove_data
56
+ builder = log_scope
57
+ case params[:type]
58
+ when '3days'
59
+ builder = builder.where("created_at >= ?", (Time.now - 3.days).beginning_of_day)
60
+ when '7days'
61
+ builder = builder.where("created_at >= ?", (Time.now - 7.days).beginning_of_day)
62
+ when '30days'
63
+ builder = builder.where("created_at >= ?", (Time.now - 30.days).beginning_of_day)
64
+ when 'all'
65
+ builder
66
+ end
67
+ builder.delete_all
68
+
69
+ redirect_to logs_url, notice: "Mod log was successfully destroyed."
70
+ end
71
+
55
72
  def load_logs
56
73
  builder ||= log_scope
57
74
  builder = builder.where("original_content ilike ?", "%#{params[:q]}%") if params[:q].present?
75
+ builder = builder.where(stage: params[:stage]) if params[:stage].present?
58
76
  builder = builder.order(id: :desc)
59
77
 
60
78
  @pagy, @logs = pagy(builder)
@@ -1,28 +1,23 @@
1
- require_dependency "censor_bear/application_controller"
1
+ require_dependency 'censor_bear/application_controller'
2
2
 
3
3
  module CensorBear
4
4
  class ModLogsController < ApplicationController
5
- before_action :set_mod_log, only: %i[show edit update destroy ignore approve ban delete]
5
+ before_action :set_mod_log, only: %i[show edit update destroy suspend ban pass undo reject]
6
6
 
7
- # GET /mod_logs
8
7
  def index
9
8
  load_mod_logs
10
9
  end
11
10
 
12
- # GET /mod_logs/1
13
11
  def show
14
12
  end
15
13
 
16
- # GET /mod_logs/new
17
14
  def new
18
15
  @mod_log = ModLog.new
19
16
  end
20
17
 
21
- # GET /mod_logs/1/edit
22
18
  def edit
23
19
  end
24
20
 
25
- # POST /mod_logs
26
21
  def create
27
22
  @mod_log = ModLog.new(mod_log_params)
28
23
 
@@ -33,7 +28,6 @@ module CensorBear
33
28
  end
34
29
  end
35
30
 
36
- # PATCH/PUT /mod_logs/1
37
31
  def update
38
32
  if @mod_log.update(mod_log_params)
39
33
  redirect_to @mod_log, notice: 'Mod log was successfully updated.'
@@ -42,7 +36,6 @@ module CensorBear
42
36
  end
43
37
  end
44
38
 
45
- # DELETE /mod_logs/1
46
39
  def destroy
47
40
  @mod_log.destroy
48
41
 
@@ -52,24 +45,32 @@ module CensorBear
52
45
  end
53
46
  end
54
47
 
55
-
56
- def ignore
48
+ def suspend
57
49
  @mod_log.suspend!
58
50
 
59
51
  redirect_to mod_logs_url, notice: 'Mod log was successfully ignored.'
60
52
  end
61
53
 
62
- def delete
63
- # TODO: 判断参数不能为空
64
- @mod_log.delete(params[:reason])
54
+ def pass
55
+ @mod_log.approve
65
56
 
66
- redirect_to mod_logs_url, notice: 'Mod log was successfully deleted.'
57
+ redirect_to mod_logs_url, notice: 'Mod log was successfully ignored.'
67
58
  end
68
59
 
69
- def approve
70
- @mod_log.approve
60
+ def undo
61
+ if @mod_log.passed?
62
+ @mod_log.undo_approve
63
+ elsif @mod_log.rejected?
64
+ @mod_log.undo_remove
65
+ end
66
+
67
+ redirect_to mod_logs_url, notice: 'Mod log was successfully ignored.'
68
+ end
69
+
70
+ def reject
71
+ @mod_log.remove(params[:reason])
71
72
 
72
- redirect_to mod_logs_url, notice: 'Mod log was successfully approved.'
73
+ redirect_to mod_logs_url, notice: 'Mod log was successfully ignored.'
73
74
  end
74
75
 
75
76
  def ban
@@ -80,8 +81,11 @@ module CensorBear
80
81
 
81
82
  def load_mod_logs
82
83
  builder ||= mod_log_scope.includes(:record)
83
- builder = builder.where("content ilike ?", "%#{params[:q]}%") if params[:q].present?
84
-
84
+ builder = builder.where('content ilike ?', "%#{params[:q]}%") if params[:q].present?
85
+ if params[:filter] != 'all'
86
+ builder = builder.pending
87
+ builder = builder.unscope(where: :aasm_state).where(aasm_state: params[:state]) if params[:state].present?
88
+ end
85
89
  builder = builder.order(id: :desc)
86
90
 
87
91
  @pagy, @mod_logs = pagy(builder)
@@ -89,7 +93,6 @@ module CensorBear
89
93
 
90
94
  private
91
95
 
92
- # Use callbacks to share common setup or constraints between actions.
93
96
  def set_mod_log
94
97
  @mod_log = ModLog.find(params[:id] || params[:mod_log_id])
95
98
  end
@@ -98,7 +101,6 @@ module CensorBear
98
101
  ModLog
99
102
  end
100
103
 
101
- # Only allow a list of trusted parameters through.
102
104
  def mod_log_params
103
105
  params.require(:mod_log).permit(:record_id, :record_type, :mod_words, :user_id, :reason)
104
106
  end
@@ -1,4 +1,4 @@
1
- require_dependency "censor_bear/application_controller"
1
+ require_dependency 'censor_bear/application_controller'
2
2
 
3
3
  module CensorBear
4
4
  class StopWordsController < ApplicationController
@@ -46,7 +46,16 @@ module CensorBear
46
46
 
47
47
  def load_stop_words
48
48
  builder ||= stop_word_scope
49
- builder = builder.where("key ilike ?", "%#{params[:q]}%") if params[:q].present?
49
+ builder = builder.where('key ilike ?', "%#{params[:q]}%") if params[:q].present?
50
+ case params[:filter]
51
+ when 'size_one'
52
+ builder = builder.where('length(key) = 1')
53
+ when 'size_two'
54
+ builder = builder.where('length(key) = 2')
55
+ when 'size_three'
56
+ builder = builder.where('length(key) = 3')
57
+ end
58
+ builder = builder.where('key ilike ?', "%#{params[:q]}%")
50
59
  builder = builder.order(id: :desc)
51
60
 
52
61
  @pagy, @stop_words = pagy(builder)
@@ -0,0 +1,4 @@
1
+ module CensorBear
2
+ module HomeHelper
3
+ end
4
+ end
@@ -1,5 +1,23 @@
1
1
  module CensorBear
2
2
  class Log < ApplicationRecord
3
3
  belongs_to :user, class_name: CensorBear.config.user_class, optional: true
4
+
5
+ def suggestion
6
+ return nil if response.blank?
7
+
8
+ response['data']&.first['results']&.first['suggestion']
9
+ end
10
+
11
+ def rate
12
+ return nil if response.blank?
13
+
14
+ response['data']&.first['results']&.first['rate']
15
+ end
16
+
17
+ def label
18
+ return nil if response.blank?
19
+
20
+ response['data']&.first['results']&.first['label']
21
+ end
4
22
  end
5
23
  end
@@ -1,22 +1,42 @@
1
1
  module CensorBear
2
2
  class ModLog < ApplicationRecord
3
- REASONS = %w(无 广告/SPAM 恶意灌水 违规内容 文不对题 重复发布 其它)
3
+ include AASM
4
+
5
+ REASONS = %w(无 广告/SPAM 恶意灌水 违规内容 文不对题 重复发布 其它).freeze
4
6
  belongs_to :record, polymorphic: true, required: false
5
7
 
8
+ validates :record_id, uniqueness: { scope: [:record_type] }
9
+
10
+ def images
11
+ return [] if record.blank?
12
+
13
+ raise NoMethodError, 'undefined censor_images for record' unless record.respond_to?(:censor_images)
14
+
15
+ record.censor_images
16
+ end
17
+
6
18
  def record_path
7
19
  return '#' if record.blank?
8
20
 
9
21
  record.censor_show_path
10
22
  end
11
23
 
12
- def delete(reason)
24
+ def remove(reason)
13
25
  return if record.blank?
14
26
  return if record.discarded?
15
27
  # ugc内容需要开软删,便于撤销&查看统计,可以定时删除统计并删除软删内容,类似回收站机制
16
- raise NoMethodError.new("undefined censor_delete for record") unless record.respond_to?(:censor_delete)
28
+ raise NoMethodError, 'undefined censor_remove for record' unless record.respond_to?(:censor_remove)
17
29
 
18
- ret = record.censor_delete
19
- handled!(reason) if ret
30
+ ret = record.censor_remove
31
+ reject!(reason) if ret
32
+ end
33
+
34
+ def undo_remove
35
+ return if record.blank?
36
+ raise NoMethodError unless record.respond_to?(:censor_undo_remove)
37
+
38
+ ret = record.censor_undo_remove
39
+ undo! if ret
20
40
  end
21
41
 
22
42
  def approve
@@ -24,7 +44,15 @@ module CensorBear
24
44
  raise NoMethodError unless record.respond_to?(:censor_approve)
25
45
 
26
46
  ret = record.censor_approve
27
- handled! if ret
47
+ pass! if ret
48
+ end
49
+
50
+ def undo_approve
51
+ return if record.blank?
52
+ raise NoMethodError unless record.respond_to?(:censor_undo_approve)
53
+
54
+ ret = record.censor_undo_approve
55
+ undo! if ret
28
56
  end
29
57
 
30
58
  def ban_user
@@ -32,23 +60,36 @@ module CensorBear
32
60
  raise NoMethodError unless record.respond_to?(:censor_ban_user)
33
61
 
34
62
  ret = record.censor_ban_user
35
- delete if ret
63
+ reject!('已被封禁该用户') if ret
36
64
  end
37
65
 
38
- scope :pending, -> { where(status: 0) }
39
- scope :handled, -> { where(status: 1) }
40
- scope :suspended, -> { where(status: 2) }
66
+ aasm do
67
+ state :pending, initial: true
68
+ state :rejected, :passed, :suspended
69
+
70
+ event :reject do
71
+ transitions from: %i[pending suspended], to: :rejected, after: Proc.new {|*args| set_reason(*args) }
72
+ end
73
+
74
+ event :pass do
75
+ transitions from: %i[pending suspended], to: :passed
76
+ end
77
+
78
+ event :suspend do
79
+ transitions from: :pending, to: :suspended, after: :unset_reason
80
+ end
41
81
 
42
- def handled!(reason = nil)
43
- update_columns(status: 1, reason: reason)
82
+ event :undo do
83
+ transitions from: %i[passed rejected], to: :pending, after: :unset_reason
84
+ end
44
85
  end
45
86
 
46
- def pend!
47
- update_columns(status: 0)
87
+ def unset_reason
88
+ update_columns(reason: nil)
48
89
  end
49
90
 
50
- def suspend!
51
- update_columns(status: 2)
91
+ def set_reason(reason)
92
+ update_columns(reason: reason)
52
93
  end
53
94
  end
54
95
  end
@@ -1,7 +1,7 @@
1
1
  module CensorBear
2
2
  class StopWord < ApplicationRecord
3
- FIELDS = %w(ugc username nickname signature dialog)
4
- ACTIONS_MAP = %w(ignore mod banned replace)
3
+ FIELDS = %w(ugc username nickname signature dialog).freeze
4
+ ACTIONS_MAP = %w(ignore mod banned replace).freeze
5
5
  belongs_to :user, class_name: CensorBear.config.user_class
6
6
  end
7
7
  end
@@ -0,0 +1,8 @@
1
+ = turbo_stream.update(:result) do
2
+ %h3{class: "text-lg"} 检测结果
3
+ %div{class: "bg-yellow-100 text-yellow-700 p-4 rounded-lg"}
4
+ %pre{class: "whitespace-pre-wrap"}
5
+ - if @r.is_a?(MatchData)
6
+ = @r.pretty_inspect
7
+ - else
8
+ = JSON.pretty_generate(@r.is_a?(CensorBear::Result) ? JSON.parse(@r.to_json) : @r)
@@ -0,0 +1,9 @@
1
+ %div{class: "flex flex-col space-y-2 rounded-lg border p-4"}
2
+ %h2{class: "text-xl font-medium text-center"} 在线调试
3
+ %div{class: "flex justify-center"}
4
+ = form_with(url: debug_path, method: :post, class: "flex flex-col items-center w-full space-y-4") do |f|
5
+ = f.select :stage, options_for_select([%w[check_text check_text], %w[aliyun_check aliyun_check], %w[check_search check_search], %w[qq_regex qq_regex], %w[wx_regex wx_regex]], params[:stage]), {}, class: "border rounded-md m py-0.5 px-1"
6
+ = f.select :type, options_for_select(CensorBear::StopWord::FIELDS.map{|f| [f.upcase, f]}, params[:type]), {}, class: "border rounded-md m py-0.5 px-1"
7
+ = f.text_area :content, value: params[:content], placeholder: "输入正文", rows: 6, class: "bg-gray-100 rounded-lg p-4 w-full"
8
+ = f.submit "检测", class: "rounded-lg px-10 py-1 bg-black text-white cursor-pointer"
9
+ #result
@@ -0,0 +1,2 @@
1
+ <h1>Home#index</h1>
2
+ <p>Find me in app/views/censor_bear/home/index.html.erb</p>
@@ -9,6 +9,12 @@
9
9
  %span{class: "text-sm bg-pink-100 text-pink-600 py-1 px-2 rounded-lg"}=t("censor_log.stage.#{log.stage}")
10
10
  - unless log.action.blank?
11
11
  %span{class: "text-sm bg-yellow-100 text-yellow-600 py-1 px-2 rounded-lg"}=t("censor_log.action.#{log.action.downcase}")
12
+ - unless log.suggestion.blank?
13
+ %span{class: "text-sm bg-green-100 text-green-600 py-1 px-2 rounded-lg"}=log.suggestion.upcase
14
+ - unless log.rate.blank?
15
+ %span{class: "text-sm bg-green-100 text-green-600 py-1 px-2 rounded-lg"}=log.rate
16
+ - unless log.label.blank?
17
+ %span{class: "text-sm bg-green-100 text-green-600 py-1 px-2 rounded-lg"}=log.label.upcase
12
18
  - unless log.user_id.blank?
13
19
  %span{class: "text-sm bg-gray-100 text-gray-600 py-1 px-2 rounded-lg font-bold"}="##{log.user_id}"
14
20
  - unless log.ip.blank?
@@ -19,7 +25,7 @@
19
25
  %span{class: "text-sm bg-gray-100 text-gray-600 py-1 px-2 rounded-lg"}= log.created_at
20
26
  %div{class: "flex space-x-2 items-center"}
21
27
  %span
22
- = link_to '详情', log
28
+ = link_to '详情', log, data: { turbo: false }
23
29
  %span
24
30
  = button_to '删除', log, method: :delete, data: { confirm: 'Are you sure?' }, class: "text-red-600 rounded-md px-2 py-0.5"
25
31
  %div{class: "flex justify-between items-center"}
@@ -32,5 +38,5 @@
32
38
  %div{class: "text-yellow-500 font-meidum text-xl"}
33
39
  - log.mod_words.each do |word|
34
40
  %span [
35
- %span= link_to word, stop_words_path(q: word), data: { turbo: false }, target: "_blank"
41
+ %span= link_to word, stop_words_path(q: word), data: { turbo: false }
36
42
  %span ]
@@ -4,10 +4,15 @@
4
4
  = form_with(url: logs_path, method: :get, class: "flex items-center") do |f|
5
5
  %div{class: "space-x-0.5"}
6
6
  = f.text_field :q, value: params[:q], placeholder: "关键词", class: "border rounded-md m py-0.5 px-1"
7
+ = f.select :stage, options_for_select([%w[aliyun_check aliyun_check], %w[check_search check_search], %w[qq_regex qq_regex], %w[wx_regex wx_regex], %w[local_check local_check]], params[:stage]), {include_blank: "-- 按Stage筛选 --"}, class: "border rounded-md m py-0.5 px-1"
7
8
  = f.submit "检索", class: "rounded-md px-2 py-1 text-sm bg-black text-white cursor-pointer"
8
9
  = link_to "重置", logs_path, class: "text-sm"
9
10
 
10
- %div=" #{@pagy.count} "
11
+ %div{class: "flex space-x-2 items-center"}
12
+ %div{class: "text-sm text-gray-600"}="共 #{@pagy.count} 条"
13
+ = form_with(url: remove_data_logs_path, method: :delete, class: "flex items-center space-x-2") do |f|
14
+ = f.select :type, options_for_select([%w[最近3天 3days], %w[最近7天 7days], %w[最近30天 30days], %w[全部 all]], params[:type]), {include_blank: "-- 选择删除范围 --"}, class: "border text-red-600 border-red-600 rounded-md m py-0.5 px-1"
15
+ = f.submit "删除日志", class: "rounded-md px-2 py-1 text-xs bg-red-600 text-white cursor-pointer"
11
16
  %div
12
17
  - if @logs.blank?
13
18
  %div{class: "flex justify-center text-gray-500 p-8 border rounded-md"} 空空如也
@@ -0,0 +1,29 @@
1
+ %div{class: "flex flex-col space-y-2"}
2
+ %div
3
+ %div{class: "font-bold"} 源内容:
4
+ %div= @log.original_content
5
+ %div
6
+ %div{class: "font-bold"} 过滤后内容:
7
+ %div= @log.filtered_content
8
+ %div
9
+ %div{class: "font-bold"} 动作:
10
+ %div= @log.action
11
+ %div
12
+ %div{class: "font-bold"} 场景:
13
+ %div= @log.scenario
14
+ %div
15
+ %div{class: "font-bold"} 检测阶段:
16
+ %div= @log.stage
17
+ %div
18
+ %div{class: "font-bold"} 命中敏感词:
19
+ %div= @log.mod_words
20
+ %div
21
+ %div{class: "font-bold"} 云检测返回标签:
22
+ %div= @log.labels
23
+ %div
24
+ %div{class: "font-bold"} 云检测返回标签:
25
+ %div= @log.labels
26
+ %div
27
+ %div{class: "font-bold"} 云检测返回:
28
+ %div{class: "p-1 bg-gray-100 rounded-lg"}
29
+ %pre= JSON.pretty_generate(@log.response)
@@ -2,44 +2,48 @@
2
2
  %div{class: "flex flex-col"}
3
3
  %div{class: "border rounded-md mb-2 p-2 flex flex-col space-y-1"}
4
4
  %div{class: "flex justify-between"}
5
- %div{class: ""}
5
+ %div{class: "text-sm"}
6
6
  = link_to mod_log.record_path, data: { turbo: false }, target: "_blank" do
7
- %span{class: "text-sm bg-indigo-100 text-indigo-600 py-1 px-2 rounded-lg"}="#{mod_log.record_type.upcase}##{mod_log.record_id}"
8
- - unless mod_log.labels.blank?
9
- - mod_log.labels.each do |label|
10
- %span{class: "text-sm bg-red-100 text-red-600 py-1 px-2 rounded-lg"}=t("censor_log.label.#{label}")
11
- - unless mod_log.mod_words.blank?
12
- - mod_log.mod_words.each do |word|
13
- %span{class: "text-sm bg-yellow-100 text-yellow-600 py-1 px-2 rounded-lg"}="##{word}"
14
- %span{class: "text-sm bg-gray-100 text-gray-600 py-1 px-2 rounded-lg"}= mod_log.created_at
15
- %div{class: "flex space-x-2 items-center"}
7
+ %span{class: "bg-indigo-100 text-indigo-600 py-1 px-2 rounded-lg"}="#{mod_log.record_type.upcase}##{mod_log.record_id}"
8
+ %span{class: "bg-yellow-100 text-yellow-600 rounded-md p-1"}=t("censor_log.aasm_state.#{mod_log.aasm_state}")
9
+ %div{class: "flex space-x-2 items-center text-sm"}
16
10
  - if mod_log.reason
17
11
  %span{class: "bg-red-100 text-red-600 p-1 rounded-lg"}
18
12
  = mod_log.reason
19
- %span{class: "bg-green-100 text-green-600 rounded-md p-1"}
20
- - if mod_log.status == 0
21
- = "待审"
22
- - if mod_log.status == 1
23
- = "已处理"
24
- - if mod_log.status == 2
25
- = "暂缓"
26
- %span
27
- = link_to "详情", mod_log, data: { turbo: false }, target: "_blank"
28
- %span
29
- = button_to '删除', mod_log, method: :delete, data: { confirm: 'Are you sure?'}, class: "text-red-600 rounded-md px-2 py-0.5"
30
- %div{class: "flex justify-between items-center"}
31
- %div{class: "flex flex-col"}
32
- %div{class: "text-sm border rounded-lg border-black p-2"}
33
- %div= mod_log.content
34
- %div
35
- %div{class: "flex space-x-2"}
36
- %span{class: "text-yellow-600"}
37
- = link_to "忽略", mod_log_ignore_path(mod_log)
38
- %span{class: "text-green-600"}
39
- = link_to "通过", mod_log_approve_path(mod_log)
40
- %div
41
- = form_with(url: mod_log_delete_path(mod_log), method: :get, class: "space-x-1 flex items-center") do |f|
42
- = f.submit "删除", class: "rounded-md px-2 py-1 text-sm bg-red-600 text-white cursor-pointer"
43
- = f.text_field :reason, value: mod_log.reason, placeholder: "不通过原因", class: "border rounded-md m py-0.5 px-1"
44
- -#%span{class: "text-red-600 font-bold"}
45
- -# = link_to "封禁用户!", mod_log_ban_path(mod_log)
13
+ %span{class: "bg-gray-100 text-gray-600 py-1 px-2 rounded-lg"}= mod_log.created_at
14
+ - if current_user.admin?
15
+ = button_to '删除', mod_log, method: :delete, data: { confirm: 'Are you sure?'}, class: "bg-red-600 text-white rounded-md px-2 py-0.5 cursor-pointer"
16
+ %div{class: "flex justify-between items-center py-2 w-full"}
17
+ %div{class: "flex flex-col rounded-lg bg-gray-50 p-3 text-sm space-y-4 w-full"}
18
+ -unless mod_log.record
19
+ %h3{class: "text-2xl text-red-600"} 对应条目已被永久删除,当前审核请求已无效,请删除
20
+ - else
21
+ %div
22
+ %span{class: "border border-pink-600 text-pink-600 rounded-lg text-xs p-0.5"}=mod_log.record.is_private ? "隐私" : "公开"
23
+ %span{class: "border border-blue-600 text-blue-600 rounded-lg text-xs p-0.5"}=mod_log.record.is_approved ? "审核通过" : "审核中..."
24
+ - unless mod_log.labels.blank?
25
+ - mod_log.labels.each do |label|
26
+ %span{class: "border border-yellow-600 text-yellow-600 rounded-lg text-xs p-0.5"}=t("censor_log.label.#{label}")
27
+ - unless mod_log.mod_words.blank?
28
+ - mod_log.mod_words.each do |word|
29
+ %span{class: "border border-red-600 text-red-600 rounded-lg text-xs p-0.5"}="##{word}"
30
+ - if mod_log.record.discarded?
31
+ %div{class: "text-gray-900 leading-relaxed"}
32
+ %del= mod_log.record.content
33
+ - else
34
+ %div{class: "text-gray-900 leading-relaxed"}= mod_log.record.content
35
+ %div
36
+ - mod_log.images.each do |img|
37
+ = image_tag img[:src], class: "w-16 rounded-md"
38
+ %div{class: "flex space-x-2 items-center justify-end"}
39
+ - if mod_log.may_suspend?
40
+ = link_to "忽略", mod_log_suspend_path(mod_log), class: "text-yellow-600 cursor-pointer"
41
+ - if mod_log.may_pass?
42
+ = link_to "通过", mod_log_pass_path(mod_log), class: "text-green-600 cursor-pointer"
43
+ - if mod_log.may_undo?
44
+ = link_to "撤销", mod_log_undo_path(mod_log), class: "text-red-600 cursor-pointer"
45
+ - if mod_log.may_reject?
46
+ = form_with(url: mod_log_reject_path(mod_log), method: :get, class: "space-x-1 flex items-center") do |f|
47
+ = f.submit "拒绝", class: "text-red-600 bg-transparent cursor-pointer"
48
+ = f.text_field :reason, value: mod_log.reason, placeholder: "不通过原因", class: "border rounded-md m py-0.5 px-1 text-sm"
49
+
@@ -6,8 +6,18 @@
6
6
  = f.text_field :q, value: params[:q], placeholder: "关键词", class: "border rounded-md m py-0.5 px-1"
7
7
  = f.submit "检索", class: "rounded-md px-2 py-1 text-sm bg-black text-white cursor-pointer"
8
8
  = link_to "重置", mod_logs_path, class: "text-sm"
9
-
10
- %div="共 #{@pagy.count} 条"
9
+ %div{class: "flex items-center space-x-2"}
10
+ %div{class: "text-sm text-gray-600"}="共 #{@pagy.count} 条"
11
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:filter] == "all" ? 'font-bold' : ''}"}
12
+ = link_to "全部", request.params.merge(filter: "all", state: "")
13
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state].blank? && params[:filter].blank? ? 'font-bold' : ''}"}
14
+ = link_to "待处理", request.params.merge(state: "", filter: "")
15
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'rejected' ? 'font-bold' : ''}"}
16
+ = link_to "被驳回", request.params.merge(state: "rejected", filter: "")
17
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'passed' ? 'font-bold' : ''}"}
18
+ = link_to "通过", request.params.merge(state: "passed", filter: "")
19
+ %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'suspended' ? 'font-bold' : ''}"}
20
+ = link_to "暂缓处理", request.params.merge(state: "suspended", filter: "")
11
21
  %div
12
22
  - if @mod_logs.blank?
13
23
  %div{class: "flex justify-center text-gray-500 p-8 border rounded-md"} 空空如也
@@ -4,10 +4,11 @@
4
4
  = form_with(url: stop_words_path, method: :get, class: "flex items-center") do |f|
5
5
  %div{class: "space-x-0.5"}
6
6
  = f.text_field :q, value: params[:q], placeholder: "关键词", class: "border rounded-md m py-0.5 px-1"
7
+ = f.select :filter, options_for_select([%w[长度为1 size_one], %w[长度为2 size_two], %w[长度为3 size_three]], params[:filter]), {include_blank: "-- 按敏感词长度筛选 --"}, class: "border rounded-md m py-0.5 px-1"
7
8
  = f.submit "检索", class: "rounded-md px-2 py-1 text-sm bg-black text-white cursor-pointer"
8
9
  = link_to "重置", stop_words_path, class: "text-sm"
9
-
10
10
  %div
11
+ %span{class: "text-sm text-gray-600"}="共 #{@pagy.count} 条"
11
12
  = link_to '批量导入', "#", class: "text-sm"
12
13
  = link_to '新增', new_stop_word_path, class: "rounded-md px-2 py-1 text-sm bg-black text-white cursor-pointer"
13
14
  %div
@@ -10,9 +10,10 @@
10
10
  %body{class: "flex flex-col space-y-6 p-4"}
11
11
  %ul{class: "flex justify-end items-center space-x-2 text-blue-600"}
12
12
  %li= link_to "审查(#{CensorBear::ModLog.pending.count})", mod_logs_path
13
- %li= link_to "敏感词", stop_words_path
14
13
  %li= link_to "日志", logs_path
15
- %li= link_to "回主站", "/"
14
+ %li= link_to "敏感词", stop_words_path
15
+ %li= link_to "调试", debugger_path
16
+ %li= link_to "控制台", main_app.send(CensorBear.config.main_app_root_path_method.to_sym)
16
17
  %li= current_user.email
17
18
  %div
18
19
  = yield
@@ -1,4 +1,4 @@
1
- en:
1
+ "zh-CN":
2
2
  censor_log:
3
3
  action:
4
4
  mod: 审核
@@ -8,7 +8,12 @@ en:
8
8
  block: 禁用
9
9
  pass: 忽略
10
10
  review: 审核
11
+ aasm_state:
12
+ pending: 待审
13
+ handled: 已处理
14
+ suspended: 暂缓处理
11
15
  stage:
16
+ check_search: 搜索检查
12
17
  aliyun_check: 阿里云检查
13
18
  tencent_check: 腾讯云检查
14
19
  local_check: 本地检查
@@ -26,4 +31,4 @@ en:
26
31
  contraband: 违禁
27
32
  meaningless: 无意义
28
33
  harmful: 不良场景
29
- customized: 自定义
34
+ customized: 自定义
data/config/routes.rb CHANGED
@@ -1,12 +1,19 @@
1
1
  CensorBear::Engine.routes.draw do
2
2
  resources :mod_logs do
3
- get :ignore
4
- get :approve
5
- get :delete
3
+ get :suspend
4
+ get :reject
5
+ get :pass
6
+ get :undo
6
7
  get :ban
7
8
  end
8
- resources :logs
9
+ resources :logs do
10
+ delete :remove_data, on: :collection
11
+ end
9
12
  resources :stop_words
10
13
 
11
- root to: "stop_words#index"
14
+ get '/welcome', to: 'home#index'
15
+ get '/debugger', to: 'home#debugger'
16
+ post '/debug', to: 'home#debug'
17
+
18
+ root to: 'mod_logs#index'
12
19
  end
@@ -6,7 +6,6 @@ class CreateCensorBearModLogs < ActiveRecord::Migration[6.1]
6
6
  t.text :content
7
7
  t.string :reason
8
8
  t.string :mod_words, array: true, default: []
9
- t.integer :status, default: 0
10
9
  t.bigint :user_id
11
10
 
12
11
  t.timestamps
@@ -0,0 +1,5 @@
1
+ class AddResponseToCensorBearLogs < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :censor_bear_logs, :response, :json
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddAasmStateToCensorBearModLogs < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :censor_bear_mod_logs, :aasm_state, :string
4
+ end
5
+ end
@@ -115,32 +115,46 @@ module CensorBear
115
115
  rate = r['rate']
116
116
  @mod_words = concat_words(r['details'])
117
117
  @labels = concat_labels(r['details'])
118
- if action == 'block' && rate >= 70 && %w[politics terrorism abuse].include?(r['label'])
118
+ # if action == 'block' && rate >= 70 && %w[politics terrorism].include?(r['label'])
119
+ # @is_mod = true
120
+ # CensorBear.info(
121
+ # d['content'], @type, 'mod', 'aliyun_check',
122
+ # filtered_content: d['filteredContent'],
123
+ # mod_words: @mod_words,
124
+ # labels: @labels,
125
+ # ip: @ip, user_id: @user_id,
126
+ # response: response
127
+ # )
128
+ # # raise NotPassedException
129
+ # elsif action == 'block' && rate >= 90
130
+ if action == 'block'
131
+ @is_mod = true
119
132
  CensorBear.info(
120
- d['content'], @type, action, 'aliyun_check',
133
+ d['content'], @type, 'mod', 'aliyun_check',
121
134
  filtered_content: d['filteredContent'],
122
135
  mod_words: @mod_words,
123
136
  labels: @labels,
124
- ip: @ip, user_id: @user_id
137
+ ip: @ip, user_id: @user_id,
138
+ response: response
125
139
  )
126
- raise NotPassedException
127
- elsif action == 'block'
140
+ elsif action == 'review'
128
141
  @is_mod = true
129
142
  CensorBear.info(
130
- d['content'], @type, action, 'aliyun_check',
143
+ d['content'], @type, 'mod', 'aliyun_check',
131
144
  filtered_content: d['filteredContent'],
132
145
  mod_words: @mod_words,
133
146
  labels: @labels,
134
- ip: @ip, user_id: @user_id
147
+ ip: @ip, user_id: @user_id,
148
+ response: response
135
149
  )
136
- elsif action == 'review'
137
- @is_mod = true
150
+ else
138
151
  CensorBear.info(
139
- d['content'], @type, action, 'aliyun_check',
152
+ d['content'], @type, 'ignore', 'aliyun_check',
140
153
  filtered_content: d['filteredContent'],
141
154
  mod_words: @mod_words,
142
155
  labels: @labels,
143
- ip: @ip, user_id: @user_id
156
+ ip: @ip, user_id: @user_id,
157
+ response: response
144
158
  )
145
159
  end
146
160
  end
@@ -149,6 +163,7 @@ module CensorBear
149
163
 
150
164
  def concat_labels(data)
151
165
  return [] if data.blank?
166
+
152
167
  labels = []
153
168
  data.map do |d|
154
169
  next if d['label'].blank?
@@ -160,6 +175,7 @@ module CensorBear
160
175
 
161
176
  def concat_words(data)
162
177
  return [] if data.blank?
178
+
163
179
  words = []
164
180
  data.each do |d|
165
181
  next if d['contexts'].blank?
@@ -17,5 +17,7 @@ module CensorBear
17
17
  attr_accessor :aliyun_green_access_key_id
18
18
  attr_accessor :aliyun_green_access_key_secret
19
19
  attr_accessor :aliyun_green_enable_internal
20
+
21
+ attr_accessor :main_app_root_path_method
20
22
  end
21
23
  end
@@ -1,3 +1,3 @@
1
1
  module CensorBear
2
- VERSION = '0.1.12'.freeze
2
+ VERSION = '0.1.17'.freeze
3
3
  end
data/lib/censor_bear.rb CHANGED
@@ -3,6 +3,7 @@ require 'censor_bear/engine'
3
3
  require 'censor_bear/censor'
4
4
  require 'censor_bear/configuration'
5
5
 
6
+ require 'aasm'
6
7
  require 'hamlit'
7
8
  require 'pagy'
8
9
  require 'aliyun_green'
@@ -24,6 +25,8 @@ module CensorBear
24
25
  @config.aliyun_green_access_key_id = ''
25
26
  @config.aliyun_green_access_key_secret = ''
26
27
  @config.aliyun_green_enable_internal = false
28
+ @config.main_app_root_path_method = 'root_path'
29
+
27
30
  @config
28
31
  end
29
32
 
@@ -45,6 +48,7 @@ module CensorBear
45
48
  labels = options[:labels] || []
46
49
  ip = options[:ip] || nil
47
50
  user_id = options[:user_id] || nil
51
+ response = options[:response] || {}
48
52
 
49
53
  CensorBear::Log.create(
50
54
  original_content: content,
@@ -56,6 +60,7 @@ module CensorBear
56
60
  labels: labels,
57
61
  ip: ip,
58
62
  user_id: user_id,
63
+ response: response
59
64
  )
60
65
  end
61
66
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: censor_bear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - 42up
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-08 00:00:00.000000000 Z
11
+ date: 2021-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 6.0.4.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: aasm
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
33
47
  - !ruby/object:Gem::Dependency
34
48
  name: aliyun_green
35
49
  requirement: !ruby/object:Gem::Requirement
@@ -112,16 +126,19 @@ files:
112
126
  - Rakefile
113
127
  - app/assets/config/censor_bear_manifest.js
114
128
  - app/assets/stylesheets/censor_bear/application.css
129
+ - app/assets/stylesheets/censor_bear/home.css
115
130
  - app/assets/stylesheets/censor_bear/logs.css
116
131
  - app/assets/stylesheets/censor_bear/mod_logs.css
117
132
  - app/assets/stylesheets/censor_bear/stop_words.css
118
133
  - app/assets/stylesheets/censor_bear/tailwind.min.css
119
134
  - app/assets/stylesheets/scaffold.css
120
135
  - app/controllers/censor_bear/application_controller.rb
136
+ - app/controllers/censor_bear/home_controller.rb
121
137
  - app/controllers/censor_bear/logs_controller.rb
122
138
  - app/controllers/censor_bear/mod_logs_controller.rb
123
139
  - app/controllers/censor_bear/stop_words_controller.rb
124
140
  - app/helpers/censor_bear/application_helper.rb
141
+ - app/helpers/censor_bear/home_helper.rb
125
142
  - app/helpers/censor_bear/logs_helper.rb
126
143
  - app/helpers/censor_bear/mod_logs_helper.rb
127
144
  - app/helpers/censor_bear/stop_words_helper.rb
@@ -131,13 +148,16 @@ files:
131
148
  - app/models/censor_bear/log.rb
132
149
  - app/models/censor_bear/mod_log.rb
133
150
  - app/models/censor_bear/stop_word.rb
151
+ - app/views/censor_bear/home/debug.turbo_stream.haml
152
+ - app/views/censor_bear/home/debugger.html.haml
153
+ - app/views/censor_bear/home/index.html.erb
134
154
  - app/views/censor_bear/logs/_form.html.erb
135
155
  - app/views/censor_bear/logs/_log.html.haml
136
156
  - app/views/censor_bear/logs/destroy.turbo_stream.haml
137
157
  - app/views/censor_bear/logs/edit.html.erb
138
158
  - app/views/censor_bear/logs/index.html.haml
139
159
  - app/views/censor_bear/logs/new.html.erb
140
- - app/views/censor_bear/logs/show.html.erb
160
+ - app/views/censor_bear/logs/show.html.haml
141
161
  - app/views/censor_bear/mod_logs/_form.html.erb
142
162
  - app/views/censor_bear/mod_logs/_mod_log.html.haml
143
163
  - app/views/censor_bear/mod_logs/destroy.turbo_stream.haml
@@ -162,6 +182,8 @@ files:
162
182
  - db/migrate/20211129110218_create_censor_bear_mod_logs.rb
163
183
  - db/migrate/20211206092626_add_ip_to_censor_bear_logs.rb
164
184
  - db/migrate/20211208041114_add_labels_to_censor_bear_logs.rb
185
+ - db/migrate/20211209093050_add_response_to_censor_bear_logs.rb
186
+ - db/migrate/20211209113710_add_aasm_state_to_censor_bear_mod_logs.rb
165
187
  - lib/censor_bear.rb
166
188
  - lib/censor_bear/censor.rb
167
189
  - lib/censor_bear/configuration.rb
@@ -1,34 +0,0 @@
1
- <p id="notice"><%= notice %></p>
2
-
3
- <p>
4
- <strong>Orginal content:</strong>
5
- <%= @log.original_content %>
6
- </p>
7
-
8
- <p>
9
- <strong>Action:</strong>
10
- <%= @log.action %>
11
- </p>
12
-
13
- <p>
14
- <strong>Scenario:</strong>
15
- <%= @log.scenario %>
16
- </p>
17
-
18
- <p>
19
- <strong>Stage:</strong>
20
- <%= @log.stage %>
21
- </p>
22
-
23
- <p>
24
- <strong>Filtered content:</strong>
25
- <%= @log.filtered_content %>
26
- </p>
27
-
28
- <p>
29
- <strong>Mod words:</strong>
30
- <%= @log.mod_words %>
31
- </p>
32
-
33
- <%= link_to 'Edit', edit_log_path(@log) %> |
34
- <%= link_to 'Back', logs_path %>