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

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

目前为 2023-08-01 提交的版本。查看 最新版本

  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.2.1
  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 Code Interpreter": "gpt-4-code-interpreter",
  29. "GPT-4 Web Browsing": "gpt-4-browsing",
  30. "GPT-4 Plugins": "gpt-4-plugins",
  31. "GPT-3.5 (Mobile)": "text-davinci-002-render-sha-mobile",
  32. "GPT-4 (Mobile)": "gpt-4-mobile",
  33. };
  34.  
  35. const CONVERSATION_API_URL =
  36. "https://chat.openai.com/backend-api/conversation";
  37. const MODELS_API_URL =
  38. "https://chat.openai.com/backend-api/models?history_and_training_disabled=false";
  39.  
  40. const ARKOSE_TOKEN_URLS = [
  41. "https://ai.fakeopen.com/api/arkose/token",
  42. "https://arkose-token.tms.im"
  43. ];
  44.  
  45. async function getArkoseToken() {
  46. const urls = [...ARKOSE_TOKEN_URLS];
  47. while (urls.length > 0) {
  48. const randomIndex = Math.floor(Math.random() * urls.length);
  49. const url = urls.splice(randomIndex, 1)[0];
  50.  
  51. try {
  52. const response = await fetch(url);
  53. if (response.ok) {
  54. const data = await response.json();
  55. if (data && "token" in data) {
  56. return data.token;
  57. } else {
  58. throw new Error("Token not present in response.");
  59. }
  60. }
  61. } catch (error) {
  62. console.error(
  63. `Error encountered while fetching arkose_token from ${url}: `,
  64. error
  65. );
  66. }
  67. }
  68.  
  69. console.error("Failed to fetch arkose_token from all URLs.");
  70. return null;
  71. }
  72.  
  73. async function handleModelsApiUrlResponse(fetchPromise) {
  74. const handleModel = (model, condition = true) => {
  75. const selectOption = document.querySelector(
  76. `#modelSelect option[value="${model}"]`
  77. );
  78.  
  79. if (selectOption && !condition) {
  80. selectOption.disabled = true;
  81. if (selectedModel === model) {
  82. selectedModel = "GPT-3.5";
  83. localStorage.setItem("selectedModel", selectedModel);
  84. document.querySelector("#modelSelect").value = selectedModel;
  85. }
  86. }
  87. };
  88.  
  89. return fetchPromise.then(async (response) => {
  90. if (response.ok) {
  91. const data = await response.clone().json();
  92. const accessibleModels = data.models.map((model) => model.slug);
  93.  
  94. handleModel("GPT-3.5 (Mobile)", true);
  95. handleModel("GPT-4 (Mobile)", accessibleModels.includes("gpt-4"));
  96.  
  97. for (const [model, mappedSlug] of Object.entries(modelMapping)) {
  98. if (!model.endsWith("(Mobile)")) {
  99. handleModel(model, accessibleModels.includes(mappedSlug));
  100. }
  101. }
  102. }
  103.  
  104. return response;
  105. });
  106. }
  107.  
  108. window.fetch = new Proxy(window.fetch, {
  109. apply: async function (target, that, args) {
  110. let resource = args[0];
  111. let options = args[1];
  112.  
  113. if (useModelSwitcher && resource === CONVERSATION_API_URL) {
  114. const requestBody = JSON.parse(options.body);
  115. requestBody.model = modelMapping[selectedModel];
  116.  
  117. if (
  118. requestBody.model.startsWith("gpt-4") &&
  119. requestBody.arkose_token === null
  120. ) {
  121. requestBody.arkose_token = await getArkoseToken();
  122. } else if (
  123. requestBody.model.startsWith("text-davinci-002-render-sha") &&
  124. requestBody.arkose_token !== null
  125. ) {
  126. requestBody.arkose_token = null;
  127. }
  128.  
  129. options = { ...options, body: JSON.stringify(requestBody) };
  130. args[0] = resource;
  131. args[1] = options;
  132. }
  133.  
  134. const fetchPromise = Reflect.apply(target, that, args);
  135.  
  136. if (resource.includes(MODELS_API_URL)) {
  137. return handleModelsApiUrlResponse(fetchPromise);
  138. }
  139.  
  140. return fetchPromise;
  141. },
  142. });
  143.  
  144. function createSwitchElement() {
  145. const switchLabel = document.createElement("label");
  146. switchLabel.className = "switch";
  147. switchLabel.title = "Check to enable the model switcher";
  148.  
  149. const switchCheckbox = document.createElement("input");
  150. switchCheckbox.type = "checkbox";
  151. switchCheckbox.id = "useModelSwitcherCheckbox";
  152. switchCheckbox.checked = useModelSwitcher;
  153. switchCheckbox.addEventListener("change", (event) => {
  154. useModelSwitcher = event.target.checked;
  155. localStorage.setItem("useModelSwitcher", useModelSwitcher);
  156. });
  157.  
  158. const switchSlider = document.createElement("span");
  159. switchSlider.className = "slider round";
  160.  
  161. switchLabel.appendChild(switchCheckbox);
  162. switchLabel.appendChild(switchSlider);
  163.  
  164. return switchLabel;
  165. }
  166.  
  167. function createModelSelectElement() {
  168. const selectContainer = document.createElement("div");
  169. selectContainer.style.position = "relative";
  170.  
  171. const select = document.createElement("select");
  172. select.id = "modelSelect";
  173. select.addEventListener("change", (event) => {
  174. selectedModel = event.target.value;
  175. localStorage.setItem("selectedModel", selectedModel);
  176. });
  177.  
  178. for (const model in modelMapping) {
  179. const option = document.createElement("option");
  180. option.text = model;
  181. option.value = model;
  182. select.appendChild(option);
  183. }
  184.  
  185. select.value = selectedModel;
  186.  
  187. const selectArrow = document.createElement("div");
  188. selectArrow.style.cssText = `
  189. position: absolute;
  190. top: 50%;
  191. right: 8px;
  192. transform: translateY(-50%);
  193. width: 12px;
  194. height: 12px;
  195. 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');
  196. background-repeat: no-repeat;
  197. background-position: center;
  198. pointer-events: none;
  199. `;
  200.  
  201. selectContainer.appendChild(select);
  202. selectContainer.appendChild(selectArrow);
  203.  
  204. return selectContainer;
  205. }
  206.  
  207. function createModelSwitcherContainer() {
  208. const container = document.createElement("div");
  209. container.style.cssText = `
  210. position: fixed;
  211. top: 10px;
  212. right: 18px;
  213. background-color: rgb(32, 33, 35);
  214. border: 1px solid #ddd;
  215. padding: 10px;
  216. border-radius: 5px;
  217. z-index: 9999;
  218. transition: 0.3s;
  219. box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  220. display: flex;
  221. align-items: center;
  222. opacity: 0.5;
  223. `;
  224.  
  225. container.addEventListener("mouseenter", () => {
  226. container.style.opacity = "1";
  227. });
  228.  
  229. container.addEventListener("mouseleave", () => {
  230. container.style.opacity = "0.5";
  231. });
  232.  
  233. const switchElement = createSwitchElement();
  234. const modelSelectElement = createModelSelectElement();
  235.  
  236. container.appendChild(switchElement);
  237. container.appendChild(modelSelectElement);
  238.  
  239. return container;
  240. }
  241.  
  242. const container = createModelSwitcherContainer();
  243. document.body.appendChild(container);
  244.  
  245. const style = document.createElement("style");
  246. style.textContent = `
  247. .switch {
  248. position: relative;
  249. display: inline-block;
  250. width: 40px;
  251. height: 20px;
  252. margin-right: 10px;
  253. }
  254.  
  255. .switch input {
  256. opacity: 0;
  257. width: 0;
  258. height: 0;
  259. }
  260.  
  261. .slider {
  262. position: absolute;
  263. cursor: pointer;
  264. top: 0;
  265. left: 0;
  266. right: 0;
  267. bottom: 0;
  268. background-color: #ccc;
  269. transition: .5s;
  270. border-radius: 35px;
  271. }
  272.  
  273. .slider:before {
  274. position: absolute;
  275. content: "";
  276. height: 16px;
  277. width: 16px;
  278. left: 2px;
  279. bottom: 2px;
  280. background-color: white;
  281. transition: .5s;
  282. border-radius: 50%;
  283. }
  284.  
  285. input:checked + .slider {
  286. background-color: #2196F3;
  287. }
  288.  
  289. input:focus + .slider {
  290. box-shadow: 0 0 1px #2196F3;
  291. }
  292.  
  293. input:checked + .slider:before {
  294. transform: translateX(20px);
  295. }
  296.  
  297. .slider.round {
  298. border-radius: 35px;
  299. }
  300.  
  301. .slider.round:before {
  302. border-radius: 50%;
  303. }
  304.  
  305. select {
  306. color: #000000;
  307. background-color: #ffffff;
  308. padding: 5px;
  309. border: none;
  310. border-radius: 5px;
  311. appearance: none;
  312. -webkit-appearance: none;
  313. -moz-appearance: none;
  314. 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');
  315. background-repeat: no-repeat;
  316. background-position: right center;
  317. padding-right: 20px;
  318. text-overflow: ellipsis;
  319. white-space: nowrap;
  320. overflow: hidden;
  321. }
  322. `;
  323.  
  324. document.head.appendChild(style);
  325. })();