// ==UserScript== // @name Medium Member Bypass // @author UniverseDev // @license GPL-3.0-or-later // @namespace http://tampermonkey.net/ // @version 13.5 // @description Modern Medium GUI with multiple bypass services and fallback with availability checks. // @match *://*.medium.com/* // @match https://freedium.cfd/* // @match https://readmedium.com/* // @match https://md.vern.cc/* // @match https://archive.is/* // @match https://archive.li/* // @match https://archive.vn/* // @match https://archive.ph/* // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @connect freedium.cfd // @connect readmedium.com // @connect md.vern.cc // @connect archive.is // @connect archive.li // @connect archive.vn // @connect archive.ph // @downloadURL none // ==/UserScript== (() => { 'use strict'; const config = { bypassUrls: { freedium: 'https://freedium.cfd', readmedium: 'https://readmedium.com', libmedium: 'https://md.vern.cc/', archive: 'https://archive.is/newest/', archiveLi: 'https://archive.li/newest/', archiveVn: 'https://archive.vn/newest/', archivePh: 'https://archive.ph/newest/', }, currentBypassIndex: GM_getValue('currentBypassIndex', 0), memberOnlyDivSelector: 'p.bf.b.bg.z.bk', autoRedirectDelay: GM_getValue('redirectDelay', 5000), autoRedirectEnabled: GM_getValue('autoRedirect', true), darkModeEnabled: GM_getValue('darkModeEnabled', false), isBypassSession: GM_getValue('isBypassSession', false), }; const bypassServiceKeys = Object.keys(config.bypassUrls); const injectStyles = () => { const style = document.createElement('style'); style.textContent = ` .medium-settings { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 360px; background-color: var(--background-color, white); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); border-radius: 16px; font-family: 'Arial', sans-serif; z-index: 10000; padding: 20px; display: none; color: var(--text-color, #333); cursor: grab; } .medium-settings.dark { --background-color: #333; --text-color: white; } .medium-settings-header { font-size: 22px; font-weight: bold; margin-bottom: 20px; text-align: center; } .medium-settings-toggle { margin: 15px 0; display: flex; justify-content: space-between; align-items: center; } .medium-settings-toggle > span { flex-grow: 1; } .medium-settings-input { margin-left: 10px; padding: 8px 10px; border: 1px solid #ccc; border-radius: 8px; box-sizing: border-box; } .medium-settings-input#redirectDelay { width: 70px; } .medium-settings-input#bypassSelector { width: 120px; appearance: auto; -webkit-appearance: auto; -moz-appearance: auto; background-repeat: no-repeat; background-position: right 10px center; } .medium-settings.dark .medium-settings-input#bypassSelector { border-color: #666; } .medium-settings-button { background-color: var(--button-bg-color, #1a8917); color: var(--button-text-color, white); border: none; padding: 8px 14px; border-radius: 20px; cursor: pointer; font-weight: bold; transition: background-color 0.3s; } .medium-settings-button:hover { background-color: #155c11; } .medium-notification { position: fixed; bottom: 20px; right: 20px; background-color: #1a8917; color: white; padding: 15px; border-radius: 20px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); font-family: 'Arial', sans-serif; z-index: 10000; opacity: 0; transform: translateY(20px); transition: all 0.3s ease; } .medium-notification.show { opacity: 1; transform: translateY(0); } .medium-settings-input:focus { outline: none; border-color: #1a8917; box-shadow: 0 0 5px rgba(26, 137, 23, 0.3); } `; document.head.appendChild(style); }; const stealthNotification = (message) => { const notification = document.createElement('div'); notification.className = 'medium-notification'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => notification.classList.add('show'), 50); setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); }, 3000); }; const getCurrentBypassService = () => { return bypassServiceKeys[config.currentBypassIndex % bypassServiceKeys.length]; }; const switchToNextBypassService = () => { config.currentBypassIndex++; GM_setValue('currentBypassIndex', config.currentBypassIndex); stealthNotification(`Trying next bypass service: ${getCurrentBypassService()}`); }; const isServiceAvailable = async (url) => { try { const response = await fetch(url, { method: 'HEAD', mode: 'no-cors' }); return response.ok || response.type === 'opaque'; } catch (error) { console.error(`Service unavailable: ${url}`, error); return false; } }; const autoBypass = async (articleUrl, bypassKey, attemptNumber = 1) => { const bypassUrlValue = config.bypassUrls[bypassKey]; const testUrl = bypassUrlValue.endsWith('/') ? bypassUrlValue : bypassUrlValue + '/'; const isAvailable = await isServiceAvailable(testUrl); if (!isAvailable) { stealthNotification(`Service unavailable: ${bypassKey}`); switchToNextBypassService(); const nextBypassService = getCurrentBypassService(); autoBypass(articleUrl, nextBypassService, attemptNumber + 1); return; } try { const bypassUrl = bypassUrlValue.endsWith('/') ? bypassUrlValue + articleUrl : bypassUrlValue + window.location.pathname; window.location.href = bypassUrl; setTimeout(() => { if (window.location.hostname.includes('medium.com') && document.querySelector(config.memberOnlyDivSelector)) { stealthNotification(`Bypass with ${bypassKey} failed.`); switchToNextBypassService(); const nextBypassService = getCurrentBypassService(); autoBypass(articleUrl, nextBypassService, attemptNumber + 1); } else { console.log(`Bypass with ${bypassKey} likely successful.`); } }, 2000); } catch (error) { console.error(`Error attempting bypass with ${bypassKey}`, error); stealthNotification(`Bypass with ${bypassKey} failed due to an error.`); switchToNextBypassService(); const nextBypassService = getCurrentBypassService(); autoBypass(articleUrl, nextBypassService, attemptNumber + 1); } }; const showMediumSettings = () => { let existingPanel = document.querySelector('.medium-settings'); if (existingPanel) { existingPanel.style.display = 'block'; return; } const settingsContainer = document.createElement('div'); settingsContainer.className = `medium-settings ${config.darkModeEnabled ? 'dark' : ''}`; settingsContainer.innerHTML = `