岐黄天使刷课助手 - 调试加载器模块

岐黄天使刷课助手的调试与模块状态监控器,用于 @require 架构下的模块状态跟踪和调试支持。

目前為 2025-05-24 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/537076/1595067/%E5%B2%90%E9%BB%84%E5%A4%A9%E4%BD%BF%E5%88%B7%E8%AF%BE%E5%8A%A9%E6%89%8B%20-%20%E8%B0%83%E8%AF%95%E5%8A%A0%E8%BD%BD%E5%99%A8%E6%A8%A1%E5%9D%97.js

  1. // ==UserScript==
  2. // @name 岐黄天使刷课助手 - 调试加载器模块
  3. // @namespace http://tampermonkey.net/qhtx-modules
  4. // @version 1.3.2
  5. // @description 岐黄天使刷课助手的调试与模块状态监控器,用于 @require 架构下的模块状态跟踪和调试支持。
  6. // @author AI助手
  7. // ==/UserScript==
  8.  
  9. /**
  10. * 调试加载器模块 - 重构版本
  11. *
  12. * 此模块已重构以适配基于 GreasyFork @require 指令的生产架构:
  13. * - 移除了所有本地开发服务器 (192.168.1.6:9999) 的 HTTP 请求
  14. * - 保留模块状态跟踪系统,用于监控 @require 加载的模块状态
  15. * - 提供备用函数注入功能,用于调试和开发场景
  16. * - 与 main.user.js 的增强错误处理机制完全兼容
  17. *
  18. * 架构说明:
  19. * - 所有模块现在通过 Tampermonkey 的 @require 机制从 GreasyFork 加载
  20. * - 版本控制通过 URL 查询参数 (?v=x.x.x&t=timestamp) 实现
  21. * - 此调试加载器仅负责状态监控和备用功能提供
  22. */
  23. (function() {
  24. 'use strict';
  25.  
  26. // 版本验证和缓存破坏验证
  27. const DEBUG_LOADER_TIMESTAMP = 1748085794476;
  28. const DEBUG_LOADER_VERSION = '1.3.2-optimized';
  29.  
  30. console.log(`[调试加载器] 版本: ${DEBUG_LOADER_VERSION}, 时间戳: ${DEBUG_LOADER_TIMESTAMP}`);
  31. console.log('[调试加载器] ✅ 已重构版本 - 无本地服务器依赖');
  32.  
  33. // 验证是否为最新版本
  34. if (window.moduleLoadTimestamp && window.moduleLoadTimestamp === DEBUG_LOADER_TIMESTAMP) {
  35. console.log('[调试加载器] ✅ 时间戳匹配,使用最新版本');
  36. if (window.moduleLoadVerification) {
  37. window.moduleLoadVerification.verifyModule('debug-loader', DEBUG_LOADER_TIMESTAMP);
  38. }
  39. } else {
  40. console.warn('[调试加载器] ⚠️ 时间戳不匹配,可能使用缓存版本');
  41. }
  42.  
  43. // 强制阻止任何本地服务器请求
  44. if (typeof GM_xmlhttpRequest !== 'undefined') {
  45. const originalGM_xmlhttpRequest = GM_xmlhttpRequest;
  46. GM_xmlhttpRequest = function(details) {
  47. if (details.url && (details.url.includes('192.168.1.6') || details.url.includes('9999') || details.url.includes('qhtx'))) {
  48. console.error('[调试加载器] 🚫 阻止本地服务器请求:', details.url);
  49. console.error('[调试加载器] 🚫 此请求已被阻止,应使用 @require 架构');
  50. console.error('[调试加载器] 🚫 如果看到此消息,说明有旧版本代码仍在尝试连接本地服务器');
  51. return; // 直接返回,不执行请求
  52. }
  53. return originalGM_xmlhttpRequest.call(this, details);
  54. };
  55. console.log('[调试加载器] 🛡️ 已安装本地服务器请求拦截器');
  56. }
  57.  
  58. // 添加模块加载状态跟踪
  59. window.moduleStatus = {
  60. loaded: new Set(),
  61. pending: new Set([
  62. // 'styles', 'utils', 'ui', 'videoPlayer',
  63. // 'videoNavigation', 'courseNavigation', 'questionBank',
  64. // 'autoAnswer', 'remoteSync'
  65. ]),
  66. startTime: Date.now(),
  67. // 显示模块加载状态
  68. showStatus: function() {
  69. const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(2);
  70. console.log(`
  71. ========== 模块加载状态 (${elapsed}秒) ==========
  72. 已加载: ${Array.from(this.loaded).join(', ') || '无'}
  73. 待加载: ${Array.from(this.pending).join(', ') || '无'}
  74. ============================================
  75. `);
  76. }
  77. };
  78.  
  79. // 防止重复执行的全局标志
  80. if (window.moduleStatusCheckerInitialized) {
  81. console.log('[调试加载器] 模块状态检查器已初始化,跳过重复执行');
  82. return;
  83. }
  84. window.moduleStatusCheckerInitialized = true;
  85.  
  86. // 一次性显示模块加载状态 (优化:移除重复刷新)
  87. let statusCheckCount = 0;
  88. const maxStatusChecks = 3; // 最多检查3次
  89.  
  90. function checkModuleStatusOnce() {
  91. statusCheckCount++;
  92. console.log(`[调试加载器] ${statusCheckCount} 次模块状态检查`);
  93.  
  94. if (window.moduleStatus.pending.size === 0) {
  95. // 所有模块已加载
  96. window.moduleStatus.showStatus();
  97. console.log('%c✅ 所有模块加载完成!', 'color: green; font-size: 14px; font-weight: bold');
  98. return; // 停止检查
  99. } else if (statusCheckCount >= maxStatusChecks) {
  100. // 达到最大检查次数
  101. window.moduleStatus.showStatus();
  102. console.log('%c⚠️ 模块加载检查完成(部分模块可能未加载)', 'color: orange; font-size: 12px');
  103. console.log('[调试加载器] 停止模块状态检查,避免重复刷新');
  104. return; // 停止检查
  105. } else {
  106. // 继续检查,但减少频率
  107. window.moduleStatus.showStatus();
  108. setTimeout(checkModuleStatusOnce, 5000); // 5秒后再检查一次
  109. }
  110. }
  111.  
  112. // 延迟开始第一次检查,给模块加载一些时间
  113. setTimeout(checkModuleStatusOnce, 1000);
  114.  
  115. // 检查模块加载状态
  116. function checkModuleLoaded(moduleName) {
  117. const now = new Date();
  118. const timeStr = now.toLocaleTimeString() + '.' + now.getMilliseconds();
  119. console.log(`[模块加载] 正在验证 ${moduleName} 状态,当前时间: ${timeStr}`);
  120.  
  121. // 详细检查window对象上的模块属性
  122. console.log(`[模块加载详情] ${moduleName} 检查:`, {
  123. '直接访问': window[moduleName],
  124. '类型': typeof window[moduleName],
  125. '是否存在': moduleName in window,
  126. 'window属性': Object.keys(window).filter(k => k.includes(moduleName)),
  127. '全局变量': Object.keys(window).filter(k => k.startsWith('qh') || k.includes('module') || k.includes('Module'))
  128. });
  129.  
  130. // 检查模块是否已加载
  131. if (typeof window[moduleName] === 'function') {
  132. window.moduleStatus.loaded.add(moduleName);
  133. window.moduleStatus.pending.delete(moduleName);
  134.  
  135. console.log(`[模块加载] %c${moduleName} 已就绪%c`,
  136. 'color: green; font-weight: bold', 'color: black');
  137.  
  138. // 输出已加载的模块列表
  139. console.log(`[模块加载] 已加载模块: ${Array.from(window.moduleStatus.loaded).join(', ')}`);
  140. console.log(`[模块加载] 待加载模块: ${Array.from(window.moduleStatus.pending).join(', ')}`);
  141.  
  142. return true;
  143. }
  144.  
  145. // 如果模块未加载,尝试检查是否有其他命名方式
  146. const possibleNames = [
  147. moduleName,
  148. moduleName.charAt(0).toUpperCase() + moduleName.slice(1), // 首字母大写
  149. moduleName + 'Module',
  150. 'qh' + moduleName.charAt(0).toUpperCase() + moduleName.slice(1), // qh前缀
  151. 'create' + moduleName.charAt(0).toUpperCase() + moduleName.slice(1), // create前缀
  152. 'init' + moduleName.charAt(0).toUpperCase() + moduleName.slice(1), // init前缀
  153. 'apply' + moduleName.charAt(0).toUpperCase() + moduleName.slice(1) // apply前缀
  154. ];
  155.  
  156. console.log(`[模块加载] 尝试其他可能的命名:`, possibleNames.map(name => ({
  157. name,
  158. exists: name in window,
  159. type: typeof window[name]
  160. })));
  161.  
  162. console.warn(`[模块加载] %c${moduleName} 未就绪%cwindow[${moduleName}] = ${window[moduleName]}`,
  163. 'color: red; font-weight: bold', 'color: black');
  164.  
  165. return false;
  166. }
  167.  
  168. /**
  169. * 检查模块文件 (已重构)
  170. *
  171. * 此函数已重构为不执行实际的 HTTP 请求。
  172. * 在 @require 架构下,模块由 Tampermonkey 自动加载,
  173. * 此函数仅用于兼容性和调试目的。
  174. *
  175. * @param {string} moduleName - 模块名称
  176. * @returns {Promise} - 始终返回成功的 Promise
  177. */
  178. function checkModuleFile(moduleName) {
  179. console.log(`[模块检查] 模块 ${moduleName} 应通过 @require GreasyFork 加载,跳过本地检查`);
  180. return Promise.resolve({
  181. module: moduleName,
  182. content: '@require-loaded',
  183. status: 200,
  184. source: 'greasyfork-require'
  185. });
  186. }
  187.  
  188. /**
  189. * 加载模块 (已重构)
  190. *
  191. * 此函数已重构为不执行实际的模块加载。
  192. * 在 @require 架构下,模块由 Tampermonkey 在脚本启动时自动加载,
  193. * 此函数仅用于兼容性和状态跟踪目的。
  194. *
  195. * @param {string} moduleName - 模块名称
  196. * @returns {Promise} - 始终返回成功的 Promise
  197. */
  198. function loadModuleWithGM(moduleName) {
  199. console.log(`[模块加载] 模块 ${moduleName} 应通过 @require GreasyFork 自动加载,跳过手动加载`);
  200. return Promise.resolve({
  201. module: moduleName,
  202. loaded: true,
  203. source: 'greasyfork-require',
  204. timestamp: Date.now()
  205. });
  206. }
  207.  
  208. /**
  209. * 检查所有模块 (已重构)
  210. *
  211. * 此函数已重构为不执行实际的模块检查。
  212. * 在 @require 架构下,模块状态检查通过检测全局函数是否存在来实现。
  213. *
  214. * @returns {Promise} - 返回模块状态检查结果
  215. */
  216. function checkAllModules() {
  217. console.log('[模块检查] @require 架构下的模块状态检查');
  218.  
  219. // 检查关键模块函数是否已加载
  220. const moduleChecks = [
  221. { name: 'styles', func: 'applyStyles' },
  222. { name: 'ui', func: 'createPanel' },
  223. { name: 'utils', func: 'checkPageType' },
  224. { name: 'questionBank', func: 'getQuestionList' },
  225. { name: 'autoAnswer', func: 'startAutoAnswer' },
  226. { name: 'videoPlayer', func: 'toggleAutoLearn' },
  227. { name: 'dailyLimitManager', func: 'qh.DailyLimitManager' }, // 检查 window.qh.DailyLimitManager
  228. { name: 'courseNavigation', func: 'collectCourseLinks' },
  229. { name: 'videoNavigation', func: 'initVideoNavigation' }
  230. // { name: 'remoteSync', func: 'syncRemoteQuestionBank' } // remoteSync 核心功能已移除,暂不强制检查
  231. ];
  232.  
  233. const results = moduleChecks.map(check => {
  234. let isLoaded = false;
  235. if (check.func.includes('.')) {
  236. const parts = check.func.split('.');
  237. let obj = window;
  238. let pathExists = true;
  239. for (const part of parts) {
  240. if (obj && typeof obj === 'object' && part in obj) {
  241. obj = obj[part];
  242. } else {
  243. pathExists = false;
  244. break;
  245. }
  246. }
  247. if (pathExists && typeof obj === 'function') { // 类也是函数类型
  248. isLoaded = true;
  249. }
  250. } else {
  251. isLoaded = typeof window[check.func] === 'function';
  252. }
  253.  
  254. const status = isLoaded ? 'loaded' : 'pending';
  255.  
  256. if (isLoaded) {
  257. window.moduleStatus.loaded.add(check.name);
  258. window.moduleStatus.pending.delete(check.name);
  259. } else {
  260. window.moduleStatus.pending.add(check.name);
  261. }
  262.  
  263. return {
  264. module: check.name,
  265. function: check.func,
  266. status: status,
  267. loaded: isLoaded
  268. };
  269. });
  270.  
  271. console.log('[模块检查] @require 模块状态检查完成:', results);
  272. return Promise.resolve(results);
  273. }
  274.  
  275. /**
  276. * 加载所有模块 (已重构)
  277. *
  278. * 此函数已重构为不执行实际的模块加载。
  279. * 在 @require 架构下,模块由 Tampermonkey 自动加载,
  280. * 此函数仅用于状态同步和兼容性目的。
  281. *
  282. * @returns {Promise} - 返回模块加载状态结果
  283. */
  284. function loadAllModules() {
  285. console.log('[模块加载] @require 架构下的模块状态同步');
  286.  
  287. // 重新检查模块状态,确保状态同步
  288. return checkAllModules().then(results => {
  289. const loadedModules = results.filter(r => r.loaded);
  290. const pendingModules = results.filter(r => !r.loaded);
  291.  
  292. console.log(`[模块加载] 模块状态同步完成:`, {
  293. total: results.length,
  294. loaded: loadedModules.length,
  295. pending: pendingModules.length,
  296. loadedModules: loadedModules.map(m => m.module),
  297. pendingModules: pendingModules.map(m => m.module)
  298. });
  299.  
  300. return results;
  301. });
  302. }
  303.  
  304. /**
  305. * 注入备用模块函数到全局作用域
  306. *
  307. * 此函数提供备用的模块函数实现,用于以下场景:
  308. * - @require 模块加载失败时的降级方案
  309. * - 开发和调试阶段的基本功能支持
  310. * - 确保核心功能在任何情况下都能正常工作
  311. *
  312. * 注意:这些是简化的备用实现,功能有限
  313. */
  314. function injectModuleFunctions() {
  315. console.log('[调试加载器] 开始注入备用模块函数到全局作用域');
  316.  
  317. // 注入styles模块
  318. if (!window.applyStyles) {
  319. window.applyStyles = function() {
  320. console.log('[模块注入] 执行注入的 applyStyles 函数');
  321. GM_addStyle(`
  322. .qh-assistant-panel {
  323. position: fixed;
  324. top: 100px;
  325. right: 10px;
  326. width: 280px;
  327. background: linear-gradient(135deg, #00a8cc, #0062bd);
  328. border-radius: 12px;
  329. padding: 15px;
  330. color: white;
  331. z-index: 9999;
  332. font-size: 14px;
  333. box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
  334. }
  335. .qh-assistant-title {
  336. font-size: 18px;
  337. font-weight: bold;
  338. text-align: center;
  339. margin-bottom: 12px;
  340. border-bottom: 2px solid rgba(255, 255, 255, 0.3);
  341. padding-bottom: 8px;
  342. }
  343. .qh-assistant-content {
  344. margin-bottom: 12px;
  345. }
  346. .qh-assistant-btn {
  347. background: linear-gradient(90deg, #4CAF50, #45a049);
  348. border: none;
  349. color: white;
  350. padding: 8px 12px;
  351. text-align: center;
  352. display: block;
  353. width: 100%;
  354. margin: 5px 0;
  355. cursor: pointer;
  356. border-radius: 4px;
  357. }
  358. `);
  359. console.log('[模块注入] applyStyles 函数执行完成');
  360. };
  361. console.log('[模块注入] 已注入 applyStyles 函数');
  362. }
  363.  
  364. // 注入ui模块
  365. if (!window.createPanel) {
  366. window.createPanel = function() {
  367. console.log('[模块注入] 执行注入的 createPanel 函数');
  368.  
  369. // 检查是否已经创建过面板
  370. if (document.querySelector('.qh-assistant-panel')) {
  371. console.log('[模块注入] 面板已存在,不重复创建');
  372. return;
  373. }
  374.  
  375. const panel = document.createElement('div');
  376. panel.className = 'qh-assistant-panel';
  377. panel.innerHTML = `
  378. <div class="qh-assistant-title">岐黄天使刷课助手 v1.3.0</div>
  379. <div class="qh-assistant-content">
  380. <div>状态: 调试模式</div>
  381. <div>当前页面: ${window.location.href}</div>
  382. </div>
  383. <button class="qh-assistant-btn" id="qh-debug-btn">调试信息</button>
  384. `;
  385. document.body.appendChild(panel);
  386.  
  387. // 添加调试按钮事件
  388. document.getElementById('qh-debug-btn').addEventListener('click', function() {
  389. console.log('[调试] 当前页面信息:', {
  390. 'URL': window.location.href,
  391. '已加载模块': Array.from(window.moduleStatus.loaded),
  392. '待加载模块': Array.from(window.moduleStatus.pending),
  393. 'window.qh': window.qh
  394. });
  395. alert('调试信息已输出到控制台');
  396. });
  397.  
  398. console.log('[模块注入] createPanel 函数执行完成');
  399. };
  400. console.log('[模块注入] 已注入 createPanel 函数');
  401. }
  402.  
  403. // 注入utils模块
  404. if (!window.checkPageType) {
  405. window.checkPageType = function() {
  406. console.log('[模块注入] 执行注入的 checkPageType 函数');
  407. console.log('[页面检查] 当前页面URL:', window.location.href);
  408. };
  409. console.log('[模块注入] 已注入 checkPageType 函数');
  410. }
  411.  
  412. console.log('[调试加载器] 模块函数注入完成');
  413. }
  414.  
  415. // 导出模块函数
  416. window.debugLoader = {
  417. checkModuleLoaded,
  418. checkModuleFile,
  419. loadModuleWithGM,
  420. checkAllModules,
  421. loadAllModules,
  422. injectModuleFunctions
  423. };
  424.  
  425. /**
  426. * 自动执行模块状态检查和备用函数准备 (优化:只执行一次)
  427. *
  428. * 在 @require 架构下,此函数主要用于:
  429. * - 检查 @require 加载的模块状态
  430. * - 在需要时提供备用函数
  431. * - 与 main.user.js 的初始化流程协调
  432. */
  433. if (!window.debugLoaderInitialized) {
  434. window.debugLoaderInitialized = true;
  435.  
  436. setTimeout(() => {
  437. console.log('[调试加载器] @require 架构下的模块状态检查开始(一次性执行)');
  438.  
  439. // 首先检查 @require 模块的加载状态
  440. checkAllModules()
  441. .then(results => {
  442. const loadedCount = results.filter(r => r.loaded).length;
  443. const totalCount = results.length;
  444.  
  445. console.log(`[调试加载器] @require 模块状态检查完成: ${loadedCount}/${totalCount} 已加载`);
  446.  
  447. // 如果有模块未加载,提供备用函数
  448. if (loadedCount < totalCount) {
  449. console.log('[调试加载器] 检测到未加载的模块,准备备用函数');
  450. injectModuleFunctions();
  451.  
  452. // 更新状态,标记备用函数已提供
  453. results.forEach(result => {
  454. if (!result.loaded) {
  455. window.moduleStatus.loaded.add(result.module + '-fallback');
  456. console.log(`[调试加载器] ${result.module} 提供了备用函数`);
  457. }
  458. });
  459. } else {
  460. console.log('[调试加载器] 所有 @require 模块已正常加载,无需备用函数');
  461. }
  462.  
  463. return loadAllModules();
  464. })
  465. .then(() => {
  466. console.log('[调试加载器] ✅ 模块状态同步完成,调试加载器初始化结束');
  467. })
  468. .catch(error => {
  469. console.error('[调试加载器] 模块状态检查出错:', error);
  470. console.log('[调试加载器] 启用备用函数作为安全措施');
  471. injectModuleFunctions();
  472. });
  473. }, 500); // 减少延迟,因为 @require 模块应该已经加载
  474. } else {
  475. console.log('[调试加载器] 已初始化,跳过重复执行');
  476. }
  477.  
  478. console.log('[调试加载器] 模块已加载 - @require 架构兼容版本');
  479. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址