diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/javascript/mastodon/locales/zh-CN.json | 27 | ||||
-rw-r--r-- | app/lib/feed_manager.rb | 23 | ||||
-rw-r--r-- | app/models/glitch/keyword_mute.rb | 72 |
3 files changed, 83 insertions, 39 deletions
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 0f4bbb7c1..bbdf34d2f 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -1,6 +1,6 @@ { "account.block": "屏蔽 @{name}", - "account.block_domain": "隐藏一切来自 {domain} 的嘟文", + "account.block_domain": "隐藏来自 {domain} 的内容", "account.disclaimer_full": "此处显示的信息可能不是全部内容。", "account.edit_profile": "修改个人资料", "account.follow": "关注", @@ -9,15 +9,17 @@ "account.follows_you": "关注了你", "account.media": "媒体", "account.mention": "提及 @{name}", - "account.mute": "静音 @{name}", + "account.mute": "隐藏 @{name}", + "account.mute_notifications": "隐藏来自 @{name} 的通知", + "account.unmute_notifications": "不再隐藏来自 @{name} 的通知", "account.posts": "嘟文", "account.report": "举报 @{name}", "account.requested": "正在等待对方同意。点击以取消发送关注请求", "account.share": "分享 @{name} 的个人资料", "account.unblock": "不再屏蔽 @{name}", - "account.unblock_domain": "不再隐藏 {domain}", + "account.unblock_domain": "不再隐藏来自 {domain} 的内容", "account.unfollow": "取消关注", - "account.unmute": "不再静音 @{name}", + "account.unmute": "不再隐藏 @{name}", "account.view_full_profile": "查看完整资料", "boost_modal.combo": "下次按住 {combo} 即可跳过此提示", "bundle_column_error.body": "载入组件出错。", @@ -31,7 +33,7 @@ "column.favourites": "收藏过的嘟文", "column.follow_requests": "关注请求", "column.home": "主页", - "column.mutes": "被静音的用户", + "column.mutes": "被隐藏的用户", "column.notifications": "通知", "column.pins": "置顶嘟文", "column.public": "跨站公共时间轴", @@ -57,10 +59,10 @@ "confirmations.block.message": "想好了,真的要屏蔽 {name}?", "confirmations.delete.confirm": "删除", "confirmations.delete.message": "想好了,真的要删除这条嘟文?", - "confirmations.domain_block.confirm": "隐藏整个网站", - "confirmations.domain_block.message": "你真的真的确定要隐藏整个 {domain}?多数情况下,屏蔽或静音几个特定的用户就应该能满足你的需要了。", - "confirmations.mute.confirm": "静音", - "confirmations.mute.message": "想好了,真的要静音 {name}?", + "confirmations.domain_block.confirm": "隐藏整个网站的内容", + "confirmations.domain_block.message": "你真的真的确定要隐藏所有来自 {domain} 的内容吗?多数情况下,屏蔽或隐藏几个特定的用户就应该能满足你的需要了。", + "confirmations.mute.confirm": "隐藏", + "confirmations.mute.message": "想好了,真的要隐藏 {name}?", "confirmations.unfollow.confirm": "取消关注", "confirmations.unfollow.message": "确定要取消关注 {name} 吗?", "embed.instructions": "要在你的网站上嵌入这条嘟文,请复制以下代码。", @@ -104,6 +106,7 @@ "loading_indicator.label": "加载中……", "media_gallery.toggle_visible": "切换显示/隐藏", "missing_indicator.label": "找不到内容", + "mute_modal.hide_notifications": "隐藏来自这个用户的通知", "navigation_bar.blocks": "被屏蔽的用户", "navigation_bar.community_timeline": "本站时间轴", "navigation_bar.edit_profile": "修改个人资料", @@ -111,7 +114,7 @@ "navigation_bar.follow_requests": "关注请求", "navigation_bar.info": "关于本站", "navigation_bar.logout": "注销", - "navigation_bar.mutes": "被静音的用户", + "navigation_bar.mutes": "被隐藏的用户", "navigation_bar.pins": "置顶嘟文", "navigation_bar.preferences": "首选项", "navigation_bar.public_timeline": "跨站公共时间轴", @@ -184,7 +187,7 @@ "status.media_hidden": "隐藏媒体内容", "status.mention": "提及 @{name}", "status.more": "更多", - "status.mute_conversation": "静音此对话", + "status.mute_conversation": "隐藏此对话", "status.open": "展开嘟文", "status.pin": "在个人资料页面置顶", "status.reblog": "转嘟", @@ -197,7 +200,7 @@ "status.share": "分享", "status.show_less": "隐藏内容", "status.show_more": "显示内容", - "status.unmute_conversation": "不再静音此对话", + "status.unmute_conversation": "不再隐藏此对话", "status.unpin": "在个人资料页面取消置顶", "tabs_bar.compose": "撰写", "tabs_bar.federated_timeline": "跨站", diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 76365c7d3..5d7f47c6f 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -149,7 +149,7 @@ class FeedManager return false if receiver_id == status.account_id return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?) - return true if keyword_filter?(status, Glitch::KeywordMute.matcher_for(receiver_id)) + return true if keyword_filter?(status, receiver_id) check_for_mutes = [status.account_id] check_for_mutes.concat(status.mentions.pluck(:account_id)) @@ -178,16 +178,23 @@ class FeedManager false end - def keyword_filter?(status, matcher) - should_filter = matcher =~ status.text - should_filter ||= matcher =~ status.spoiler_text + def keyword_filter?(status, receiver_id) + text_matcher = Glitch::KeywordMute.text_matcher_for(receiver_id) + tag_matcher = Glitch::KeywordMute.tag_matcher_for(receiver_id) + + should_filter = text_matcher.matches?(status.text) + should_filter ||= text_matcher.matches?(status.spoiler_text) + should_filter ||= tag_matcher.matches?(status.tags) if status.reblog? - should_filter ||= matcher =~ status.reblog.text - should_filter ||= matcher =~ status.reblog.spoiler_text + reblog = status.reblog + + should_filter ||= text_matcher.matches?(reblog.text) + should_filter ||= text_matcher.matches?(reblog.spoiler_text) + should_filter ||= tag_matcher.matches?(status.tags) end - !!should_filter + should_filter end def filter_from_mentions?(status, receiver_id) @@ -199,7 +206,7 @@ class FeedManager should_filter = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked should_filter ||= (status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists?) # of if the account is silenced and I'm not following them - should_filter ||= keyword_filter?(status, Glitch::KeywordMute.matcher_for(receiver_id)) # or if the mention contains a muted keyword + should_filter ||= keyword_filter?(status, receiver_id) # or if the mention contains a muted keyword should_filter end diff --git a/app/models/glitch/keyword_mute.rb b/app/models/glitch/keyword_mute.rb index 009de1880..a2481308f 100644 --- a/app/models/glitch/keyword_mute.rb +++ b/app/models/glitch/keyword_mute.rb @@ -16,51 +16,85 @@ class Glitch::KeywordMute < ApplicationRecord validates_presence_of :keyword - after_commit :invalidate_cached_matcher + after_commit :invalidate_cached_matchers - def self.matcher_for(account_id) - Matcher.new(account_id) + def self.text_matcher_for(account_id) + TextMatcher.new(account_id) + end + + def self.tag_matcher_for(account_id) + TagMatcher.new(account_id) end private - def invalidate_cached_matcher - Rails.cache.delete("keyword_mutes:regex:#{account_id}") + def invalidate_cached_matchers + Rails.cache.delete(TextMatcher.cache_key(account_id)) + Rails.cache.delete(TagMatcher.cache_key(account_id)) end - class Matcher + class RegexpMatcher attr_reader :account_id attr_reader :regex def initialize(account_id) @account_id = account_id - regex_text = Rails.cache.fetch("keyword_mutes:regex:#{account_id}") { regex_text_for_account } + regex_text = Rails.cache.fetch(self.class.cache_key(account_id)) { make_regex_text } @regex = /#{regex_text}/ end - def =~(str) - regex =~ str + protected + + def keywords + Glitch::KeywordMute.where(account_id: account_id).pluck(:whole_word, :keyword) end - private + def boundary_regex_for_keyword(keyword) + sb = keyword =~ /\A[[:word:]]/ ? '\b' : '' + eb = keyword =~ /[[:word:]]\Z/ ? '\b' : '' - def keywords - Glitch::KeywordMute.where(account_id: account_id).select(:keyword, :id, :whole_word) + /(?mix:#{sb}#{Regexp.escape(keyword)}#{eb})/ + end + end + + class TextMatcher < RegexpMatcher + def self.cache_key(account_id) + format('keyword_mutes:regex:text:%s', account_id) + end + + def matches?(str) + !!(regex =~ str) end - def regex_text_for_account - kws = keywords.find_each.with_object([]) do |kw, a| - a << (kw.whole_word ? boundary_regex_for_keyword(kw.keyword) : kw.keyword) + private + + def make_regex_text + kws = keywords.map! do |whole_word, keyword| + whole_word ? boundary_regex_for_keyword(keyword) : keyword end Regexp.union(kws).source end + end - def boundary_regex_for_keyword(keyword) - sb = keyword =~ /\A[[:word:]]/ ? '\b' : '' - eb = keyword =~ /[[:word:]]\Z/ ? '\b' : '' + class TagMatcher < RegexpMatcher + def self.cache_key(account_id) + format('keyword_mutes:regex:tag:%s', account_id) + end - /(?mix:#{sb}#{Regexp.escape(keyword)}#{eb})/ + def matches?(tags) + tags.pluck(:name).any? { |n| regex =~ n } + end + + private + + def make_regex_text + kws = keywords.map! do |whole_word, keyword| + term = (Tag::HASHTAG_RE =~ keyword) ? $1 : keyword + whole_word ? boundary_regex_for_keyword(term) : term + end + + Regexp.union(kws).source end end end |