├── README.md ├── background.js ├── foreground.js └── manifest.json /README.md: -------------------------------------------------------------------------------- 1 | # Funcaptcha Solver 2 | This plugin enables image recognition for Funcaptcha using the cap.guru api. It was created purely for testing purposes. If Funcaptcha officials want it to be removed, it is enough to contact zfcsoftware@gmail.com mail address. If removal is requested, this repository will be removed quickly. 3 | 4 | # Usage 5 | Simply enter your cap.guru api key in the apiKey variable at the beginning of the foreground.js file and install this plugin in the browser you want to use. 6 | 7 | Question text is required for cap guru to identify what is in the image. The question text is being sent, but it only supports the English language. Therefore you should use it in an English language browser. 8 | 9 | https://github.com/user-attachments/assets/73a6b1fd-e551-4a24-ac41-556e2e544f1f 10 | 11 | # Support Us 12 | This library is completely open source and is constantly being updated. Please star this repo to support this project. Starring and supporting the project will ensure that it receives updates. If you want to support it further, you can consider sponsoring me (https://github.com/sponsors/zfcsoftware) 13 | 14 | # Disclaimer of Liability 15 | This repo was created purely for testing purposes. It is not intended to harm Funcaptcha. In case of any dispute, this repo will be removed if Funcaptcha officials contact zfcsoftware@gmail.com. 16 | 17 | This repo was coded and released as open source to better understand the working logic of the Chrome extension and Javascript. The repo owner assumes no responsibility for any legal issues that may arise from your use of the plugin in this repo. People who use the plugin in this repo accept and declare that all problems that may arise are their own. 18 | 19 | The repo owner has no affiliation with cap.guru. The relevant site has been selected as a service for the image recognition api. Problems related to this site are not related to the repo owner. # funcaptcha-solver 20 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 2 | console.log(request.data); 3 | if (request.action === "fetchLink") { 4 | fetch(request.url, { 5 | "headers": { 6 | "content-type": "application/json" 7 | }, 8 | "body": JSON.stringify(request.data), 9 | "method": "POST" 10 | }) 11 | .then(response => response.json()) 12 | .then(data => { 13 | console.log(data); 14 | sendResponse({ data: data }); 15 | }) 16 | .catch(err => { 17 | console.error("İstek hatası:", err); 18 | sendResponse({ data: "Error fetching the link" }); 19 | }); 20 | return true; 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /foreground.js: -------------------------------------------------------------------------------- 1 | const apiKey = '' 2 | 3 | function simulateUserClick(element) { 4 | const rect = element.getBoundingClientRect(); 5 | const x = rect.left + rect.width / 2; 6 | const y = rect.top + rect.height / 2; 7 | 8 | element.dispatchEvent(new MouseEvent('mousemove', { 9 | view: window, 10 | bubbles: true, 11 | cancelable: true, 12 | clientX: x, 13 | clientY: y, 14 | })); 15 | 16 | element.dispatchEvent(new MouseEvent('mousedown', { 17 | view: window, 18 | bubbles: true, 19 | cancelable: true, 20 | clientX: x, 21 | clientY: y, 22 | })); 23 | 24 | element.dispatchEvent(new MouseEvent('mouseup', { 25 | view: window, 26 | bubbles: true, 27 | cancelable: true, 28 | clientX: x, 29 | clientY: y, 30 | })); 31 | 32 | element.dispatchEvent(new MouseEvent('click', { 33 | view: window, 34 | bubbles: true, 35 | cancelable: true, 36 | clientX: x, 37 | clientY: y, 38 | })); 39 | } 40 | 41 | 42 | 43 | async function blobToBase64(blobUrl) { 44 | const response = await fetch(blobUrl); 45 | const blob = await response.blob(); 46 | const arrayBuffer = await blob.arrayBuffer(); 47 | const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))); 48 | return base64String; 49 | } 50 | 51 | async function getImageBlob() { 52 | let repeat = 0 53 | while ((!document.querySelector("#root .screen img") || !document.querySelector('#root .screen button.button')) && repeat < 10) { 54 | await new Promise(resolve => setTimeout(resolve, 1000)) 55 | } 56 | await new Promise(resolve => setTimeout(resolve, 1000)) 57 | 58 | if (document.querySelector('.match-game-alert')) return resolve(true) 59 | 60 | if (!document.querySelector("#root .screen img") || !document.querySelector('#root .screen button.button')) return false 61 | return window.getComputedStyle(document.querySelector("#root .screen img")) 62 | .getPropertyValue('background-image') 63 | .match(/url\(["']?(.*?)["']?\)/) 64 | .filter(item => !item.includes("url") && item.includes("blob:"))[0] 65 | } 66 | 67 | function createTask(base64Data, message) { 68 | return new Promise((resolve, reject) => { 69 | chrome.runtime.sendMessage({ 70 | action: "fetchLink", 71 | url: "https://ipv6.cap.guru/in.php", 72 | data: { 73 | "key": apiKey, 74 | "type": "base64", 75 | "method": "base64", 76 | "body": 'data:image/jpeg;base64,' + base64Data, 77 | "click": "funcap2", 78 | "textinstructions": message, 79 | "now": "1", 80 | json: 1 81 | } 82 | }, (response) => { 83 | resolve(response.data); 84 | }); 85 | }) 86 | } 87 | 88 | function taskResult(id) { 89 | return new Promise((resolve, reject) => { 90 | chrome.runtime.sendMessage({ 91 | action: "fetchLink", 92 | url: "https://ipv6.cap.guru/res.php", 93 | data: { 94 | "key": apiKey, 95 | action: "get", 96 | "json": 1, 97 | id 98 | } 99 | }, (response) => { 100 | resolve(response.data); 101 | }); 102 | }) 103 | } 104 | 105 | async function detectClickedLengh(base64Data, message) { 106 | let taskData = await createTask(base64Data, message) 107 | if (!taskData || !taskData.request) return false 108 | let id = taskData.request 109 | let taskRes = null 110 | 111 | while (taskRes == null) { 112 | let sessionRes = await taskResult(id) 113 | if (sessionRes && sessionRes.status == 1) taskRes = sessionRes 114 | else if (sessionRes.request == "ERROR_CAPTCHA_UNSOLVABLE") taskRes = false 115 | else await new Promise(resolve => setTimeout(resolve, 1000)) 116 | } 117 | return taskRes.request 118 | } 119 | 120 | 121 | async function solveFuncaptcha() { 122 | let startButton = null 123 | let repeat = 0 124 | while (!startButton && repeat < 10) { 125 | startButton = document.querySelector('#root .screen button.button') 126 | if (!startButton) await new Promise(resolve => setTimeout(resolve, 1000)) 127 | repeat++ 128 | } 129 | if (!startButton) return false 130 | simulateUserClick(startButton) 131 | let whileStatus = true 132 | while (whileStatus) { 133 | if (document.querySelector('.match-game-alert')) { 134 | simulateUserClick(document.querySelector('#root .screen button.button')) 135 | whileStatus = false 136 | solveFuncaptcha() 137 | continue 138 | } 139 | await new Promise(resolve => setTimeout(resolve, 1000)) 140 | let blobData = await getImageBlob() 141 | if (blobData === true) { 142 | simulateUserClick(document.querySelector('#root .screen button.button')) 143 | whileStatus = false 144 | solveFuncaptcha() 145 | continue 146 | } 147 | if (!blobData) whileStatus = false 148 | let base64Data = await blobToBase64(blobData) 149 | let result = await detectClickedLengh(base64Data, document.querySelector('[role="text"]').textContent) 150 | 151 | if (document.querySelector('.match-game-alert')) { 152 | simulateUserClick(document.querySelector('#root .screen button.button')) 153 | whileStatus = false 154 | solveFuncaptcha() 155 | continue 156 | } 157 | for (let i = 0; i < (Number(result) - 1); i++) { 158 | simulateUserClick(document.querySelector('.right-arrow')) 159 | await new Promise(resolve => setTimeout(resolve, 100)) 160 | } 161 | await new Promise(resolve => setTimeout(resolve, 500)) 162 | simulateUserClick(document.querySelector('#root .screen button.button')) 163 | } 164 | } 165 | 166 | 167 | 168 | window.onload = () => { 169 | if (document.URL.includes("arkoselabs")) solveFuncaptcha() 170 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Funcaptcha Solver", 3 | "version": "1.0", 4 | "manifest_version": 3, 5 | "background": { 6 | "service_worker": "background.js" 7 | }, 8 | "content_scripts": [ 9 | { 10 | "all_frames": true, 11 | "js": [ 12 | "./foreground.js" 13 | ], 14 | "matches": [ 15 | "*://*/*" 16 | ], 17 | "run_at": "document_start" 18 | } 19 | ], 20 | "host_permissions": [ 21 | "*://*/*" 22 | ], 23 | "permissions": [ 24 | "activeTab", 25 | "scripting", 26 | "background" 27 | ] 28 | } --------------------------------------------------------------------------------