Bedrock Learning Autosolver

Takes a screenshot of Bedrock Learning, sends it to Gemini, and displays the answer in a beautifully styled new tab.

  1. // ==UserScript==
  2. // @name Bedrock Learning Autosolver
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description Takes a screenshot of Bedrock Learning, sends it to Gemini, and displays the answer in a beautifully styled new tab.
  6. // @author Your Name
  7. // @match https://app.bedrocklearning.org/*
  8. // @grant GM_openInTab
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. 'use strict';
  17.  
  18. const GEMINI_API_KEY_KEY = 'Your-Google-API-Key-Here';
  19. let geminiApiKey = GM_getValue(GEMINI_API_KEY_KEY, null);
  20. const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=';
  21. const DEFAULT_PROMPT = "Analyze the image and identify any questions. Answer the questions with as much detail as possible. Show your reasoning.";
  22. const ADDITIONAL_PROMPT_MESSAGE = "Enter any additional instructions or questions to send with the image (or leave blank for default prompt):";
  23.  
  24. async function checkApiKey() {
  25. if (!geminiApiKey) {
  26. geminiApiKey = prompt("Enter your Google AI Studio API Key:");
  27. if (geminiApiKey) {
  28. GM_setValue(GEMINI_API_KEY_KEY, geminiApiKey);
  29. alert("API key saved. Press Ctrl+X again to process the question.");
  30. } else {
  31. alert("API key required for the script to function.");
  32. }
  33. return false;
  34. }
  35. return true;
  36. }
  37.  
  38. async function captureScreenshot() {
  39. if (typeof html2canvas === "undefined") {
  40. await new Promise(resolve => {
  41. const script = document.createElement("script");
  42. script.src = "https://html2canvas.hertzen.com/dist/html2canvas.min.js";
  43. script.onload = resolve;
  44. document.head.appendChild(script);
  45. });
  46. }
  47. return html2canvas(document.body, {
  48. useCORS: true,
  49. allowTaint: true,
  50. scrollX: 0,
  51. scrollY: 0,
  52. windowWidth: document.body.scrollWidth,
  53. windowHeight: document.body.scrollHeight,
  54. width: document.body.scrollWidth,
  55. height: document.body.scrollHeight
  56. });
  57. }
  58.  
  59. function convertCanvasToBlob(canvas) {
  60. return new Promise((resolve, reject) => {
  61. canvas.toBlob(blob => {
  62. blob ? resolve(blob) : reject(new Error('Failed to convert canvas to blob.'));
  63. }, 'image/png');
  64. });
  65. }
  66.  
  67. async function sendImageToGemini(imageBlob, additionalPrompt = "") {
  68. if (!await checkApiKey()) return;
  69.  
  70. const reader = new FileReader();
  71. reader.readAsDataURL(imageBlob);
  72.  
  73. return new Promise((resolve, reject) => {
  74. reader.onloadend = () => {
  75. const base64Image = reader.result.split(',')[1];
  76. const promptText = additionalPrompt.trim() !== "" ? additionalPrompt : DEFAULT_PROMPT;
  77.  
  78. const payload = {
  79. contents: [
  80. {
  81. parts: [
  82. { text: promptText },
  83. {
  84. inline_data: {
  85. mime_type: "image/png",
  86. data: base64Image
  87. }
  88. }
  89. ]
  90. }
  91. ]
  92. };
  93.  
  94. GM_xmlhttpRequest({
  95. method: "POST",
  96. url: GEMINI_API_URL + geminiApiKey,
  97. headers: { "Content-Type": "application/json" },
  98. data: JSON.stringify(payload),
  99. onload: function (response) {
  100. if (response.status >= 200 && response.status < 300) {
  101. try {
  102. const jsonResponse = JSON.parse(response.responseText);
  103. const answer = jsonResponse?.candidates?.[0]?.content?.parts?.[0]?.text || "No answer found.";
  104. displayAnswerInNewTab(answer);
  105. } catch (error) {
  106. reject("Error parsing response: " + error.message);
  107. }
  108. } else {
  109. reject(`API Error: ${response.status} - ${response.responseText}`);
  110. }
  111. },
  112. onerror: function (error) {
  113. reject("Request error: " + error);
  114. }
  115. });
  116. };
  117. reader.onerror = () => reject(new Error('Failed to read image.'));
  118. });
  119. }
  120.  
  121. function displayAnswerInNewTab(answer) {
  122. const newTabContent = `
  123. <!DOCTYPE html>
  124. <html lang="en">
  125. <head>
  126. <meta charset="UTF-8">
  127. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  128. <title>Gemini Answer</title>
  129. <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
  130. <style>
  131. @keyframes bgAnimation {
  132. 0% { background-position: 0% 50%; }
  133. 50% { background-position: 100% 50%; }
  134. 100% { background-position: 0% 50%; }
  135. }
  136.  
  137. body {
  138. font-family: 'Poppins', sans-serif;
  139. background: linear-gradient(135deg, #1E1E2F, #2D2D3F);
  140. background-size: 300% 300%;
  141. animation: bgAnimation 10s infinite alternate;
  142. color: #FFF;
  143. text-align: center;
  144. display: flex;
  145. justify-content: center;
  146. align-items: center;
  147. height: 100vh;
  148. margin: 0;
  149. }
  150. .container {
  151. background: rgba(255, 255, 255, 0.1);
  152. padding: 20px;
  153. border-radius: 12px;
  154. max-width: 600px;
  155. box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
  156. transition: transform 0.3s ease, box-shadow 0.3s ease;
  157. }
  158. .container:hover {
  159. transform: translateY(-5px);
  160. box-shadow: 0 10px 20px rgba(255, 255, 255, 0.4);
  161. }
  162. h1 {
  163. font-size: 22px;
  164. font-weight: 600;
  165. margin-bottom: 10px;
  166. }
  167. pre {
  168. white-space: pre-wrap;
  169. word-wrap: break-word;
  170. font-size: 14px;
  171. background: rgba(255, 255, 255, 0.1);
  172. padding: 10px;
  173. border-radius: 8px;
  174. text-align: left;
  175. max-height: 300px;
  176. overflow-y: auto;
  177. font-family: 'Poppins', sans-serif;
  178. }
  179. button {
  180. margin-top: 10px;
  181. padding: 10px 20px;
  182. font-size: 14px;
  183. border: none;
  184. border-radius: 8px;
  185. background: #FFC857;
  186. color: #222;
  187. cursor: pointer;
  188. transition: 0.3s;
  189. }
  190. button:hover {
  191. background: #FFA500;
  192. }
  193. </style>
  194. </head>
  195. <body>
  196. <div class="container">
  197. <h1>Gemini Answer</h1>
  198. <pre id="answer">${answer}</pre>
  199. <button onclick="copyToClipboard()">📋 Copy</button>
  200. </div>
  201.  
  202. <script>
  203. function copyToClipboard() {
  204. const answerText = document.getElementById("answer").textContent;
  205. navigator.clipboard.writeText(answerText).then(() => {
  206. alert("Copied to clipboard!");
  207. }).catch(err => console.error("Copy failed:", err));
  208. }
  209. </script>
  210. </body>
  211. </html>
  212. `;
  213.  
  214. GM_openInTab(`data:text/html;charset=utf-8,${encodeURIComponent(newTabContent)}`, { active: true });
  215. }
  216.  
  217. document.addEventListener('keydown', async function (event) {
  218. if (event.ctrlKey && event.key === 'x') {
  219. event.preventDefault();
  220. try {
  221. const canvas = await captureScreenshot();
  222. const imageBlob = await convertCanvasToBlob(canvas);
  223. const additionalPrompt = prompt(ADDITIONAL_PROMPT_MESSAGE);
  224. await sendImageToGemini(imageBlob, additionalPrompt);
  225. } catch (error) {
  226. alert("Error: " + error);
  227. }
  228. }
  229. });
  230.  
  231. console.log("Uber Aesthetic Solver script loaded.");
  232. })();