Medium Member Bypass

Modern Medium GUI with multiple bypass services.

Tính đến 16-12-2024. Xem phiên bản mới nhất.

  1. // ==UserScript==
  2. // @name Medium Member Bypass
  3. // @author UniverseDev
  4. // @license GPL-3.0-or-later
  5. // @namespace http://tampermonkey.net/
  6. // @version 12.9
  7. // @description Modern Medium GUI with multiple bypass services.
  8. // @match *://*.medium.com/*
  9. // @match https://freedium.cfd/*
  10. // @match https://readmedium.com/*
  11. // @match https://md.vern.cc/*
  12. // @grant GM_registerMenuCommand
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // ==/UserScript==
  16.  
  17. (() => {
  18. 'use strict';
  19.  
  20. const config = {
  21. bypassUrls: {
  22. freedium: 'https://freedium.cfd',
  23. readmedium: 'https://readmedium.com',
  24. libmedium: 'https://md.vern.cc/',
  25. },
  26. currentBypass: GM_getValue('currentBypass', 'freedium'),
  27. memberOnlyDivSelector: 'p.bf.b.bg.z.bk',
  28. autoRedirectDelay: GM_getValue('redirectDelay', 5000),
  29. autoRedirectEnabled: GM_getValue('autoRedirect', true),
  30. darkModeEnabled: GM_getValue('darkModeEnabled', false),
  31. isBypassSession: GM_getValue('isBypassSession', false),
  32. };
  33.  
  34. const injectStyles = () => {
  35. const style = document.createElement('style');
  36. style.textContent = `
  37. .medium-settings {
  38. position: fixed;
  39. top: 50%;
  40. left: 50%;
  41. transform: translate(-50%, -50%);
  42. width: 360px;
  43. background-color: var(--background-color, white);
  44. box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
  45. border-radius: 16px;
  46. font-family: 'Arial', sans-serif;
  47. z-index: 10000;
  48. padding: 20px;
  49. display: none;
  50. color: var(--text-color, #333);
  51. cursor: grab;
  52. }
  53. .medium-settings.dark {
  54. --background-color: #333;
  55. --text-color: white;
  56. }
  57. .medium-settings-header {
  58. font-size: 22px;
  59. font-weight: bold;
  60. margin-bottom: 20px;
  61. text-align: center;
  62. }
  63. .medium-settings-toggle {
  64. margin: 15px 0;
  65. display: flex;
  66. justify-content: space-between;
  67. align-items: center;
  68. }
  69. .medium-settings-input {
  70. margin-left: 10px;
  71. width: 70px;
  72. padding: 6px;
  73. text-align: center;
  74. border: 1px solid #ccc;
  75. border-radius: 4px;
  76. }
  77. .medium-settings-button {
  78. background-color: var(--button-bg-color, #1a8917);
  79. color: var(--button-text-color, white);
  80. border: none;
  81. padding: 8px 14px;
  82. border-radius: 20px;
  83. cursor: pointer;
  84. font-weight: bold;
  85. transition: background-color 0.3s;
  86. }
  87. .medium-settings-button:hover {
  88. background-color: #155c11;
  89. }
  90. .medium-notification {
  91. position: fixed;
  92. bottom: 20px;
  93. right: 20px;
  94. background-color: #1a8917;
  95. color: white;
  96. padding: 15px;
  97. border-radius: 20px;
  98. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  99. font-family: 'Arial', sans-serif;
  100. z-index: 10000;
  101. opacity: 0;
  102. transform: translateY(20px);
  103. transition: all 0.3s ease;
  104. }
  105. .medium-notification.show {
  106. opacity: 1;
  107. transform: translateY(0);
  108. }
  109. `;
  110. document.head.appendChild(style);
  111. };
  112.  
  113. const stealthNotification = (message) => {
  114. const notification = document.createElement('div');
  115. notification.className = 'medium-notification';
  116. notification.textContent = message;
  117. document.body.appendChild(notification);
  118.  
  119. setTimeout(() => notification.classList.add('show'), 50);
  120. setTimeout(() => {
  121. notification.classList.remove('show');
  122. setTimeout(() => notification.remove(), 300);
  123. }, 3000);
  124. };
  125.  
  126. const showMediumSettings = () => {
  127. let existingPanel = document.querySelector('.medium-settings');
  128. if (existingPanel) {
  129. existingPanel.style.display = 'block';
  130. return;
  131. }
  132.  
  133. const settingsContainer = document.createElement('div');
  134. settingsContainer.className = `medium-settings ${config.darkModeEnabled ? 'dark' : ''}`;
  135.  
  136. settingsContainer.innerHTML = `
  137. <div class="medium-settings-header">Medium Settings</div>
  138. <div class="medium-settings-toggle">
  139. <span>Auto-Redirect</span>
  140. <button class="medium-settings-button" id="toggleRedirect">${config.autoRedirectEnabled ? 'ON' : 'OFF'}</button>
  141. </div>
  142. <div class="medium-settings-toggle">
  143. <span>Redirect Delay (ms)</span>
  144. <input type="number" class="medium-settings-input" id="redirectDelay" value="${config.autoRedirectDelay}" />
  145. </div>
  146. <div class="medium-settings-toggle">
  147. <span>Dark Mode</span>
  148. <button class="medium-settings-button" id="toggleDarkMode">${config.darkModeEnabled ? 'ON' : 'OFF'}</button>
  149. </div>
  150. <div class="medium-settings-toggle">
  151. <span>Bypass Service</span>
  152. <select id="bypassSelector" class="medium-settings-input">
  153. ${Object.keys(config.bypassUrls).map((key) => `
  154. <option value="${key}" ${config.currentBypass === key ? 'selected' : ''}>${key}</option>
  155. `).join('')}
  156. </select>
  157. </div>
  158. <div class="medium-settings-toggle">
  159. <button class="medium-settings-button" id="bypassNow">Bypass Now</button>
  160. </div>
  161. <div class="medium-settings-toggle">
  162. <button class="medium-settings-button" id="resetDefaults">Reset to Default</button>
  163. </div>
  164. <div class="medium-settings-toggle">
  165. <button class="medium-settings-button" id="saveSettings">Save</button>
  166. <button class="medium-settings-button" id="closeSettings">Close</button>
  167. </div>
  168. `;
  169.  
  170. document.body.appendChild(settingsContainer);
  171.  
  172. let isDragging = false;
  173. let startX, startY;
  174.  
  175. settingsContainer.addEventListener('mousedown', (e) => {
  176. isDragging = true;
  177. startX = e.clientX - settingsContainer.offsetLeft;
  178. startY = e.clientY - settingsContainer.offsetTop;
  179. settingsContainer.style.cursor = 'grabbing';
  180. });
  181.  
  182. document.addEventListener('mousemove', (e) => {
  183. if (!isDragging) return;
  184. settingsContainer.style.left = `${e.clientX - startX}px`;
  185. settingsContainer.style.top = `${e.clientY - startY}px`;
  186. });
  187.  
  188. document.addEventListener('mouseup', () => {
  189. isDragging = false;
  190. settingsContainer.style.cursor = 'grab';
  191. });
  192.  
  193. settingsContainer.querySelector('#toggleRedirect').addEventListener('click', () => {
  194. config.autoRedirectEnabled = !config.autoRedirectEnabled;
  195. GM_setValue('autoRedirect', config.autoRedirectEnabled);
  196. settingsContainer.querySelector('#toggleRedirect').textContent = config.autoRedirectEnabled ? 'ON' : 'OFF';
  197. stealthNotification('Auto-Redirect toggled');
  198. });
  199.  
  200. settingsContainer.querySelector('#toggleDarkMode').addEventListener('click', () => {
  201. config.darkModeEnabled = !config.darkModeEnabled;
  202. GM_setValue('darkModeEnabled', config.darkModeEnabled);
  203. settingsContainer.classList.toggle('dark', config.darkModeEnabled);
  204. settingsContainer.querySelector('#toggleDarkMode').textContent = config.darkModeEnabled ? 'ON' : 'OFF';
  205. stealthNotification('Dark Mode toggled');
  206. });
  207.  
  208. settingsContainer.querySelector('#bypassSelector').addEventListener('change', (e) => {
  209. config.currentBypass = e.target.value;
  210. GM_setValue('currentBypass', config.currentBypass);
  211. stealthNotification(`Bypass service set to ${config.currentBypass}`);
  212. });
  213.  
  214. settingsContainer.querySelector('#bypassNow').addEventListener('click', () => {
  215. window.location.href = config.bypassUrls[config.currentBypass] + window.location.pathname;
  216. });
  217.  
  218. settingsContainer.querySelector('#resetDefaults').addEventListener('click', () => {
  219. config.autoRedirectDelay = 5000;
  220. config.autoRedirectEnabled = true;
  221. config.darkModeEnabled = false;
  222. config.currentBypass = 'freedium';
  223.  
  224. GM_setValue('redirectDelay', config.autoRedirectDelay);
  225. GM_setValue('autoRedirect', config.autoRedirectEnabled);
  226. GM_setValue('darkModeEnabled', config.darkModeEnabled);
  227. GM_setValue('currentBypass', config.currentBypass);
  228.  
  229. settingsContainer.querySelector('#redirectDelay').value = config.autoRedirectDelay;
  230. settingsContainer.querySelector('#toggleRedirect').textContent = 'ON';
  231. settingsContainer.querySelector('#toggleDarkMode').textContent = 'OFF';
  232. settingsContainer.querySelector('#bypassSelector').value = 'freedium';
  233. settingsContainer.classList.remove('dark');
  234. stealthNotification('Settings reset to defaults');
  235. });
  236.  
  237. settingsContainer.querySelector('#saveSettings').addEventListener('click', () => {
  238. const newDelay = parseInt(settingsContainer.querySelector('#redirectDelay').value, 10);
  239. if (!isNaN(newDelay) && newDelay >= 0) {
  240. config.autoRedirectDelay = newDelay;
  241. GM_setValue('redirectDelay', newDelay);
  242. stealthNotification('Settings saved');
  243. }
  244. });
  245.  
  246. settingsContainer.querySelector('#closeSettings').addEventListener('click', () => {
  247. settingsContainer.style.display = 'none';
  248. });
  249.  
  250. settingsContainer.style.display = 'block';
  251. };
  252.  
  253. const performAutoRedirect = () => {
  254. // Prevent redirection loop
  255. if (config.isBypassSession) {
  256. GM_setValue('isBypassSession', false);
  257. return;
  258. }
  259.  
  260. // Redirect logic
  261. if (config.autoRedirectEnabled && document.querySelector(config.memberOnlyDivSelector)) {
  262. setTimeout(() => {
  263. window.location.href = config.bypassUrls[config.currentBypass] + window.location.pathname;
  264. }, config.autoRedirectDelay);
  265. stealthNotification('Redirecting to bypass service...');
  266. }
  267. };
  268.  
  269. const initializeScript = () => {
  270. injectStyles();
  271.  
  272. const isBypassPage = Object.values(config.bypassUrls).some((url) => window.location.href.startsWith(url));
  273.  
  274. if (isBypassPage) {
  275. GM_setValue('isBypassSession', true);
  276. } else if (window.location.href.startsWith('https://medium.com/')) {
  277. GM_registerMenuCommand('Open Medium Settings', showMediumSettings);
  278. performAutoRedirect();
  279. }
  280. };
  281.  
  282. initializeScript();
  283. })();