ChatGPT 模型切换器(支持 GPT-4 Mobile 及所有可用模型)

通过启用 GPT-4 Mobile 模型,解除 ChatGPT 网页端对 GPT-4 模型使用次数的限制。同时还可启用其他模型进行切换,提供更多的灵活性。一般来说,该脚本不会与其他流行的 ChatGPT 脚本产生冲突。

当前为 2023-07-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name ChatGPT Model Switcher (Supports GPT-4 Mobile and All Available Models)
  3. // @name:zh-CN ChatGPT 模型切换器(支持 GPT-4 Mobile 及所有可用模型)
  4. // @name:zh-TW ChatGPT 模型切换器(支持 GPT-4 Mobile 及所有可用模型)
  5. // @namespace https://github.com/hydrotho/ChatGPT_Model_Switcher
  6. // @copyright 2023, Hydrotho (https://github.com/hydrotho)
  7. // @version 1.1.2
  8. // @description Override GPT-4 usage limits in the ChatGPT web interface by enabling the GPT-4 Mobile model. Additional models can also be enabled for switching, providing more flexibility. Generally, this script does not conflict with other popular ChatGPT scripts.
  9. // @description:zh-CN 通过启用 GPT-4 Mobile 模型,解除 ChatGPT 网页端对 GPT-4 模型使用次数的限制。同时还可启用其他模型进行切换,提供更多的灵活性。一般来说,该脚本不会与其他流行的 ChatGPT 脚本产生冲突。
  10. // @description:zh-TW 通过启用 GPT-4 Mobile 模型,解除 ChatGPT 网页端对 GPT-4 模型使用次数的限制。同时还可启用其他模型进行切换,提供更多的灵活性。一般来说,该脚本不会与其他流行的 ChatGPT 脚本产生冲突。
  11. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAH1UExURUxpcXeqn3WqnHWonHSpnHWonHWpnG22knWpnHWpnHSmm3apm3SpnHWonHWpnHSonHWpnHWpm3apnXWpnHWpm3WpnP///8fc19fm43mrn67Nxf7///r8+6HFvNPk4JS8ssXb1XirnsDY0sPa1Pj7+qbHv5i/tXeqnvz9/X6uoo65roq2q+Tu7P3+/qrKwqDEu9bm4vP39qfIwPv9/NXl4ezz8Xqsn+nx73msn5/Dusnd2N7q59zp5pC6r4CwpKLFvIOxpszf2oSypsTa1fn7+/P49t/r6JrAt8LZ1L/X0d3q53aqnczf287h3Ie0qc7g3Pr8/LTQybDOxpvBuObv7c/h3PX5+Ory8ODr6OPt65G7sLnTzYWzp/n7+oi1qv7+/tTk4J7Cucve2Z3Cub7X0H+vo8LZ053CuKnJwff6+tnn4/3+/fD29XytoYWzqJe+tJa+tHapnHeqnaHEu8vf2oGxpazLw3utoMre2ZW9s7XRyu/19H2uou/186XHv6jJwNDi3sjd2OLt6u308ufw7tfm4rjTzK3MxOjw7tvp5dHi3sjd15m/tvL39q/Nxvb5+OPu64y3rIOyptnn5LbSy+Ds6eHs6tbl4cHZ0/v8/H6vo4GwpZ7Dus/h3fb6+ZK7sfT49/f6+aLFvavLw6zLxM3g28bc1pQLf2QAAAAVdFJOUwAtv5bz1PQH/dUuj5WQ/CyYwJHykqKEGP8AAAAJcEhZcwAAAHYAAAB2AU57JggAAAIcSURBVDjLhdNle9swEABgFdK0Kw7uHMfp6iTeAksaThpoUmZuV1x5zMxbx8wM7Xj7nZNjx/L2rNl9kXR6H51snwmhsWFTWQn8FSWGygKihLGmFP4ZpUXG7P5GWDcKZVEDeaKC1mfnHxUvoSV19YQOVFWTLdpiUfJ2POx/jOEzAy4tWU7KctPG95FpOjT0IA2PT80aSHEOpKQ5mSUxIA7bD2OzI5vdTNTt1QXBDvAxMT/7qkE+h8PdyoYC+DX0YgYyX4W+FwBunqYOhpp0YAl/1eN22Or5DPD8Jd6sBTiOZgYa8SfUysAMH+wWW/AK3ndbUWRADKUVMGIex1YrRGcs3uvYxcCzKVCAJTb66FZsFGDXTgHPMjD2WgWcFeCkHd/uoOshj0MD16QoLOI2+Q406ifpPXh4gisaOIXD4JiZXUoqwARx/Ab80zB7TJMzmK17nr4BK2eCOnocJGMMBBH9tO6FqYhveUJSwZsxBrpRDDltl6G3G7/8+K6AtLOZARu65hYwcLfL8s4l30EGCTzGwH6MA3Tew9u0Tp1HBmYOT+u+xZ62nl4AB91uGRQ+ZWAZ53HQqgMwgn3n6BC90+bl0nLJB51qH+QaphUD3EWuHVNuuhiQwlrPaS3n6zhEW+2G3I3TkSE3A5XalG860o/j/sSkcGAf62tS8MdvFfe3Oyf2tugyhBRB3qC/XuF/ADFWVOUHhFSXG4rXA78BYbiLJDUXqsMAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=
  12. // @grant none
  13. // @author Hydrotho
  14. // @match http*://chat.openai.com/*
  15. // @supportURL https://github.com/hydrotho/ChatGPT_Model_Switcher/issues
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19. (function () {
  20. "use strict";
  21.  
  22. let useModelSwitcher = localStorage.getItem("useModelSwitcher") !== "false";
  23. let selectedModel = localStorage.getItem("selectedModel") || "GPT-4 (Mobile)";
  24.  
  25. const modelMapping = {
  26. "GPT-3.5": "text-davinci-002-render-sha",
  27. "GPT-4": "gpt-4",
  28. "GPT-4 Web Browsing": "gpt-4-browsing",
  29. "GPT-4 Plugins": "gpt-4-plugins",
  30. "GPT-3.5 (Mobile)": "text-davinci-002-render-sha-mobile",
  31. "GPT-4 (Mobile)": "gpt-4-mobile",
  32. };
  33.  
  34. const CONVERSATION_API_URL =
  35. "https://chat.openai.com/backend-api/conversation";
  36. const MODELS_API_URL =
  37. "https://chat.openai.com/backend-api/models?history_and_training_disabled=false";
  38.  
  39. const ARKOSE_TOKEN_URLS = [
  40. "https://ai.fakeopen.com/api/arkose/token",
  41. "https://arkose-token.tms.im"
  42. ];
  43.  
  44. async function getArkoseToken() {
  45. const urls = [...ARKOSE_TOKEN_URLS];
  46. while (urls.length > 0) {
  47. const randomIndex = Math.floor(Math.random() * urls.length);
  48. const url = urls.splice(randomIndex, 1)[0];
  49.  
  50. try {
  51. const response = await fetch(url);
  52. if (response.ok) {
  53. const data = await response.json();
  54. if (data && "token" in data) {
  55. return data.token;
  56. } else {
  57. throw new Error("Token not present in response.");
  58. }
  59. }
  60. } catch (error) {
  61. console.error(
  62. `Error encountered while fetching arkose_token from ${url}: `,
  63. error
  64. );
  65. }
  66. }
  67.  
  68. console.error("Failed to fetch arkose_token from all URLs.");
  69. return null;
  70. }
  71.  
  72. async function handleModelsApiUrlResponse(fetchPromise) {
  73. return fetchPromise.then((response) => {
  74. if (response.ok) {
  75. response
  76. .clone()
  77. .json()
  78. .then((data) => {
  79. const accessibleModels = data.models.map((model) => model.slug);
  80. Object.keys(modelMapping).forEach((model) => {
  81. const mappedSlug = modelMapping[model];
  82. const selectOption = document.querySelector(
  83. `#modelSelect option[value="${model}"]`
  84. );
  85. if (selectOption && !accessibleModels.includes(mappedSlug)) {
  86. selectOption.disabled = true;
  87. if (selectedModel === model) {
  88. selectedModel = "GPT-3.5";
  89. localStorage.setItem("selectedModel", selectedModel);
  90. document.querySelector("#modelSelect").value = selectedModel;
  91. }
  92. }
  93. });
  94. });
  95. }
  96. return response;
  97. });
  98. }
  99.  
  100. window.fetch = new Proxy(window.fetch, {
  101. apply: async function (target, that, args) {
  102. let resource = args[0];
  103. let options = args[1];
  104.  
  105. if (useModelSwitcher && resource === CONVERSATION_API_URL) {
  106. const requestBody = JSON.parse(options.body);
  107. requestBody.model = modelMapping[selectedModel];
  108.  
  109. if (
  110. requestBody.model.startsWith("gpt-4") &&
  111. requestBody.arkose_token === null
  112. ) {
  113. requestBody.arkose_token = await getArkoseToken();
  114. } else if (
  115. requestBody.model.startsWith("text-davinci-002-render-sha") &&
  116. requestBody.arkose_token !== null
  117. ) {
  118. requestBody.arkose_token = null;
  119. }
  120.  
  121. options = { ...options, body: JSON.stringify(requestBody) };
  122. args[0] = resource;
  123. args[1] = options;
  124. }
  125.  
  126. const fetchPromise = Reflect.apply(target, that, args);
  127.  
  128. if (resource.includes(MODELS_API_URL)) {
  129. return handleModelsApiUrlResponse(fetchPromise);
  130. }
  131.  
  132. return fetchPromise;
  133. },
  134. });
  135.  
  136. function createSwitchElement() {
  137. const switchLabel = document.createElement("label");
  138. switchLabel.className = "switch";
  139. switchLabel.title = "Check to enable the model switcher";
  140.  
  141. const switchCheckbox = document.createElement("input");
  142. switchCheckbox.type = "checkbox";
  143. switchCheckbox.id = "useModelSwitcherCheckbox";
  144. switchCheckbox.checked = useModelSwitcher;
  145. switchCheckbox.addEventListener("change", (event) => {
  146. useModelSwitcher = event.target.checked;
  147. localStorage.setItem("useModelSwitcher", useModelSwitcher);
  148. });
  149.  
  150. const switchSlider = document.createElement("span");
  151. switchSlider.className = "slider round";
  152.  
  153. switchLabel.appendChild(switchCheckbox);
  154. switchLabel.appendChild(switchSlider);
  155.  
  156. return switchLabel;
  157. }
  158.  
  159. function createModelSelectElement() {
  160. const selectContainer = document.createElement("div");
  161. selectContainer.style.position = "relative";
  162.  
  163. const select = document.createElement("select");
  164. select.id = "modelSelect";
  165. select.addEventListener("change", (event) => {
  166. selectedModel = event.target.value;
  167. localStorage.setItem("selectedModel", selectedModel);
  168. });
  169.  
  170. for (const model in modelMapping) {
  171. const option = document.createElement("option");
  172. option.text = model;
  173. option.value = model;
  174. select.appendChild(option);
  175. }
  176.  
  177. select.value = selectedModel;
  178.  
  179. const selectArrow = document.createElement("div");
  180. selectArrow.style.cssText = `
  181. position: absolute;
  182. top: 50%;
  183. right: 8px;
  184. transform: translateY(-50%);
  185. width: 12px;
  186. height: 12px;
  187. background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23333" width="18px" height="18px"%3E%3Cpath d="M7 10l5 5 5-5z"/%3E%3Cpath d="M0 0h24v24H0z" fill="none"/%3E%3C/svg%3E');
  188. background-repeat: no-repeat;
  189. background-position: center;
  190. pointer-events: none;
  191. `;
  192.  
  193. selectContainer.appendChild(select);
  194. selectContainer.appendChild(selectArrow);
  195.  
  196. return selectContainer;
  197. }
  198.  
  199. function createModelSwitcherContainer() {
  200. const container = document.createElement("div");
  201. container.style.cssText = `
  202. position: fixed;
  203. top: 10px;
  204. right: 18px;
  205. background-color: rgb(32, 33, 35);
  206. border: 1px solid #ddd;
  207. padding: 10px;
  208. border-radius: 5px;
  209. z-index: 9999;
  210. transition: 0.3s;
  211. box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  212. display: flex;
  213. align-items: center;
  214. opacity: 0.5;
  215. `;
  216.  
  217. container.addEventListener("mouseenter", () => {
  218. container.style.opacity = "1";
  219. });
  220.  
  221. container.addEventListener("mouseleave", () => {
  222. container.style.opacity = "0.5";
  223. });
  224.  
  225. const switchElement = createSwitchElement();
  226. const modelSelectElement = createModelSelectElement();
  227.  
  228. container.appendChild(switchElement);
  229. container.appendChild(modelSelectElement);
  230.  
  231. return container;
  232. }
  233.  
  234. const container = createModelSwitcherContainer();
  235. document.body.appendChild(container);
  236.  
  237. const style = document.createElement("style");
  238. style.textContent = `
  239. .switch {
  240. position: relative;
  241. display: inline-block;
  242. width: 40px;
  243. height: 20px;
  244. margin-right: 10px;
  245. }
  246.  
  247. .switch input {
  248. opacity: 0;
  249. width: 0;
  250. height: 0;
  251. }
  252.  
  253. .slider {
  254. position: absolute;
  255. cursor: pointer;
  256. top: 0;
  257. left: 0;
  258. right: 0;
  259. bottom: 0;
  260. background-color: #ccc;
  261. transition: .5s;
  262. border-radius: 35px;
  263. }
  264.  
  265. .slider:before {
  266. position: absolute;
  267. content: "";
  268. height: 16px;
  269. width: 16px;
  270. left: 2px;
  271. bottom: 2px;
  272. background-color: white;
  273. transition: .5s;
  274. border-radius: 50%;
  275. }
  276.  
  277. input:checked + .slider {
  278. background-color: #2196F3;
  279. }
  280.  
  281. input:focus + .slider {
  282. box-shadow: 0 0 1px #2196F3;
  283. }
  284.  
  285. input:checked + .slider:before {
  286. transform: translateX(20px);
  287. }
  288.  
  289. .slider.round {
  290. border-radius: 35px;
  291. }
  292.  
  293. .slider.round:before {
  294. border-radius: 50%;
  295. }
  296.  
  297. select {
  298. color: #000000;
  299. background-color: #ffffff;
  300. padding: 5px;
  301. border: none;
  302. border-radius: 5px;
  303. appearance: none;
  304. -webkit-appearance: none;
  305. -moz-appearance: none;
  306. background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23333" width="18px" height="18px"%3E%3Cpath d="M7 10l5 5 5-5z"/%3E%3Cpath d="M0 0h24v24H0z" fill="none"/%3E%3C/svg%3E');
  307. background-repeat: no-repeat;
  308. background-position: right center;
  309. padding-right: 20px;
  310. text-overflow: ellipsis;
  311. white-space: nowrap;
  312. overflow: hidden;
  313. }
  314. `;
  315.  
  316. document.head.appendChild(style);
  317. })();