Formify

Let AI solve your google forms

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