Formify

Let AI solve your google forms

目前為 2025-03-18 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Formify
  3. // @version 4
  4. // @description Let AI solve your google forms
  5. // @author rohitaryal
  6. // @license MIT
  7. // @grant GM_addElement
  8. // @grant GM_addStyle
  9. // @namespace https://docs.google.com/
  10. // @match https://docs.google.com/forms/*
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=docs.google.com
  12. // ==/UserScript==
  13.  
  14. const css = `body.hidden {
  15. overflow: hidden;
  16. }
  17.  
  18. .dialog-container * {
  19. margin: 0;
  20. padding: 0;
  21. font-family: system-ui;
  22. box-sizing: border-box;
  23. }
  24.  
  25. .dialog-container {
  26. position: absolute;
  27. top: 0;
  28. left: 0;
  29. height: 100vh;
  30. width: 98vw;
  31. background-color: rgba(0, 0, 0, 0.836);
  32. z-index: 999;
  33. display: none;
  34. }
  35.  
  36. .dialog-container .dialog {
  37. z-index: 1000;
  38. position: relative;
  39. top: 50%;
  40. left: 50%;
  41. transform: translate(-50%, -50%);
  42. height: 80%;
  43. width: 25rem;
  44. background-color: white;
  45. border-radius: .5rem;
  46. overflow: hidden;
  47. display: flex;
  48. align-items: center;
  49. flex-direction: column;
  50. }
  51.  
  52. .dialog-container.active {
  53. display: block;
  54. }
  55.  
  56. .dialog-container .dialog .formify-header {
  57. display: flex;
  58. width: 100%;
  59. padding: .5rem 1rem .5rem .5rem;
  60. gap: 1rem;
  61. align-items: center;
  62. font-size: xx-large;
  63. font-family: system-ui;
  64. border-bottom: 4px solid rgba(0, 0, 255, 0.45);
  65. }
  66.  
  67. .dialog-container .dialog .formify-header .formify-text {
  68. flex: 1;
  69. }
  70.  
  71. .dialog-container .dialog .formify-header .formify-text b {
  72. font-weight: 600;
  73. font-size: 1.2em;
  74. background-clip: text;
  75. color: transparent;
  76. background-image: linear-gradient(to right, #159957, #155799, rgb(81, 81, 255));
  77. animation: animateheader 2s linear infinite;
  78. }
  79.  
  80. @keyframes animateheader {
  81.  
  82. 0%,
  83. 100% {
  84. filter: hue-rotate(0deg);
  85. }
  86.  
  87. 100% {
  88. filter: hue-rotate(360deg);
  89. }
  90. }
  91.  
  92. .dialog-container .dialog .formify-header .close {
  93. cursor: pointer;
  94. transition-duration: .2s;
  95. }
  96.  
  97. .dialog-container .dialog .formify-header .close:hover {
  98. opacity: .5;
  99. }
  100.  
  101. .dialog-container .dialog .formify-header .close {
  102. font-size: 2.5rem;
  103. }
  104.  
  105. .dialog-container .dialog .formify-header .close:active {
  106. transform: scale(1.1);
  107. }
  108.  
  109. .dialog-container .dialog .form-body {
  110. flex: 1;
  111. width: 100%;
  112. overflow-y: auto;
  113. }
  114.  
  115. .dialog-container .dialog .form-body ul {
  116. list-style-type: none;
  117. }
  118.  
  119. .dialog-container .dialog .form-body ul li {
  120. font-size: large;
  121. padding: 1rem;
  122. display: flex;
  123. align-items: center;
  124. border-bottom: 1px solid rgba(0, 0, 255, 0.192);
  125. }
  126.  
  127. .dialog-container .dialog .form-body ul li code {
  128. background-color: rgba(128, 128, 128, 0.084);
  129. padding: .2em;
  130. border-radius: 5px;
  131. color: rgb(21, 21, 21);
  132. }
  133.  
  134. .dialog-container .dialog .form-body ul label {
  135. flex: 1;
  136. font-size: 1.2rem;
  137. padding: .5rem 0;
  138. }
  139.  
  140. .dialog-container .dialog .form-body ul input,
  141. .dialog-container .dialog .form-body ul select {
  142. outline: none;
  143. border: 2px solid rgba(0, 0, 255, 0.192);
  144. font-size: 1.2rem;
  145. padding: .5rem;
  146. width: 60%;
  147. border-radius: .5rem;
  148. background-color: transparent;
  149. }
  150.  
  151. .dialog-container .dialog .form-footer {
  152. width: 100%;
  153. display: flex;
  154. padding: .1rem .5rem;
  155. gap: .5rem;
  156. align-items: center;
  157. border-top: 2px solid rgba(0, 0, 0, 0.107);
  158. }
  159.  
  160. .dialog-container .dialog .form-footer img {
  161. cursor: pointer;
  162. text-decoration: none;
  163. }
  164.  
  165. .dialog-container .dialog .form-footer a {
  166. text-decoration: none;
  167. color: #155799;
  168. }
  169.  
  170. .ai-container {
  171. overflow: hidden;
  172. display: flex;
  173. flex-direction: column;
  174. height: fit-content;
  175. margin: .5rem;
  176. border-radius: 1rem;
  177. box-shadow: rgba(0, 0, 0, 0.15) 0px 2px 8px;
  178. }
  179.  
  180. .ai-container.inactive {
  181. display: none;
  182. }
  183.  
  184. .ai-container .container-header {
  185. display: flex;
  186. padding: .5rem 1rem;
  187. align-items: center;
  188. background-color: rgba(0, 0, 255, 0.192);
  189. justify-content: space-between;
  190. border-bottom: 1px solid rgba(0, 0, 0, 0.201);
  191. }
  192.  
  193. .ai-container .container-header .buttons button {
  194. border: none;
  195. cursor: pointer;
  196. padding: .5rem;
  197. border-radius: .3rem;
  198. background-color: transparent;
  199. }
  200.  
  201. .ai-container .container-header .buttons button:hover {
  202. color: white;
  203. background-color: rgba(0, 0, 0, 0.322);
  204. }
  205.  
  206. .ai-container .container-body {
  207. padding: 1rem;
  208. color: rgb(20, 20, 34);
  209. }
  210.  
  211.  
  212. .aiChatDialog {
  213. position: fixed; /* Stay in place on the screen */
  214. top: 0; /* stick to the top */
  215. right: 0; /* stick to the right */
  216. transform: translate(0,0); /* Reset transform properties, crucial */
  217. background-color: #f9f9f9;
  218. border: 1px solid #ccc;
  219. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  220. width: 300px;
  221. height: 400px;
  222. display: none;
  223. flex-direction: column;
  224. border-radius: 5px;
  225. overflow: hidden;
  226. z-index: 1000;
  227. left: auto; /* Add this to ensure it doesn't try to be on the left */
  228. }
  229.  
  230.  
  231. .dialogHeader {
  232. background-color: #ddd;
  233. padding: 10px;
  234. cursor: move;
  235. display: flex;
  236. justify-content: space-between;
  237. align-items: center;
  238. }
  239.  
  240. .closeButton {
  241. cursor: pointer;
  242. font-size: 16px;
  243. }
  244.  
  245. .chatHistory {
  246. flex-grow: 1;
  247. padding: 10px;
  248. overflow-y: auto;
  249. }
  250.  
  251. .chatInputArea {
  252. padding: 10px;
  253. border-top: 1px solid #ccc;
  254. display: flex;
  255. outline: none;
  256. align-items: center;
  257. }
  258.  
  259. .messageInput {
  260. flex-grow: 1;
  261. padding: 8px;
  262. border: 1px solid #ccc;
  263. border-radius: 4px;
  264. margin-right: 5px;
  265. }
  266.  
  267. .sendButton {
  268. padding: 8px 12px;
  269. background-color: #4CAF50;
  270. color: white;
  271. border: none;
  272. border-radius: 4px;
  273. cursor: pointer;
  274. }
  275.  
  276. .message {
  277. margin-bottom: 5px;
  278. padding: 8px;
  279. border-radius: 5px;
  280. }
  281.  
  282. .userMessage {
  283. background-color: #DCF8C6;
  284. text-align: right;
  285. }
  286.  
  287. .aiMessage {
  288. background-color: #ECE5DD;
  289. text-align: left;
  290. }`;
  291.  
  292. const js = `const overlay = document.querySelector(".dialog-container");
  293. const dialog = document.querySelector(".dialog-container .dialog");
  294. const closeButton = document.querySelector(".dialog-container .dialog .formify-header .close");
  295. const apiField = document.querySelector('.dialog-container .dialog input#apikey');
  296. const modelSelect = document.querySelector('.dialog-container .dialog select#ai-model');
  297. const searchEngineSelect = document.querySelector('.dialog-container .dialog select#search-engine');
  298. const customPromptField = document.querySelector('.dialog-container .dialog input#custom-prompt');
  299.  
  300. const setItem = (key, value) => {
  301. const storage = localStorage.getItem('formify');
  302. const parsedStorage = JSON.parse(storage || '{}');
  303.  
  304. parsedStorage[key] = value;
  305.  
  306. localStorage.setItem('formify', JSON.stringify(parsedStorage));
  307. }
  308.  
  309. const getItem = (key) => {
  310. const storage = localStorage.getItem('formify');
  311. const parsedStorage = JSON.parse(storage || '{}');
  312. return parsedStorage[key];
  313. }
  314.  
  315. const apiKey = getItem('apiKey');
  316. const model = getItem('model');
  317. const searchEngine = getItem('searchURL');
  318. const customPrompt = getItem('customPrompt');
  319.  
  320. if (!model)
  321. setItem('model', "gemini-2.0-flash-lite");
  322.  
  323. if (!searchEngine)
  324. setItem('searchURL', "https://www.google.com/search?q=");
  325.  
  326. if (!customPrompt)
  327. setItem("customPrompt", "Your answer must include one of the options i provided here and please answer shortly and no markdown like response allowed, plain text and write the full answer, provide short description if possible");
  328.  
  329. // Re-assigning values to the fields
  330. apiField.value = getItem('apiKey');
  331. modelSelect.value = getItem('model');
  332. searchEngineSelect.value = getItem('searchURL');
  333. customPromptField.value = getItem('customPrompt');
  334.  
  335. overlay.addEventListener('click', (e) => {
  336. if (
  337. e.target === overlay ||
  338. e.target === closeButton
  339. ) {
  340. overlay.classList.toggle("active");
  341. }
  342. });
  343.  
  344. apiField.addEventListener('input', (e) => {
  345. const apiKey = e.target.value;
  346. setItem('apiKey', apiKey);
  347. });
  348.  
  349. modelSelect.addEventListener('change', (e) => {
  350. const selectedModel = e.target.value;
  351. setItem('model', selectedModel);
  352. });
  353.  
  354. searchEngineSelect.addEventListener('change', (e) => {
  355. const selectedEngine = e.target.value;
  356. setItem('searchURL', selectedEngine);
  357. });
  358.  
  359. customPromptField.addEventListener('input', (e) => {
  360. const promptValue = e.target.value;
  361. setItem('customPrompt', promptValue);
  362. });`;
  363. // src/utils/parsers/HTMLFormParser.ts
  364. var formHeaderParser = (form) => {
  365. const formContentContainer = form.querySelector(".lrKTG");
  366. if (!formContentContainer)
  367. throw new Error("[!] Form content not found. Are you sure you are providing correct form?");
  368. const formHeader = formContentContainer.querySelector(".m7w29c.O8VmIc.tIvQIf");
  369. if (!formHeader)
  370. console.warn("[W] Form header was not found");
  371. const formTitleContainer = formHeader?.querySelector(".ahS2Le");
  372. const formDescriptionContainer = formHeader?.querySelector(".cBGGJ.OIC90c");
  373. return {
  374. formTitle: formTitleContainer?.textContent || document.title,
  375. formDescription: formDescriptionContainer?.textContent || ""
  376. };
  377. };
  378. var formQuestionParser = (form) => {
  379. if (!(form instanceof HTMLFormElement))
  380. throw new Error("[!] I strictly require HTMLFormElement to parse header");
  381. const questionContainer = form.querySelector(".o3Dpx[role='list']");
  382. if (!questionContainer)
  383. throw new Error("Question container is missing. Are you sure you are providing correct form?");
  384. const questionList = questionContainer.querySelectorAll(".Qr7Oae[role='listitem']");
  385. if (!questionList.length)
  386. console.warn("[W] No questions found.");
  387. const parsedQuestions = [...questionList]?.map((questionContainer2) => {
  388. const infoContainerDiv = questionContainer2.querySelector("div[jsmodel='CP1oW']");
  389. const dataParams = infoContainerDiv?.getAttribute("data-params");
  390. const betterDataParams = dataParams?.replace("%.@.", "[").replace(/"/g, "'");
  391. const question = JSON.parse(betterDataParams || "[]")[0];
  392. const questionTitle = question[1];
  393. const extraInformation = question[9] || null;
  394. const questionType = question[3];
  395. const submitID = question[4][0][0];
  396. const isRequiredQuestion = question[4][0][2];
  397. const options = question[4][0][1]?.map((option) => {
  398. return {
  399. value: option[0],
  400. moreInfo: option[5] || null
  401. };
  402. });
  403. return {
  404. title: questionTitle,
  405. moreInfo: extraInformation,
  406. type: questionType,
  407. id: submitID,
  408. required: isRequiredQuestion,
  409. options
  410. };
  411. });
  412. return parsedQuestions;
  413. };
  414. var parse = () => {
  415. const form = document.querySelector("form#mG61Hd");
  416. if (!form) {
  417. throw new Error("Form element not found");
  418. }
  419. const { formDescription, formTitle } = formHeaderParser(form);
  420. const parsedQuestionList = formQuestionParser(form);
  421. return {
  422. title: formTitle,
  423. description: formDescription,
  424. questions: parsedQuestionList
  425. };
  426. };
  427.  
  428. // src/utils/parsers/GlobalVariableParser.ts
  429. var parse2 = (data) => {
  430. const formTitle = data[1][8];
  431. const formDescription = data[1][0];
  432. const questionList = data[1][1];
  433. const parsedQuestionList = questionList.map((question) => {
  434. const questionTitle = question[1];
  435. const extraInformation = question[9] || null;
  436. const questionType = question[3];
  437. const submitID = question[4][0][0];
  438. const isRequiredQuestion = !!question[4][0][2];
  439. const options = question[4][0][1]?.map((option) => {
  440. return {
  441. value: option[0],
  442. moreInfo: option[5] || null
  443. };
  444. });
  445. return {
  446. title: questionTitle,
  447. moreInfo: extraInformation,
  448. type: questionType,
  449. id: submitID,
  450. required: isRequiredQuestion,
  451. options
  452. };
  453. });
  454. return {
  455. title: formTitle,
  456. description: formDescription,
  457. questions: parsedQuestionList
  458. };
  459. };
  460.  
  461. // src/utils/Utils.ts
  462. var groupedLog = (title, ...args) => {
  463. console.groupCollapsed(title);
  464. args.forEach((arg) => console.log(arg));
  465. console.groupEnd();
  466. };
  467.  
  468. // src/utils/StorageUtils.ts
  469. var getItem = (key) => {
  470. const storage = localStorage.getItem("formify");
  471. try {
  472. const parsedStorage = JSON.parse(storage || "{}");
  473. return parsedStorage[key] || null;
  474. } catch (err) {
  475. groupedLog("Failed to parse JSON from localStorage", err);
  476. return null;
  477. }
  478. };
  479. var setItem = (key, value) => {
  480. const storage = localStorage.getItem("formify");
  481. let parsedStorage = {};
  482. try {
  483. parsedStorage = JSON.parse(storage || "{}");
  484. } catch (err) {
  485. groupedLog("Failed to parse JSON from localStorage", err);
  486. }
  487. parsedStorage[key] = value;
  488. localStorage.setItem("formify", JSON.stringify(parsedStorage));
  489. };
  490.  
  491. // src/utils/NetworkUtils.ts
  492. var request = async (requestURL, requestOption = {
  493. method: "GET"
  494. }) => {
  495. if (requestOption.method == "GET" && requestOption.body) {
  496. groupedLog("Removing body from GET request.", requestURL, requestOption);
  497. delete requestOption.body;
  498. }
  499. try {
  500. const response = await fetch(requestURL, requestOption);
  501. const responseBody = await response.text();
  502. if (response.status != 200) {
  503. groupedLog(`Server responded with status ${response.status}`, requestURL, requestOption, responseBody);
  504. } else {
  505. groupedLog("Server responded successfully", requestURL, requestOption, responseBody);
  506. }
  507. return {
  508. success: response.status == 200,
  509. response: responseBody,
  510. statusText: response.statusText
  511. };
  512. } catch (err) {
  513. groupedLog("Failed to send request.", requestURL, requestOption, err);
  514. return {
  515. success: false,
  516. statusText: "ERROR",
  517. response: err instanceof Error ? err.message : String(err)
  518. };
  519. }
  520. };
  521.  
  522. // src/utils/AIUtils.ts
  523. var getAIResponse = async (prompt2) => {
  524. const model = getItem("model") || "gemini-2.0-flash";
  525. if (model == "gemini-2.0-flash" || model == "gemini-2.0-pro-experimental" || model == "gemini-2.0-flash-lite") {
  526. return getGeminiResponse(prompt2);
  527. } else {
  528. return "Model not supported for now: " + model;
  529. }
  530. };
  531. var getGeminiResponse = async (prompt2) => {
  532. const model = getItem("model") || "gemini-2.0-flash";
  533. const apiKey = getItem("apiKey") || "";
  534. const response = await request(`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`, {
  535. headers: new Headers({
  536. "Content-Type": "application/json"
  537. }),
  538. method: "POST",
  539. body: JSON.stringify({
  540. contents: [{
  541. parts: [
  542. {
  543. text: prompt2.prompt
  544. }
  545. ]
  546. }]
  547. })
  548. });
  549. if (!response.success) {
  550. return "Failed to fetch: " + response.statusText;
  551. }
  552. try {
  553. const parsedContent = JSON.parse(response.response);
  554. return parsedContent?.candidates?.[0]?.content?.parts?.[0]?.text;
  555. } catch (err) {
  556. return "Failed to parse response: " + err.message;
  557. }
  558. };
  559.  
  560. // src/utils/DOMUtils.ts
  561. var answerModal = ({ options, question, answer }) => {
  562. const div = document.createElement("div");
  563. div.classList.add("ai-container");
  564. const header = document.createElement("span");
  565. header.classList.add("container-header");
  566. const modelNameSpan = document.createElement("span");
  567. modelNameSpan.classList.add("model-name");
  568. modelNameSpan.textContent = `\uD83E\uDD95 ${getItem("model") || "gemini-2.0-flash"}`;
  569. const buttons = document.createElement("span");
  570. buttons.classList.add("buttons");
  571. const body = document.createElement("span");
  572. body.classList.add("container-body");
  573. body.textContent = answer;
  574. const option = options?.map((option2) => option2.value)?.join(" ") || "";
  575. const buttonConfigs = [
  576. {
  577. id: "copy",
  578. title: "Copy answer to clipboard",
  579. text: "Copy",
  580. onclick: (e) => {
  581. e.preventDefault();
  582. navigator.clipboard.writeText(answer);
  583. e.target.textContent = "Copied";
  584. setTimeout(() => {
  585. e.target.textContent = "Copy";
  586. }, 2000);
  587. }
  588. },
  589. {
  590. id: "regenerate",
  591. title: "Re-generate answer",
  592. text: "Re-generate",
  593. onclick: async (e) => {
  594. e.preventDefault();
  595. body.textContent = "Re-generating answer... \uD83E\uDD95";
  596. const response = await getAIResponse({
  597. prompt: getItem("customPrompt") + `
  598. ` + question + option
  599. });
  600. body.textContent = response;
  601. }
  602. },
  603. {
  604. id: "open-chat",
  605. title: "Open this question in chat",
  606. text: "Open in Chat",
  607. onclick: (e) => {
  608. e.preventDefault();
  609. openAIChat();
  610. sendMessage(getItem("customPrompt") + `
  611. ` + question + option);
  612. }
  613. },
  614. {
  615. id: "search",
  616. title: "Search this question",
  617. text: "Search",
  618. onclick: (e) => {
  619. const anchor = document.createElement("a");
  620. anchor.href = (getItem("searchURL") || "https://www.google.com/search?q=") + question + option;
  621. anchor.target = "_blank";
  622. anchor.click();
  623. }
  624. }
  625. ];
  626. buttonConfigs.forEach(({ id, title, text, onclick }) => {
  627. const button = document.createElement("button");
  628. button.setAttribute("type", "button");
  629. button.id = id;
  630. button.title = title;
  631. button.textContent = text;
  632. buttons.appendChild(button);
  633. button.addEventListener("click", onclick);
  634. });
  635. header.appendChild(modelNameSpan);
  636. header.appendChild(buttons);
  637. div.appendChild(header);
  638. div.appendChild(body);
  639. return div;
  640. };
  641. var toggleDialog = (force) => {
  642. const dialog = document.querySelector(".dialog-container");
  643. if (!dialog) {
  644. groupedLog("Dialog container not found");
  645. return;
  646. }
  647. if (force === true) {
  648. dialog.classList.remove("hidden");
  649. } else if (force === false) {
  650. dialog.classList.add("active");
  651. } else {
  652. dialog.classList.toggle("active");
  653. }
  654. };
  655. var toggleAnswers = (force) => {
  656. const aiResponse = document.querySelectorAll(".ai-container");
  657. aiResponse.forEach((response) => {
  658. if (force === true) {
  659. response.classList.remove("hidden");
  660. } else if (force === false) {
  661. response.classList.add("hidden");
  662. } else {
  663. response.classList.toggle("hidden");
  664. }
  665. });
  666. };
  667. var dialogWidth = "300px";
  668. var dialogHeight = "400px";
  669. var aiChatDialog = null;
  670. var chatHistory;
  671. var messageInput;
  672. var isDragging = false;
  673. var offsetX;
  674. var offsetY;
  675. function addMessage(text, isUser) {
  676. const messageElement = document.createElement("div");
  677. messageElement.classList.add("message");
  678. messageElement.classList.add(isUser ? "userMessage" : "aiMessage");
  679. messageElement.textContent = text;
  680. chatHistory.appendChild(messageElement);
  681. chatHistory.scrollTop = chatHistory.scrollHeight;
  682. }
  683. async function sendMessage(msg = "") {
  684. const message = messageInput.value.trim() || msg;
  685. if (message) {
  686. addMessage(message, true);
  687. messageInput.value = "";
  688. try {
  689. const aiResponse = await getAIResponse({ prompt: message });
  690. addMessage(aiResponse, false);
  691. } catch (error) {
  692. addMessage("Error: Could not get AI response.", false);
  693. console.error("Error fetching AI response:", error);
  694. }
  695. }
  696. }
  697. function createAIChatDialog() {
  698. aiChatDialog = document.createElement("div");
  699. aiChatDialog.id = "aiChatDialog";
  700. aiChatDialog.style.width = dialogWidth;
  701. aiChatDialog.style.height = dialogHeight;
  702. const dialogHeader = document.createElement("div");
  703. dialogHeader.id = "dialogHeader";
  704. const headerText = document.createTextNode("Formify");
  705. dialogHeader.appendChild(headerText);
  706. const closeButton = document.createElement("span");
  707. closeButton.id = "closeButton";
  708. const closeText = document.createTextNode("×");
  709. closeButton.appendChild(closeText);
  710. chatHistory = document.createElement("div");
  711. chatHistory.id = "chatHistory";
  712. const chatInputArea = document.createElement("div");
  713. chatInputArea.id = "chatInputArea";
  714. messageInput = document.createElement("input");
  715. messageInput.type = "text";
  716. messageInput.id = "messageInput";
  717. messageInput.placeholder = "Type your message...";
  718. const sendButton = document.createElement("button");
  719. sendButton.id = "sendButton";
  720. const sendText = document.createTextNode("Send");
  721. sendButton.appendChild(sendText);
  722. dialogHeader.appendChild(closeButton);
  723. chatInputArea.appendChild(messageInput);
  724. chatInputArea.appendChild(sendButton);
  725. aiChatDialog.appendChild(dialogHeader);
  726. aiChatDialog.appendChild(chatHistory);
  727. aiChatDialog.appendChild(chatInputArea);
  728. closeButton.addEventListener("click", closeAIChat);
  729. sendButton.addEventListener("click", () => sendMessage());
  730. messageInput.addEventListener("keydown", (event) => {
  731. if (event.key === "Enter") {
  732. sendMessage();
  733. }
  734. });
  735. dialogHeader.addEventListener("mousedown", (e) => {
  736. isDragging = true;
  737. offsetX = e.clientX - aiChatDialog.offsetLeft;
  738. offsetY = e.clientY - aiChatDialog.offsetTop;
  739. });
  740. document.addEventListener("mouseup", () => {
  741. isDragging = false;
  742. });
  743. document.addEventListener("mousemove", (e) => {
  744. if (!isDragging)
  745. return;
  746. let newX = e.clientX - offsetX;
  747. let newY = e.clientY - offsetY;
  748. const maxX = window.innerWidth - aiChatDialog.offsetWidth;
  749. const maxY = window.innerHeight - aiChatDialog.offsetHeight;
  750. newX = Math.max(0, Math.min(newX, maxX));
  751. newY = Math.max(0, Math.min(newY, maxY));
  752. aiChatDialog.style.left = newX + "px";
  753. aiChatDialog.style.top = newY + "px";
  754. aiChatDialog.style.right = "auto";
  755. });
  756. aiChatDialog.classList.add("aiChatDialog");
  757. dialogHeader.classList.add("dialogHeader");
  758. closeButton.classList.add("closeButton");
  759. chatHistory.classList.add("chatHistory");
  760. chatInputArea.classList.add("chatInputArea");
  761. messageInput.classList.add("messageInput");
  762. sendButton.classList.add("sendButton");
  763. document.body.appendChild(aiChatDialog);
  764. addMessage("Starting conversation...", false);
  765. return aiChatDialog;
  766. }
  767. function closeAIChat() {
  768. if (aiChatDialog) {
  769. aiChatDialog.style.display = "none";
  770. }
  771. }
  772. function openAIChat() {
  773. if (!aiChatDialog) {
  774. createAIChatDialog();
  775. }
  776. aiChatDialog.style.display = "flex";
  777. }
  778. var ready = () => {
  779. const dialogContainer = document.createElement("div");
  780. dialogContainer.className = "dialog-container";
  781. const dialog = document.createElement("div");
  782. dialog.className = "dialog";
  783. const header = document.createElement("span");
  784. header.className = "formify-header";
  785. const logo = document.createElement("img");
  786. logo.alt = "F is for Formify";
  787. logo.height = 40;
  788. logo.src = "data:image/png;base64,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";
  789. const headerText = document.createElement("span");
  790. headerText.className = "formify-text";
  791. const boldText = document.createElement("b");
  792. boldText.innerText = "Formify";
  793. headerText.appendChild(boldText);
  794. const closeBtn = document.createElement("span");
  795. closeBtn.className = "close";
  796. closeBtn.innerText = "×";
  797. header.appendChild(logo);
  798. header.appendChild(headerText);
  799. header.appendChild(closeBtn);
  800. const formBody = document.createElement("span");
  801. formBody.className = "form-body";
  802. const ul = document.createElement("ul");
  803. const listItems = [
  804. { label: "API Key", type: "text", id: "apikey", placeholder: "Paste API key here" },
  805. {
  806. label: "AI Model",
  807. type: "select",
  808. id: "ai-model",
  809. options: [
  810. { value: "gemini-2.0-pro-exp-02-05", text: "Gemini 2.0 Pro Experimental" },
  811. { value: "gemini-2.0-flash", text: "Gemini 2.0 Flash" },
  812. { value: "gemini-2.0-flash-lite", text: "Gemini 2.0 Flash-Lite" },
  813. { value: "gemini-1.5-pro", text: "Gemini 1.5 Pro" },
  814. { value: "gpt-4.5-preview", text: "gpt-4.5-preview" },
  815. { value: "gpt-4o-mini", text: "gpt-4o-mini" }
  816. ]
  817. },
  818. {
  819. label: "Search Engine",
  820. type: "select",
  821. id: "search-engine",
  822. options: [
  823. { value: "https://www.google.com/search?q=", text: "Google" },
  824. { value: "https://chatgpt.com/?q=", text: "ChatGPT Search" },
  825. { value: "https://www.bing.com/search?q=", text: "Bing" },
  826. { value: "https://search.yahoo.com/search?p=", text: "Yahoo" },
  827. { value: "https://duckduckgo.com/?q=", text: "DuckDuckGo" },
  828. { value: "https://www.baidu.com/s?wd=", text: "Baidu" },
  829. { value: "https://www.yandex.com/search/?text=", text: "Yandex" },
  830. { value: "https://www.ecosia.org/search?q=", text: "Ecosia" },
  831. { value: "https://www.ask.com/web?q=", text: "Ask" },
  832. { value: "https://www.startpage.com/do/search?q=", text: "Startpage" },
  833. { value: "https://search.brave.com/search?q=", text: "Brave Search" }
  834. ]
  835. },
  836. { label: "Custom Prompt", type: "text", id: "custom-prompt", placeholder: "Custom prompt to feed the model" },
  837. { label: "Dialog Shortcuts", type: "code", value: "ALT + K" },
  838. { label: "AI Response hide/unhide", type: "code", value: "ALT + M" }
  839. ];
  840. listItems.forEach((item) => {
  841. const li = document.createElement("li");
  842. const label = document.createElement("label");
  843. label.innerText = item.label;
  844. if (item.type === "text") {
  845. const input = document.createElement("input");
  846. input.type = "text";
  847. input.setAttribute("name", item.id || "");
  848. input.setAttribute("id", item.id || "");
  849. input.setAttribute("placeholder", item.placeholder || "");
  850. li.appendChild(label);
  851. li.appendChild(input);
  852. } else if (item.type === "select") {
  853. const select = document.createElement("select");
  854. select.setAttribute("name", item.id || "");
  855. select.setAttribute("id", item.id || "");
  856. item.options.forEach((optionData) => {
  857. const option = document.createElement("option");
  858. option.setAttribute("value", optionData.value);
  859. option.textContent = optionData.text;
  860. select.appendChild(option);
  861. });
  862. li.appendChild(label);
  863. li.appendChild(select);
  864. } else if (item.type === "code") {
  865. const code = document.createElement("code");
  866. code.textContent = item.value || "";
  867. li.appendChild(label);
  868. li.appendChild(code);
  869. }
  870. ul.appendChild(li);
  871. });
  872. formBody.appendChild(ul);
  873. const formFooter = document.createElement("span");
  874. formFooter.className = "form-footer";
  875. const footerLinks = [
  876. { href: "https://github.com/rohitaryal/formify", text: "Submit a bug ↗" },
  877. { href: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", text: "Hmm ↗" }
  878. ];
  879. footerLinks.forEach((linkData) => {
  880. const link = document.createElement("a");
  881. link.target = "_blank";
  882. link.href = linkData.href;
  883. link.innerText = linkData.text;
  884. formFooter.appendChild(link);
  885. });
  886. dialog.appendChild(header);
  887. dialog.appendChild(formBody);
  888. dialog.appendChild(formFooter);
  889. dialogContainer.appendChild(dialog);
  890. document.body.appendChild(dialogContainer);
  891. };
  892.  
  893. // src/index.ts
  894. (async function() {
  895. GM_addStyle(css);
  896. ready();
  897. GM_addElement("script", {
  898. textContent: js
  899. });
  900. let apiKey = getItem("apiKey");
  901. if (!apiKey) {
  902. apiKey = prompt("Please paste your API key, you can generate free api key from: https://aistudio.google.com/apikey");
  903. if (apiKey) {
  904. setItem("apiKey", apiKey);
  905. } else {
  906. alert("API key is required to interact with the AI model.");
  907. }
  908. }
  909. document.addEventListener("keydown", (e) => {
  910. if (e.altKey && e.key == "k" || e.key == "Escape") {
  911. toggleDialog();
  912. }
  913. if (e.key == "m" && e.altKey) {
  914. toggleAnswers();
  915. }
  916. });
  917. const scrapedContent = window.FB_PUBLIC_LOAD_DATA_ ? parse2(window.FB_PUBLIC_LOAD_DATA_) : parse();
  918. const questionContainers = document.querySelectorAll(".Qr7Oae[role='listitem']");
  919. for (let i = 0;i < questionContainers.length; i++) {
  920. const questionContainer = questionContainers[i];
  921. const question = scrapedContent.questions[i];
  922. const prompt2 = getItem("customPrompt") + `
  923. ` + question.title + (question.options?.map((option) => option.value + ", ") || "");
  924. const aiAnswer = await getAIResponse({ prompt: prompt2 });
  925. const options = questionContainer.querySelectorAll("label");
  926. for (const option of options || []) {
  927. const betterOptionText = option.textContent?.trim();
  928. const betterAiAnswer = aiAnswer.trim();
  929. if (betterAiAnswer.includes(betterOptionText)) {
  930. if (question.type == 2 || question.type == 4) {
  931. option.click();
  932. if (question.type == 2) {
  933. break;
  934. }
  935. }
  936. }
  937. }
  938. const answer = answerModal({
  939. question: question.title,
  940. options: question.options,
  941. answer: aiAnswer
  942. });
  943. questionContainer.appendChild(answer);
  944. }
  945. })();