├── README.md └── LDStatus.user.js /README.md: -------------------------------------------------------------------------------- 1 | # LDStatus 2 | 3 | LDStatus 是一个油猴脚本,用于在浏览 Linux.do 网站时显示用户的信任级别进度。通过这个脚本,您可以实时查看自己的信任级别进度,而无需频繁切换到 connect.linux.do 页面。 4 | 5 | ## 功能特点 6 | 7 | - **浮动窗口**:在 Linux.do 页面左侧显示一个可拖动的浮动窗口 8 | - **实时数据**:从 connect.linux.do 自动获取信任级别数据 9 | - **清晰展示**:以"目标: 已完成数 / 需要完成数"的形式展示数据,并显示24小时内的活动数据 10 | - **折叠功能**:支持窗口折叠为小图标,不影响浏览体验 11 | - **自动刷新**:每五分钟自动刷新数据,保持信息最新 12 | - **可拖动**:支持拖动调整窗口位置,放置在您喜欢的位置 13 | - **直观颜色**:绿色数字表示已达成目标,红色数字表示未达成目标 14 | - **主题切换**:支持深色和亮色两种主题,可根据个人喜好随时切换 15 | - **自动更新**:支持脚本自动更新,无需手动重新安装即可获取最新版本 16 | - **状态记忆**:自动记忆窗口位置和折叠状态,下次访问时自动恢复 17 | 18 | ## 安装方法 19 | 20 | ### 前提条件 21 | 22 | 在安装脚本之前,您需要先安装一个用户脚本管理器扩展。推荐使用 Tampermonkey,它支持大多数主流浏览器: 23 | 24 | - [Chrome 版 Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 25 | - [Firefox 版 Tampermonkey](https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/) 26 | - [Edge 版 Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) 27 | - [Safari 版 Tampermonkey](https://apps.apple.com/app/apple-store/id1482490089) 28 | 29 | ### 方法一:直接安装(推荐) 30 | 31 | 1. 确保您已经安装了 Tampermonkey 或其他用户脚本管理器 32 | 2. 点击以下链接直接安装脚本: 33 | - [安装 LDStatus 脚本](https://github.com/1e0n/LinuxDoStatus/raw/master/LDStatus.user.js) 34 | 3. Tampermonkey 将自动检测并提示您安装脚本 35 | 4. 点击"安装"按钮完成安装 36 | 5. 安装后,脚本将自动检测更新,无需手动重新安装 37 | 38 | ### 方法二:手动安装 39 | 40 | 1. 安装 Tampermonkey 浏览器扩展 41 | 2. 访问 [LDStatus.user.js 文件](https://github.com/1e0n/LinuxDoStatus/blob/master/LDStatus.user.js) 42 | 3. 点击"Raw"按钮查看原始文件 43 | 4. Tampermonkey 应该会自动检测并提示安装 44 | 5. 如果没有自动提示,请手动复制文件内容 45 | 46 | ### 方法三:手动复制粘贴 47 | 48 | 1. 安装 Tampermonkey 浏览器扩展 49 | 2. 点击浏览器工具栏中的 Tampermonkey 图标 50 | 3. 选择"添加新脚本" 51 | 4. 删除编辑器中的所有默认代码 52 | 5. 将 [LDStatus.user.js](https://github.com/1e0n/LinuxDoStatus/blob/master/LDStatus.user.js) 的内容复制并粘贴到编辑器中 53 | 6. 点击"文件"菜单,然后选择"保存" 54 | 55 | ## 使用方法 56 | 57 | 安装脚本后,访问 [Linux.do](https://linux.do) 网站,脚本将自动运行并在页面左侧显示信任级别浮动窗口。 58 | 59 | - **展开/折叠**:点击窗口右上角的箭头按钮可以展开/折叠窗口 60 | - **刷新数据**:点击刷新按钮可以手动刷新数据(脚本也会每五分钟自动刷新) 61 | - **切换主题**:点击主题切换按钮(🌙/☀️)可以在深色和亮色主题之间切换 62 | - **移动窗口**:拖动窗口标题栏可以调整窗口位置 63 | - **查看进度**:绿色数字表示已达成目标,红色数字表示未达成目标 64 | - **变化标识**:当目标完成数有变化时,会显示黄色的⬆(增加)或蓝色的⬇(减少)标识及变化数值,即使刷新后数值没有变化也会保留标识 65 | - **活动统计**:在窗口底部显示24小时内的活动数据,包括浏览话题数、回复话题数、已读帖子数、获赞数和点赞数 66 | 67 | ## 注意事项 68 | 69 | - 脚本需要您已经登录 Linux.do 账号 70 | - 如果数据加载失败,请确保您已登录并刷新页面 71 | - 脚本仅在 Linux.do 域名下运行,不会在其他网站上激活 72 | 73 | ## 自动更新 74 | 75 | LDStatus 脚本支持自动更新功能。当 GitHub 仓库中的脚本版本更新后,您的浏览器将自动检测并提示您更新到最新版本。这意味着您无需手动重新安装脚本即可获得最新功能和修复。 76 | 77 | 自动更新的工作原理: 78 | 1. 脚本每天会自动检查是否有新版本 79 | 2. 如果发现新版本,Tampermonkey 会提示您更新 80 | 3. 点击更新按钮即可完成更新,无需重新访问 GitHub 81 | 82 | ## 更新日志 83 | ### v1.12 84 | - 删除了面板下方"近期的活动"显示区域,简化界面 85 | - 新增进度指示条功能:在每行数据下方添加3像素高的进度条 86 | - 进度条颜色逻辑:正常项目已完成显示绿色/未完成显示红色,举报相关项目颜色相反 87 | - 清理了相关的localStorage缓存数据 88 | 89 | ### v1.11 90 | - 文本优化,“获赞:单日最高数量”按照原始英文解释,这里应该指的是"获得过赞的总天数" 91 | 92 | ### v1.10 93 | - 完全重构时间统计逻辑,改用自然日计算替代相对时间窗口 94 | - "今天"一栏现在显示今天0点到现在的数据变化 95 | - "昨天"一栏现在显示昨天一整天(0-24点)的数据变化 96 | - 优化数据存储机制,每天只保留首末两个数据点,显著减少存储压力 97 | - 自动刷新间隔保持为5分钟 98 | 99 | ### v1.9 100 | - "近期的活动"区块支持显示最近两天(昨天/今天)的数据,并以列形式展示,增加趋势箭头和变化数值,便于对比每日活跃度。 101 | - 所有数字列和表头均右对齐,视觉更整齐。 102 | - 新增表头,明确每列含义(昨天/今天/变化)。 103 | - 亮色和暗色主题下,所有下部(近期的活动)文本和数值颜色与上部保持一致,风格统一。 104 | - 优化暗色主题下昨天数值的颜色,提升可读性。 105 | - 优化亮色主题下昨天和今天的数值颜色,提升对比度和一致性。 106 | - 数据自动刷新时间从两分钟改为五分钟,减少服务器压力。 107 | - 主题切换、面板拖动、折叠、刷新、自动更新等功能保持兼容。 108 | 109 | ### v1.8 110 | - 添加了亮色和深色两种主题,可以根据个人喜好切换 111 | - 在标题栏添加了主题切换按钮(🌙/☀️图标) 112 | - 修复了点赞帖子数据显示不正确的问题 113 | - 优化了数据处理逻辑,提高了数据准确性 114 | 115 | ### v1.7 116 | - 在窗口标题添加了当前脚本的版本号 117 | - 在窗口右上角添加了一个检查更新按钮(🔎图标) 118 | - 修复了之前自动更新会失败的问题 119 | 120 | ### v1.6 121 | - 添加窗口状态记忆功能,自动记忆窗口位置和折叠状态 122 | 123 | ### v1.5 124 | - 添加自动更新功能,脚本现在可以自动检测并更新到最新版本 125 | 126 | ### v1.4 127 | - 添加24小时内活动数据统计功能 128 | - 在浮动窗口底部显示用户24小时内的浏览话题数、回复话题数、已读帖子数、获赞数和点赞数 129 | 130 | ### v1.3 131 | - 修复帖子界面按钮消失的问题 132 | - 使用更特定的CSS选择器避免与网站原有元素冲突 133 | 134 | ### v1.2 135 | - 改进目标完成数变化的标识功能,即使刷新后数值没有变化也会保留标识 136 | - 增加变化标识的颜色:黄色表示增加,蓝色表示减少 137 | 138 | ### v1.1 139 | - 将数据刷新时间从每分钟改为每两分钟 140 | - 添加目标完成数变化的标识功能(⬆表示增加,⬇表示减少) 141 | 142 | ### v1.0 143 | - 初始版本发布 144 | - 实现基本的信任级别数据获取和显示 145 | - 添加浮动窗口和折叠功能 146 | - 支持自动刷新和手动刷新 147 | 148 | ## 反馈与贡献 149 | 150 | 如果您有任何问题、建议或反馈,请在 [GitHub Issues](https://github.com/1e0n/LinuxDoStatus/issues) 上提交。 151 | 152 | 欢迎通过 Pull Requests 贡献代码改进脚本。 153 | -------------------------------------------------------------------------------- /LDStatus.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name LDStatus 3 | // @namespace http://tampermonkey.net/ 4 | // @version 1.12 5 | // @description 在 Linux.do 页面显示信任级别进度 6 | // @author 1e0n 7 | // @match https://linux.do/* 8 | // @grant GM_xmlhttpRequest 9 | // @grant GM_setValue 10 | // @grant GM_getValue 11 | // @grant GM_info 12 | // @connect connect.linux.do 13 | // @connect github.com 14 | // @connect raw.githubusercontent.com 15 | // @updateURL https://raw.githubusercontent.com/1e0n/LinuxDoStatus/master/LDStatus.user.js 16 | // @downloadURL https://raw.githubusercontent.com/1e0n/LinuxDoStatus/master/LDStatus.user.js 17 | // ==/UserScript== 18 | 19 | (function() { 20 | 'use strict'; 21 | 22 | // 创建样式 - 使用更特定的选择器以避免影响帖子界面的按钮 23 | const style = document.createElement('style'); 24 | style.textContent = ` 25 | /* 深色主题 */ 26 | #ld-trust-level-panel.ld-dark-theme { 27 | background-color: #2d3748; 28 | color: #e2e8f0; 29 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); 30 | } 31 | 32 | #ld-trust-level-panel.ld-dark-theme #ld-trust-level-header { 33 | background-color: #1a202c; 34 | color: white; 35 | } 36 | 37 | #ld-trust-level-panel.ld-dark-theme .ld-trust-level-item.ld-success .ld-value { 38 | color: #68d391; 39 | } 40 | 41 | #ld-trust-level-panel.ld-dark-theme .ld-trust-level-item.ld-fail .ld-value { 42 | color: #fc8181; 43 | } 44 | 45 | #ld-trust-level-panel.ld-dark-theme .ld-loading { 46 | color: #a0aec0; 47 | } 48 | 49 | 50 | 51 | #ld-trust-level-panel.ld-dark-theme .ld-version { 52 | color: #a0aec0; 53 | } 54 | 55 | /* 亮色主题 - 提高对比度 */ 56 | #ld-trust-level-panel.ld-light-theme { 57 | background-color: #ffffff; 58 | color: #1a202c; 59 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 60 | border: 1px solid #e2e8f0; 61 | } 62 | 63 | #ld-trust-level-panel.ld-light-theme #ld-trust-level-header { 64 | background-color: #3182ce; /* 更深的蓝色 */ 65 | color: #ffffff; 66 | border-bottom: 1px solid #2c5282; /* 添加底部边框 */ 67 | } 68 | 69 | #ld-trust-level-panel.ld-light-theme .ld-trust-level-item.ld-success .ld-value { 70 | color: #276749; /* 更深的绿色 */ 71 | font-weight: bold; 72 | } 73 | 74 | #ld-trust-level-panel.ld-light-theme .ld-trust-level-item.ld-fail .ld-value { 75 | color: #c53030; 76 | font-weight: bold; 77 | } 78 | 79 | /* 亮色主题下的文本颜色 */ 80 | #ld-trust-level-panel.ld-light-theme .ld-name { 81 | color: #2d3748; /* 深灰色 */ 82 | } 83 | 84 | #ld-trust-level-panel.ld-light-theme .ld-loading { 85 | color: #4a5568; 86 | } 87 | 88 | 89 | 90 | #ld-trust-level-panel.ld-light-theme .ld-version { 91 | color: #e2e8f0; 92 | } 93 | 94 | /* 共用样式 */ 95 | #ld-trust-level-panel { 96 | position: fixed; 97 | left: 10px; 98 | top: 100px; 99 | width: 210px; 100 | border-radius: 8px; 101 | z-index: 9999; 102 | font-family: Arial, sans-serif; 103 | transition: all 0.3s ease; 104 | overflow: hidden; 105 | font-size: 12px; 106 | } 107 | 108 | #ld-trust-level-header { 109 | padding: 8px 10px; 110 | cursor: move; 111 | display: flex; 112 | justify-content: space-between; 113 | align-items: center; 114 | user-select: none; 115 | } 116 | 117 | .ld-header-content { 118 | display: flex; 119 | width: 100%; 120 | align-items: center; 121 | justify-content: space-between; 122 | white-space: nowrap; 123 | } 124 | 125 | .ld-header-content > span:first-child { 126 | margin-right: auto; 127 | font-weight: bold; 128 | } 129 | 130 | #ld-trust-level-content { 131 | padding: 10px; 132 | max-height: none; 133 | overflow-y: visible; 134 | } 135 | 136 | .ld-trust-level-item { 137 | margin-bottom: 6px; 138 | display: block; 139 | width: 100%; 140 | } 141 | 142 | .ld-item-content { 143 | display: flex; 144 | white-space: nowrap; 145 | width: 100%; 146 | justify-content: space-between; 147 | } 148 | 149 | .ld-trust-level-item .ld-name { 150 | flex: 0 1 auto; 151 | overflow: hidden; 152 | text-overflow: ellipsis; 153 | max-width: 60%; 154 | } 155 | 156 | .ld-trust-level-item .ld-value { 157 | font-weight: bold; 158 | flex: 0 0 auto; 159 | text-align: right; 160 | min-width: 70px; 161 | } 162 | 163 | /* 这些样式已移动到主题特定样式中 */ 164 | 165 | .ld-toggle-btn, .ld-refresh-btn, .ld-update-btn, .ld-theme-btn { 166 | background: none; 167 | border: none; 168 | color: white; 169 | cursor: pointer; 170 | font-size: 14px; 171 | margin-left: 5px; 172 | } 173 | 174 | .ld-version { 175 | font-size: 10px; 176 | color: #a0aec0; 177 | margin-left: 5px; 178 | font-weight: normal; 179 | } 180 | 181 | .ld-collapsed { 182 | width: 40px !important; 183 | height: 40px !important; 184 | min-width: 40px !important; 185 | max-width: 40px !important; 186 | border-radius: 8px; 187 | overflow: hidden; 188 | transform: none !important; 189 | } 190 | 191 | .ld-collapsed #ld-trust-level-header { 192 | justify-content: center; 193 | width: 40px !important; 194 | height: 40px !important; 195 | min-width: 40px !important; 196 | max-width: 40px !important; 197 | padding: 0; 198 | display: flex; 199 | align-items: center; 200 | } 201 | 202 | .ld-collapsed #ld-trust-level-header > div { 203 | justify-content: center; 204 | width: 100%; 205 | height: 100%; 206 | } 207 | 208 | .ld-collapsed #ld-trust-level-content { 209 | display: none !important; 210 | } 211 | 212 | .ld-collapsed .ld-header-content > span, 213 | .ld-collapsed .ld-refresh-btn, 214 | .ld-collapsed .ld-update-btn, 215 | .ld-collapsed .ld-theme-btn, 216 | .ld-collapsed .ld-version { 217 | display: none !important; 218 | } 219 | 220 | .ld-collapsed .ld-toggle-btn { 221 | margin: 0; 222 | font-size: 16px; 223 | display: flex; 224 | justify-content: center; 225 | align-items: center; 226 | width: 100%; 227 | height: 100%; 228 | } 229 | 230 | .ld-loading { 231 | text-align: center; 232 | padding: 10px; 233 | } 234 | 235 | /* 深色主题下的变化指示器 */ 236 | .ld-dark-theme .ld-increase { 237 | color: #ffd700; /* 黄色 */ 238 | } 239 | 240 | .ld-dark-theme .ld-decrease { 241 | color: #4299e1; /* 蓝色 */ 242 | } 243 | 244 | /* 亮色主题下的变化指示器 */ 245 | .ld-light-theme .ld-increase { 246 | color: #d69e2e; /* 深黄色 */ 247 | font-weight: bold; 248 | } 249 | 250 | .ld-light-theme .ld-decrease { 251 | color: #2b6cb0; /* 深蓝色 */ 252 | font-weight: bold; 253 | } 254 | 255 | 256 | 257 | 258 | /* 进度条样式 */ 259 | .ld-progress-bar { 260 | height: 3px; 261 | width: 100%; 262 | margin-top: 2px; 263 | margin-bottom: 4px; 264 | border-radius: 1px; 265 | overflow: hidden; 266 | background-color: transparent; 267 | position: relative; 268 | } 269 | 270 | .ld-progress-fill { 271 | height: 100%; 272 | border-radius: 1px; 273 | transition: width 0.3s ease; 274 | position: relative; 275 | } 276 | 277 | /* 正常项目:已完成绿色,未完成红色 */ 278 | .ld-progress-normal .ld-progress-fill { 279 | background-color: #68d391; /* 绿色 - 已完成部分 */ 280 | } 281 | 282 | .ld-progress-normal { 283 | background-color: #fc8181; /* 红色 - 未完成部分 */ 284 | } 285 | 286 | /* 反向项目(被举报帖子、发起举报用户):已完成红色,未完成绿色 */ 287 | .ld-progress-reverse .ld-progress-fill { 288 | background-color: #fc8181; /* 红色 - 已完成部分 */ 289 | } 290 | 291 | .ld-progress-reverse { 292 | background-color: #68d391; /* 绿色 - 未完成部分 */ 293 | } 294 | 295 | /* 深色主题下的进度条颜色调整 */ 296 | .ld-dark-theme .ld-progress-normal .ld-progress-fill { 297 | background-color: #68d391; /* 绿色 */ 298 | } 299 | 300 | .ld-dark-theme .ld-progress-normal { 301 | background-color: #fc8181; /* 红色 */ 302 | } 303 | 304 | .ld-dark-theme .ld-progress-reverse .ld-progress-fill { 305 | background-color: #fc8181; /* 红色 */ 306 | } 307 | 308 | .ld-dark-theme .ld-progress-reverse { 309 | background-color: #68d391; /* 绿色 */ 310 | } 311 | 312 | /* 亮色主题下的进度条颜色调整 */ 313 | .ld-light-theme .ld-progress-normal .ld-progress-fill { 314 | background-color: #276749; /* 深绿色 */ 315 | } 316 | 317 | .ld-light-theme .ld-progress-normal { 318 | background-color: #c53030; /* 深红色 */ 319 | } 320 | 321 | .ld-light-theme .ld-progress-reverse .ld-progress-fill { 322 | background-color: #c53030; /* 深红色 */ 323 | } 324 | 325 | .ld-light-theme .ld-progress-reverse { 326 | background-color: #276749; /* 深绿色 */ 327 | } 328 | 329 | `; 330 | document.head.appendChild(style); 331 | 332 | // 定义存储键 333 | const STORAGE_KEY_POSITION = 'ld_panel_position'; 334 | const STORAGE_KEY_COLLAPSED = 'ld_panel_collapsed'; 335 | const STORAGE_KEY_THEME = 'ld_panel_theme'; 336 | 337 | // 创建面板 338 | const panel = document.createElement('div'); 339 | panel.id = 'ld-trust-level-panel'; 340 | 341 | // 设置默认主题 342 | const currentTheme = GM_getValue(STORAGE_KEY_THEME, 'dark'); 343 | panel.classList.add(currentTheme === 'dark' ? 'ld-dark-theme' : 'ld-light-theme'); 344 | 345 | // 获取脚本版本号 346 | const scriptVersion = GM_info.script.version; 347 | 348 | // 创建面板头部 349 | const header = document.createElement('div'); 350 | header.id = 'ld-trust-level-header'; 351 | header.innerHTML = ` 352 |
353 | Status 354 | v${scriptVersion} 355 | 356 | 357 | 358 | 359 |
360 | `; 361 | 362 | // 创建内容区域 363 | const content = document.createElement('div'); 364 | content.id = 'ld-trust-level-content'; 365 | content.innerHTML = '
加载中...
'; 366 | 367 | // 组装面板 368 | panel.appendChild(header); 369 | panel.appendChild(content); 370 | document.body.appendChild(panel); 371 | 372 | // 保存窗口位置的函数 373 | function savePanelPosition() { 374 | const transform = window.getComputedStyle(panel).transform; 375 | if (transform && transform !== 'none') { 376 | const matrix = new DOMMatrix(transform); 377 | GM_setValue(STORAGE_KEY_POSITION, { x: matrix.e, y: matrix.f }); 378 | } 379 | } 380 | 381 | // 保存窗口折叠状态的函数 382 | function savePanelCollapsedState() { 383 | GM_setValue(STORAGE_KEY_COLLAPSED, panel.classList.contains('ld-collapsed')); 384 | } 385 | 386 | // 恢复窗口状态 387 | function restorePanelState() { 388 | // 恢复折叠状态 389 | const isCollapsed = GM_getValue(STORAGE_KEY_COLLAPSED, false); 390 | if (isCollapsed) { 391 | panel.classList.add('ld-collapsed'); 392 | toggleBtn.textContent = '▶'; // 右箭头 393 | } else { 394 | panel.classList.remove('ld-collapsed'); 395 | toggleBtn.textContent = '◀'; // 左箭头 396 | } 397 | 398 | // 恢复位置 399 | const position = GM_getValue(STORAGE_KEY_POSITION, null); 400 | if (position) { 401 | panel.style.transform = `translate(${position.x}px, ${position.y}px)`; 402 | } 403 | } 404 | 405 | // 拖动功能 406 | let isDragging = false; 407 | let lastX, lastY; 408 | 409 | header.addEventListener('mousedown', (e) => { 410 | if (panel.classList.contains('ld-collapsed')) return; 411 | 412 | isDragging = true; 413 | lastX = e.clientX; 414 | lastY = e.clientY; 415 | 416 | // 添加拖动时的样式 417 | panel.style.transition = 'none'; 418 | document.body.style.userSelect = 'none'; 419 | }); 420 | 421 | document.addEventListener('mousemove', (e) => { 422 | if (!isDragging) return; 423 | 424 | // 使用 transform 而不是改变 left/top 属性,性能更好 425 | const dx = e.clientX - lastX; 426 | const dy = e.clientY - lastY; 427 | 428 | const currentTransform = window.getComputedStyle(panel).transform; 429 | const matrix = new DOMMatrix(currentTransform === 'none' ? '' : currentTransform); 430 | 431 | const newX = matrix.e + dx; 432 | const newY = matrix.f + dy; 433 | 434 | panel.style.transform = `translate(${newX}px, ${newY}px)`; 435 | 436 | lastX = e.clientX; 437 | lastY = e.clientY; 438 | }); 439 | 440 | document.addEventListener('mouseup', () => { 441 | if (!isDragging) return; 442 | 443 | isDragging = false; 444 | panel.style.transition = ''; 445 | document.body.style.userSelect = ''; 446 | 447 | // 保存窗口位置 448 | savePanelPosition(); 449 | }); 450 | 451 | // 展开/收起功能 452 | const toggleBtn = header.querySelector('.ld-toggle-btn'); 453 | toggleBtn.addEventListener('click', () => { 454 | panel.classList.toggle('ld-collapsed'); 455 | toggleBtn.textContent = panel.classList.contains('ld-collapsed') ? '▶' : '◀'; 456 | 457 | // 保存折叠状态 458 | savePanelCollapsedState(); 459 | }); 460 | 461 | // 刷新按钮 462 | const refreshBtn = header.querySelector('.ld-refresh-btn'); 463 | refreshBtn.addEventListener('click', fetchTrustLevelData); 464 | 465 | // 检查更新按钮 466 | const updateBtn = header.querySelector('.ld-update-btn'); 467 | updateBtn.addEventListener('click', checkForUpdates); 468 | 469 | // 主题切换按钮 470 | const themeBtn = header.querySelector('.ld-theme-btn'); 471 | themeBtn.addEventListener('click', toggleTheme); 472 | 473 | // 更新主题按钮图标 474 | updateThemeButtonIcon(); 475 | 476 | // 切换主题函数 477 | function toggleTheme() { 478 | const isDarkTheme = panel.classList.contains('ld-dark-theme'); 479 | 480 | // 切换主题类 481 | panel.classList.remove(isDarkTheme ? 'ld-dark-theme' : 'ld-light-theme'); 482 | panel.classList.add(isDarkTheme ? 'ld-light-theme' : 'ld-dark-theme'); 483 | 484 | // 保存主题设置 485 | GM_setValue(STORAGE_KEY_THEME, isDarkTheme ? 'light' : 'dark'); 486 | 487 | // 更新主题按钮图标 488 | updateThemeButtonIcon(); 489 | } 490 | 491 | // 更新主题按钮图标 492 | function updateThemeButtonIcon() { 493 | const isDarkTheme = panel.classList.contains('ld-dark-theme'); 494 | themeBtn.textContent = isDarkTheme ? '🌙' : '☀️'; // 月亮或太阳图标 495 | themeBtn.title = isDarkTheme ? '切换为亮色主题' : '切换为深色主题'; 496 | 497 | // 在亮色主题下调整按钮颜色 498 | if (!isDarkTheme) { 499 | document.querySelectorAll('.ld-toggle-btn, .ld-refresh-btn, .ld-update-btn, .ld-theme-btn').forEach(btn => { 500 | btn.style.color = 'white'; // 亮色主题下按钮使用白色,因为标题栏是蓝色 501 | btn.style.textShadow = '0 0 1px rgba(0,0,0,0.3)'; // 添加文字阴影增强可读性 502 | }); 503 | } else { 504 | document.querySelectorAll('.ld-toggle-btn, .ld-refresh-btn, .ld-update-btn, .ld-theme-btn').forEach(btn => { 505 | btn.style.color = 'white'; 506 | btn.style.textShadow = 'none'; 507 | }); 508 | } 509 | } 510 | 511 | // 检查脚本更新 512 | function checkForUpdates() { 513 | const updateURL = 'https://raw.githubusercontent.com/1e0n/LinuxDoStatus/master/LDStatus.user.js'; 514 | 515 | // 显示正在检查的状态 516 | updateBtn.textContent = '⌛'; // 沙漏图标 517 | updateBtn.title = '正在检查更新...'; 518 | 519 | GM_xmlhttpRequest({ 520 | method: 'GET', 521 | url: updateURL, 522 | onload: function(response) { 523 | if (response.status === 200) { 524 | // 提取远程脚本的版本号 525 | const versionMatch = response.responseText.match(/@version\s+([\d\.]+)/); 526 | if (versionMatch && versionMatch[1]) { 527 | const remoteVersion = versionMatch[1]; 528 | 529 | // 比较版本 530 | if (remoteVersion > scriptVersion) { 531 | // 有新版本 532 | updateBtn.textContent = '⚠️'; // 警告图标 533 | updateBtn.title = `发现新版本 v${remoteVersion},点击前往更新页面`; 534 | updateBtn.style.color = '#ffd700'; // 黄色 535 | 536 | // 点击按钮跳转到更新页面 537 | updateBtn.onclick = function() { 538 | window.open(updateURL, '_blank'); 539 | }; 540 | } else { 541 | // 已是最新版本 542 | updateBtn.textContent = '✔'; // 勾选图标 543 | updateBtn.title = '已是最新版本'; 544 | updateBtn.style.color = '#68d391'; // 绿色 545 | 546 | // 3秒后恢复原样式 547 | setTimeout(() => { 548 | updateBtn.textContent = '🔎'; // 放大镜图标 549 | updateBtn.title = '检查更新'; 550 | updateBtn.style.color = 'white'; 551 | updateBtn.onclick = checkForUpdates; 552 | }, 3000); 553 | } 554 | } else { 555 | handleUpdateError(); 556 | } 557 | } else { 558 | handleUpdateError(); 559 | } 560 | }, 561 | onerror: handleUpdateError 562 | }); 563 | 564 | // 处理更新检查错误 565 | function handleUpdateError() { 566 | updateBtn.textContent = '❌'; // 错误图标 567 | updateBtn.title = '检查更新失败,请稍后再试'; 568 | updateBtn.style.color = '#fc8181'; // 红色 569 | 570 | // 3秒后恢复原样式 571 | setTimeout(() => { 572 | updateBtn.textContent = '🔎'; // 放大镜图标 573 | updateBtn.title = '检查更新'; 574 | updateBtn.style.color = 'white'; 575 | }, 3000); 576 | } 577 | } 578 | 579 | // 获取信任级别数据 580 | function fetchTrustLevelData() { 581 | content.innerHTML = '
加载中...
'; 582 | 583 | GM_xmlhttpRequest({ 584 | method: 'GET', 585 | url: 'https://connect.linux.do', 586 | onload: function(response) { 587 | if (response.status === 200) { 588 | parseTrustLevelData(response.responseText); 589 | } else { 590 | content.innerHTML = '
获取数据失败,请稍后再试
'; 591 | } 592 | }, 593 | onerror: function() { 594 | content.innerHTML = '
获取数据失败,请稍后再试
'; 595 | } 596 | }); 597 | } 598 | 599 | // 解析信任级别数据 600 | function parseTrustLevelData(html) { 601 | const parser = new DOMParser(); 602 | const doc = parser.parseFromString(html, 'text/html'); 603 | 604 | // 查找信任级别区块 605 | const trustLevelSection = Array.from(doc.querySelectorAll('.bg-white.p-6.rounded-lg')).find(div => { 606 | const heading = div.querySelector('h2'); 607 | return heading && heading.textContent.includes('信任级别'); 608 | }); 609 | 610 | if (!trustLevelSection) { 611 | content.innerHTML = '
未找到信任级别数据,请确保已登录
'; 612 | return; 613 | } 614 | 615 | // 获取用户名和当前级别 616 | const heading = trustLevelSection.querySelector('h2').textContent.trim(); 617 | const match = heading.match(/(.*) - 信任级别 (\d+) 的要求/); 618 | const username = match ? match[1] : '未知用户'; 619 | const targetLevel = match ? match[2] : '未知'; 620 | 621 | // 获取表格数据 622 | const tableRows = trustLevelSection.querySelectorAll('table tr'); 623 | const requirements = []; 624 | 625 | for (let i = 1; i < tableRows.length; i++) { // 跳过表头 626 | const row = tableRows[i]; 627 | const cells = row.querySelectorAll('td'); 628 | 629 | if (cells.length >= 3) { 630 | const name = cells[0].textContent.trim(); 631 | const current = cells[1].textContent.trim(); 632 | const required = cells[2].textContent.trim(); 633 | const isSuccess = cells[1].classList.contains('text-green-500'); 634 | 635 | // 提取当前完成数的数字部分 636 | const currentMatch = current.match(/(\d+)/); 637 | const currentValue = currentMatch ? parseInt(currentMatch[1], 10) : 0; 638 | 639 | // 查找上一次的数据记录 640 | let changeValue = 0; 641 | let hasChanged = false; 642 | 643 | if (previousRequirements.length > 0) { 644 | const prevReq = previousRequirements.find(pr => pr.name === name); 645 | if (prevReq) { 646 | // 如果完成数有变化,更新变化值 647 | if (currentValue !== prevReq.currentValue) { 648 | changeValue = currentValue - prevReq.currentValue; 649 | hasChanged = true; 650 | } else if (prevReq.changeValue) { 651 | // 如果完成数没有变化,但之前有变化值,保留之前的变化值 652 | changeValue = prevReq.changeValue; 653 | hasChanged = true; 654 | } 655 | } 656 | } 657 | 658 | requirements.push({ 659 | name, 660 | current, 661 | required, 662 | isSuccess, 663 | currentValue, 664 | changeValue, // 变化值 665 | hasChanged // 是否有变化 666 | }); 667 | } 668 | } 669 | 670 | // 获取总体结果 671 | const resultText = trustLevelSection.querySelector('p.text-red-500, p.text-green-500'); 672 | const isMeetingRequirements = resultText ? !resultText.classList.contains('text-red-500') : false; 673 | 674 | // 渲染数据 675 | renderTrustLevelData(username, targetLevel, requirements, isMeetingRequirements); 676 | 677 | // 保存当前数据作为下次比较的基准 678 | previousRequirements = [...requirements]; 679 | } 680 | 681 | // 渲染信任级别数据 682 | function renderTrustLevelData(username, targetLevel, requirements, isMeetingRequirements) { 683 | let html = ` 684 |
685 | ${username} - 信任级别 ${targetLevel} 686 |
687 |
688 | ${isMeetingRequirements ? '已' : '未'}符合信任级别 ${targetLevel} 要求 689 |
690 | `; 691 | 692 | requirements.forEach(req => { 693 | // 简化项目名称 694 | let name = req.name; 695 | // 将一些常见的长名称缩短 696 | name = name.replace('已读帖子(所有时间)', '已读帖子(总)'); 697 | name = name.replace('浏览的话题(所有时间)', '浏览话题(总)'); 698 | name = name.replace('获赞:点赞用户数量', '点赞用户数'); 699 | name = name.replace('获赞:单日最高数量', '总获赞天数'); 700 | name = name.replace('被禁言(过去 6 个月)', '被禁言'); 701 | name = name.replace('被封禁(过去 6 个月)', '被封禁'); 702 | 703 | // 提取数字部分以简化显示 704 | let current = req.current; 705 | let required = req.required; 706 | 707 | // 尝试从字符串中提取数字 708 | const currentMatch = req.current.match(/(\d+)/); 709 | const requiredMatch = req.required.match(/(\d+)/); 710 | 711 | if (currentMatch) current = currentMatch[1]; 712 | if (requiredMatch) required = requiredMatch[1]; 713 | 714 | // 添加目标完成数变化的标识 715 | let changeIndicator = ''; 716 | if (req.hasChanged) { 717 | const diff = req.changeValue; 718 | if (diff > 0) { 719 | changeIndicator = ` ▲${diff}`; // 增加标识,黄色 720 | } else if (diff < 0) { 721 | changeIndicator = ` ▼${Math.abs(diff)}`; // 减少标识,蓝色 722 | } 723 | } 724 | 725 | // 计算进度百分比 726 | const currentValue = parseInt(current, 10) || 0; 727 | const requiredValue = parseInt(required, 10) || 0; 728 | 729 | let progressPercent; 730 | if (requiredValue === 0) { 731 | // 当目标值为0时,如果当前值也是0,则视为100%完成 732 | progressPercent = (currentValue === 0) ? 100 : 0; 733 | } else { 734 | progressPercent = Math.min((currentValue / requiredValue) * 100, 100); 735 | } 736 | 737 | // 判断是否为反向项目(被举报相关) 738 | const isReverseItem = name.includes('被举报') || name.includes('发起举报'); 739 | const progressClass = isReverseItem ? 'ld-progress-reverse' : 'ld-progress-normal'; 740 | 741 | html += ` 742 |
743 |
744 | ${name} 745 | ${current}${changeIndicator} / ${required} 746 |
747 |
748 |
749 |
750 |
751 | `; 752 | }); 753 | 754 | content.innerHTML = html; 755 | } 756 | 757 | // 存储上一次获取的数据,用于比较变化 758 | let previousRequirements = []; 759 | 760 | // 清理近期活动相关的localStorage缓存 761 | localStorage.removeItem('ld_daily_stats'); 762 | 763 | // 初始加载 764 | fetchTrustLevelData(); 765 | 766 | // 恢复窗口状态和主题 767 | // 在所有DOM操作完成后执行,确保 toggleBtn 和 themeBtn 已经定义 768 | setTimeout(() => { 769 | restorePanelState(); 770 | updateThemeButtonIcon(); 771 | }, 100); 772 | 773 | // 定时刷新(每五分钟) 774 | setInterval(fetchTrustLevelData, 300000); 775 | })(); 776 | --------------------------------------------------------------------------------