- // ==UserScript==
- // @name PatreonExpander
- // @namespace https://github.com/frosn0w/iOSscripts
- // @version 2.25.324
- // @description Simplify elements, expand contents and comments
- // @author frosn0w
- // @match *://*.patreon.com/*
- // @run-at document-end
- // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MzYgNDc2Ij48dGl0bGU+UGF0cmVvbiBsb2dvPC90aXRsZT48cGF0aCBkYXRhLWZpbGw9IjEiIGQ9Ik00MzYgMTQzYy0uMDg0LTYwLjc3OC00Ny41Ny0xMTAuNTkxLTEwMy4yODUtMTI4LjU2NUMyNjMuNTI4LTcuODg0IDE3Mi4yNzktNC42NDkgMTA2LjIxNCAyNi40MjQgMjYuMTQyIDY0LjA4OS45ODggMTQ2LjU5Ni4wNTEgMjI4Ljg4M2MtLjc3IDY3LjY1MyA2LjAwNCAyNDUuODQxIDEwNi44MyAyNDcuMTEgNzQuOTE3Ljk0OCA4Ni4wNzItOTUuMjc5IDEyMC43MzctMTQxLjYyMyAyNC42NjItMzIuOTcyIDU2LjQxNy00Mi4yODUgOTUuNTA3LTUxLjkyOUMzOTAuMzA5IDI2NS44NjUgNDM2LjA5NyAyMTMuMDExIDQzNiAxNDNaIj48L3BhdGg+PC9zdmc+
- // @grant none
- // @license MIT
- // ==/UserScript==
-
- // 常量统一管理
- const CONFIG = {
- MAX_EXECUTIONS: 50,
- INTERVAL_DELAY: 2888,
- REMAIN_DAYS: 1,
- STYLE_ID: "patreon-expander-styles",
- TARGET_SELECTORS: {
- POST_TITLE: 'span[data-tag="post-title"]',
- COMMENT_ROW: 'div[data-tag="comment-row"]',
- // 添加更多常用选择器...
- },
- };
-
- // 全局样式(合并所有样式,避免重复注入)
- const globalStyles = `
- p { line-height: 1.4 !important; }
- div[data-tag="comment-row"]::before,
- div[data-tag="comment-row"]::after {
- width: 0 !important;
- border-left: 0 !important;
- }
- .TAI-body-div p {
- margin: 8px 0 !important;
- }
- .TAI-body-div ul {
- margin: 6px 0 !important;
- }
- `;
- // 日期处理器(避免重复计算)
- const DateFormatter = {
- currentDate: new Date(),
-
- refresh() {
- this.currentDate = new Date();
- },
-
- get formattedDate() {
- return `${this.currentMonth}月${this.currentDay}日`;
- },
-
- get yesterdayDate() {
- const date = new Date(this.currentDate);
- date.setDate(date.getDate() - 1);
- return `${date.getMonth() + 1}月${date.getDate()}日`;
- },
-
- get currentMonth() {
- return this.currentDate.getMonth() + 1;
- },
-
- get currentDay() {
- return this.currentDate.getDate();
- },
- };
- // 安全移除函数
- const safeRemove = (() => {
- const removedCache = new WeakSet();
- return (element, selector = null, levels = 0) => {
- if (!element || !element?.parentElement || removedCache.has(element)) {
- return false;
- }
- try {
- const target = selector ? element.closest(selector) : element;
- if (!target) return false;
- let parent = target;
- for (let i = 0; i < levels; i++) parent = parent?.parentElement;
- if (!parent) return false;
- parent.remove();
- removedCache.add(element);
- return true;
- } catch (error) {
- console.error("Removal failed:", error);
- return false;
- }
- };
- })();
- // 移除过期发布
- function shouldRemovePost(text) {
- const { currentMonth, currentDay } = DateFormatter;
- return (
- text.includes(" 天前") ||
- (text.includes(`${currentMonth}月`) &&
- parseInt(text.split(`${currentMonth}月`)[1]) <
- currentDay - CONFIG.REMAIN_DAYS) ||
- (text.includes(`${currentMonth}月`) &&
- parseInt(text.split(`${currentMonth}月`)[0]) === currentMonth - 1)
- );
- }
- // 处理 link
- function processLinks(element) {
- const { textContent, href, dataset } = element;
- switch (true) {
- case /小时前|分钟前/.test(textContent):
- element.textContent = DateFormatter.formattedDate;
- break;
- case textContent.includes("昨天"):
- element.textContent = DateFormatter.yesterdayDate;
- break;
- case dataset.tag === "post-published-at" && shouldRemovePost(textContent):
- safeRemove(element, 'div[data-tag="post-card"]', 2);
- break;
- // 删除赠送卡片
- case href === "https://www.patreon.com/user/gift?u=80821958":
- safeRemove(element, null, 4);
- break;
- case textContent === "Skip navigation":
- element.remove();
- break;
- case dataset.tag === "comment-avatar-wrapper":
- safeRemove(element, null, 1);
- break;
- }
- }
- // 处理 button
- function processButtons(button) {
- const { textContent, ariaExpanded, ariaLabel, dataset } = button;
- const BUTTON_INTERVALS = {
- 展开: 2888,
- 加载更多留言: 1688,
- 加载回复: 1888,
- };
- switch (true) {
- case ariaExpanded === "false" && ariaLabel === "打开导航":
- safeRemove(button, "header");
- break;
- case ["收起", "收起回复"].includes(textContent):
- safeRemove(button, null, 1);
- break;
- case textContent === "展开":
- case textContent === "加载更多留言":
- case textContent === "加载回复":
- if (!button.autoClicker) {
- button.autoClicker = setInterval(() => {
- document.contains(button)
- ? button.click()
- : clearInterval(button.autoClicker);
- }, BUTTON_INTERVALS[textContent]);
- }
- break;
- case dataset.tag === "commenter-name" &&
- textContent === "贝乐斯 Think Analyze Invest":
- safeRemove(button, '[data-tag="commenter-name"]');
- break;
- }
- }
- // 处理 Div
- function processDivs(element) {
- const { dataset, id, ariaExpanded, textContent } = element;
- switch (true) {
- case id === "main-app-navigation":
- safeRemove(element, null, 1);
- break;
- // 移除导航栏
- case ariaExpanded === "false" && textContent.includes("我的会籍"):
- safeRemove(element, "nav", 3);
- break;
- // 移除头图
- case dataset.tag === "creation-name" &&
- textContent.includes("Love & Peace !"):
- safeRemove(element, null, 4);
- break;
- // 移除搜索框
- case dataset.tag === "search-input-box":
- safeRemove(element, null, 5);
- break;
- case dataset.tag === "chip-container":
- safeRemove(element, null, 2);
- break;
- case dataset.tag === "post-details":
- safeRemove(element);
- break;
- // 移除已删除留言区域
- case dataset.tag === "comment-body" &&
- textContent.includes("此留言已被删除。"):
- safeRemove(element, null, 3);
- break;
- // 移除评论相关功能组件
- case dataset.tag === "comment-actions":
- safeRemove(element);
- break;
- case dataset.tag === "comment-field-box":
- safeRemove(element, null, 3);
- break;
- // 缩窄页边距
- case dataset.tag === "post-stream-container":
- element.parentNode?.style.setProperty("padding-left", "4px");
- element.parentNode?.style.setProperty("padding-right", "4px");
- break;
- }
- }
- // 通过span插入CSS标签,控制正文部分格式
- function processSpans(element) {
- if (element.getAttribute("data-tag") === "post-title") {
- element.parentNode?.parentNode?.parentNode?.parentNode?.classList.add(
- "TAI-body-div"
- );
- }
- }
- // 主逻辑
- (function init() {
- const style = document.createElement("style");
- style.id = CONFIG.STYLE_ID;
- style.textContent = globalStyles;
- document.head.appendChild(style);
-
- let executionCount = 0;
- const interval = setInterval(() => {
- if (executionCount >= CONFIG.MAX_EXECUTIONS) return clearInterval(interval);
-
- DateFormatter.refresh();
- document.querySelectorAll("a").forEach(processLinks);
- document.querySelectorAll("button").forEach(processButtons);
- document.querySelectorAll("div").forEach(processDivs);
- document.querySelectorAll("span").forEach(processSpans);
-
- executionCount++;
- }, CONFIG.INTERVAL_DELAY);
- })();