您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically fetch and update udemy cookies automatically
// ==UserScript== // @name Cookie Updater // @description Automatically fetch and update udemy cookies automatically // @namespace https://greasyfork.org/users/1508709 // @version 1.0.7 // @author https://github.com/sitien173 // @match *://*.itauchile.udemy.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_cookie // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @connect udemy-cookies-worker-commercial.sitienbmt.workers.dev // @run-at document-start // ==/UserScript== /* eslint-disable */ /* global GM_getValue, GM_setValue */ (function() { // Configuration const DEFAULT_CONFIG = { workerUrl: 'https://udemy-cookies-worker-commercial.sitienbmt.workers.dev/', licenseKey: '', autoUpdateInterval: 60 * 60 * 1000, // 1 hour autoUpdateEnabled: false, showNotifications: true, autoReload: true, retryAttempts: 3, showUiButtons: true }; let config = { ...DEFAULT_CONFIG }; // Load configuration function loadConfig() { const savedConfig = GM_getValue('config', {}); config = { ...DEFAULT_CONFIG, ...savedConfig }; } // Save configuration function saveConfig() { GM_setValue('config', config); } // Persist and return a stable device id for this client function getOrCreateDeviceId() { let id = GM_getValue('deviceId', ''); if (!id) { try { if (crypto && crypto.randomUUID) { id = crypto.randomUUID(); } } catch (e) {} if (!id) { id = (Date.now().toString(36) + Math.random().toString(36).slice(2, 10)); } // Keep it simple and URL-safe id = id.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 64); GM_setValue('deviceId', id); } return id; } // Fetch cookies from worker with retry logic using GM_xmlhttpRequest async function fetchCookiesFromWorker() { let lastError; for (let attempt = 1; attempt <= config.retryAttempts; attempt++) { try { console.log(`Fetching cookies from worker (attempt ${attempt}/${config.retryAttempts})...`); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: config.workerUrl + '?key=' + encodeURIComponent(config.licenseKey) + '&device=' + encodeURIComponent(getOrCreateDeviceId()), onload: function(response) { if (response.status === 200) { try { if (response.responseText === '{}') { reject(new Error('Invalid license key')); return; } const cookies = JSON.parse(response.responseText); console.log(`Successfully fetched ${cookies.length} cookies from worker`); resolve(cookies); } catch (error) { console.error('Failed to parse JSON response:', error); reject(error); } } else { reject(new Error(`HTTP ${response.status}: ${response.statusText}`)); } }, onerror: function(error) { console.error('Network error:', error); reject(error); } }); }); } catch (error) { lastError = error; console.error(`Attempt ${attempt} failed:`, error); if (attempt < config.retryAttempts) { await new Promise(resolve => setTimeout(resolve, 2000)); } } } throw new Error(`Failed to fetch cookies after ${config.retryAttempts} attempts. Last error: ${lastError.message}`); } // Save cookie using GM_cookie function saveCookie(cookie, url) { const gmAvailable = typeof GM_cookie !== 'undefined' && GM_cookie && typeof GM_cookie.set === 'function'; if (gmAvailable) { return new Promise((resolve, reject) => { const cookieDetails = { name: cookie.name, value: cookie.value, url: url, domain: cookie.domain || undefined, path: cookie.path || '/', secure: cookie.secure || false, httpOnly: cookie.httpOnly || false, sameSite: cookie.sameSite || 'no_restriction' }; if (cookie.expirationDate) { cookieDetails.expirationDate = cookie.expirationDate; } GM_cookie.set(cookieDetails, (result, error) => { if (error) { console.error('Failed to save cookie:', error); reject(error); } else { console.log(`Successfully saved cookie: ${cookie.name}`); resolve(result); } }); }); } // Fallback for environments where GM_cookie is not available (e.g., iOS Safari) return new Promise((resolve) => { // HttpOnly cannot be set via document.cookie // Domain is omitted to restrict to current host (safest cross-browser) let cookieStr = `${cookie.name}=${encodeURIComponent(cookie.value)}`; cookieStr += `; path=${cookie.path || '/'}`; if (cookie.secure) cookieStr += '; Secure'; // SameSite handling if (cookie.sameSite && typeof cookie.sameSite === 'string') { const s = cookie.sameSite.toLowerCase(); if (s === 'lax' || s === 'strict' || s === 'none') { cookieStr += `; SameSite=${s.charAt(0).toUpperCase() + s.slice(1)}`; if (s === 'none' && cookieStr.indexOf('Secure') === -1) { cookieStr += '; Secure'; } } } if (cookie.expirationDate) { const d = new Date(0); d.setUTCSeconds(cookie.expirationDate); cookieStr += `; Expires=${d.toUTCString()}`; } document.cookie = cookieStr; console.warn('GM_cookie not available, used document.cookie fallback. Some cookies (e.g., HttpOnly/third-party) cannot be set.'); resolve(true); }); } // Remove cookie using GM_cookie function removeCookie(name, url) { const gmAvailable = typeof GM_cookie !== 'undefined' && GM_cookie && typeof GM_cookie.delete === 'function'; if (gmAvailable) { return new Promise((resolve, reject) => { GM_cookie.delete({ name: name, url: url }, (result, error) => { if (error) { console.error('Failed to remove cookie:', error); reject(error); } else { console.log(`Successfully removed cookie: ${name}`); resolve(result); } }); }); } // Fallback: expire the cookie for current host return new Promise((resolve) => { document.cookie = `${name}=; path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT`; resolve(true); }); } // Get all cookies for current domain using GM_cookie function getAllCookies(url) { const gmAvailable = typeof GM_cookie !== 'undefined' && GM_cookie && typeof GM_cookie.list === 'function'; if (gmAvailable) { return new Promise((resolve, reject) => { GM_cookie.list({ url: url }, (cookies, error) => { if (error) { console.error('Failed to get cookies:', error); reject(error); } else { resolve(cookies); } }); }); } // Fallback: parse document.cookie return new Promise((resolve) => { const cookieStr = document.cookie || ''; const pairs = cookieStr ? cookieStr.split('; ') : []; const results = pairs.map(p => { const eqIdx = p.indexOf('='); const name = eqIdx >= 0 ? p.slice(0, eqIdx) : p; const value = eqIdx >= 0 ? decodeURIComponent(p.slice(eqIdx + 1)) : ''; return { name, value }; }); resolve(results); }); } // Update cookies from worker async function updateCookiesFromWorker() { try { console.log('Starting cookie update process...'); const newCookies = await fetchCookiesFromWorker(); if (!newCookies || !Array.isArray(newCookies) || newCookies.length === 0) { console.log('No cookies were fetched from worker.'); showNotification('No cookies were fetched from worker.', 'warning'); return { success: false, message: 'No cookies fetched' }; } const currentUrl = window.location.href; const existingCookies = await getAllCookies(currentUrl); const existingCookieNames = existingCookies.map(c => c.name); let successCount = 0; let errorCount = 0; // Process each new cookie for (const cookie of newCookies) { try { // Remove existing cookie if it exists if (existingCookieNames.includes(cookie.name)) { await removeCookie(cookie.name, currentUrl); console.log(`Removed existing cookie: ${cookie.name}`); } // Add new cookie await saveCookie(cookie, currentUrl); successCount++; console.log(`Successfully saved cookie: ${cookie.name}`); } catch (error) { errorCount++; console.error(`Failed to process cookie ${cookie.name}:`, error); } } const message = `Updated ${successCount} cookies successfully${errorCount > 0 ? `, ${errorCount} failed` : ''}`; showNotification(message, errorCount > 0 ? 'error' : 'success'); // Auto reload if enabled if (config.autoReload && successCount > 0) { setTimeout(() => { window.location.reload(); }, 1000); } return { success: true, stats: { total: newCookies.length, success: successCount, error: errorCount } }; } catch (error) { console.error('Error updating cookies:', error); showNotification('Failed to update cookies: ' + error.message, 'error'); return { success: false, error: error.message }; } } // Show notification function showNotification(message, type = 'info') { if (!config.showNotifications) return; const existingNotification = document.getElementById('udemy-cookie-notification'); if (existingNotification) { existingNotification.remove(); } const notification = document.createElement('div'); notification.id = 'udemy-cookie-notification'; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 20px; border-radius: 5px; color: white; font-family: Arial, sans-serif; font-size: 14px; z-index: 10000; max-width: 300px; word-wrap: break-word; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); transition: opacity 0.3s ease; `; switch (type) { case 'success': notification.style.backgroundColor = '#4CAF50'; break; case 'error': notification.style.backgroundColor = '#f44336'; break; case 'warning': notification.style.backgroundColor = '#ff9800'; break; default: notification.style.backgroundColor = '#2196F3'; } notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.style.opacity = '0'; setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 300); } }, 3000); } // Create settings panel function createSettingsPanel() { const panel = document.createElement('div'); panel.id = 'udemy-cookie-settings-panel'; panel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); z-index: 10001; font-family: Arial, sans-serif; min-width: 400px; `; panel.innerHTML = ` <h2 style="margin-top: 0; color: #333;">Udemy Cookie Updater Settings</h2> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">Worker URL:</label> <input type="text" id="worker-url" value="${config.workerUrl}" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> </div> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">License Key:</label> <input type="text" id="license-key" value="${config.licenseKey}" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> </div> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">Auto Update Interval (minutes):</label> <input type="number" id="auto-update-interval" value="${config.autoUpdateInterval / 60000}" min="1" max="60" style="width: 100px; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> </div> <div style="margin-bottom: 15px;"> <label style="display: flex; align-items: center; cursor: pointer;"> <input type="checkbox" id="auto-update-enabled" ${config.autoUpdateEnabled ? 'checked' : ''} style="margin-right: 8px;"> Enable Auto Update </label> </div> <div style="margin-bottom: 15px;"> <label style="display: flex; align-items: center; cursor: pointer;"> <input type="checkbox" id="show-notifications" ${config.showNotifications ? 'checked' : ''} style="margin-right: 8px;"> Show Notifications </label> </div> <div style="margin-bottom: 15px;"> <label style="display: flex; align-items: center; cursor: pointer;"> <input type="checkbox" id="auto-reload" ${config.autoReload ? 'checked' : ''} style="margin-right: 8px;"> Auto Reload Page After Update </label> </div> <div style="margin-bottom: 15px;"> <label style="display: flex; align-items: center; cursor: pointer;"> <input type="checkbox" id="show-ui-buttons" ${config.showUiButtons ? 'checked' : ''} style="margin-right: 8px;"> Show Floating Buttons (Fetch Cookies, Settings) </label> </div> <div style="display: flex; gap: 10px; justify-content: flex-end;"> <button id="save-settings" style="padding: 10px 20px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">Save Settings</button> <button id="cancel-settings" style="padding: 10px 20px; background: #9E9E9E; color: white; border: none; border-radius: 4px; cursor: pointer;">Cancel</button> </div> `; panel.querySelector('#save-settings').addEventListener('click', () => { config.workerUrl = panel.querySelector('#worker-url').value; config.licenseKey = panel.querySelector('#license-key').value; config.autoUpdateInterval = parseInt(panel.querySelector('#auto-update-interval').value) * 60000; config.autoUpdateEnabled = panel.querySelector('#auto-update-enabled').checked; config.showNotifications = panel.querySelector('#show-notifications').checked; config.autoReload = panel.querySelector('#auto-reload').checked; config.showUiButtons = panel.querySelector('#show-ui-buttons').checked; saveConfig(); panel.remove(); showNotification('Settings saved successfully!', 'success'); renderFloatingControls(); }); panel.querySelector('#cancel-settings').addEventListener('click', () => { panel.remove(); }); panel.addEventListener('click', (e) => { if (e.target === panel) { panel.remove(); } }); document.body.appendChild(panel); } // Create floating UI controls (Fetch Cookies, Settings) function renderFloatingControls() { const existing = document.getElementById('udemy-cookie-controls'); if (existing) { existing.remove(); } if (!config.showUiButtons) return; const container = document.createElement('div'); container.id = 'udemy-cookie-controls'; container.style.cssText = ` position: fixed; bottom: 20px; right: 20px; display: flex; flex-direction: column; gap: 10px; z-index: 10002; `; const btnStyle = ` padding: 10px 14px; background: #1f2937; color: white; border: none; border-radius: 6px; cursor: pointer; font-family: Arial, sans-serif; font-size: 13px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); `; const fetchBtn = document.createElement('button'); fetchBtn.textContent = 'Fetch Cookies'; fetchBtn.style.cssText = btnStyle; fetchBtn.addEventListener('click', async () => { await updateCookiesFromWorker(); }); const settingsBtn = document.createElement('button'); settingsBtn.textContent = 'Settings'; settingsBtn.style.cssText = btnStyle + 'background:#2563EB;'; settingsBtn.addEventListener('click', () => { createSettingsPanel(); }); container.appendChild(fetchBtn); container.appendChild(settingsBtn); document.body.appendChild(container); } // Auto-update functionality function startAutoUpdate() { const lastUpdate = GM_getValue('lastCookieUpdate', 0); const now = Date.now(); if (now - lastUpdate > config.autoUpdateInterval) { updateCookiesFromWorker(); GM_setValue('lastCookieUpdate', now); } setInterval(() => { updateCookiesFromWorker(); GM_setValue('lastCookieUpdate', Date.now()); }, config.autoUpdateInterval); } // Register menu commands function registerMenuCommands() { GM_registerMenuCommand('Update Cookies Now', async () => { await updateCookiesFromWorker(); }); GM_registerMenuCommand('Open Settings', () => { createSettingsPanel(); }); } // Initialize function initialize() { loadConfig(); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); return; } registerMenuCommands(); if (config.autoUpdateEnabled) { startAutoUpdate(); } // Render floating UI controls based on settings renderFloatingControls(); } initialize(); })();