feat: anti goods reply

Closes #276

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-02-17 18:49:16 +08:00
parent 0b246d03a6
commit 957c326148
9 changed files with 141 additions and 0 deletions

View File

@@ -29,6 +29,8 @@ class Constants {
static const urlPattern =
r'https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]';
static const goodsUrlPrefix = "https://gaoneng.bilibili.com/tetris";
// 超分辨率滤镜
static const List<String> mpvAnime4KShaders = [
'Anime4K_Clamp_Highlights.glsl',

View File

@@ -1,6 +1,8 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
import 'package:PiliPlus/grpc/grpc_repo.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/video/reply/item.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:dio/dio.dart';
@@ -20,6 +22,7 @@ class ReplyHttp {
required int page,
int sort = 1,
required String banWordForReply,
required bool antiGoodsReply,
}) async {
var res = !isLogin
? await Request().get(
@@ -81,6 +84,37 @@ class ReplyHttp {
});
}
}
// antiGoodsReply
if (antiGoodsReply) {
// topReplies
if (replyData.topReplies?.isNotEmpty == true) {
replyData.topReplies!.removeWhere((item) {
bool hasMatch = needRemove(item);
// remove subreplies
if (hasMatch.not) {
if (item.replies?.isNotEmpty == true) {
item.replies!.removeWhere(needRemove);
}
}
return hasMatch;
});
}
// replies
if (replyData.replies?.isNotEmpty == true) {
replyData.replies!.removeWhere((item) {
bool hasMatch = needRemove(item);
// remove subreplies
if (hasMatch.not) {
if (item.replies?.isNotEmpty == true) {
item.replies!.removeWhere(needRemove);
}
}
return hasMatch;
});
}
}
return LoadingState.success(replyData);
} else {
return LoadingState.error(res.data['message']);
@@ -92,10 +126,12 @@ class ReplyHttp {
required int oid,
required CursorReq cursor,
required String banWordForReply,
required bool antiGoodsReply,
}) async {
dynamic res = await GrpcRepo.mainList(type: type, oid: oid, cursor: cursor);
if (res['status']) {
MainListReply mainListReply = res['data'];
// keyword filter
if (banWordForReply.isNotEmpty) {
// upTop
if (mainListReply.hasUpTop() &&
@@ -121,12 +157,65 @@ class ReplyHttp {
});
}
}
// antiGoodsReply
if (antiGoodsReply) {
// upTop
if (mainListReply.hasUpTop() && needRemoveGrpc(mainListReply.upTop)) {
mainListReply.clearUpTop();
}
// replies
if (mainListReply.replies.isNotEmpty) {
mainListReply.replies.removeWhere((item) {
bool hasMatch = needRemoveGrpc(item);
// remove subreplies
if (hasMatch.not) {
if (item.replies.isNotEmpty) {
item.replies.removeWhere(needRemoveGrpc);
}
}
return hasMatch;
});
}
}
return LoadingState.success(mainListReply);
} else {
return LoadingState.error(res['msg']);
}
}
// ref BiliRoamingX
static bool needRemoveGrpc(ReplyInfo reply) {
if ((reply.content.url.isNotEmpty &&
reply.content.url.values.any((url) {
return url.hasExtra() &&
(url.extra.goodsCmControl == 1 ||
url.extra.goodsItemId != 0 ||
url.extra.goodsPrefetchedCache.isNotEmpty);
})) ||
reply.content.message.contains(Constants.goodsUrlPrefix)) {
return true;
}
return false;
}
static bool needRemove(ReplyItemModel reply) {
try {
if ((reply.content?.jumpUrl?.isNotEmpty == true &&
reply.content!.jumpUrl!.values.any((url) {
return url['extra'] != null &&
(url['extra']['goods_cm_control'] == 1 ||
url['extra']['goods_item_id'] != 0 ||
url['extra']['goods_prefetched_cache'].isNotEmpty);
})) ||
reply.content?.message?.contains(Constants.goodsUrlPrefix) == true) {
return true;
}
} catch (_) {}
return false;
}
static Future<LoadingState> replyReplyList({
required bool isLogin,
required int oid,
@@ -134,6 +223,7 @@ class ReplyHttp {
required int pageNum,
required int type,
required String banWordForReply,
required bool antiGoodsReply,
bool? isCheck,
}) async {
var res = await Request().get(
@@ -157,6 +247,11 @@ class ReplyHttp {
.hasMatch(item.content?.message ?? ''));
}
}
if (antiGoodsReply) {
if (replyData.replies?.isNotEmpty == true) {
replyData.replies!.removeWhere(needRemove);
}
}
return LoadingState.success(replyData);
} else {
return LoadingState.error(
@@ -174,6 +269,7 @@ class ReplyHttp {
required int rpid,
required CursorReq cursor,
required String banWordForReply,
required bool antiGoodsReply,
}) async {
dynamic res = await GrpcRepo.detailList(
type: type,
@@ -191,6 +287,11 @@ class ReplyHttp {
.hasMatch(item.content.message));
}
}
if (antiGoodsReply) {
if (detailListReply.root.replies.isNotEmpty) {
detailListReply.root.replies.removeWhere(needRemoveGrpc);
}
}
return LoadingState.success(detailListReply);
} else {
return LoadingState.error(res['msg']);
@@ -204,6 +305,7 @@ class ReplyHttp {
required int rpid,
required CursorReq cursor,
required String banWordForReply,
required bool antiGoodsReply,
}) async {
dynamic res = await GrpcRepo.dialogList(
type: type,
@@ -221,6 +323,11 @@ class ReplyHttp {
.hasMatch(item.content.message));
}
}
if (antiGoodsReply) {
if (dialogListReply.replies.isNotEmpty) {
dialogListReply.replies.removeWhere(needRemoveGrpc);
}
}
return LoadingState.success(dialogListReply);
} else {
return LoadingState.error(res['msg']);

View File

@@ -34,6 +34,7 @@ abstract class ReplyController extends CommonController {
late final banWordForReply = GStorage.banWordForReply;
late final enableCommAntifraud = GStorage.enableCommAntifraud;
late final antiGoodsReply = GStorage.antiGoodsReply;
@override
void onInit() {
@@ -321,6 +322,7 @@ abstract class ReplyController extends CommonController {
sort: ReplySortType.time.index,
page: 1,
banWordForReply: '',
antiGoodsReply: false,
);
if (context.mounted.not) return;
if (res is Error) {
@@ -348,6 +350,7 @@ abstract class ReplyController extends CommonController {
pageNum: 1,
type: replyType,
banWordForReply: '',
antiGoodsReply: false,
);
if (context.mounted.not) return;
if (res1 is Error) {
@@ -369,6 +372,7 @@ abstract class ReplyController extends CommonController {
type: replyType,
banWordForReply: '',
isCheck: true,
antiGoodsReply: false,
);
if (context.mounted.not) return;
if (res2 is Error) {
@@ -405,6 +409,7 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
type: replyType,
banWordForReply: '',
isCheck: true,
antiGoodsReply: false,
);
if (res3 is Error) {
break;
@@ -439,6 +444,7 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
type: replyType,
banWordForReply: '',
isCheck: true,
antiGoodsReply: false,
);
if (res4 is Error) {
break;

View File

@@ -49,6 +49,7 @@ class DynamicDetailController extends ReplyController {
mode: mode.value,
),
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
)
: ReplyHttp.replyList(
isLogin: isLogin,
@@ -58,5 +59,6 @@ class DynamicDetailController extends ReplyController {
sort: sortType.value.index,
page: currentPage,
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
);
}

View File

@@ -83,6 +83,7 @@ class HtmlRenderController extends ReplyController {
mode: mode.value,
),
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
)
: ReplyHttp.replyList(
isLogin: isLogin,
@@ -92,5 +93,6 @@ class HtmlRenderController extends ReplyController {
sort: sortType.value.index,
page: currentPage,
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
);
}

View File

@@ -1980,6 +1980,19 @@ List<SettingsModel> get extraSettings => [
setKey: SettingBoxKey.antiGoodsDyn,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '屏蔽带货评论',
leading: Stack(
alignment: Alignment.center,
children: [
Icon(Icons.shopping_bag_outlined, size: 14),
Icon(Icons.not_interested),
],
),
setKey: SettingBoxKey.antiGoodsReply,
defaultVal: false,
),
SettingsModel(
settingsType: SettingsType.sw1tch,
enableFeedback: true,

View File

@@ -28,6 +28,7 @@ class VideoReplyController extends ReplyController {
mode: mode.value,
),
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
)
: ReplyHttp.replyList(
isLogin: isLogin,
@@ -37,5 +38,6 @@ class VideoReplyController extends ReplyController {
sort: sortType.value.index,
page: currentPage,
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
);
}

View File

@@ -185,6 +185,7 @@ class VideoReplyReplyController extends ReplyController
mode: mode.value,
),
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
)
: GlobalData().grpcReply
? ReplyHttp.replyReplyListGrpc(
@@ -197,6 +198,7 @@ class VideoReplyReplyController extends ReplyController
mode: mode.value,
),
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
)
: ReplyHttp.replyReplyList(
isLogin: isLogin,
@@ -205,6 +207,7 @@ class VideoReplyReplyController extends ReplyController
pageNum: currentPage,
type: replyType.index,
banWordForReply: banWordForReply,
antiGoodsReply: antiGoodsReply,
);
@override

View File

@@ -375,6 +375,9 @@ class GStorage {
static bool get antiGoodsDyn =>
GStorage.setting.get(SettingBoxKey.antiGoodsDyn, defaultValue: false);
static bool get antiGoodsReply =>
GStorage.setting.get(SettingBoxKey.antiGoodsReply, defaultValue: false);
static List<double> get dynamicDetailRatio => List<double>.from(setting
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
@@ -609,6 +612,7 @@ class SettingBoxKey {
coinWithLike = 'coinWithLike',
isPureBlackTheme = 'isPureBlackTheme',
antiGoodsDyn = 'antiGoodsDyn',
antiGoodsReply = 'antiGoodsReply',
// Sponsor Block
enableSponsorBlock = 'enableSponsorBlock',