diff --git a/changelog/v8.0.6+274.md b/changelog/v8.0.6+274.md new file mode 100644 index 00000000..943d3bb7 --- /dev/null +++ b/changelog/v8.0.6+274.md @@ -0,0 +1,9 @@ +- 内置屏蔽用户名单 +- 优化内置ip +- 修复里站无法快速屏蔽评论区用户的bug + +-------------------- + +- Built-in block user list +- Optimize built-in IP +- Fix bug that can't block users from comments in EX site \ No newline at end of file diff --git a/lib/src/enum/config_enum.dart b/lib/src/enum/config_enum.dart index fe915bf8..b9d480bf 100644 --- a/lib/src/enum/config_enum.dart +++ b/lib/src/enum/config_enum.dart @@ -36,6 +36,7 @@ enum ConfigEnum { oldGalleryHistory('history'), searchHistory('searchHistory'), myTagsSetting('MyTagsSetting'), + builtInBlockedUser('builtInBlockedUser'), /// page config downloadPageBodyType('downloadPageGalleryType'), diff --git a/lib/src/l18n/en_US.dart b/lib/src/l18n/en_US.dart index 0f30d65a..f73c11ff 100644 --- a/lib/src/l18n/en_US.dart +++ b/lib/src/l18n/en_US.dart @@ -740,6 +740,8 @@ class en_US { 'removeBlockRuleFailed': 'Remove block rule failed', 'inputNumberHint': 'Please input a correct number', 'inputRegexHint': 'Please input a correct regex', + 'useBuiltInBlockedUsers': 'Enable Built-in User Blocklist', + 'useBuiltInBlockedUsersHint': 'Filter out gallery comments from users on the blocklist', 'blockingRules': 'Block Rules', 'blockingRulesHint': 'Additional blocking rules for gallerys and comments', 'blockingTarget': 'Blocking Target', diff --git a/lib/src/l18n/ko_KR.dart b/lib/src/l18n/ko_KR.dart index b9f28a26..bb1e4f5e 100644 --- a/lib/src/l18n/ko_KR.dart +++ b/lib/src/l18n/ko_KR.dart @@ -739,6 +739,8 @@ class ko_KR { 'removeBlockRuleFailed': 'Remove block rule failed', 'inputNumberHint': 'Please input a correct number', 'inputRegexHint': 'Please input a correct regex', + 'useBuiltInBlockedUsers': 'Enable Built-in User Blocklist', + 'useBuiltInBlockedUsersHint': 'Filter out gallery comments from users on the blocklist', 'blockingRules': 'Block Rules', 'blockingRulesHint': 'Additional blocking rules for gallerys and comments', 'blockingTarget': 'Blocking Target', diff --git a/lib/src/l18n/pt_BR.dart b/lib/src/l18n/pt_BR.dart index 7ad5d4bf..070bf191 100644 --- a/lib/src/l18n/pt_BR.dart +++ b/lib/src/l18n/pt_BR.dart @@ -742,6 +742,8 @@ class pt_BR { 'removeBlockRuleFailed': 'Remove block rule failed', 'inputNumberHint': 'Please input a correct number', 'inputRegexHint': 'Please input a correct regex', + 'useBuiltInBlockedUsers': 'Enable Built-in User Blocklist', + 'useBuiltInBlockedUsersHint': 'Filter out gallery comments from users on the blocklist', 'blockingRules': 'Block Rules', 'blockingRulesHint': 'Additional blocking rules for gallerys and comments', 'blockingTarget': 'Blocking Target', diff --git a/lib/src/l18n/zh_CN.dart b/lib/src/l18n/zh_CN.dart index e3dd468e..0ff1fedc 100644 --- a/lib/src/l18n/zh_CN.dart +++ b/lib/src/l18n/zh_CN.dart @@ -745,6 +745,8 @@ favnote:匹配收藏备注 'removeBlockRuleFailed': '删除屏蔽规则失败', 'inputNumberHint': '请输入正确的数字', 'inputRegexHint': '请输入合法的正则表达式', + 'useBuiltInBlockedUsers': '使用内置用户屏蔽名单', + 'useBuiltInBlockedUsersHint': '过滤掉在名单中的用户评论', 'blockingRules': '屏蔽规则', 'blockingRulesHint': '针对画廊和评论设置额外的屏蔽规则', 'blockingTarget': '屏蔽目标', diff --git a/lib/src/l18n/zh_TW.dart b/lib/src/l18n/zh_TW.dart index 9cdaf75b..89544bc7 100644 --- a/lib/src/l18n/zh_TW.dart +++ b/lib/src/l18n/zh_TW.dart @@ -745,6 +745,8 @@ favnote:配對收藏備註 'removeBlockRuleFailed': '刪除隱藏規則失敗', 'inputNumberHint': '請輸入正確的數字', 'inputRegexHint': '請輸入合法的正規表示式', + 'useBuiltInBlockedUsers': '使用內置用戶屏蔽名單', + 'useBuiltInBlockedUsersHint': '過濾掉在名單中的用戶評論', 'blockingRules': '隱藏規則', 'blockingRulesHint': '針對畫廊和評論設定額外的隱藏規則', 'blockingTarget': '隱藏目標', diff --git a/lib/src/main.dart b/lib/src/main.dart index 4833c2cb..32ee5665 100644 --- a/lib/src/main.dart +++ b/lib/src/main.dart @@ -7,6 +7,7 @@ import 'package:jhentai/src/network/eh_request.dart'; import 'package:jhentai/src/network/jh_request.dart'; import 'package:jhentai/src/service/app_update_service.dart'; import 'package:jhentai/src/service/archive_download_service.dart'; +import 'package:jhentai/src/service/built_in_blocked_user_service.dart'; import 'package:jhentai/src/service/cloud_service.dart'; import 'package:jhentai/src/service/frame_rate_service.dart'; import 'package:jhentai/src/service/gallery_download_service.dart'; @@ -89,6 +90,7 @@ List lifeCircleBeans = [ styleSetting, superResolutionSetting, userSetting, + builtInBlockedUserService, ]; void main(List args) async { diff --git a/lib/src/pages/setting/preference/setting_preference_page.dart b/lib/src/pages/setting/preference/setting_preference_page.dart index 4dc817f7..591b1b5d 100644 --- a/lib/src/pages/setting/preference/setting_preference_page.dart +++ b/lib/src/pages/setting/preference/setting_preference_page.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:jhentai/src/extension/widget_extension.dart'; import 'package:jhentai/src/model/tab_bar_icon.dart'; import 'package:jhentai/src/service/tag_search_order_service.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import '../../../consts/locale_consts.dart'; import '../../../l18n/locale_text.dart'; @@ -51,6 +52,7 @@ class SettingPreferencePage extends StatelessWidget { _buildShowUtcTime(), _buildShowDawnInfo(), _buildShowEncounterMonster(), + _buildUseBuiltInBlockedUsers(), _buildBlockRules(), ], ).withListTileTheme(context), @@ -427,7 +429,7 @@ class SettingPreferencePage extends StatelessWidget { onChanged: preferenceSetting.saveShowDawnInfo, ); } - + Widget _buildShowEncounterMonster() { return SwitchListTile( title: Text('showEncounterMonster'.tr), @@ -435,4 +437,27 @@ class SettingPreferencePage extends StatelessWidget { onChanged: preferenceSetting.saveShowHVInfo, ); } + + Widget _buildUseBuiltInBlockedUsers() { + return ListTile( + title: Text('useBuiltInBlockedUsers'.tr), + subtitle: Text('useBuiltInBlockedUsersHint'.tr), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.help), + onPressed: () => launchUrlString( + 'https://raw.githubusercontent.com/jiangtian616/JHenTai/refs/heads/master/built_in_blocked_user.json', + mode: LaunchMode.externalApplication, + ), + ), + Switch( + value: preferenceSetting.useBuiltInBlockedUsers.value, + onChanged: preferenceSetting.saveUseBuiltInBlockedUsers, + ) + ], + ), + ); + } } diff --git a/lib/src/service/built_in_blocked_user_service.dart b/lib/src/service/built_in_blocked_user_service.dart new file mode 100644 index 00000000..9f2ca1ff --- /dev/null +++ b/lib/src/service/built_in_blocked_user_service.dart @@ -0,0 +1,107 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:get/get.dart'; +import 'package:jhentai/src/enum/config_enum.dart'; +import 'package:jhentai/src/setting/preference_setting.dart'; +import 'package:jhentai/src/utils/eh_spider_parser.dart'; +import 'package:retry/retry.dart'; + +import '../network/eh_request.dart'; +import 'jh_service.dart'; +import 'local_block_rule_service.dart'; +import 'log.dart'; + +BuiltInBlockedUserService builtInBlockedUserService = BuiltInBlockedUserService(); + +typedef EHUser = ({int userId, String name}); + +class BuiltInBlockedUserService with JHLifeCircleBeanWithConfigStorage implements JHLifeCircleBean, ExtraBlockRuleProvider { + final RxList _blockedUsers = RxList(); + + List get blockedUsers => _blockedUsers.toList(); + + @override + ConfigEnum get configEnum => ConfigEnum.builtInBlockedUser; + + @override + List get initDependencies => super.initDependencies..addAll([localBlockRuleService]); + + @override + void applyBeanConfig(String configString) { + List list = jsonDecode(configString); + if (list.isNotEmpty) { + _blockedUsers.value = list.map((map) => (userId: map['userId'] as int, name: map['name'] as String)).toList(); + } + } + + @override + String toConfigString() { + return jsonEncode(_blockedUsers.map((user) => {'userId': user.userId, 'name': user.name}).toList()); + } + + @override + Future doInitBean() async { + localBlockRuleService.registerExtraBlockRuleProvider(this); + } + + @override + Future doAfterBeanReady() async { + String json; + try { + json = await retry( + () => ehRequest.get( + url: 'https://raw.githubusercontent.com/jiangtian616/JHenTai/refs/heads/master/built_in_blocked_user.json', + parser: simpleParser, + ), + maxAttempts: 5, + onRetry: (error) => log.warning('Get built-in blocked user data failed, retrying', error), + ); + } on DioException catch (e) { + log.error('Get built-in blocked user data failed after 5 times', e); + return; + } + + Map map = jsonDecode(json); + if (map.isEmpty) { + log.warning('Built-in blocked user data is empty'); + return; + } + + int version = map['version'] as int; + int formatVersion = map['formatVersion'] as int; + String updateTime = map['updateTime'] as String; + List users = (map['blockedUsers'] as List).map((map) => (userId: map['userId'] as int, name: map['name'] as String)).toList(); + + log.info('Built-in blocked user data loaded, version: $version, formatVersion: $formatVersion, updateTime: $updateTime, user count: ${users.length}'); + + _blockedUsers.value = users; + + saveBeanConfig(); + } + + @override + LocalBlockTargetEnum get target => LocalBlockTargetEnum.comment; + + @override + Map> get extraGroupedRules => preferenceSetting.useBuiltInBlockedUsers.isTrue + ? { + 'userIdGroup': _blockedUsers + .map((user) => LocalBlockRule( + target: LocalBlockTargetEnum.comment, + attribute: LocalBlockAttributeEnum.userId, + pattern: LocalBlockPatternEnum.equal, + expression: user.userId.toString(), + )) + .toList(), + 'nameGroup': _blockedUsers + .map((user) => LocalBlockRule( + target: LocalBlockTargetEnum.comment, + attribute: LocalBlockAttributeEnum.userName, + pattern: LocalBlockPatternEnum.equal, + expression: user.name, + )) + .toList(), + } + : {}; +} diff --git a/lib/src/service/local_block_rule_service.dart b/lib/src/service/local_block_rule_service.dart index 54bce10a..fea2f693 100644 --- a/lib/src/service/local_block_rule_service.dart +++ b/lib/src/service/local_block_rule_service.dart @@ -12,8 +12,15 @@ import 'log.dart'; LocalBlockRuleService localBlockRuleService = LocalBlockRuleService(); +abstract interface class ExtraBlockRuleProvider { + LocalBlockTargetEnum get target; + + Map> get extraGroupedRules; +} + class LocalBlockRuleService with JHLifeCircleBeanErrorCatch implements JHLifeCircleBean { final List handlers = []; + final List extraBlockRuleProviders = []; @override Future doInitBean() async { @@ -48,6 +55,14 @@ class LocalBlockRuleService with JHLifeCircleBeanErrorCatch implements JHLifeCir LocalBlockRuleHandler getHandlerByRule(LocalBlockRule rule) => handlers.where((h) => h.matchRule(rule)).sorted((a, b) => a.order - b.order).first; + void registerExtraBlockRuleProvider(ExtraBlockRuleProvider provider) { + extraBlockRuleProviders.add(provider); + } + + void unregisterExtraBlockRuleProvider(ExtraBlockRuleProvider provider) { + extraBlockRuleProviders.remove(provider); + } + Future> getBlockRules() async { List datas = await BlockRuleDao.selectBlockRules(); return datas @@ -167,6 +182,22 @@ class LocalBlockRuleService with JHLifeCircleBeanErrorCatch implements JHLifeCir return hit; }); }); + + for (ExtraBlockRuleProvider provider in extraBlockRuleProviders) { + if (provider.target == targetEnum) { + Map> extraGroupedRules = provider.extraGroupedRules; + extraGroupedRules.forEach((groupId, rules) { + results.removeWhere((item) { + bool hit = true; + for (LocalBlockRule rule in rules) { + LocalBlockRuleHandler handler = getHandlerByRule(rule); + hit = hit && handler.executeRule(item, rule); + } + return hit; + }); + }); + } + } } catch (e) { log.error('executeRules failed, items:$items', e); } @@ -617,7 +648,8 @@ enum LocalBlockPatternEnum { const LocalBlockPatternEnum(this.code, this.attributes, this.desc); - static List withAttribute(LocalBlockAttributeEnum? attribute) => LocalBlockPatternEnum.values.where((e) => e.attributes.contains(attribute)).toList(); + static List withAttribute(LocalBlockAttributeEnum? attribute) => + LocalBlockPatternEnum.values.where((e) => e.attributes.contains(attribute)).toList(); static LocalBlockPatternEnum fromCode(int code) { return LocalBlockPatternEnum.values.where((e) => e.code == code).first; diff --git a/lib/src/setting/preference_setting.dart b/lib/src/setting/preference_setting.dart index 036e7b79..28004c69 100644 --- a/lib/src/setting/preference_setting.dart +++ b/lib/src/setting/preference_setting.dart @@ -36,7 +36,8 @@ class PreferenceSetting with JHLifeCircleBeanWithConfigStorage implements JHLife RxBool showUtcTime = false.obs; RxBool showDawnInfo = false.obs; RxBool showHVInfo = false.obs; - + RxBool useBuiltInBlockedUsers = true.obs; + @override ConfigEnum get configEnum => ConfigEnum.preferenceSetting; @@ -70,6 +71,7 @@ class PreferenceSetting with JHLifeCircleBeanWithConfigStorage implements JHLife showUtcTime.value = map['showUtcTime'] ?? showUtcTime.value; showDawnInfo.value = map['showDawnInfo'] ?? showDawnInfo.value; showHVInfo.value = map['showHVInfo'] ?? showHVInfo.value; + useBuiltInBlockedUsers.value = map['useBuiltInBlockedUsers'] ?? useBuiltInBlockedUsers.value; } @override @@ -99,6 +101,7 @@ class PreferenceSetting with JHLifeCircleBeanWithConfigStorage implements JHLife 'showUtcTime': showUtcTime.value, 'showDawnInfo': showDawnInfo.value, 'showHVInfo': showHVInfo.value, + 'useBuiltInBlockedUsers': useBuiltInBlockedUsers.value, }); } @@ -252,6 +255,12 @@ class PreferenceSetting with JHLifeCircleBeanWithConfigStorage implements JHLife this.showHVInfo.value = showHVInfo; await saveBeanConfig(); } + + Future saveUseBuiltInBlockedUsers(bool useBuiltInBlockedUsers) async { + log.debug('saveUseBuiltInBlockedUsers:$useBuiltInBlockedUsers'); + this.useBuiltInBlockedUsers.value = useBuiltInBlockedUsers; + await saveBeanConfig(); + } } enum Scroll2TopButtonModeEnum { scrollUp, scrollDown, never, always } diff --git a/lib/src/utils/eh_spider_parser.dart b/lib/src/utils/eh_spider_parser.dart index e1179b05..77eef603 100644 --- a/lib/src/utils/eh_spider_parser.dart +++ b/lib/src/utils/eh_spider_parser.dart @@ -43,6 +43,8 @@ import '../service/log.dart'; typedef HtmlParser = T Function(Headers headers, dynamic data); +HtmlParser simpleParser = (headers, data) => data as String; + class EHSpiderParser { static Map loginPage2UserInfoOrErrorMsg(Headers headers, dynamic data) { Map map = {}; diff --git a/pubspec.yaml b/pubspec.yaml index 9fff9fac..b0528d8e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: jhentai description: A flutter app for E-Hentai/EXHentai publish_to: 'none' -version: 8.0.6+273 +version: 8.0.6+274 environment: sdk: '>=3.0.0 <4.0.0'