- // ==UserScript==
- // @name 贴吧助手
- // @namespace 蒋晓楠
- // @version 20240307
- // @description 能按照用户和关键词屏蔽不想看的贴,屏蔽低于指定等级用户,也可以给用户打标签帮助自己记忆此人的特点,屏蔽置顶直播
- // @author 蒋晓楠
- // @license MIT
- // @match https://tieba.baidu.com/p/*
- // @match https://tieba.baidu.com/f?*kw=*
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_deleteValue
- // @grant GM_notification
- // @grant GM_addStyle
- // @grant GM_addElement
- // @grant GM_registerMenuCommand
- // ==/UserScript==
- //执行间隔(秒)
- let Interval = 1;
- //过期数据清理延时(秒)
- let ClearExpireDataDelay = 30;
- //过期数据清理几率百分比,范围0-100
- let ClearExpireDataPercent = 2;
- //以下不懂的不要修改
- //页面类型
- const PAGE_TYPE_LIST = 0;//列表
- const PAGE_TYPE_POST = 1;//帖子
- let PageType = location.pathname === "/f" ? PAGE_TYPE_LIST : PAGE_TYPE_POST;
-
- //获取数据
- function GetData() {
- return GM_getValue("List", { User: [], UserTime: {}, Keyword: { Title: {}, Content: {} }, UserTag: {} });
- }
-
- //保存数据
- function SaveData(Data) {
- GM_setValue("List", Data);
- }
-
- //修改配置
- function SetConfig(Key, Value) {
- GM_setValue("Config:" + Key, Value);
- }
-
- //获取配置
- function GetConfig(Key, Default) {
- return GM_getValue("Config:" + Key, Default);
- }
-
- //删除配置
- function RemoveConfig(Key) {
- GM_deleteValue("Config:" + Key);
- }
-
- //提醒
- function Tips(Message) {
- GM_notification({ title: "贴吧助手", text: Message, timeout: 3000 });
- }
-
- //初始化样式
- function InitCSS() {
- GM_addStyle(`.JXNButton{border:1px solid;margin-right:10px}.BlockUser{width:90%}.JXNInput{width:50px;}.JXNTag{border:1px solid black;margin:5px 1px;display:inline-block;min-width:50px;}.JXNTagBox{width:90px;}.JXNHolder{width:250px;position:absolute;left:10px;z-index:1;top:135px}.PostListBlockUser{position:absolute;right:0;margin-right:0;z-index:1}`);
- }
-
- //获取有效天数
- function GetExpireDay() {
- return GetConfig("ExpireDay", 180);
- }
-
- //设置有效天数
- function SetExpireDay(Value) {
- SetConfig("ExpireDay", parseInt(Value));
- }
-
- //获取当前时间
- function GetNowTime() {
- let Now = new Date();
- return parseInt(Now.getFullYear().toString() + (Now.getMonth() + 1).toString().padStart(2, "0") + Now.getDate().toString().padStart(2, "0"));
- }
-
- //获取新的过期时间
- function GetNewExpiredDay() {
- let Now = new Date();
- Now.setDate(Now.getDate() + GetExpireDay());
- return parseInt(Now.getFullYear().toString() + (Now.getMonth() + 1).toString().padStart(2, "0") + Now.getDate().toString().padStart(2, "0"));
- }
-
- //屏蔽用户存在
- function BlockUserIsExist(Id) {
- return GetData().User.indexOf(Id) > -1;
- }
-
- //添加屏蔽用户
- function AddBlockUser(Id) {
- let Data = GetData();
- Data.User.push(Id);
- Data.UserTime[Id] = GetNewExpiredDay();
- SaveData(Data);
- }
-
- //移除屏蔽用户
- function RemoveBlockUser(Id) {
- let Data = GetData();
- let Index = Data.User.indexOf(Id);
- if (Index > -1) {
- Data.User.splice(Index, 1);
- delete Data.UserTime[Id];
- SaveData(Data);
- }
- }
-
- //获取当前关键词类型
- function GetNowKeyWordType() {
- return parseInt(document.querySelector(".JXNMatchType").value);
- }
-
- //获取当前关键词模式
- function GetNowKeyWordMode() {
- return parseInt(document.querySelector(".JXNMatchMode").value);
- }
-
- //屏蔽关键词存在
- function BlockKeywordIsExist(Type, Value) {
- return GetData().Keyword[Type === 0 ? "Title" : "Content"][Value] !== undefined;
- }
-
- //添加屏蔽关键词
- function AddBlockKeyword(Type, Mode, Value) {
- let Data = GetData();
- Data.Keyword[Type === 0 ? "Title" : "Content"][Value] = { Mode: Mode, Time: GetNewExpiredDay() };
- SaveData(Data);
- }
-
- //移除屏蔽关键词
- function RemoveBlockKeyword(Type, Value) {
- let Data = GetData();
- let KeyWord = Data.Keyword;
- let Key = Type === 0 ? "Title" : "Content";
- if (KeyWord[Key][Value] !== undefined) {
- delete Data.Keyword[Key][Value];
- SaveData(Data);
- }
- }
-
- //获取用户标签
- function GetUserTags(UserId) {
- let Data = GetData();
- if (Data.UserTag[UserId] === undefined) {
- return {};
- } else {
- return Data.UserTag[UserId];
- }
- }
-
- //用户添加标签
- function UserAddTag(UserId, Tag) {
- let Data = GetData();
- //初始化该用户的标签
- if (Data.UserTag[UserId] === undefined) {
- Data.UserTag[UserId] = {};
- }
- Data.UserTag[UserId][Tag] = GetNewExpiredDay();
- SaveData(Data);
- }
-
- //用户移除标签
- function UserRemoveTag(UserId, Tag) {
- let Data = GetData();
- let UserTags = Data.UserTag[UserId];
- if (UserTags !== undefined && UserTags[Tag] !== undefined) {
- Object.getOwnPropertyNames(UserTags).length === 1 ? delete Data.UserTag[UserId] : delete UserTags[Tag];
- SaveData(Data);
- }
- }
-
- //帖子列表的额外屏蔽
- function PostListExtraBlock() {
- //置顶直播
- if (GetConfig("BlockTopLive") === true && document.querySelector("[id^=pagelet_live]") !== null) {
- document.querySelector("[id^=pagelet_live]").remove();
- }
- //关闭会员红贴
- if (GetConfig("BanMemberPost", true) && document.querySelector(".member_thread_title_frs") !== null) {
- document.querySelectorAll(".member_thread_title_frs").forEach((TitleHolder) => {
- TitleHolder.classList.remove("member_thread_title_frs");
- });
- }
- }
-
- //内容过滤
- function ContentFilter(Content) {
- Content = Content.trim();
- if (Content !== "") {
- //过滤非中文/英文/数字
- Content = Content.replace(/[^\da-zA-Z\u4e00-\u9fa5]/g, "");
- }
- return Content;
- }
-
- //检测标题关键词
- function CheckKeywordTitle(Title) {
- let TitleKeyWords = GetData().Keyword.Title;
- for (const Keyword in TitleKeyWords) {
- if (TitleKeyWords[Keyword].Mode === 0) {
- if (Title.indexOf(Keyword) > -1) {
- return Keyword;
- }
- } else if (Keyword === Title) {
- return Keyword;
- }
- }
- return false;
- }
-
- //检测内容关键词
- function CheckKeywordContent(Content) {
- let ContentWords = GetData().Keyword.Content;
- for (const Keyword in ContentWords) {
- if (ContentWords[Keyword].Mode === 0) {
- if (Content.indexOf(Keyword) > -1) {
- return Keyword;
- }
- } else if (Keyword === Content) {
- return Keyword;
- }
- }
- return false;
- }
-
- //帖子列表初始化
- function InitPostList() {
- let StartTime = (new Date()).getTime();
- let Posts = document.querySelectorAll(".j_thread_list:not(.JXNProcessed)");
- let Number = Posts.length;
- if (Number > 0) {
- let BlockAlphaNumberTitle = GetConfig("BlockAlphaNumberTitle", false),
- EnableContentFilter = GetConfig("ContentFilter", false);
- for (let i = 0; i < Posts.length; i++) {
- let Post = Posts[i];
- let User = Post.querySelector(".tb_icon_author");
- if (User === null) {
- //添加已处理标记
- Post.classList.add("JXNProcessed");
- } else {
- let DisplayName = Post.querySelector(".frs-author-name").textContent;
- let UserId = JSON.parse(Post.querySelector(".tb_icon_author").getAttribute("data-field")).user_id;
- let Title = Post.querySelector(".threadlist_title>a").getAttribute("title");
- //屏蔽纯字母数字标题
- if (BlockAlphaNumberTitle && /^[0-9a-zA-Z]*$/.test(Title)) {
- console.log(`${DisplayName}(${UserId})的帖子[${Title}]被屏蔽因为标题为纯字母数字`);
- Post.remove();
- }
- //按用户ID屏蔽
- else if (BlockUserIsExist(UserId)) {
- console.log(`${DisplayName}(${UserId})的帖子[${Title}]被屏蔽因为在用户屏蔽列表内`);
- Post.remove();
- } else {
- //按关键词屏蔽
- let Result = CheckKeywordTitle(Title);
- if (Result === false) {
- let Content = Post.querySelector(".threadlist_abs_onlyline");
- if (Content !== null) {
- Content = Content.textContent;
- if (EnableContentFilter)
- Content = ContentFilter(Content);
- if (Content !== "") {
- Result = CheckKeywordContent(Content);
- if (Result !== false) {
- console.log(`${DisplayName}(${UserId})的帖子[${Title}]被屏蔽因为内容触发关键词[${Result}]`);
- Post.remove();
- continue;
- }
- }
- }
- //添加屏蔽按钮
- let BlockButton = document.createElement("button");
- BlockButton.classList.add("JXNButton");
- BlockButton.classList.add("PostListBlockUser");
- BlockButton.textContent = "屏蔽用户";
- BlockButton.onclick = () => {
- if (BlockUserIsExist(UserId)) {
- Tips("此用户已存在");
- } else {
- AddBlockUser(UserId)
- //添加取消屏蔽按钮
- let CancelButton = document.createElement("button");
- CancelButton.classList.add("JXNButton");
- CancelButton.classList.add("PostListBlockUser");
- CancelButton.textContent = "取消屏蔽";
- CancelButton.onclick = () => {
- if (BlockUserIsExist(UserId)) {
- RemoveBlockUser(UserId);
- CancelButton.remove();
- Tips("已取消对此用户的屏蔽");
- } else {
- Tips("此用户不存在");
- }
- }
- Post.prepend(CancelButton);
- Tips("添加成功,刷新后将自动屏蔽此用户的发帖与回复");
- BlockButton.remove();
- }
- }
- Post.prepend(BlockButton);
- Post.classList.add("JXNProcessed");
- } else {
- console.log(`${DisplayName}(${UserId})的帖子[${Title}]被屏蔽因为标题触发关键词[${Result}]`);
- Post.remove();
- }
- }
- }
- }
- //额外屏蔽
- PostListExtraBlock();
- console.log("屏蔽用时:" + ((new Date()).getTime() - StartTime) + "毫秒");
- }
- }
-
- //初始化用户面板
- function InitUserPanel(UserId, UserBlock) {
- setTimeout(() => {
- let BlockButton = GM_addElement(UserBlock, "button", {
- class: "JXNButton BlockUser",
- textContent: "屏蔽"
- });
- BlockButton.onclick = () => {
- if (BlockUserIsExist(UserId)) {
- Tips("此用户已存在");
- } else {
- AddBlockUser(UserId)
- //添加取消屏蔽按钮
- let CancelButton = GM_addElement(UserBlock, "button", {
- textContent: "取消屏蔽", class: "JXNButton BlockUser"
- });
- CancelButton.onclick = () => {
- if (BlockUserIsExist(UserId)) {
- RemoveBlockUser(UserId);
- Tips("已取消对此用户的屏蔽");
- } else {
- Tips("此用户不存在");
- }
- }
- Tips("添加成功,刷新后将自动屏蔽此用户的发帖与回复");
- BlockButton.remove();
- }
- }
- let Tags = GetUserTags(UserId);
- for (const Tag in Tags) {
- let NowTag = GM_addElement(UserBlock, "button", {
- textContent: Tag,
- class: "JXNTag",
- title: "点击删除此标签"
- });
- NowTag.onclick = () => {
- UserRemoveTag(UserId, Tag);
- Tips("删除完成");
- NowTag.remove();
- }
- }
- //创建添加标签框
- let TagBox = GM_addElement(UserBlock, "input", {
- type: "text",
- class: "JXNInput JXNTagBox"
- });
- let Button = GM_addElement(UserBlock, "button", {
- textContent: "添加",
- class: "JXNButton JXNTagRemove",
- title: "添加标签"
- });
- Button.onclick = () => {
- let Tag = TagBox.value;
- if (Tag !== "") {
- UserAddTag(UserId, Tag);
- Tips("添加完成,刷新后会正确显示");
- }
- }
- }, Interval);
- }
-
- //帖子内初始化
- function InitPosts() {
- let StartTime = (new Date()).getTime();
- let Posts = document.querySelectorAll(".l_post:not([data-index]):not(.JXNProcessed)");
- let Number = Posts.length;
- if (Number > 0) {
- let BlockLevelUserValue = GetConfig("BlockLevelUser", 2),
- EnableContentFilter = GetConfig("ContentFilter", false);
- let ContentResult;
- for (let i = 0; i < Number; i++) {
- let Post = Posts[i];
- //按等级屏蔽用户
- if (BlockLevelUserValue > 18) {
- Post.remove();
- continue;
- }
- let UserBlock = Post.querySelector(".p_author");
- let DisplayName = UserBlock.querySelector(".p_author_name").textContent;
- let UserId = JSON.parse(Post.querySelector(".d_name").getAttribute("data-field")).user_id;
- if (BlockLevelUserValue > 1 && parseInt(UserBlock.querySelector(".d_badge_lv").textContent) < BlockLevelUserValue) {
- console.log(`${DisplayName}(${UserId})的楼层被屏蔽因为等级低于${BlockLevelUserValue}`);
- Post.remove();
- continue;
- }
- if (BlockUserIsExist(UserId)) {
- console.log(`${DisplayName}(${UserId})的楼层被屏蔽因为在用户屏蔽列表内`);
- Post.remove();
- } else {
- let Content = Post.querySelector(".d_post_content").textContent;
- if (EnableContentFilter)
- Content = ContentFilter(Content);
- //检测内容关键词
- if (Content.length > 0 && (ContentResult = CheckKeywordContent(Content)) !== false) {
- console.log(`${DisplayName}(${UserId})的楼层被屏蔽因为内容触发关键词[${ContentResult}]`);
- Post.remove();
- continue;
- }
- //初始化用户面板
- InitUserPanel(UserId, UserBlock);
- //添加已处理标记
- Post.classList.add("JXNProcessed");
- }
- }
- console.log("屏蔽用时:" + ((new Date()).getTime() - StartTime) + "毫秒");
- }
- }
-
- //通过地址初始化相应功能
- function InitFunctionByURL() {
- let Function = PageType === PAGE_TYPE_LIST ? InitPostList : InitPosts;
- //初始执行
- Function();
- //定期执行
- setInterval(() => {
- Function();
- }, Interval * 1000);
- }
-
- //初始化操作界面
- function InitUI() {
- //脚本菜单
- //屏蔽置顶直播
- GM_registerMenuCommand((GetConfig("BlockTopLive", false) === true ? "✅" : "❎") + "屏蔽置顶直播", () => {
- SetConfig("BlockTopLive", GetConfig("BlockTopLive", false) !== true);
- });
- //屏蔽纯字母数字标题
- GM_registerMenuCommand((GetConfig("BlockAlphaNumberTitle", false) === true ? "✅" : "❎") + "屏蔽纯字母数字标题", () => {
- SetConfig("BlockAlphaNumberTitle", GetConfig("BlockAlphaNumberTitle", false) !== true);
- });
- //关闭会员红贴
- GM_registerMenuCommand((GetConfig("BanMemberPost", true) === true ? "✅" : "❎") + "关闭会员红贴", () => {
- SetConfig("BanMemberPost", GetConfig("BanMemberPost", false) !== true);
- });
- //内容预过滤
- GM_registerMenuCommand((GetConfig("ContentFilter", false) === true ? "✅" : "❎") + "内容预过滤", () => {
- SetConfig("ContentFilter", GetConfig("ContentFilter", false) !== true);
- });
- //导出数据
- GM_registerMenuCommand("导出数据", () => {
- let ExportJson = document.createElement("a");
- ExportJson.download = "贴吧助手.json";
- ExportJson.href = URL.createObjectURL(new Blob([JSON.stringify(GetData())]));
- ExportJson.click();
- });
- //页面菜单
- //油猴脚本不提供在指定元素后面创建的api
- let Holder = document.createElement("div");
- Holder.classList.add("JXNHolder");
- let PositionElement = document.getElementById("head");
- PositionElement.after(Holder);
- //有效天数
- GM_addElement(Holder, "span", { textContent: "有效天数" });
- let Expire = GM_addElement(Holder, "input", {
- type: "number",
- min: 1,
- step: 1,
- value: GetExpireDay(), class: "JXNInput"
- });
- GM_addElement(Holder, "br", {});
- if (PageType === PAGE_TYPE_POST) {
- //按等级屏蔽用户
- GM_addElement(Holder, "span", { textContent: "屏蔽低于此等级用户" });
- let BlockLevelUser = GM_addElement(Holder, "input", {
- type: "number", value: GetConfig("BlockLevelUser", 2), min: 1, max: 19, step: 1
- });
- BlockLevelUser.onchange = () => {
- SetConfig("BlockLevelUser", this.value);
- }
- GM_addElement(Holder, "br", {});
- }
- //关键词
- GM_addElement(Holder, "span", { textContent: "关键词" });
- let Keyword = GM_addElement(Holder, "input", {
- type: "text",
- });
- GM_addElement(Holder, "br", {});
- //匹配类型
- GM_addElement(Holder, "span", { textContent: "匹配类型" });
- let MatchType = GM_addElement(Holder, "select", { class: "JXNMatchType" });
- MatchType.add(new Option("标题", 0));
- MatchType.add(new Option("内容", 1));
- MatchType.value = GetConfig("MatchType", 0);
- //匹配模式
- GM_addElement(Holder, "span", { textContent: "匹配模式" });
- let MatchMode = GM_addElement(Holder, "select", { class: "JXNMatchMode" });
- MatchMode.add(new Option("部分", 0));
- MatchMode.add(new Option("全部", 1));
- MatchMode.value = GetConfig("MatchMode", 0);
- GM_addElement(Holder, "br", {});
- let AddKeyword = GM_addElement(Holder, "button", {
- textContent: "添加", class: "JXNButton"
- });
- let RemoveKeyword = GM_addElement(Holder, "button", {
- textContent: "删除", class: "JXNButton"
- });
- //导入选择块
- let ImportFile = GM_addElement(Holder, "input", {
- type: "file",
- hidden: true,
- accept: "application/json",
- value: false
- });
- //导入数据
- let ImportList = GM_addElement(Holder, "button", {
- textContent: "导入数据", class: "JXNButton"
- });
- //绑定事件
- //有效天数
- Expire.onchange = () => {
- SetExpireDay(Expire.value);
- }
- //匹配类型
- MatchType.onchange = () => {
- SetConfig("MatchType", MatchType.value);
- }
- //匹配模式
- MatchMode.onchange = () => {
- SetConfig("MatchMode", MatchMode.value);
- }
- //添加删除关键词
- AddKeyword.onclick = () => {
- let Value = Keyword.value;
- if (Value !== "") {
- if (BlockKeywordIsExist(GetNowKeyWordType(), Value)) {
- Tips("此关键词已存在");
- } else {
- AddBlockKeyword(GetNowKeyWordType(), GetNowKeyWordMode(), Value);
- Tips("添加成功");
- }
- }
- };
- RemoveKeyword.onclick = () => {
- let Value = Keyword.value;
- if (Value !== "") {
- if (BlockKeywordIsExist(GetNowKeyWordType(), Value)) {
- RemoveBlockKeyword(GetNowKeyWordType(), Value);
- Tips("删除成功");
- } else {
- Tips("此关键词不存在");
- }
- }
- }
- //导入数据
- ImportFile.onchange = () => {
- if (ImportFile.files.length > 0) {
- let JsonList = ImportFile.files[0];
- let Reader = new FileReader();
- Reader.onload = (Result) => {
- try {
- SaveData(JSON.parse(Result.target.result));
- Tips("导入完成");
- } catch (e) {
- alert("读取的文件格式不正确");
- }
- };
- Reader.readAsText(JsonList);
- }
- }
- ImportList.onclick = () => {
- ImportFile.click();
- }
- }
-
- //清理过期数据
- function ClearExpireData() {
- //浏览帖子达到一定时间才去执行清理
- setTimeout(() => {
- if (1 + Math.round(Math.random() * 99) <= ClearExpireDataPercent) {
- let NowDate = GetNowTime();
- let Data = GetData();
- //清理用户
- for (let i = 0; i < Data.User.length; i++) {
- let Id = Data.User[i];
- if (Data.UserTime[Id] < NowDate) {
- console.log(`删除过期用户[${Id}]因为它的时间${Data.UserTime[Id]}]小于当前时间`);
- RemoveBlockUser(Id);
- }
- }
- //清理标签
- let UserTag = Data.UserTag;
- for (const UserId in UserTag) {
- let Tags = UserTag[UserId];
- for (const Tag in Tags) {
- if (Tags[Tag] < NowDate) {
- console.log(`删除过期用户[${UserId}]的标签[${Tag}]因为它的时间${Tags[Tag]}小于当前时间`);
- UserRemoveTag(UserId, Tag);
- }
- }
- }
- //清理关键词
- let KeyWord = Data.Keyword;
- for (const BaseKey in KeyWord) {
- let Value = KeyWord[BaseKey];
- for (const Key in Value) {
- let TheKeyWord = Value[Key];
- if (TheKeyWord.Time < NowDate) {
- console.log(`删除过期${BaseKey === "Title" ? "标题" : "内容"}关键词[${Key}]因为它的时间[${TheKeyWord.Time}]小于当前时间`);
- RemoveBlockKeyword(BaseKey === "Title" ? 0 : 1, Key);
- }
- }
- }
- }
- }, ClearExpireDataDelay * 1000);
- }
-
- //显示信息
- function DisplayInfo() {
- setTimeout(() => {
- let Data = GetData();
- console.log("完整数据", Data);
- console.log("屏蔽数量:关键词-标题(" + Object.keys(Data.Keyword.Title).length + "),关键词-内容(" + Object.keys(Data.Keyword.Content).length + "),用户(" + Data.User.length + ")");
- console.log("当前时间:" + GetNowTime());
- }, 1000);
- }
-
- //运行
- function Run() {
- InitCSS();
- InitUI();
- ClearExpireData();
- InitFunctionByURL();
- DisplayInfo();
- }
-
- Run();