Javi

dump: examprepper

Dec 21st, 2025
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.50 KB | None | 0 0
  1. // ==UserScript==
  2. // @name ExamPrepper Content Dumper
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Dump accordion content from ExamPrepper pages to OPFS (AJAX-aware)
  6. // @match https://www.examprepper.co/exam/*
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. (async function() {
  11. 'use strict';
  12. alert('*')
  13. const FILENAME = 'questions.html';
  14. const PAGE_SEPARATOR = '\n\n';
  15.  
  16. let isProcessing = false;
  17. let processedPages = new Set();
  18.  
  19. console.log('[ExamPrepper Dumper] Script started');
  20. console.log('[ExamPrepper Dumper] Current URL:', window.location.href);
  21.  
  22. // Wait 2 seconds for initial page load
  23. console.log('[ExamPrepper Dumper] Waiting 2 seconds for page to load...');
  24. await sleep(2000);
  25.  
  26. // Process the first page
  27. await processPage();
  28.  
  29. // Set up MutationObserver to detect AJAX page changes
  30. const observer = new MutationObserver(async (mutations) => {
  31. // Check if accordion items have changed (new page loaded)
  32. for (const mutation of mutations) {
  33. if (mutation.addedNodes.length > 0) {
  34. // Check if new accordion items were added
  35. const hasNewAccordion = Array.from(mutation.addedNodes).some(node => {
  36. if (node.nodeType === 1) { // Element node
  37. return node.classList?.contains('chakra-accordion__item') ||
  38. node.querySelector?.('.chakra-accordion__item');
  39. }
  40. return false;
  41. });
  42.  
  43. if (hasNewAccordion && !isProcessing) {
  44. console.log('[ExamPrepper Dumper] New content detected after AJAX load');
  45. await sleep(1000); // Give it a moment to settle
  46. await processPage();
  47. }
  48. }
  49. }
  50. });
  51.  
  52. // Start observing the document body for changes
  53. observer.observe(document.body, {
  54. childList: true,
  55. subtree: true
  56. });
  57.  
  58. console.log('[ExamPrepper Dumper] Now monitoring for AJAX page changes...');
  59.  
  60. async function processPage() {
  61. if (isProcessing) {
  62. console.log('[ExamPrepper Dumper] Already processing, skipping...');
  63. return;
  64. }
  65.  
  66. isProcessing = true;
  67.  
  68. try {
  69. // Get OPFS root
  70. const root = await navigator.storage.getDirectory();
  71.  
  72. // Find all accordion items
  73. const accordionItems = document.querySelectorAll('.chakra-accordion__item');
  74. console.log(`[ExamPrepper Dumper] Found ${accordionItems.length} accordion items`);
  75.  
  76. if (accordionItems.length === 0) {
  77. console.warn('[ExamPrepper Dumper] No accordion items found! Check if page loaded correctly or CAPTCHA present.');
  78. isProcessing = false;
  79. return;
  80. }
  81.  
  82. // Create a unique page identifier (could use URL + first question text)
  83. const firstItemText = accordionItems[0]?.textContent?.substring(0, 100) || '';
  84. const pageId = window.location.href + firstItemText;
  85.  
  86. if (processedPages.has(pageId)) {
  87. console.log('[ExamPrepper Dumper] This page was already processed, skipping...');
  88. isProcessing = false;
  89. return;
  90. }
  91.  
  92. processedPages.add(pageId);
  93. console.log(`[ExamPrepper Dumper] Processing new page (total pages processed: ${processedPages.size})`);
  94.  
  95. let pageContent = '';
  96.  
  97. // Loop through each accordion item
  98. for (let i = 0; i < accordionItems.length; i++) {
  99. const item = accordionItems[i];
  100. console.log(`[ExamPrepper Dumper] Processing item ${i + 1}/${accordionItems.length}`);
  101.  
  102. // Find "Show Answer" button within this item
  103. const buttons = item.querySelectorAll('button');
  104. let showAnswerButton = null;
  105.  
  106. for (const button of buttons) {
  107. if (button.textContent.trim() === 'Show Answer') {
  108. showAnswerButton = button;
  109. break;
  110. }
  111. }
  112.  
  113. if (showAnswerButton) {
  114. console.log(`[ExamPrepper Dumper] Clicking "Show Answer" button for item ${i + 1}`);
  115. showAnswerButton.click();
  116.  
  117. // Wait 1 second after clicking
  118. await sleep(1000);
  119.  
  120. // Append outerHTML to content
  121. pageContent += item.outerHTML;
  122. console.log(`[ExamPrepper Dumper] Item ${i + 1} content captured`);
  123. } else {
  124. console.warn(`[ExamPrepper Dumper] No "Show Answer" button found in item ${i + 1}`);
  125. }
  126. }
  127.  
  128. // Append page content to file
  129. await appendToFile(root, FILENAME, pageContent + PAGE_SEPARATOR);
  130. console.log('[ExamPrepper Dumper] Content appended to file successfully');
  131.  
  132. // Wait 2 more seconds
  133. console.log('[ExamPrepper Dumper] Waiting 2 seconds before looking for Next button...');
  134. await sleep(2000);
  135.  
  136. // Find and click "Next" button
  137. const allButtons = document.querySelectorAll('button');
  138. let nextButton = null;
  139.  
  140. for (const button of allButtons) {
  141. if (button.textContent.trim() === 'Next') {
  142. nextButton = button;
  143. break;
  144. }
  145. }
  146.  
  147. if (nextButton) {
  148. console.log('[ExamPrepper Dumper] "Next" button found, clicking...');
  149. nextButton.click();
  150. console.log('[ExamPrepper Dumper] Waiting for AJAX to load next page...');
  151. } else {
  152. console.log('[ExamPrepper Dumper] No "Next" button found. Either all pages exhausted or CAPTCHA/error present.');
  153. console.log('[ExamPrepper Dumper] ✓ Scraping complete!');
  154. observer.disconnect(); // Stop observing
  155. }
  156.  
  157. } catch (error) {
  158. console.error('[ExamPrepper Dumper] Error occurred:', error);
  159. } finally {
  160. isProcessing = false;
  161. }
  162. }
  163.  
  164. // Helper function to sleep
  165. function sleep(ms) {
  166. return new Promise(resolve => setTimeout(resolve, ms));
  167. }
  168.  
  169. // Helper function to append content to OPFS file
  170. async function appendToFile(root, filename, content) {
  171. const fileHandle = await root.getFileHandle(filename, { create: true });
  172. const writable = await fileHandle.createWritable({ keepExistingData: true });
  173.  
  174. // Get current file size to append at the end
  175. const file = await fileHandle.getFile();
  176. const size = file.size;
  177.  
  178. // Seek to end and write
  179. await writable.write({ type: 'write', position: size, data: content });
  180. await writable.close();
  181.  
  182. console.log(`[ExamPrepper Dumper] Appended ${content.length} characters to ${filename} (total size: ${size + content.length} bytes)`);
  183. }
  184. })();
Advertisement
Add Comment
Please, Sign In to add comment