├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── documentation.md │ ├── enhancement.md │ ├── feature_request.md │ ├── help_wanted.md │ └── question.md └── dependabot.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── area_hash.csv ├── backup.csv ├── chrome.json ├── cleaned.txt ├── dbfp.txt ├── hash.csv ├── nop.txt └── questions.txt ├── cmd └── hcsolver │ ├── config │ └── storage.go │ ├── database │ └── database.go │ ├── main.go │ ├── router │ └── router.go │ └── start.bat ├── docker-compose.yml ├── go.mod ├── go.sum ├── internal ├── handlers │ ├── task │ │ ├── hc_task_manager.go │ │ ├── hc_task_worker.go │ │ ├── struct.go │ │ └── validator │ │ │ └── validator.go │ └── user │ │ ├── struct.go │ │ └── user.go ├── hcaptcha │ ├── fingerprint │ │ ├── events │ │ │ ├── 0.go │ │ │ ├── 107.go │ │ │ ├── 1101.go │ │ │ ├── 1103.go │ │ │ ├── 1105.go │ │ │ ├── 1107.go │ │ │ ├── 1302.go │ │ │ ├── 1401.go │ │ │ ├── 1402.go │ │ │ ├── 1403.go │ │ │ ├── 1901.go │ │ │ ├── 1902.go │ │ │ ├── 1904.go │ │ │ ├── 201.go │ │ │ ├── 211.go │ │ │ ├── 2401.go │ │ │ ├── 2402.go │ │ │ ├── 2407.go │ │ │ ├── 2408.go │ │ │ ├── 2409.go │ │ │ ├── 2410.go │ │ │ ├── 2411.go │ │ │ ├── 2412.go │ │ │ ├── 2413.go │ │ │ ├── 2414.go │ │ │ ├── 2415.go │ │ │ ├── 2416.go │ │ │ ├── 2417.go │ │ │ ├── 2420.go │ │ │ ├── 2801.go │ │ │ ├── 2805.go │ │ │ ├── 3.go │ │ │ ├── 301.go │ │ │ ├── 304.go │ │ │ ├── 3210.go │ │ │ ├── 3211.go │ │ │ ├── 3401.go │ │ │ ├── 3403.go │ │ │ ├── 3501.go │ │ │ ├── 3502.go │ │ │ ├── 3503.go │ │ │ ├── 3504.go │ │ │ ├── 3801.go │ │ │ ├── 3802.go │ │ │ ├── 401.go │ │ │ ├── 402.go │ │ │ ├── 407.go │ │ │ ├── 412.go │ │ │ ├── 604.go │ │ │ ├── 702.go │ │ │ ├── 803.go │ │ │ ├── 901.go │ │ │ ├── 905.go │ │ │ ├── events.go │ │ │ ├── string_encryption.go │ │ │ ├── string_encryption_test.go │ │ │ └── struct.go │ │ ├── fingerprint.go │ │ ├── fingerprint_test.go │ │ ├── jwt.go │ │ ├── rand.go │ │ ├── stamp.go │ │ ├── stamp_test.go │ │ ├── struct.go │ │ └── xxhash.go │ ├── hcaptcha.go │ ├── headers.go │ ├── hsw.go │ ├── motiondata.go │ └── struct.go ├── model │ ├── task.go │ └── user.go ├── recognizer │ ├── nocap.go │ ├── recognizer.go │ ├── struct.go │ └── utils.go ├── routes │ ├── task │ │ └── task.go │ └── user │ │ └── user.go └── utils │ └── utils.go └── scripts ├── Makefile ├── config.toml ├── loop.sh └── start.bat /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report an issue or bug related to project documentation 4 | title: '[Bug] Issue Title Here' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Issue Title: 11 | [Short description of the issue] 12 | 13 | ### Issue Description: 14 | [Provide a more detailed description of the issue. What problem are you encountering?] 15 | 16 | ### Steps to Reproduce: 17 | 1. [Step 1] 18 | 2. [Step 2] 19 | 3. [Step 3] 20 | ... 21 | 22 | ### Expected Behavior: 23 | [Explain what you expected to happen.] 24 | 25 | ### Actual Behavior: 26 | [Explain what actually happened.] 27 | 28 | ### Additional Information: 29 | - **Operating System:** [Your OS version] 30 | - **Browser (if applicable):** [Browser name and version] 31 | - **Related Issue (if any):** #issue_number 32 | - **Screenshots/Code Snippets (if applicable):** 33 | 34 | ```plaintext 35 | Paste your code or code snippet here. 36 | ``` 37 | 38 | ### Notes: 39 | [Add any additional notes or context that might be relevant.] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord 4 | url: https://discord.gg/b7EwhxGJZY 5 | about: Please ask and answer questions here, not in DMS or issue. 6 | - name: Telegram 7 | url: https://t.me/implex_ltd 8 | about: Community backup. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation Issue 3 | about: Report an issue or improvement related to project documentation 4 | title: '[Documentation] Issue Title Here' 5 | labels: 'documentation' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Documentation Issue 11 | 12 | **Describe the issue** 13 | A clear and concise description of what the issue is with the documentation. 14 | 15 | **URL or File Path** 16 | Provide the URL or file path to the documentation page or file where the issue is located. 17 | 18 | **Expected Documentation** 19 | A clear and concise description of what the correct documentation should be. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the documentation issue here. 23 | 24 | ### Proposed Documentation Update (if applicable) 25 | Provide a proposed update to the documentation, if you have one. 26 | 27 | ### Related Issues (if any) 28 | Any related issues or pull requests that might be relevant to this documentation issue. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an enhancement or improvement for this project 4 | title: '[Enhancement] Your Enhancement Idea Title' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Enhancement Request 11 | 12 | **Is your enhancement request related to a problem? Please describe.** 13 | A clear and concise description of what the problem or limitation is. 14 | 15 | **Describe the enhancement you'd like** 16 | A clear and concise description of what you want to be enhanced or improved. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or enhancements you've thought about. 20 | 21 | **Additional context** 22 | Add any other context, screenshots, or examples about the enhancement request here. 23 | 24 | ### Acceptance Criteria (if applicable) 25 | Define the criteria that need to be met for this enhancement to be considered complete. 26 | 27 | ### Related Issues (if any) 28 | Any related issues or pull requests that might be relevant to this enhancement request. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea or enhancement for this project 4 | title: '[Feature Request] Your Feature Idea Title' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Feature Request 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | 24 | ### Acceptance Criteria (if applicable) 25 | Define the criteria that need to be met for this feature to be considered complete. 26 | 27 | ### Related Issues (if any) 28 | Any related issues or pull requests that might be relevant to this feature request. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help_wanted.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Help Wanted Issue 3 | about: Mark an issue as "Help Wanted" to invite community contributions 4 | title: '[Help Wanted] Short Description of the Issue' 5 | labels: 'help wanted' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Help Wanted Issue 11 | 12 | **Issue Description** 13 | A brief description of the issue or task that needs help. 14 | 15 | **Expected Outcome** 16 | Describe what the desired outcome or solution should be. 17 | 18 | **Skills Required** 19 | Mention any specific skills or knowledge that might be helpful for addressing this issue. 20 | 21 | **Getting Started** 22 | Provide instructions or pointers on how a contributor can get started working on this issue. 23 | 24 | **Additional Information** 25 | Any additional context, notes, or resources that might be useful for contributors. 26 | 27 | ### Acceptance Criteria (if applicable) 28 | Define the criteria that need to be met for this issue to be considered resolved. 29 | 30 | ### Related Issues (if any) 31 | Any related issues or pull requests that might be relevant to this help wanted issue. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question or seek clarification 4 | title: '[Question] Short Description of Your Question' 5 | labels: 'question' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Question 11 | 12 | **Description** 13 | Please provide details about your question or what you need clarification on. 14 | 15 | **Context** 16 | Give some context or background information to help others understand your question better. 17 | 18 | **What I've Tried** 19 | If you've attempted any solutions or research, please describe what you've tried so far. 20 | 21 | **Additional Information** 22 | Any additional details, screenshots, or examples that might be relevant to your question. 23 | 24 | ### Related Issues (if any) 25 | Any related issues or pull requests that might be relevant to your question. 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | vendor/ 16 | 17 | # Go workspace file 18 | go.work 19 | .idea/* 20 | .idea 21 | 22 | # Private things 23 | mydatabase.db/* 24 | Dockerfile 25 | .env 26 | .tmp 27 | *.json 28 | *.py 29 | *.js 30 | 31 | # assets 32 | assets/logs/* 33 | assets/old_questions.txt 34 | assets/cleaned.txt 35 | 36 | 37 | data/* 38 | assets/backup.csv 39 | assets/cleaned.txt 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Crapsolver backend API** 2 | 3 | ## **Running SurrealDB** 4 | 5 | ### Docker 6 | 7 | ``` 8 | docker run --rm --pull always -p 8000:8000 -v /mydata:/mydata surrealdb/surrealdb:1.0.0-beta.9-20230402 start --log trace --user root --pass rootnikoontoplmao5245 9 | ``` 10 | 11 | ### Windows 12 | ``` 13 | surreal.exe start --user root --pass root --bind 0.0.0.0:8080 file:mydatabase.db 14 | ``` -------------------------------------------------------------------------------- /assets/backup.csv: -------------------------------------------------------------------------------- 1 | user:066qf5y1jgi7rfivty08,1000,0,false 2 | user:0zof9gl15fv1fs8enacl,3491,9,true 3 | user:1gduotjr3dh7rxoo6edi,500,0,false 4 | user:2k40cwx8h1jgmfzbegst,500,0,false 5 | user:3jm0p3uonxio4tcezkzl,500,0,false 6 | user:3lf50nj3w4i4s01njet4,500,0,false 7 | user:3th9wce54ujtzt4idp2z,9998,2,false 8 | user:48bew5qdiefg0stt5e41,500,0,false 9 | user:5bxyg6aowm0pgp25n1p4,494,6,false 10 | user:5g5fioq61o12cym7qydc,500,0,false 11 | user:5lnm8xnpo5m8e9yr6sl9,499,1,false 12 | user:5vejdjb60z75q9fibf1z,2956,44,false 13 | user:6c2vi4wo7pug3suuja2i,490,10,false 14 | user:8gjcp6re9zpythm7v5em,500,0,false 15 | user:9x15m7ly0tflwbwxe6zu,500,0,false 16 | user:a1b7dcs4n325acy6rhrq,0,0,false 17 | user:a237grw0fmlmzyuqt9c7,500,0,false 18 | user:adcd8ipihhmvucbadpd5,500,0,false 19 | user:cbzu3hcw92007hplzqb2,500,0,false 20 | user:e1n6zp0pvjig0lgomt3m,500,0,false 21 | user:fepioknnegydtsdfwkvx,477,23,false 22 | user:ggxl9uip42bnq0tgs4ch,93819,22181,true 23 | user:hkvssuqqkhm0nrx5wp4g,500,0,false 24 | user:j5fdnqzxnq2uki4phqin,494,6,false 25 | user:k0fyypz1fy1zyiwopk45,1176,324,true 26 | user:kvylk4vy08a9gt3qksj0,500,0,false 27 | user:lli2xo68fuhs01tdfgi9,500,0,false 28 | user:mi5a2eueua4sz7tz3n7u,500,0,false 29 | user:n2830u8o704vsiefwwui,1000,0,false 30 | user:p0oksa2jryah5ujx0juq,1496,4,false 31 | user:p1ie79nl0huxatebfrml,500,0,false 32 | user:qam8vo6ytu2z3su2yc7p,500,0,false 33 | user:re6l4aq3z3uukk5nevz9,492,8,true 34 | user:remvbyyivz63djjwpuof,500,0,false 35 | user:rpjjzc9ibrf4tu6a18di,500,0,false 36 | user:tb4orkvitbcs7ih4putl,500,0,false 37 | user:th1c24uj4t6wdpvqpem3,2500,0,false 38 | user:th9pw78p05wt499qg039,500,0,false 39 | user:ttyalbxxjwtxt2abkvvy,500,0,false 40 | user:tzoq5aw9k091ighjwq7x,2472,28,false 41 | user:vcjqx3itf81tmai6t4pc,500,0,false 42 | user:vy2wxrq15txzyu167dtu,500,0,false 43 | user:wkiw8toit90nleub0rxn,3000,0,false 44 | user:wmusz2iz8ojyuerawbw9,494,6,false 45 | user:x17lcsdfwezng4jcbd8c,500,0,false 46 | user:xog3xntlmh1k53edezw2,500,0,false 47 | user:xxrfndpjlut9oku3udgf,2490,10,false 48 | user:zkd7d01tnbngi0kf5wfq,0,0,false -------------------------------------------------------------------------------- /assets/dbfp.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Implex-ltd/HcaptchaSolver/ca49dd8e0bdce085e8f589dcbc5f19afc94c835c/assets/dbfp.txt -------------------------------------------------------------------------------- /assets/nop.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Implex-ltd/HcaptchaSolver/ca49dd8e0bdce085e8f589dcbc5f19afc94c835c/assets/nop.txt -------------------------------------------------------------------------------- /cmd/hcsolver/config/storage.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net/http" 7 | "os" 8 | "time" 9 | 10 | "github.com/BurntSushi/toml" 11 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha/fingerprint" 12 | "github.com/Implex-ltd/hcsolver/internal/recognizer" 13 | "github.com/mattn/go-colorable" 14 | "go.uber.org/zap" 15 | "go.uber.org/zap/zapcore" 16 | ) 17 | 18 | var ( 19 | Logger *zap.Logger 20 | Config = Cfg{} 21 | ) 22 | 23 | type Cfg struct { 24 | API struct { 25 | Port int `toml:"port"` 26 | } `toml:"api"` 27 | Ratelimit struct { 28 | APIMax int `toml:"api_max"` 29 | APIExpiration int `toml:"api_expiration"` 30 | } `toml:"ratelimit"` 31 | Database struct { 32 | Username string `toml:"username"` 33 | Password string `toml:"password"` 34 | IP string `toml:"ip"` 35 | Port int `toml:"port"` 36 | } `toml:"database"` 37 | } 38 | 39 | func CreateLogFile() *os.File { 40 | logFileName := fmt.Sprintf("../../assets/logs/%s", time.Now().Format("2006-01-02_15-04-05")+".json") 41 | 42 | err := os.MkdirAll("../../assets/logs/", os.ModePerm) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | file, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 48 | if err != nil { 49 | panic(err) 50 | } 51 | return file 52 | } 53 | 54 | func LoadSettings() { 55 | rand.New(rand.NewSource(time.Now().UnixNano())) 56 | 57 | if _, err := toml.DecodeFile("../../scripts/config.toml", &Config); err != nil { 58 | panic(err) 59 | } 60 | 61 | encoder := zap.NewDevelopmentEncoderConfig() 62 | encoder.EncodeLevel = zapcore.CapitalColorLevelEncoder 63 | 64 | fileEncoder := zap.NewProductionEncoderConfig() 65 | fileEncoder.EncodeTime = zapcore.ISO8601TimeEncoder 66 | 67 | core := zapcore.NewTee( 68 | zapcore.NewCore( 69 | zapcore.NewConsoleEncoder(encoder), 70 | zapcore.AddSync(colorable.NewColorableStdout()), 71 | zapcore.DebugLevel, 72 | ), 73 | /*zapcore.NewCore( 74 | zapcore.NewJSONEncoder(fileEncoder), 75 | zapcore.AddSync(CreateLogFile()), 76 | zapcore.DebugLevel, 77 | ),*/ 78 | ) 79 | 80 | Logger = zap.New(core) 81 | count, err := recognizer.LoadHash("../../assets/hash.csv") 82 | if err != nil { 83 | panic(err) 84 | } 85 | 86 | Logger.Info("Loaded hash csv", 87 | zap.Int("count", count), 88 | ) 89 | 90 | recognizer.Hashlist.Range(func(key, value interface{}) bool { 91 | prompt := key.(string) 92 | hashes := value.([]string) 93 | 94 | Logger.Info("Loaded hash", 95 | zap.String("prompt", prompt), 96 | zap.Int("count", len(hashes)), 97 | ) 98 | 99 | return true 100 | }) 101 | 102 | selectCount, err := recognizer.LoadHashSelect("../../assets/area_hash.csv") 103 | if err != nil { 104 | panic(err) 105 | } 106 | 107 | Logger.Info("Loaded select hash csv", 108 | zap.Int("count", selectCount), 109 | ) 110 | 111 | recognizer.Selectlist.Range(func(key, value interface{}) bool { 112 | prompt := key.(string) 113 | hashDataList := value.([]recognizer.HashData) 114 | 115 | Logger.Info("Loaded hash data", 116 | zap.String("prompt", prompt), 117 | zap.Int("count", len(hashDataList)), 118 | ) 119 | 120 | return true 121 | }) 122 | 123 | recognizer.LoadAnswer("../../assets/questions.txt") 124 | 125 | // recognizer http client 126 | t := http.DefaultTransport.(*http.Transport).Clone() 127 | t.MaxIdleConns = 500 128 | t.MaxConnsPerHost = 500 129 | t.MaxIdleConnsPerHost = 500 130 | 131 | recognizer.Client = &http.Client{ 132 | Timeout: 15 * time.Second, 133 | Transport: t, 134 | } 135 | 136 | fingerprint.CollectFpArray.RandomiseIndex() 137 | } 138 | -------------------------------------------------------------------------------- /cmd/hcsolver/database/database.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Implex-ltd/hcsolver/internal/model" 7 | "github.com/Implex-ltd/hcsolver/internal/utils" 8 | "github.com/surrealdb/surrealdb.go" 9 | 10 | ) 11 | 12 | var ( 13 | TaskDB *surrealdb.DB 14 | FpDB *surrealdb.DB 15 | UserDB *surrealdb.DB 16 | ) 17 | 18 | // Add new field to all records into the DB. 19 | func mergeAll() { 20 | UserDB.Query(` 21 | UPDATE user MERGE { 22 | settings: { 23 | bypass_restricted_sites: false, 24 | }, 25 | }; 26 | `, nil) 27 | } 28 | 29 | func showUsers() { 30 | req, err := UserDB.Select("user") 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | var FingerprintSlice []model.User 36 | err = surrealdb.Unmarshal(req, &FingerprintSlice) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | for _, fp := range FingerprintSlice { 42 | fmt.Println(fp.BypassRestrictedSites) 43 | 44 | } 45 | } 46 | 47 | func doBackup() { 48 | req, err := UserDB.Select("user") 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | var FingerprintSlice []model.User 54 | err = surrealdb.Unmarshal(req, &FingerprintSlice) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | for _, fp := range FingerprintSlice { 60 | line := fmt.Sprintf("%s,%d,%d,%v", fp.ID, fp.Balance, fp.SolvedHcaptcha, fp.BypassRestrictedSites) 61 | fmt.Println(line) 62 | 63 | utils.AppendLine(line, "backup.csv") 64 | } 65 | utils.AppendLine("-----------------------------------------", "backup.csv") 66 | } 67 | 68 | func ConnectDB(Ip, User, Pass string, Port int) { 69 | var err error 70 | 71 | TaskDB, err = surrealdb.New(fmt.Sprintf("ws://%s:%d/rpc", Ip, Port)) 72 | if err != nil { 73 | panic(err) 74 | } 75 | 76 | if _, err = TaskDB.Signin(map[string]interface{}{ 77 | "user": User, 78 | "pass": Pass, 79 | }); err != nil { 80 | panic(err) 81 | } 82 | 83 | if _, err = TaskDB.Use("task", "test"); err != nil { 84 | panic(err) 85 | } 86 | 87 | FpDB, err = surrealdb.New(fmt.Sprintf("ws://%s:%d/rpc", Ip, Port)) 88 | if err != nil { 89 | panic(err) 90 | } 91 | 92 | if _, err = FpDB.Signin(map[string]interface{}{ 93 | "user": User, 94 | "pass": Pass, 95 | }); err != nil { 96 | panic(err) 97 | } 98 | 99 | if _, err = FpDB.Use("fingerprint", "fp"); err != nil { 100 | panic(err) 101 | } 102 | 103 | UserDB, err = surrealdb.New(fmt.Sprintf("ws://%s:%d/rpc", Ip, Port)) 104 | if err != nil { 105 | panic(err) 106 | } 107 | 108 | if _, err = UserDB.Signin(map[string]interface{}{ 109 | "user": User, 110 | "pass": Pass, 111 | }); err != nil { 112 | panic(err) 113 | } 114 | 115 | if _, err = UserDB.Use("users", "user"); err != nil { 116 | panic(err) 117 | } 118 | 119 | //mergeAll() 120 | 121 | //showUsers() 122 | doBackup() 123 | } 124 | -------------------------------------------------------------------------------- /cmd/hcsolver/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | 7 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/config" 8 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/database" 9 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/router" 10 | "github.com/Implex-ltd/hcsolver/internal/utils" 11 | _ "github.com/Implex-ltd/hcsolver/internal/utils" 12 | "github.com/gofiber/fiber/v2" 13 | "github.com/surrealdb/surrealdb.go" 14 | ) 15 | 16 | type Fingerprint struct { 17 | ID string `json:"id,omitempty"` 18 | Fingerprint string `json:"fp"` 19 | } 20 | 21 | func check_fp() { 22 | req, err := database.FpDB.Select("fp") 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | var FingerprintSlice []Fingerprint 28 | err = surrealdb.Unmarshal(req, &FingerprintSlice) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | valid := 0 34 | for _, fp := range FingerprintSlice { 35 | fmt.Println(fp.ID) 36 | _, err := base64.RawStdEncoding.DecodeString(fp.Fingerprint) 37 | if err != nil { 38 | continue 39 | } 40 | valid++ 41 | 42 | } 43 | 44 | fmt.Println("valid fp:", valid) 45 | fmt.Println("total fp:", len(FingerprintSlice)) 46 | } 47 | 48 | func save_fp() { 49 | req, err := database.FpDB.Select("fp") 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | var FingerprintSlice []Fingerprint 55 | err = surrealdb.Unmarshal(req, &FingerprintSlice) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | valid := 0 61 | for _, fp := range FingerprintSlice { 62 | fmt.Println(fp.ID) 63 | utils.AppendLine(fp.Fingerprint, "dbfp.txt") 64 | valid++ 65 | 66 | } 67 | 68 | fmt.Println("valid fp:", valid) 69 | fmt.Println("total fp:", len(FingerprintSlice)) 70 | } 71 | 72 | func main() { 73 | config.LoadSettings() 74 | database.ConnectDB(config.Config.Database.IP, config.Config.Database.Username, config.Config.Database.Password, config.Config.Database.Port) 75 | 76 | save_fp() 77 | 78 | app := fiber.New() 79 | router.SetupRoutes(app) 80 | 81 | config.Logger.Info("DB Connected and api online") 82 | if err := app.Listen(fmt.Sprintf(":%d", config.Config.API.Port)); err != nil { 83 | panic(err) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cmd/hcsolver/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/gofiber/fiber/v2" 8 | 9 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/config" 10 | 11 | taskRoutes "github.com/Implex-ltd/hcsolver/internal/routes/task" 12 | userRoutes "github.com/Implex-ltd/hcsolver/internal/routes/user" 13 | 14 | "github.com/gofiber/fiber/v2/middleware/limiter" 15 | "github.com/gofiber/fiber/v2/middleware/logger" 16 | ) 17 | 18 | func SetupRoutes(app *fiber.App) { 19 | api := app.Group("/api", logger.New()) 20 | 21 | api.Use(limiter.New(limiter.Config{ 22 | Next: func(c *fiber.Ctx) bool { 23 | return c.IP() == "127.0.0.1" 24 | }, 25 | Max: config.Config.Ratelimit.APIMax, 26 | Expiration: time.Duration(config.Config.Ratelimit.APIExpiration) * time.Second, 27 | KeyGenerator: func(c *fiber.Ctx) string { 28 | return c.Get("x-forwarded-for") 29 | }, 30 | LimitReached: func(c *fiber.Ctx) error { 31 | return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ 32 | "success": false, 33 | "data": errors.New("ratelimit exceeded"), 34 | }) 35 | }, 36 | })) 37 | 38 | taskRoutes.SetupTaskRoutes(api) 39 | userRoutes.SetupUserRoutes(api) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/hcsolver/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | go run . 3 | pause -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | surrealdb: 5 | container_name: surrealdb 6 | image: surrealdb/surrealdb:1.0.0-beta.9-20230402 7 | command: start --log debug --user root --pass 56484qsd844qsdq48sd4 file://database.db 8 | ports: 9 | - "88:8000" 10 | volumes: 11 | - ./data/database.db:/database.db -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Implex-ltd/hcsolver 2 | 3 | go 1.21.0 4 | 5 | require ( 6 | github.com/0xF7A4C6/GoCycle v0.0.0-20230623065856-dd8a5a180c87 7 | github.com/BurntSushi/toml v1.3.2 8 | github.com/Implex-ltd/cleanhttp v0.0.0-20230928154826-e43956ac2a1b 9 | github.com/Implex-ltd/fingerprint-client v0.0.0-20230830235428-380e1bbcfa4b 10 | github.com/bogdanfinn/fhttp v0.5.24 11 | github.com/cespare/xxhash v1.1.0 12 | github.com/gofiber/fiber/v2 v2.50.0 13 | github.com/golang-jwt/jwt/v5 v5.0.0 14 | github.com/google/uuid v1.3.1 15 | github.com/mattn/go-colorable v0.1.13 16 | github.com/shivakar/xxhash v0.0.0-20160821164220-5ea66fb45566 17 | github.com/surrealdb/surrealdb.go v0.2.1 18 | github.com/valyala/fasthttp v1.50.0 19 | github.com/zenthangplus/goccm v1.1.3 20 | go.uber.org/zap v1.26.0 21 | gonum.org/v1/plot v0.14.0 22 | ) 23 | 24 | require ( 25 | git.sr.ht/~sbinet/gg v0.5.0 // indirect 26 | github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect 27 | github.com/andybalholm/brotli v1.0.6 // indirect 28 | github.com/bogdanfinn/tls-client v1.6.1 // indirect 29 | github.com/bogdanfinn/utls v1.5.16 // indirect 30 | github.com/campoy/embedmd v1.0.0 // indirect 31 | github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect 32 | github.com/go-fonts/liberation v0.3.1 // indirect 33 | github.com/go-jose/go-jose/v3 v3.0.1 // indirect 34 | github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 // indirect 35 | github.com/go-pdf/fpdf v0.9.0 // indirect 36 | github.com/go-stack/stack v1.8.1 // indirect 37 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect 38 | github.com/gorilla/websocket v1.5.0 // indirect 39 | github.com/klauspost/compress v1.17.1 // indirect 40 | github.com/mattn/go-isatty v0.0.20 // indirect 41 | github.com/mattn/go-runewidth v0.0.15 // indirect 42 | github.com/mssola/user_agent v0.6.0 // indirect 43 | github.com/philhofer/fwd v1.1.2 // indirect 44 | github.com/playwright-community/playwright-go v0.3800.0 // indirect 45 | github.com/pmezard/go-difflib v1.0.0 // indirect 46 | github.com/rivo/uniseg v0.4.4 // indirect 47 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect 48 | github.com/tinylib/msgp v1.1.8 // indirect 49 | github.com/valyala/bytebufferpool v1.0.0 // indirect 50 | github.com/valyala/tcplisten v1.0.0 // indirect 51 | go.uber.org/multierr v1.11.0 // indirect 52 | golang.org/x/crypto v0.14.0 // indirect 53 | golang.org/x/image v0.13.0 // indirect 54 | golang.org/x/net v0.17.0 // indirect 55 | golang.org/x/sys v0.13.0 // indirect 56 | golang.org/x/text v0.13.0 // indirect 57 | ) 58 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | git.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo= 2 | git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE= 3 | git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8= 4 | git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo= 5 | github.com/0xF7A4C6/GoCycle v0.0.0-20230623065856-dd8a5a180c87 h1:V7ysylc0ejPqpii7EBTrdtHXq3xS9rJa8eRzfQ3HKm8= 6 | github.com/0xF7A4C6/GoCycle v0.0.0-20230623065856-dd8a5a180c87/go.mod h1:FFXl+R9/Wf68Mo523+cULQTQgLCUdTT4MHHyHN2z6bQ= 7 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 8 | github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= 9 | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 10 | github.com/Implex-ltd/cleanhttp v0.0.0-20230928154826-e43956ac2a1b h1:EDnCO5V8s/SHPYAtsI4517lG3LkVPtZQtBi5vAae9Yc= 11 | github.com/Implex-ltd/cleanhttp v0.0.0-20230928154826-e43956ac2a1b/go.mod h1:Ae2H0S489uYbplD58DpdwN3laKMuWjirUrSWyQdciTc= 12 | github.com/Implex-ltd/fingerprint-client v0.0.0-20230830235428-380e1bbcfa4b h1:TudRrLEGAwByx88oj6sEJJmumNJoKymzAeQ22xsMBAc= 13 | github.com/Implex-ltd/fingerprint-client v0.0.0-20230830235428-380e1bbcfa4b/go.mod h1:wqakRN43WgxWu2rZJMJomsWxS0DuGZo3or/gNOn6YvI= 14 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 15 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 16 | github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= 17 | github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= 18 | github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= 19 | github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= 20 | github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= 21 | github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 22 | github.com/bogdanfinn/fhttp v0.5.24 h1:OlyBKjvJp6a3TotN3wuj4mQHHRbfK7QUMrzCPOZGhRc= 23 | github.com/bogdanfinn/fhttp v0.5.24/go.mod h1:brqi5woc5eSCVHdKYBV8aZLbO7HGqpwyDLeXW+fT18I= 24 | github.com/bogdanfinn/tls-client v1.6.1 h1:GTIqQssFoIvLaDf4btoYRzDhUzudLqYD4axvfUCXl3I= 25 | github.com/bogdanfinn/tls-client v1.6.1/go.mod h1:FtwQ3DndVZ0xAOO704v4iNAgbHOcEc5kPk9tjICTNQ0= 26 | github.com/bogdanfinn/utls v1.5.16 h1:NhhWkegEcYETBMj9nvgO4lwvc6NcLH+znrXzO3gnw4M= 27 | github.com/bogdanfinn/utls v1.5.16/go.mod h1:mHeRCi69cUiEyVBkKONB1cAbLjRcZnlJbGzttmiuK4o= 28 | github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY= 29 | github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= 30 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 31 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 32 | github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= 33 | github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= 34 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 36 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 37 | github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= 38 | github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= 39 | github.com/go-fonts/latin-modern v0.3.1 h1:/cT8A7uavYKvglYXvrdDw4oS5ZLkcOU22fa2HJ1/JVM= 40 | github.com/go-fonts/latin-modern v0.3.1/go.mod h1:ysEQXnuT/sCDOAONxC7ImeEDVINbltClhasMAqEtRK0= 41 | github.com/go-fonts/liberation v0.3.1 h1:9RPT2NhUpxQ7ukUvz3jeUckmN42T9D9TpjtQcqK/ceM= 42 | github.com/go-fonts/liberation v0.3.1/go.mod h1:jdJ+cqF+F4SUL2V+qxBth8fvBpBDS7yloUL5Fi8GTGY= 43 | github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= 44 | github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= 45 | github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 h1:NxXI5pTAtpEaU49bpLpQoDsu1zrteW/vxzTz8Cd2UAs= 46 | github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= 47 | github.com/go-pdf/fpdf v0.9.0 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw= 48 | github.com/go-pdf/fpdf v0.9.0/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y= 49 | github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= 50 | github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= 51 | github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= 52 | github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= 53 | github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= 54 | github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 55 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= 56 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 57 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 58 | github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 59 | github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 60 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 61 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 62 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 63 | github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= 64 | github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 65 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 66 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 67 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 68 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 69 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 70 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 71 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 72 | github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4= 73 | github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= 74 | github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= 75 | github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= 76 | github.com/playwright-community/playwright-go v0.3800.0 h1:9ATUzVh8Hio6W1LvfjPX76PSR9jPc5YwyOzNzMzQV6w= 77 | github.com/playwright-community/playwright-go v0.3800.0/go.mod h1:mbNzMqt04IVRdhVfXWqmCxd81gCdL3BA5hj6/pVAIqM= 78 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 79 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 80 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 81 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 82 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 83 | github.com/shivakar/xxhash v0.0.0-20160821164220-5ea66fb45566 h1:h1L+TECcYYeIRv4ZWpcCe5fo0SEWVdqdpaBjv4NbqfQ= 84 | github.com/shivakar/xxhash v0.0.0-20160821164220-5ea66fb45566/go.mod h1:w6mBKRlJaGS+Cavn1+D4hefmELN0JhpRLMGT3MZhSbY= 85 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= 86 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 87 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 88 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 89 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 90 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 91 | github.com/surrealdb/surrealdb.go v0.2.1 h1:E4rCnD75Ftq8/wTgbQ9kJgMACi3xMziXtMlRkm6Jh1g= 92 | github.com/surrealdb/surrealdb.go v0.2.1/go.mod h1:CloW70O49xyVO/rGO9cAZ62FEbl0/hreRHEJuamnndQ= 93 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc= 94 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng= 95 | github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= 96 | github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= 97 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 98 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 99 | github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= 100 | github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 101 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= 102 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 103 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 104 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 105 | github.com/zenthangplus/goccm v1.1.3 h1:66XVj24yexO2fCkBum8b+y6tOJ7Giq05LIn2vn3whGE= 106 | github.com/zenthangplus/goccm v1.1.3/go.mod h1:DUzu/BC4TkgUfXP8J1P6Md73Djt+0l0CHq001Pt4weA= 107 | go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= 108 | go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= 109 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 110 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 111 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 112 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 113 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 114 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 115 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 116 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 117 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 118 | golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= 119 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 120 | golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= 121 | golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 122 | golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= 123 | golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= 124 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 125 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 126 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 127 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 128 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 129 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 130 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 131 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 132 | golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 133 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 134 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 135 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 136 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 138 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 139 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 140 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 145 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 146 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 147 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 148 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 149 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 150 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 151 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 152 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 153 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 154 | golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 155 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 156 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 157 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 158 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 159 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 160 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 161 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 162 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 163 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 164 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 165 | golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= 166 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 167 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 168 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 169 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 170 | gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= 171 | gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= 172 | gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE= 173 | gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU= 174 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 175 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 176 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 177 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 178 | honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= 179 | rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= 180 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 181 | -------------------------------------------------------------------------------- /internal/handlers/task/hc_task_manager.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/config" 8 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha" 9 | "github.com/google/uuid" 10 | "go.uber.org/zap" 11 | "go.uber.org/zap/zapcore" 12 | ) 13 | 14 | func Newtask(config *hcaptcha.Config) (*HcaptchaTask, error) { 15 | T := HcaptchaTask{ 16 | Status: TaskStatus{ 17 | Status: STATUS_SOLVING, 18 | }, 19 | ID: uuid.NewString(), 20 | Config: config, 21 | } 22 | 23 | return &T, nil 24 | } 25 | 26 | func (T *HcaptchaTask) Create() error { 27 | hc, err := hcaptcha.NewHcaptcha(T.Config) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | config.Logger.Info("new task", 33 | zap.String("ua", T.Config.UserAgent), 34 | zap.String("key", T.Config.SiteKey), 35 | zap.String("dns", T.Config.Domain), 36 | zap.String("proxy", T.Config.Proxy), 37 | zap.String("rqdata", T.Config.Rqdata), 38 | zap.Bool("inv", T.Config.Invisible), 39 | zap.Bool("a11y", T.Config.FreeTextEntry), 40 | zap.Int("type", T.Config.TaskType), 41 | zap.Bool("turbo", T.Config.Turbo), 42 | zap.Int("st", T.Config.TurboSt), 43 | zap.String("hc_accessibility", T.Config.HcAccessibility), 44 | ) 45 | 46 | T.Captcha = hc 47 | return nil 48 | } 49 | 50 | func (T *HcaptchaTask) Solve() (*hcaptcha.ResponseCheckCaptcha, error) { 51 | st := time.Now() 52 | 53 | site, err := T.Captcha.CheckSiteConfig() 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | if !site.Pass { 59 | return nil, fmt.Errorf("checksiteconfig wont pass") 60 | } 61 | 62 | var captcha *hcaptcha.Captcha 63 | 64 | if !T.Config.FreeTextEntry { 65 | captcha, err = T.Captcha.GetChallenge(site) 66 | if err != nil { 67 | return nil, err 68 | } 69 | } else { 70 | captcha, err = T.Captcha.GetChallengeFreeTextEntry(site) 71 | if err != nil { 72 | return nil, err 73 | } 74 | } 75 | 76 | if captcha.GeneratedPassUUID != "" { 77 | config.Logger.Info("solved (oneclick)", 78 | zap.Object("perf", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 79 | enc.AddInt64("hsw", T.Captcha.HswProcessing.Milliseconds()) 80 | enc.AddInt64("recognition", T.Captcha.AnswerProcessing.Milliseconds()) 81 | enc.AddInt64("task", time.Since(st).Milliseconds()) 82 | enc.AddInt64("cc", T.Captcha.CheckProcessing.Milliseconds()) 83 | enc.AddInt64("gc", T.Captcha.GetProcessing.Milliseconds()) 84 | enc.AddInt64("gs", T.Captcha.SiteConfigProcessing.Milliseconds()) 85 | return nil 86 | })), 87 | zap.Object("config", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 88 | enc.AddString("rqdata", T.Config.Rqdata) 89 | enc.AddBool("inv", T.Config.Invisible) 90 | enc.AddBool("a11y", T.Config.FreeTextEntry) 91 | enc.AddInt("type", T.Config.TaskType) 92 | enc.AddString("ua", T.Config.UserAgent) 93 | enc.AddString("key", T.Config.SiteKey) 94 | enc.AddString("dns", T.Config.Domain) 95 | enc.AddString("proxy", T.Config.Proxy) 96 | return nil 97 | })), 98 | zap.Object("captcha", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 99 | enc.AddString("key", captcha.GeneratedPassUUID) 100 | enc.AddString("prompt", captcha.RequesterQuestion.En) 101 | enc.AddString("type", captcha.RequestType) 102 | return nil 103 | })), 104 | ) 105 | 106 | return &hcaptcha.ResponseCheckCaptcha{ 107 | C: captcha.C, 108 | Pass: captcha.Pass, 109 | GeneratedPassUUID: captcha.GeneratedPassUUID, 110 | Expiration: captcha.Expiration, 111 | }, nil 112 | } 113 | 114 | if T.Config.OneClick { 115 | config.Logger.Info("one-click failed", 116 | zap.Object("perf", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 117 | enc.AddInt64("hsw", T.Captcha.HswProcessing.Milliseconds()) 118 | enc.AddInt64("recognition", T.Captcha.AnswerProcessing.Milliseconds()) 119 | enc.AddInt64("task", time.Since(st).Milliseconds()) 120 | enc.AddInt64("cc", T.Captcha.CheckProcessing.Milliseconds()) 121 | enc.AddInt64("gc", T.Captcha.GetProcessing.Milliseconds()) 122 | enc.AddInt64("gs", T.Captcha.SiteConfigProcessing.Milliseconds()) 123 | return nil 124 | })), 125 | zap.Object("config", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 126 | enc.AddString("rqdata", T.Config.Rqdata) 127 | enc.AddBool("inv", T.Config.Invisible) 128 | enc.AddBool("a11y", T.Config.FreeTextEntry) 129 | enc.AddInt("type", T.Config.TaskType) 130 | enc.AddString("ua", T.Config.UserAgent) 131 | enc.AddString("key", T.Config.SiteKey) 132 | enc.AddString("dns", T.Config.Domain) 133 | enc.AddString("proxy", T.Config.Proxy) 134 | return nil 135 | })), 136 | zap.Object("captcha", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 137 | enc.AddString("key", "") 138 | enc.AddString("prompt", captcha.RequesterQuestion.En) 139 | enc.AddString("type", captcha.RequestType) 140 | return nil 141 | })), 142 | ) 143 | 144 | return nil, fmt.Errorf("one-click failed, captcha spawned") 145 | } 146 | 147 | response, err := T.Captcha.CheckCaptcha(captcha) 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | if !response.Pass { 153 | return nil, fmt.Errorf("captcha wont pass") 154 | } 155 | 156 | config.Logger.Info("solved", 157 | zap.Object("perf", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 158 | enc.AddInt64("hsw", T.Captcha.HswProcessing.Milliseconds()) 159 | enc.AddInt64("recognition", T.Captcha.AnswerProcessing.Milliseconds()) 160 | enc.AddInt64("task", time.Since(st).Milliseconds()) 161 | enc.AddInt64("cc", T.Captcha.CheckProcessing.Milliseconds()) 162 | enc.AddInt64("gc", T.Captcha.GetProcessing.Milliseconds()) 163 | enc.AddInt64("gs", T.Captcha.SiteConfigProcessing.Milliseconds()) 164 | return nil 165 | })), 166 | zap.Object("config", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 167 | enc.AddString("rqdata", T.Config.Rqdata) 168 | enc.AddBool("inv", T.Config.Invisible) 169 | enc.AddBool("a11y", T.Config.FreeTextEntry) 170 | enc.AddInt("type", T.Config.TaskType) 171 | enc.AddString("ua", T.Config.UserAgent) 172 | enc.AddString("key", T.Config.SiteKey) 173 | enc.AddString("dns", T.Config.Domain) 174 | enc.AddString("proxy", T.Config.Proxy) 175 | return nil 176 | })), 177 | zap.Object("captcha", zapcore.ObjectMarshalerFunc(func(enc zapcore.ObjectEncoder) error { 178 | enc.AddString("key", response.GeneratedPassUUID) 179 | enc.AddString("prompt", captcha.RequesterQuestion.En) 180 | enc.AddString("type", captcha.RequestType) 181 | return nil 182 | })), 183 | ) 184 | 185 | return response, nil 186 | } 187 | -------------------------------------------------------------------------------- /internal/handlers/task/hc_task_worker.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "regexp" 7 | "strings" 8 | "time" 9 | 10 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/config" 11 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/database" 12 | "github.com/Implex-ltd/hcsolver/internal/handlers/task/validator" 13 | "github.com/Implex-ltd/hcsolver/internal/handlers/user" 14 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha" 15 | "github.com/Implex-ltd/hcsolver/internal/model" 16 | "github.com/gofiber/fiber/v2" 17 | "github.com/surrealdb/surrealdb.go" 18 | "go.uber.org/zap" 19 | ) 20 | 21 | const ( 22 | TYPE_ENTERPRISE = 0 23 | TYPE_NORMAL = 1 24 | 25 | SUBMIT = 7000 26 | MIN = 1000 27 | ) 28 | 29 | var ( 30 | SitekeyPattern = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) 31 | DomainPattern = regexp.MustCompile(`^(?i)[a-z0-9-]+(\.[a-z0-9-]+)+$`) 32 | ) 33 | 34 | func IsValidUUID(input string) bool { 35 | return SitekeyPattern.MatchString(input) 36 | } 37 | 38 | func IsDomainName(input string) bool { 39 | return DomainPattern.MatchString(input) 40 | } 41 | 42 | func checkBody(B *BodyNewSolveTask) (errors []string) { 43 | if B.Domain == "" { 44 | errors = append(errors, "domain is missing") 45 | } else { 46 | if !IsDomainName(B.Domain) { 47 | errors = append(errors, "domain is invalid") 48 | } 49 | } 50 | 51 | if B.SiteKey == "" { 52 | errors = append(errors, "site_key is missing") 53 | } else { 54 | if !IsValidUUID(B.SiteKey) { 55 | errors = append(errors, "site_key is invalid") 56 | } 57 | } 58 | 59 | if B.TaskType != TYPE_ENTERPRISE /* && B.TaskType != TYPE_NORMAL */ { 60 | errors = append(errors, "task_type is invalid") 61 | } 62 | 63 | if len(B.UserAgent) > 255 { 64 | errors = append(errors, "user-agent is invalid") 65 | } 66 | 67 | if B.Proxy == "" { 68 | errors = append(errors, "please provide proxy") 69 | } else { 70 | if len(B.Proxy) > 500 || !strings.HasPrefix(B.Proxy, "http") { 71 | errors = append(errors, "invalid proxy format, please use http(s)://user:pass@ip:port or http(s)://ip:port") 72 | } 73 | } 74 | 75 | if B.Rqdata != "" { 76 | if len(B.Rqdata) > 15000 { 77 | errors = append(errors, "rqdata seems too long, please contact support") 78 | } 79 | } 80 | 81 | if B.Href != "" { 82 | if len(B.Href) > 5000 { 83 | errors = append(errors, "href seems too long, please contact support") 84 | } 85 | 86 | _, err := url.Parse(B.Href) 87 | if err != nil { 88 | errors = append(errors, "cannot parse href, please provide right link (get it from your motionData on chrome devtools)") 89 | } 90 | } else { 91 | B.Href = fmt.Sprintf("https://%s", B.Domain) 92 | } 93 | 94 | if B.HcAccessibility != "" { 95 | if len(B.HcAccessibility) > 15000 { 96 | errors = append(errors, "hc_accessibility seems too long, please contact support") 97 | } 98 | } 99 | 100 | if !B.FreeTextEntry { 101 | errors = append(errors, "please enable a11y_tfe, if your target doesn't support it, we can't solve your captchas for now") 102 | } 103 | 104 | if B.Dr != "" { 105 | if len(B.Dr) > 500 || !strings.HasPrefix(B.Dr, "https") { 106 | errors = append(errors, "dr must be a link") 107 | } 108 | } 109 | 110 | if len(errors) != 0 { 111 | return errors 112 | } 113 | 114 | return nil 115 | } 116 | 117 | func CreateTask(c *fiber.Ctx) error { 118 | authToken, ok := c.GetReqHeaders()["Authorization"] 119 | if !ok { 120 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 121 | "success": false, 122 | "data": "you must provide api-key for this endpoint", 123 | }) 124 | } 125 | auth := authToken[0] 126 | 127 | db := database.TaskDB 128 | task := new(model.Task) 129 | 130 | var taskData BodyNewSolveTask 131 | 132 | if err := c.BodyParser(&taskData); err != nil { 133 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 134 | "success": false, 135 | "data": "invalid task body", 136 | }) 137 | } 138 | 139 | if err := checkBody(&taskData); err != nil { 140 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 141 | "success": false, 142 | "data": fmt.Sprintf("invalid task body fields: %v", strings.Join(err, ", ")), 143 | }) 144 | } 145 | 146 | settings, err := validator.Validate(taskData.SiteKey) 147 | if err != nil { 148 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 149 | "success": false, 150 | "data": "cannot validate this site-key", 151 | }) 152 | } 153 | 154 | switch taskData.Turbo { 155 | case true: 156 | if taskData.TurboSt < 1000 { 157 | taskData.TurboSt = 1000 158 | } 159 | if taskData.TurboSt > 15000 { 160 | taskData.TurboSt = 15000 161 | } 162 | case false: 163 | taskData.TurboSt = SUBMIT 164 | } 165 | 166 | if settings != nil { 167 | if settings.AlwaysText && !taskData.FreeTextEntry { 168 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 169 | "success": false, 170 | "data": fmt.Sprintf("bad config 'a11y_tfe' for the current domain '%s', please check required settings using '/api/misc/check/%s'", taskData.Domain, taskData.Domain), 171 | }) 172 | } 173 | 174 | if taskData.Turbo { 175 | fmt.Println(taskData.TurboSt) 176 | if taskData.TurboSt < settings.MinSubmitTime || taskData.TurboSt > settings.MaxSubmitTime { 177 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 178 | "success": false, 179 | "data": fmt.Sprintf("bad config 'turbo_st' for the current domain '%s', please check required settings using '/api/misc/check/%s'", taskData.Domain, taskData.Domain), 180 | }) 181 | } 182 | } 183 | 184 | if !taskData.OneclickOnly && settings.OneclickOnly { 185 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 186 | "success": false, 187 | "data": fmt.Sprintf("bad config 'oneclick_only' for the current domain '%s', please check required settings using '/api/misc/check/%s'", taskData.Domain, taskData.Domain), 188 | }) 189 | } 190 | 191 | if taskData.Domain != settings.Domain { 192 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 193 | "success": false, 194 | "data": "domain doesn't match the site-key", 195 | }) 196 | } 197 | } 198 | 199 | authUser, err := user.GetUserByID(auth) 200 | if err != nil { 201 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 202 | "success": false, 203 | "data": "invalid api-key", 204 | }) 205 | } 206 | 207 | selectedUser := new(model.User) 208 | err = surrealdb.Unmarshal(authUser, &selectedUser) 209 | if err != nil { 210 | return c.Status(500).JSON(fiber.Map{ 211 | "success": false, 212 | "data": "failed to parse user, please report to admin", 213 | }) 214 | } 215 | 216 | if selectedUser.Balance <= 0 { 217 | return c.Status(fiber.StatusTeapot).JSON(fiber.Map{ 218 | "success": false, 219 | "data": "no credit left, please refill balance", 220 | }) 221 | } 222 | 223 | if selectedUser.ThreadUsedHcaptcha >= selectedUser.ThreadMaxHcaptcha { 224 | return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ 225 | "success": false, 226 | "data": "you reached max thread limit, please buy more slot", 227 | }) 228 | } 229 | 230 | if settings != nil { 231 | if !settings.Enabled && !selectedUser.BypassRestrictedSites { 232 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 233 | "success": false, 234 | "data": "this site-key is disabled, please contact support", 235 | }) 236 | } 237 | } 238 | 239 | T, err := Newtask(&hcaptcha.Config{ 240 | UserAgent: taskData.UserAgent, 241 | SiteKey: taskData.SiteKey, 242 | Domain: taskData.Domain, 243 | Proxy: taskData.Proxy, 244 | Logger: config.Logger, 245 | TaskType: taskData.TaskType, 246 | Invisible: taskData.Invisible, 247 | FreeTextEntry: taskData.FreeTextEntry, 248 | Turbo: taskData.Turbo, 249 | TurboSt: taskData.TurboSt, 250 | HcAccessibility: taskData.HcAccessibility, 251 | OneClick: taskData.OneclickOnly, 252 | Rqdata: taskData.Rqdata, 253 | Href: taskData.Href, 254 | Dr: taskData.Dr, 255 | Exec: taskData.Exec, 256 | }) 257 | 258 | if err != nil { 259 | return c.Status(500).JSON(fiber.Map{ 260 | "success": false, 261 | "data": fmt.Sprintf("new task initialization failed: %v", err.Error()), 262 | }) 263 | } 264 | 265 | if err := T.Create(); err != nil { 266 | return c.Status(500).JSON(fiber.Map{ 267 | "success": false, 268 | "data": fmt.Sprintf("could not create new task: %v", err.Error()), 269 | }) 270 | } 271 | 272 | task.Status = T.Status.Status 273 | task.Token = T.Status.Token 274 | 275 | data, err := db.Create("task", task) 276 | if err != nil { 277 | config.Logger.Error("db-error", zap.Error(err)) 278 | 279 | return c.Status(500).JSON(fiber.Map{ 280 | "success": false, 281 | "data": fmt.Sprintf("db error (please report to admin): %v", err.Error()), 282 | }) 283 | } 284 | 285 | if data == nil { 286 | return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ 287 | "success": false, 288 | "data": "your payload is probably wrong, or a backend issue. please read documentation.", 289 | }) 290 | } 291 | 292 | createTask := make([]model.Task, 1) 293 | 294 | if err := surrealdb.Unmarshal(data, &createTask); err != nil { 295 | config.Logger.Error("db-error", zap.Error(err)) 296 | 297 | return c.Status(500).JSON(fiber.Map{ 298 | "success": false, 299 | "data": fmt.Sprintf("db error (please report to admin): %v", err.Error()), 300 | }) 301 | } 302 | 303 | go func(task *model.Task) { 304 | captcha, err := T.Solve() 305 | 306 | if err != nil { 307 | config.Logger.Error("error-solve", zap.Error(err)) 308 | 309 | task.Status = STATUS_ERROR 310 | task.Success = false 311 | task.Error = err.Error() 312 | 313 | if _, err = db.Change(createTask[0].ID, task); err != nil { 314 | config.Logger.Error("db-error", zap.Error(err)) 315 | return 316 | } 317 | 318 | go func() { 319 | time.Sleep(120 * time.Second) 320 | database.TaskDB.Delete(createTask[0].ID) 321 | }() 322 | 323 | return 324 | } 325 | 326 | task.Req = captcha.C.Req 327 | task.Status = STATUS_SOLVED 328 | task.Success = true 329 | task.Token = captcha.GeneratedPassUUID 330 | task.Expiration = captcha.Expiration 331 | task.UserAgent = T.Captcha.Manager.Manager.Fingerprint.Browser.UserAgent 332 | 333 | if _, err := db.Update(createTask[0].ID, task); err != nil { 334 | config.Logger.Error("db-error", zap.Error(err)) 335 | return 336 | } 337 | 338 | go func() { 339 | database.UserDB.Query(` 340 | BEGIN TRANSACTION; 341 | 342 | UPDATE $user SET balance -= $to_add; 343 | UPDATE $user SET solved_hcaptcha += $to_add; 344 | 345 | COMMIT TRANSACTION; 346 | `, map[string]any{ 347 | "user": auth, 348 | "to_add": 1, 349 | }) 350 | 351 | time.Sleep(time.Duration(task.Expiration) * time.Second) 352 | database.TaskDB.Delete(createTask[0].ID) 353 | }() 354 | }(task) 355 | 356 | return c.Status(200).JSON(fiber.Map{ 357 | "success": true, 358 | "data": data, 359 | }) 360 | } 361 | 362 | func GetTask(c *fiber.Ctx) error { 363 | id := c.Params("taskId") 364 | 365 | if !strings.HasPrefix(id, "task:") { 366 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 367 | "success": false, 368 | "data": "please provide 'task:id' format", 369 | }) 370 | } 371 | 372 | data, err := database.TaskDB.Select(id) 373 | if err != nil { 374 | config.Logger.Error("error-GetTask", zap.Error(err)) 375 | return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ 376 | "success": false, 377 | "data": err.Error(), 378 | }) 379 | } 380 | 381 | return c.Status(fiber.StatusOK).JSON(fiber.Map{ 382 | "success": true, 383 | "data": data, 384 | }) 385 | } 386 | 387 | func GetSitekeySettings(c *fiber.Ctx) error { 388 | id := c.Params("siteKey") 389 | 390 | if !IsValidUUID(id) { 391 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 392 | "success": false, 393 | "data": "invalid site-key", 394 | }) 395 | } 396 | 397 | settings, err := validator.Validate(id) 398 | if err != nil { 399 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 400 | "success": false, 401 | "data": err.Error(), 402 | }) 403 | } 404 | 405 | if settings == nil { 406 | return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ 407 | "success": true, 408 | "data": "this site-key doesn't have any restrictions.", 409 | }) 410 | } else { 411 | return c.Status(fiber.StatusOK).JSON(fiber.Map{ 412 | "success": true, 413 | "data": settings, 414 | }) 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /internal/handlers/task/struct.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha" 5 | ) 6 | 7 | const ( 8 | STATUS_SOLVING = 0 9 | STATUS_SOLVED = 1 10 | STATUS_ERROR = 2 11 | ) 12 | 13 | type TaskStatus struct { 14 | Status int 15 | Token string 16 | } 17 | 18 | type HcaptchaTask struct { 19 | Config *hcaptcha.Config 20 | Captcha *hcaptcha.Hcap 21 | Status TaskStatus 22 | ID string 23 | } 24 | 25 | type BodyNewSolveTask struct { 26 | Domain string `json:"domain"` 27 | SiteKey string `json:"site_key"` 28 | UserAgent string `json:"user_agent"` 29 | Proxy string `json:"proxy"` 30 | TaskType int `json:"task_type"` 31 | Invisible bool `json:"invisible"` 32 | Rqdata string `json:"rqdata"` 33 | FreeTextEntry bool `json:"a11y_tfe"` 34 | Turbo bool `json:"turbo"` 35 | TurboSt int `json:"turbo_st"` 36 | HcAccessibility string `json:"hc_accessibility"` 37 | OneclickOnly bool `json:"oneclick_only"` 38 | 39 | // motion data customisation 40 | Href string `json:"href"` 41 | Exec bool `json:"exec"` 42 | Dr string `json:"dr"` 43 | } 44 | -------------------------------------------------------------------------------- /internal/handlers/task/validator/validator.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | type Settings struct { 4 | MinSubmitTime int 5 | MaxSubmitTime int 6 | Domain string 7 | AlwaysText bool 8 | OneclickOnly bool 9 | Enabled bool 10 | Rate float64 11 | } 12 | 13 | var ( 14 | WebsiteSettings = map[string]Settings{ 15 | /* 16 | Epic game 17 | */ 18 | "b364b1fd-e3d8-4d24-8c41-77a19604b00d": { 19 | MinSubmitTime: 3200, 20 | MaxSubmitTime: 13000, 21 | AlwaysText: false, 22 | Domain: "www.epicgames.com", 23 | Enabled: false, 24 | }, 25 | 26 | /* 27 | Discord 28 | */ 29 | 30 | // friend req 31 | "a9b5fb07-92ff-493f-86fe-352a2803b3df": { 32 | MinSubmitTime: 3200, 33 | MaxSubmitTime: 13000, 34 | AlwaysText: true, 35 | Domain: "discord.com", 36 | Enabled: true, 37 | }, 38 | // join guild 39 | "b2b02ab5-7dae-4d6f-830e-7b55634c888b": { 40 | MinSubmitTime: 3200, 41 | MaxSubmitTime: 13000, 42 | AlwaysText: true, 43 | Domain: "discord.com", 44 | Enabled: true, 45 | }, 46 | // phone / email / login 47 | "f5561ba9-8f1e-40ca-9b5b-a0b3f719ef34": { 48 | MinSubmitTime: 3200, 49 | MaxSubmitTime: 13000, 50 | AlwaysText: true, 51 | Domain: "discord.com", 52 | Enabled: true, 53 | }, 54 | // register 55 | "4c672d35-0701-42b2-88c3-78380b0db560": { 56 | MinSubmitTime: 3200, 57 | MaxSubmitTime: 13000, 58 | AlwaysText: true, 59 | Domain: "discord.com", 60 | Enabled: false, 61 | }, 62 | } 63 | ) 64 | 65 | func Validate(sitekey string) (*Settings, error) { 66 | settings, exists := WebsiteSettings[sitekey] 67 | if exists { 68 | return &settings, nil 69 | } 70 | 71 | return nil, nil 72 | } 73 | -------------------------------------------------------------------------------- /internal/handlers/user/struct.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | type BodyAddBalance struct { 4 | Amount int `json:"amount"` 5 | User string `json:"user"` 6 | } 7 | 8 | type BodySetBypass struct { 9 | Enabled bool `json:"enabled"` 10 | User string `json:"user"` 11 | } 12 | -------------------------------------------------------------------------------- /internal/handlers/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/config" 10 | "github.com/Implex-ltd/hcsolver/cmd/hcsolver/database" 11 | "github.com/Implex-ltd/hcsolver/internal/model" 12 | "github.com/gofiber/fiber/v2" 13 | "github.com/surrealdb/surrealdb.go" 14 | "go.uber.org/zap" 15 | ) 16 | 17 | var ( 18 | mut = sync.Mutex{} 19 | ) 20 | 21 | /* 22 | Example: 23 | 24 | if err := EditUser(user, func(u *model.User) { 25 | u.Balance += balance 26 | }); err != nil { 27 | return err 28 | } 29 | */ 30 | func EditUser(user string, change func(*model.User)) (out interface{}, err error) { 31 | mut.Lock() 32 | defer mut.Unlock() 33 | 34 | data, err := database.UserDB.Select(user) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | selectedUser := new(model.User) 40 | err = surrealdb.Unmarshal(data, &selectedUser) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | change(selectedUser) 46 | 47 | if out, err := database.UserDB.Update(user, selectedUser); err != nil { 48 | return out, err 49 | } 50 | 51 | return out, nil 52 | } 53 | 54 | func GetUserByID(user string) (interface{}, error) { 55 | if !strings.HasPrefix(user, "user:") { 56 | return nil, errors.New("please provide 'user:id' format") 57 | } 58 | 59 | data, err := database.UserDB.Select(user) 60 | if err != nil { 61 | return false, nil 62 | } 63 | 64 | return data, nil 65 | } 66 | 67 | func CreateUser(c *fiber.Ctx) error { 68 | db := database.UserDB 69 | 70 | data, err := db.Create("user", model.User{ 71 | Balance: 0.0, 72 | SolvedHcaptcha: 0, 73 | ThreadMaxHcaptcha: 100, 74 | ThreadUsedHcaptcha: 0, 75 | BypassRestrictedSites: false, 76 | }) 77 | 78 | if err != nil { 79 | config.Logger.Error("db-error", zap.Error(err)) 80 | 81 | return c.Status(500).JSON(fiber.Map{ 82 | "success": false, 83 | "data": fmt.Sprintf("db error (please report to admin): %v", err.Error()), 84 | }) 85 | } 86 | 87 | return c.Status(200).JSON(fiber.Map{ 88 | "success": true, 89 | "data": data, 90 | }) 91 | } 92 | 93 | func GetUser(c *fiber.Ctx) error { 94 | id := c.Params("userId") 95 | 96 | if id == "" { 97 | return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ 98 | "success": false, 99 | "data": "invalid user ID", 100 | }) 101 | } 102 | 103 | data, err := GetUserByID(id) 104 | if err != nil { 105 | return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ 106 | "success": false, 107 | "data": err.Error(), 108 | }) 109 | } 110 | 111 | return c.Status(fiber.StatusOK).JSON(fiber.Map{ 112 | "success": true, 113 | "data": data, 114 | }) 115 | } 116 | 117 | func AddBalance(c *fiber.Ctx) error { 118 | var data BodyAddBalance 119 | 120 | if err := c.BodyParser(&data); err != nil { 121 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 122 | "success": false, 123 | "data": "invalid task body", 124 | }) 125 | } 126 | 127 | if !strings.HasPrefix(data.User, "user:") || data.Amount <= 0.0 { 128 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 129 | "success": false, 130 | "data": "invalid task body", 131 | }) 132 | } 133 | 134 | user, err := EditUser(data.User, func(u *model.User) { 135 | u.Balance += data.Amount 136 | }) 137 | 138 | if err != nil { 139 | return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ 140 | "success": false, 141 | "data": err.Error(), 142 | }) 143 | } 144 | 145 | return c.Status(fiber.StatusOK).JSON(fiber.Map{ 146 | "success": true, 147 | "data": user, 148 | }) 149 | } 150 | 151 | func SetypassRestricted(c *fiber.Ctx) error { 152 | var data BodySetBypass 153 | 154 | if err := c.BodyParser(&data); err != nil { 155 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 156 | "success": false, 157 | "data": "invalid task body", 158 | }) 159 | } 160 | 161 | if !strings.HasPrefix(data.User, "user:") { 162 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ 163 | "success": false, 164 | "data": "invalid task body", 165 | }) 166 | } 167 | 168 | user, err := EditUser(data.User, func(u *model.User) { 169 | u.BypassRestrictedSites = data.Enabled 170 | }) 171 | 172 | if err != nil { 173 | return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ 174 | "success": false, 175 | "data": err.Error(), 176 | }) 177 | } 178 | 179 | return c.Status(fiber.StatusOK).JSON(fiber.Map{ 180 | "success": true, 181 | "data": user, 182 | }) 183 | } 184 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/0.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/Implex-ltd/hcsolver/internal/utils" 12 | ) 13 | 14 | 15 | func (B *EventManager) Event_0() FingerprintEvent { 16 | return FingerprintEvent{ 17 | 0, 18 | fmt.Sprintf("%.14f", utils.RandomFloat64(15, 40)), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/107.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: https://gist.github.com/nikolahellatrigger/ea00832b010c0db8f0a0d5ca0d467072 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_107() FingerprintEvent { 8 | event := B.Fingerprint.Events["107"].(map[string]interface{}) 9 | 10 | data := Stringify([]interface{}{ 11 | B.Fingerprint.Screen["Width"], 12 | B.Fingerprint.Screen["Height"], 13 | B.Fingerprint.Screen["AvailWidth"], 14 | B.Fingerprint.Screen["AvailHeight"], 15 | B.Fingerprint.Screen["ColorDepth"], 16 | B.Fingerprint.Screen["PixelDepth"], 17 | event["touchEvent"].(bool), 18 | B.Fingerprint.Screen["MaxTouchPoints"], 19 | B.Fingerprint.Screen["DevicePixelRatio"], 20 | B.Fingerprint.Screen["outerWidth"], 21 | B.Fingerprint.Screen["outerHeight"], 22 | event["MediaQueryList"].([]interface{})[0], 23 | event["MediaQueryList"].([]interface{})[1], 24 | event["MediaQueryList"].([]interface{})[2], 25 | event["MediaQueryList"].([]interface{})[3], 26 | }) 27 | 28 | return FingerprintEvent{ 29 | 107, 30 | data, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1101.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1101() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1101, 10 | Stringify(B.Fingerprint.Hash["1101"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1103.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1103() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1103, 10 | Stringify(B.Fingerprint.Hash["1103"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1105.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1105() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1105, 10 | Stringify(B.Fingerprint.Hash["1105"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1107.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1107() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1107, 10 | Stringify(B.Fingerprint.Events["201"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1302.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1302() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1302, 10 | Stringify(B.Fingerprint.Events["1302"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1401.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1401() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1401, 10 | Stringify(B.Fingerprint.Timezone[0].(string)), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1402.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1402() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1402, 10 | Stringify(B.Fingerprint.Timezone), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1403.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1403() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1403, 10 | Stringify(EncStr(B.Fingerprint.Timezone[0].(string))), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1901.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1901() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1901, 10 | Stringify(B.Fingerprint.Hash["1901"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1902.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1902() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1902, 10 | Stringify(B.Fingerprint.Events["1902"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/1904.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_1904() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 1904, 10 | Stringify(B.Fingerprint.Events["1904"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/201.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_201() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 201, 10 | Stringify(B.Fingerprint.Hash["201"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/211.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_211() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 211, 10 | Stringify(B.Fingerprint.Events["211"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2401.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2401() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2401, 10 | Stringify(B.Fingerprint.Hash["2401"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2402.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2402() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2402, 10 | Stringify([]interface{}{ 11 | B.Fingerprint.Webgl.Vendor, 12 | B.Fingerprint.Webgl.Renderer, 13 | }), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2407.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2407() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2407, 10 | Stringify(B.Fingerprint.Hash["2407"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2408.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2408() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2408, 10 | "true", 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2409.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2409() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2409, 10 | Stringify(B.Fingerprint.Events["2409"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2410.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2410() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2410, 10 | Stringify(B.Fingerprint.Events["2410"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2411.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2411() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2411, 10 | Stringify(B.Fingerprint.Events["2411"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2412.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2412() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2412, 10 | Stringify(B.Fingerprint.Events["2412"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2413.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2413() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2413, 10 | Stringify(B.Fingerprint.Events["2413"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2414.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2414() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2414, 10 | Stringify(B.Fingerprint.Events["2414"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2415.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2415() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2415, 10 | Stringify(B.Fingerprint.Events["2415"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2416.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2416() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2416, 10 | Stringify(B.Fingerprint.Events["2416"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2417.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2417() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2417, 10 | Stringify(B.Fingerprint.Events["2417"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2420.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2420() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2420, 10 | Stringify([]interface{}{ 11 | EncStr(B.Fingerprint.Webgl.Vendor), 12 | EncStr(B.Fingerprint.Webgl.Renderer), 13 | }), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2801.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2801() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2801, 10 | Stringify(B.Fingerprint.Hash["2801"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/2805.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_2805() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 2805, 10 | // "[[277114314453,277114314460,277114314451,357114314456,277114314452,554228628898,57114314443,717114314371391,554228628897,277114314456,1108457257862,277114314450,554228628919,277114314460,277114314451],false]", 11 | Stringify(B.Fingerprint.Events["2805"]), 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/Implex-ltd/hcsolver/internal/utils" 11 | ) 12 | 13 | func (B *EventManager) Event_3() FingerprintEvent { 14 | return FingerprintEvent{ 15 | 3, 16 | fmt.Sprintf("%.2f", utils.RandomFloat64(30000, 60000)), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/301.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_301() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 301, 10 | Stringify(B.Fingerprint.Hash["301"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/304.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import "fmt" 8 | 9 | func (B *EventManager) Event_304() FingerprintEvent { 10 | return FingerprintEvent{ 11 | 304, 12 | fmt.Sprintf("%d", B.Fingerprint.Properties.CSS), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3210.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_3210() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 3210, 10 | Stringify(B.Fingerprint.Events["3210"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3211.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "strconv" 9 | ) 10 | 11 | func (B *EventManager) Event_3211() FingerprintEvent { 12 | event := B.Fingerprint.Events["3210"].([]any) 13 | numberAsString := strconv.FormatFloat(event[0].(float64), 'f', -1, 64) 14 | 15 | return FingerprintEvent{ 16 | 3211, 17 | Stringify(EncStr(numberAsString)), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3401.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import _ "github.com/Implex-ltd/hcsolver/internal/utils" 8 | 9 | func (B *EventManager) Event_3401(hash string) FingerprintEvent { 10 | return FingerprintEvent{ 11 | 3401, 12 | hash, //utils.RandomHash(19), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3403.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import "fmt" 8 | 9 | func (B *EventManager) Event_3403() FingerprintEvent { 10 | return FingerprintEvent{ 11 | 3403, 12 | Stringify([]interface{}{ 13 | []interface{}{ 14 | []interface{}{ 15 | fmt.Sprintf("https://newassets.hcaptcha.com/captcha/v1/%s/hcaptcha.js", B.HcapVersion), 16 | 0, 17 | 5, 18 | }, 19 | }, 20 | []interface{}{ 21 | []interface{}{ 22 | "*", 23 | 84, 24 | 9, 25 | }, 26 | }, 27 | }), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3501.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import "github.com/Implex-ltd/hcsolver/internal/utils" 8 | 9 | func (B *EventManager) Event_3501() FingerprintEvent { 10 | return FingerprintEvent{ 11 | 3501, 12 | Stringify([]interface{}{ 13 | /*[]interface{}{ 14 | "img:imgs.hcaptcha.com", 15 | 0, 16 | utils.RandomFloat64(20, 60), 17 | },*/ 18 | []interface{}{ 19 | "navigation:newassets.hcaptcha.com", 20 | utils.RandomFloat64(10, 50), 21 | utils.RandomFloat64(10, 50), 22 | utils.RandomFloat64(10, 50), 23 | }, 24 | []interface{}{ 25 | "script:newassets.hcaptcha.com", 26 | utils.RandomFloat64(5, 50), 27 | utils.RandomFloat64(10, 50), 28 | }, 29 | []interface{}{ 30 | "xmlhttprequest:hcaptcha.com", 31 | 0, 32 | utils.RandomFloat64(150, 250), 33 | }, 34 | }), 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3502.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/Implex-ltd/hcsolver/internal/utils" 11 | ) 12 | 13 | func (B *EventManager) Event_3502() FingerprintEvent { 14 | return FingerprintEvent{ 15 | 3502, 16 | fmt.Sprintf("%.14f", utils.RandomFloat64(1, 20)), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3503.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/Implex-ltd/hcsolver/internal/utils" 11 | ) 12 | 13 | func (B *EventManager) Event_3503() FingerprintEvent { 14 | return FingerprintEvent{ 15 | 3503, 16 | fmt.Sprintf("%.2f", utils.RandomFloat64(0, 1)), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3504.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | func (B *EventManager) Event_3504() FingerprintEvent { 13 | return FingerprintEvent{ 14 | 3504, 15 | fmt.Sprintf("%.1f", float64(time.Now().UnixNano())/1e6), 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3801.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_3801() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 3801, 10 | "45149", 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/3802.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/Implex-ltd/hcsolver/internal/utils" 11 | ) 12 | 13 | func (B *EventManager) Event_3802() FingerprintEvent { 14 | return FingerprintEvent{ 15 | 3802, 16 | fmt.Sprintf("%.13f", utils.RandomFloat64(300, 500)), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/401.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_401() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 401, 10 | Stringify(B.Fingerprint.Hash["401"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/402.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | import "fmt" 8 | 9 | func (B *EventManager) Event_402() FingerprintEvent { 10 | return FingerprintEvent{ 11 | 402, 12 | fmt.Sprintf("%v", B.Fingerprint.Properties.WindowFunctions), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/407.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_407(extraKeys ...[]string) FingerprintEvent { 8 | defaultKeys := []string{ 9 | "Raven", 10 | "_sharedLibs", 11 | "hsw", 12 | "__wdata", 13 | } 14 | 15 | keys := append([]string(nil), defaultKeys...) 16 | if len(extraKeys) > 0 { 17 | keys = append(keys, extraKeys[0]...) 18 | } 19 | 20 | return FingerprintEvent{ 21 | 407, 22 | Stringify([]interface{}{ 23 | []interface{}{ 24 | "loadTimes", 25 | "csi", 26 | "app", 27 | }, 28 | 35, 29 | 34, 30 | nil, 31 | false, 32 | false, 33 | true, 34 | 37, 35 | true, 36 | true, 37 | true, 38 | true, 39 | true, 40 | keys, 41 | []interface{}{ 42 | []interface{}{ 43 | "getElementsByClassName", 44 | []interface{}{}, 45 | }, 46 | []interface{}{ 47 | "getElementById", 48 | []interface{}{}, 49 | }, 50 | []interface{}{ 51 | "querySelector", 52 | []interface{}{}, 53 | }, 54 | []interface{}{ 55 | "querySelectorAll", 56 | []interface{}{}, 57 | }, 58 | }, 59 | []interface{}{}, 60 | true, 61 | }), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/412.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_412() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 412, 10 | Stringify(B.Fingerprint.Hash["412"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/604.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: https://gist.github.com/nikolahellatrigger/c4d6cf4ddb0ab219c38ddd133dc772eb 3 | */ 4 | 5 | package events 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "strings" 11 | ) 12 | 13 | func (B *EventManager) Event_604() FingerprintEvent { 14 | event := B.Fingerprint.Events["702"].(map[string]interface{}) 15 | log.Println(B.Fingerprint.Browser.UserAgent) 16 | 17 | return FingerprintEvent{ 18 | 604, 19 | Stringify([]interface{}{ 20 | B.Fingerprint.Browser.AppVersion, 21 | B.Fingerprint.Browser.UserAgent, 22 | B.Fingerprint.Browser.DeviceMemory, 23 | B.Fingerprint.Browser.HardwareConcurrency, 24 | B.Fingerprint.Browser.Language, 25 | B.Fingerprint.Browser.Languages, 26 | B.Fingerprint.Browser.Platform, 27 | nil, 28 | []interface{}{ 29 | fmt.Sprintf("Google Chrome %s", strings.Split(event["BrowserVersion"].(string), ".")[0]), 30 | "Not=A?Brand 8", 31 | fmt.Sprintf("Chromium %s", strings.Split(event["BrowserVersion"].(string), "."))[0], 32 | }, 33 | B.Fingerprint.Browser.Mobile, 34 | event["OsName"], 35 | B.Fingerprint.Properties.MIMETypes, 36 | B.Fingerprint.Properties.Plugins, 37 | B.Fingerprint.Browser.PDFViewerEnabled, 38 | 39 | B.Fingerprint.Connection.DownlinkMax, // "downlinkMax" in navigator.connection, 40 | B.Fingerprint.Connection.Rtt, // null == navigator.connection ? void 0 : navigator.connection.rtt, 41 | false, // navigator.webdriver, 42 | false, // null === (result = window.clientInformation) || void 0 === result ? void 0 : result.webdriver, 43 | true, // "share" in navigator, 44 | 45 | "[object Keyboard]", 46 | strings.Contains(strings.ToLower(B.Fingerprint.Browser.UserAgent), "brave"), 47 | strings.Contains(strings.ToLower(B.Fingerprint.Browser.UserAgent), "duckduckgo"), 48 | }), 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/702.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_702() FingerprintEvent { 8 | event := B.Fingerprint.Events["702"].(map[string]interface{}) 9 | 10 | return FingerprintEvent{ 11 | 702, 12 | Stringify([]interface{}{ 13 | event["OsName"], 14 | event["OsVersion"], 15 | event["AndroidModel"], 16 | event["Arch"], 17 | event["CPU"], 18 | event["BrowserVersion"], 19 | }), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/803.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_803() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 803, 10 | Stringify(B.Fingerprint.Events["803"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/901.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_901() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 901, 10 | Stringify(B.Fingerprint.Hash["901"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/905.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Behaviours: 3 | */ 4 | 5 | package events 6 | 7 | func (B *EventManager) Event_905() FingerprintEvent { 8 | return FingerprintEvent{ 9 | 905, 10 | Stringify(B.Fingerprint.Events["905"]), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/events.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | func Stringify(data any) string { 8 | jsonString, err := json.Marshal(data) 9 | if err != nil { 10 | return "" 11 | } 12 | 13 | return string(jsonString) 14 | } 15 | 16 | func (E *EventManager) Event_407Custom(keys []string) FingerprintEvent { 17 | return E.Event_407(keys) 18 | } 19 | 20 | func (E *EventManager) Event_3401Custom(hash string) FingerprintEvent { 21 | return E.Event_3401(hash) 22 | } 23 | 24 | func (E *EventManager) BuildEvents(customKeys []string, domHash string) (Events [][]interface{}) { 25 | eventsMethods := []func(*EventManager) FingerprintEvent{ 26 | (*EventManager).Event_3, 27 | (*EventManager).Event_1902, 28 | (*EventManager).Event_1901, 29 | (*EventManager).Event_1101, 30 | (*EventManager).Event_1103, 31 | (*EventManager).Event_1105, 32 | (*EventManager).Event_201, 33 | (*EventManager).Event_1107, 34 | (*EventManager).Event_211, 35 | func(e *EventManager) FingerprintEvent { 36 | return e.Event_3401Custom(domHash) 37 | }, 38 | (*EventManager).Event_3403, 39 | (*EventManager).Event_803, 40 | (*EventManager).Event_604, 41 | (*EventManager).Event_2801, 42 | (*EventManager).Event_2805, 43 | (*EventManager).Event_107, 44 | (*EventManager).Event_301, 45 | (*EventManager).Event_304, 46 | (*EventManager).Event_1401, 47 | (*EventManager).Event_1402, 48 | (*EventManager).Event_1403, 49 | (*EventManager).Event_3504, 50 | (*EventManager).Event_3501, 51 | (*EventManager).Event_3503, 52 | (*EventManager).Event_3502, 53 | (*EventManager).Event_401, 54 | (*EventManager).Event_402, 55 | func(e *EventManager) FingerprintEvent { 56 | return e.Event_407Custom(customKeys) 57 | }, 58 | (*EventManager).Event_412, 59 | (*EventManager).Event_2408, 60 | (*EventManager).Event_2402, 61 | (*EventManager).Event_2420, 62 | (*EventManager).Event_2401, 63 | (*EventManager).Event_2407, 64 | (*EventManager).Event_2409, 65 | (*EventManager).Event_2410, 66 | (*EventManager).Event_2411, 67 | (*EventManager).Event_2412, 68 | (*EventManager).Event_2413, 69 | (*EventManager).Event_2414, 70 | (*EventManager).Event_2415, 71 | (*EventManager).Event_2416, 72 | (*EventManager).Event_2417, 73 | (*EventManager).Event_901, 74 | (*EventManager).Event_905, 75 | (*EventManager).Event_1302, 76 | (*EventManager).Event_1904, 77 | (*EventManager).Event_702, 78 | (*EventManager).Event_3210, 79 | (*EventManager).Event_3211, 80 | (*EventManager).Event_0, 81 | } 82 | 83 | for _, eventMethod := range eventsMethods { 84 | event := eventMethod(E) 85 | Events = append(Events, []interface{}{ 86 | event.EventID, 87 | event.Value, 88 | }) 89 | } 90 | 91 | // utils.ShuffleSlice(Events) 92 | return Events 93 | } 94 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/string_encryption.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "html" 7 | "math/rand" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | "unicode/utf8" 12 | ) 13 | 14 | const ( 15 | charset = "abcdefghijklmnopqrstuvwxyz" 16 | ) 17 | 18 | func Reverse(s string) string { 19 | size := len(s) 20 | buf := make([]byte, size) 21 | 22 | for start := 0; start < size; { 23 | r, n := utf8.DecodeRuneInString(s[start:]) 24 | start += n 25 | utf8.EncodeRune(buf[size-start:], r) 26 | } 27 | 28 | return string(buf) 29 | } 30 | 31 | func getRandNum(A, g int) int { 32 | return rand.Intn(g-A+1) + A 33 | } 34 | 35 | func getRandomChar(min, max int) byte { 36 | return byte(rand.Intn(max-min+1) + min) 37 | } 38 | 39 | func indexOf(str, substr string) int { 40 | index := strings.Index(str, substr) 41 | return index 42 | } 43 | 44 | func EncStr(input string) []string { 45 | inputArr := func() string { 46 | var out []byte 47 | 48 | for i := 0; i < 13; i++ { 49 | char := getRandomChar(65, 90) 50 | out = append(out, char) 51 | } 52 | 53 | return string(out) 54 | }() 55 | 56 | randA := getRandNum(1, 26) 57 | 58 | Reversed := func(input string) (out string) { 59 | words := []string{} 60 | 61 | for _, word := range strings.Split(input, " ") { 62 | words = append(words, Reverse(word)) 63 | } 64 | 65 | chars := strings.Split(strings.Join(words, " "), "") 66 | 67 | swap := func(c string) string { 68 | if !strings.Contains(charset, strings.ToLower(c)) { 69 | return c 70 | } 71 | 72 | i := indexOf(charset, strings.ToLower(c)) 73 | b := string(charset[(i+randA)%26]) 74 | 75 | if c == strings.ToUpper(c) { 76 | return strings.ToUpper(b) 77 | } else { 78 | return b 79 | } 80 | } 81 | 82 | output := []string{} 83 | for _, c := range chars { 84 | output = append(output, swap(c)) 85 | } 86 | 87 | return strings.Join(output, "") 88 | }(input) 89 | 90 | b64 := Reverse(base64.StdEncoding.EncodeToString([]byte(Reversed))) 91 | b64rand := getRandNum(1, len(b64)-1) 92 | 93 | final := func(b64 string, i int) string { 94 | return regexp.MustCompile(fmt.Sprintf("[%v%s]", inputArr, strings.ToLower(inputArr))).ReplaceAllStringFunc(b64[i:]+b64[:i], func(A string) string { 95 | if A == strings.ToUpper(A) { 96 | return strings.ToLower(A) 97 | } else { 98 | return strings.ToUpper(A) 99 | } 100 | }) 101 | }(b64, b64rand) 102 | 103 | return []string{ 104 | final, 105 | fmt.Sprintf("%x", randA), 106 | fmt.Sprintf("%x", b64rand), 107 | inputArr, 108 | } 109 | } 110 | 111 | func decode(s string) string { 112 | if strings.IndexByte(s, ';') >= 0 { 113 | s = html.UnescapeString(s) 114 | } 115 | return s 116 | } 117 | 118 | func DecStr(input []string) string { 119 | b64rand, _ := strconv.ParseInt(input[2], 16, 64) 120 | inputArr := input[3] 121 | final := input[0] 122 | 123 | decrypted := regexp.MustCompile(fmt.Sprintf("[%v%s]", inputArr, strings.ToLower(inputArr))).ReplaceAllStringFunc(final, func(A string) string { 124 | if A == strings.ToUpper(A) { 125 | return strings.ToLower(A) 126 | } else { 127 | return strings.ToUpper(A) 128 | } 129 | }) 130 | 131 | b64Len := len(decrypted) 132 | reversedB64 := decrypted[b64Len-int(b64rand):] + decrypted[:b64Len-int(b64rand)] 133 | decoded, _ := base64.StdEncoding.DecodeString(Reverse(reversedB64)) 134 | 135 | reversedStr := decode(Reverse(string(decoded))) 136 | return reversedStr 137 | } 138 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/string_encryption_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestDecStr(t *testing.T) { 9 | type args struct { 10 | input []string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want string 16 | }{ 17 | { 18 | name: "1", 19 | args: args{ 20 | input: EncStr("Google Inc. (NVIDIA)"), 21 | }, 22 | }, 23 | { 24 | name: "1", 25 | args: args{ 26 | input: []string{"o3IzMxCzM1yjNWUD", "8", "b", "EKSYKCEHOCWTT"}, 27 | }, 28 | }, 29 | } 30 | 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | fmt.Println(tt.args.input) 34 | 35 | got := DecStr(tt.args.input) 36 | fmt.Println(got) 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/events/struct.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | type EventManager struct { 4 | UserAgent, HcapVersion, Href string 5 | Fingerprint *FpModel 6 | } 7 | 8 | type FingerprintEvent struct { 9 | EventID int64 10 | Value string 11 | } 12 | 13 | type FpModel struct { 14 | Hash map[string]any `json:"hash"` 15 | Properties Properties `json:"properties"` 16 | Browser Browser `json:"browser"` 17 | Connection Connection `json:"connection"` 18 | Timezone []interface{} `json:"timezone"` 19 | Screen map[string]interface{} `json:"screen"` 20 | Webgl Webgl `json:"webgl"` 21 | Events map[string]interface{} `json:"events"` 22 | } 23 | 24 | type Connection struct { 25 | DownlinkMax bool `json:"downlinkMax"` 26 | Rtt float64 `json:"rtt"` 27 | } 28 | 29 | type Properties struct { 30 | CSS int64 `json:"css"` 31 | MIMETypes int64 `json:"MimeTypes"` 32 | Plugins int64 `json:"Plugins"` 33 | WindowFunctions int64 `json:"WindowFunctions"` 34 | } 35 | 36 | type Browser struct { 37 | UserAgent string `json:"UserAgent"` 38 | AppVersion string `json:"AppVersion"` 39 | DeviceMemory int64 `json:"DeviceMemory"` 40 | HardwareConcurrency int64 `json:"HardwareConcurrency"` 41 | Language string `json:"Language"` 42 | Languages []string `json:"Languages"` 43 | PDFViewerEnabled bool `json:"PDFViewerEnabled"` 44 | Platform string `json:"platform"` 45 | Mobile bool `json:"Mobile"` 46 | } 47 | 48 | type Webgl struct { 49 | Vendor string `json:"vendor"` 50 | Renderer string `json:"renderer"` 51 | } 52 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/fingerprint.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net/http" 10 | "regexp" 11 | "strings" 12 | 13 | "github.com/0xF7A4C6/GoCycle" 14 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha/fingerprint/events" 15 | "github.com/Implex-ltd/hcsolver/internal/utils" 16 | ) 17 | 18 | var ( 19 | CollectFpArray, _ = GoCycle.NewFromFile("../../assets/cleaned.txt") 20 | hcRegex = regexp.MustCompile(`/captcha/v1/[A-Za-z0-9]+/static/images`) 21 | VERSION, _ = checkForUpdate() 22 | WASM = "1.40.10" 23 | ) 24 | 25 | var ( 26 | // get task img 27 | KEYS_1_UNIQUE = strings.Join([]string{ 28 | "__localeData__", 29 | "regeneratorRuntime", 30 | "0", 31 | "__BILLING_STANDALONE__", 32 | "webpackChunkdiscord_app", 33 | "platform", 34 | "__SECRET_EMOTION__", 35 | "__SENTRY__", 36 | "hcaptcha", 37 | "hcaptchaOnLoad", 38 | "__timingFunction", 39 | "DiscordErrors", 40 | "clearImmediate", 41 | "__OVERLAY__", 42 | "grecaptcha", 43 | "GLOBAL_ENV", 44 | "setImmediate", 45 | "1", 46 | "IntlPolyfill", 47 | "__DISCORD_WINDOW_ID", 48 | }, ",") 49 | 50 | KEYS_1_UNIQUE_INV = strings.Join([]string{ 51 | "__wdata", 52 | "sessionStorage", 53 | "localStorage", 54 | "hsw", 55 | "_sharedLibs", 56 | }, ",") 57 | 58 | // get task txt 59 | KEYS_2_UNIQUE = strings.Join([]string{ 60 | "GLOBAL_ENV", 61 | "0", 62 | "__OVERLAY__", 63 | "hcaptcha", 64 | "__localeData__", 65 | "IntlPolyfill", 66 | "__timingFunction", 67 | "grecaptcha", 68 | "1", 69 | "hcaptchaOnLoad", 70 | "clearImmediate", 71 | "setImmediate", 72 | "DiscordErrors", 73 | "__BILLING_STANDALONE__", 74 | "__DISCORD_WINDOW_ID", 75 | "webpackChunkdiscord_app", 76 | "__SECRET_EMOTION__", 77 | "regeneratorRuntime", 78 | "platform", 79 | "__SENTRY__", 80 | }, ",") 81 | KEYS_2_UNIQUE_INV = strings.Join([]string{ 82 | "sessionStorage", 83 | "hsw", 84 | "image_label_binary", 85 | "__wdata", 86 | "_sharedLibs", 87 | "localStorage", 88 | }, ",") 89 | 90 | // submit task txt 91 | KEYS_3_UNIQUE = strings.Join([]string{ 92 | "__SECRET_EMOTION__", 93 | "IntlPolyfill", 94 | "regeneratorRuntime", 95 | "DiscordErrors", 96 | "1", 97 | "__localeData__", 98 | "hcaptchaOnLoad", 99 | "grecaptcha", 100 | "__BILLING_STANDALONE__", 101 | "clearImmediate", 102 | "hcaptcha", 103 | "platform", 104 | "__timingFunction", 105 | "webpackChunkdiscord_app", 106 | "__DISCORD_WINDOW_ID", 107 | "0", 108 | "__SENTRY__", 109 | "GLOBAL_ENV", 110 | "__OVERLAY__", 111 | "setImmediate", 112 | }, ",") 113 | KEYS_3_UNIQUE_INV = strings.Join([]string{ 114 | "__wdata", 115 | "text_free_entry", 116 | "hsw", 117 | "image_label_binary", 118 | "localStorage", 119 | "_sharedLibs", 120 | "sessionStorage", 121 | }, ",") 122 | ) 123 | 124 | func NewFingerprintBuilder(useragent, href string) (*Builder, error) { 125 | fp, err := CollectFpArray.Next() 126 | if err != nil { 127 | panic(err) 128 | } 129 | 130 | decodedData, err := base64.StdEncoding.DecodeString(fp) 131 | if err != nil { 132 | fmt.Println("Error decoding Base64:", err) 133 | return nil, err 134 | } 135 | 136 | var data events.FpModel 137 | if err := json.Unmarshal(decodedData, &data); err != nil { 138 | return nil, err 139 | } 140 | 141 | return &Builder{ 142 | Manager: &events.EventManager{ 143 | Href: href, 144 | UserAgent: useragent, 145 | HcapVersion: VERSION, 146 | Fingerprint: &data, 147 | }, 148 | }, nil 149 | } 150 | 151 | func (B *Builder) GenerateProfile() (*Profile, error) { 152 | uaSplit := strings.Split(B.Manager.UserAgent, "Mozilla/") 153 | if len(uaSplit) != 2 { 154 | return nil, fmt.Errorf("invalid UA") 155 | } 156 | 157 | B.Manager.UserAgent = B.Manager.Fingerprint.Browser.UserAgent 158 | 159 | hash, _ := RandHash([]byte("Raven,alert,atob,blur,btoa,caches,cancelAnimationFrame,cancelIdleCallback,captureEvents,chrome,clearInterval,clearTimeout,clientInformation,close,closed,confirm,cookieStore,createImageBitmap,credentialless,crossOriginIsolated,crypto,customElements,devicePixelRatio,document,documentPictureInPicture,external,fence,fetch,find,focus,frameElement,frames,getComputedStyle,getScreenDetails,getSelection,history,indexedDB,innerHeight,innerWidth,isSecureContext,launchQueue,length,localStorage,location,locationbar,matchMedia,menubar,moveBy,moveTo,name,navigation,navigator,onabort,onafterprint,onanimationend,onanimationiteration,onanimationstart,onappinstalled,onauxclick,onbeforeinput,onbeforeinstallprompt,onbeforematch,onbeforeprint,onbeforetoggle,onbeforeunload,onbeforexrselect,onblur,oncancel,oncanplay,oncanplaythrough,onchange,onclick,onclose,oncontentvisibilityautostatechange,oncontextlost,oncontextmenu,oncontextrestored,oncuechange,ondblclick,ondevicemotion,ondeviceorientation,ondeviceorientationabsolute,ondrag,ondragend,ondragenter,ondragleave,ondragover,ondragstart,ondrop,ondurationchange,onemptied,onended,onerror,onfocus,onformdata,ongotpointercapture,onhashchange,oninput,oninvalid,onkeydown,onkeypress,onkeyup,onlanguagechange,onload,onloadeddata,onloadedmetadata,onloadstart,onlostpointercapture,onmessage,onmessageerror,onmousedown,onmouseenter,onmouseleave,onmousemove,onmouseout,onmouseover,onmouseup,onmousewheel,onoffline,ononline,onpagehide,onpageshow,onpause,onplay,onplaying,onpointercancel,onpointerdown,onpointerenter,onpointerleave,onpointermove,onpointerout,onpointerover,onpointerrawupdate,onpointerup,onpopstate,onprogress,onratechange,onrejectionhandled,onreset,onresize,onscroll,onscrollend,onsearch,onsecuritypolicyviolation,onseeked,onseeking,onselect,onselectionchange,onselectstart,onslotchange,onstalled,onstorage,onsubmit,onsuspend,ontimeupdate,ontoggle,ontransitioncancel,ontransitionend,ontransitionrun,ontransitionstart,onunhandledrejection,onunload,onvolumechange,onwaiting,onwebkitanimationend,onwebkitanimationiteration,onwebkitanimationstart,onwebkittransitionend,onwheel,open,openDatabase,opener,origin,originAgentCluster,outerHeight,outerWidth,pageXOffset,pageYOffset,parent,performance,personalbar,postMessage,print,prompt,queryLocalFonts,queueMicrotask,releaseEvents,reportError,requestAnimationFrame,requestIdleCallback,resizeBy,resizeTo,scheduler,screen,screenLeft,screenTop,screenX,screenY,scroll,scrollBy,scrollTo,scrollX,scrollY,scrollbars,self,sessionStorage,setInterval,setTimeout,sharedStorage,showDirectoryPicker,showOpenFilePicker,showSaveFilePicker,speechSynthesis,status,statusbar,stop,structuredClone,styleMedia,toolbar,top,trustedTypes,visualViewport,webkitCancelAnimationFrame,webkitRequestAnimationFrame,webkitRequestFileSystem,webkitResolveLocalFileSystemURL,window")) 160 | //hash, _ := RandHash([]byte(utils.RandomString(80))) 161 | 162 | p := Profile{ 163 | UserAgent: B.Manager.UserAgent, 164 | Screen: Screen{ 165 | ColorDepth: B.Manager.Fingerprint.Screen["ColorDepth"].(float64), 166 | PixelDepth: B.Manager.Fingerprint.Screen["PixelDepth"].(float64), 167 | Width: B.Manager.Fingerprint.Screen["Width"].(float64), 168 | Height: B.Manager.Fingerprint.Screen["Height"].(float64), 169 | AvailWidth: B.Manager.Fingerprint.Screen["AvailWidth"].(float64), 170 | AvailHeight: B.Manager.Fingerprint.Screen["AvailHeight"].(float64), 171 | }, 172 | Navigator: Navigator{ 173 | UserAgent: B.Manager.Fingerprint.Browser.UserAgent, 174 | Language: B.Manager.Fingerprint.Browser.Language, 175 | Languages: B.Manager.Fingerprint.Browser.Languages, 176 | Platform: B.Manager.Fingerprint.Browser.Platform, 177 | MaxTouchPoints: B.Manager.Fingerprint.Screen["MaxTouchPoints"].(float64), 178 | NotificationQueryPermission: nil, 179 | PluginsUndefined: false, 180 | }, 181 | Hash: Hash{ 182 | Performance: HashString([]byte("navigation:newassets.hcaptcha.comscript:newassets.hcaptcha.comxmlhttprequest:hcaptcha.com")), 183 | Canvas: utils.RandomHash(19), 184 | WebGL: "-1", 185 | WebRTC: "-1", 186 | Audio: "-1", 187 | ParrentWindow: utils.RandomHash(19), 188 | CommonKeys: hash, 189 | }, 190 | Misc: Misc{ 191 | UniqueKeys: KEYS_1_UNIQUE, 192 | InvUniqueKeys: KEYS_1_UNIQUE_INV, 193 | CommonKeysTails: strings.Join([]string{ 194 | "chrome", 195 | "fence", 196 | "caches", 197 | "cookieStore", 198 | "ondevicemotion", 199 | "ondeviceorientation", 200 | "ondeviceorientationabsolute", 201 | "launchQueue", 202 | "sharedStorage", 203 | "documentPictureInPicture", 204 | "onbeforematch", 205 | "getScreenDetails", 206 | "queryLocalFonts", 207 | "showDirectoryPicker", 208 | "showOpenFilePicker", 209 | "showSaveFilePicker", 210 | "originAgentCluster", 211 | "credentialless", 212 | "speechSynthesis", 213 | "oncontentvisibilityautostatechange", 214 | "onscrollend", 215 | "webkitRequestFileSystem", 216 | "webkitResolveLocalFileSystemURL", 217 | "Raven", 218 | }, ","), 219 | }, 220 | } 221 | 222 | B.Profile = &p 223 | return &p, nil 224 | } 225 | 226 | func (B *Builder) Build(jwt string, isSubmit, isText bool) (*Ndata, error) { 227 | /*Profile, err := B.GenerateProfile() 228 | if err != nil { 229 | return nil, err 230 | }*/ 231 | 232 | token, err := ParseJWT(jwt) 233 | if err != nil { 234 | return nil, err 235 | } 236 | 237 | stamp, err := GetStamp(uint(token.Difficuly), token.PowData) 238 | if err != nil { 239 | return nil, fmt.Errorf("pow error") 240 | } 241 | 242 | V := strings.Split(token.Location, "https://newassets.hcaptcha.com/c/") 243 | if len(V) == 1 { 244 | return nil, fmt.Errorf("cant parse jwt location") 245 | } 246 | 247 | N := Ndata{ 248 | ProofSpec: ProofSpec{ 249 | Difficulty: int64(token.Difficuly), 250 | FingerprintType: int64(token.FingerprintType), 251 | Type: token.VmType, 252 | Data: token.PowData, 253 | Location: token.Location, 254 | TimeoutValue: int64(token.TimeoutValue), 255 | }, 256 | Rand: []float64{ 257 | utils.RandomFloat64Precission(0, 1, 10000000000000000.0), 258 | }, 259 | Components: Components{ 260 | Version: fmt.Sprintf("%v/%v", WASM, V[1]), 261 | Navigator: B.Profile.Navigator, 262 | Screen: B.Profile.Screen, 263 | DevicePixelRatio: B.Manager.Fingerprint.Screen["DevicePixelRatio"].(float64), 264 | HasSessionStorage: true, 265 | HasLocalStorage: true, 266 | HasIndexedDB: true, 267 | WebGlHash: B.Profile.Hash.WebGL, 268 | CanvasHash: B.Profile.Hash.Canvas, 269 | HasTouch: B.Manager.Fingerprint.Events["107"].(map[string]interface{})["touchEvent"].(bool), 270 | NotificationAPIPermission: "Denied", 271 | Chrome: strings.Contains(B.Manager.Fingerprint.Browser.UserAgent, "Chrome"), 272 | ToStringLength: 33, 273 | ErrFirefox: nil, 274 | RBotScore: 0, 275 | RBotScoreSuspiciousKeys: []string{}, 276 | RBotScore2: 0, 277 | AudioHash: B.Profile.Hash.Audio, 278 | Extensions: []bool{ 279 | false, 280 | }, 281 | ParentWinHash: B.Profile.Hash.ParrentWindow, 282 | WebrtcHash: B.Profile.Hash.WebRTC, 283 | PerformanceHash: B.Profile.Hash.Performance, 284 | UniqueKeys: B.Profile.Misc.UniqueKeys, 285 | InvUniqueKeys: B.Profile.Misc.InvUniqueKeys, 286 | CommonKeysHash: B.Profile.Hash.CommonKeys, 287 | CommonKeysTail: B.Profile.Misc.CommonKeysTails, 288 | Features: Features{ 289 | PerformanceEntries: true, 290 | WebAudio: true, 291 | WebRTC: true, 292 | Canvas2D: true, 293 | Fetch: true, 294 | }, 295 | }, 296 | //FingerprintEvents: B.Manager.BuildEvents(), 297 | FingerprintSuspiciousEvents: []string{}, 298 | Stamp: stamp, 299 | Href: B.Manager.Href, 300 | Errs: Errs{ 301 | List: []string{}, 302 | }, 303 | StackData: []string{}, 304 | Perf: [][]any{ 305 | { 306 | 1, 307 | float64(utils.RandomNumber(5, 100)), 308 | }, 309 | { 310 | 2, 311 | float64(utils.RandomNumber(20, 300)), 312 | }, 313 | { 314 | 3, 315 | 0.0, 316 | }, 317 | }, 318 | } 319 | 320 | // ugly as fuck part made as test to fix 321 | if isText { 322 | N.Components.InvUniqueKeys = KEYS_2_UNIQUE_INV 323 | N.Components.UniqueKeys = KEYS_2_UNIQUE 324 | 325 | N.FingerprintEvents = B.Manager.BuildEvents([]string{"Raven", "_sharedLibs", "hsw", "__wdata", "image_label_binary"}, "14673576476674870845") 326 | } else { 327 | N.Components.InvUniqueKeys = KEYS_1_UNIQUE_INV 328 | N.Components.UniqueKeys = KEYS_1_UNIQUE 329 | 330 | N.FingerprintEvents = B.Manager.BuildEvents([]string{"Raven", "_sharedLibs", "hsw", "__wdata"}, "4226317358175830201") 331 | } 332 | 333 | if isSubmit { 334 | N.StackData = []string{ 335 | "new Promise ()", 336 | } 337 | 338 | N.Components.InvUniqueKeys = KEYS_3_UNIQUE_INV 339 | N.Components.UniqueKeys = KEYS_3_UNIQUE 340 | 341 | N.FingerprintEvents = B.Manager.BuildEvents([]string{"Raven", "_sharedLibs", "hsw", "__wdata", "image_label_binary", "text_free_entry"}, "2530917404755245142") 342 | } 343 | 344 | b, err := json.Marshal(N) 345 | if err != nil { 346 | panic(err) 347 | } 348 | 349 | _, rand_int := RandHash(b) 350 | N.Rand = append(N.Rand, rand_int) 351 | 352 | return &N, nil 353 | } 354 | 355 | func checkForUpdate() (string, error) { 356 | response, err := http.Get("https://hcaptcha.com/1/api.js?render=explicit&onload=hcaptchaOnLoad") 357 | if err != nil { 358 | return "", err 359 | } 360 | 361 | defer response.Body.Close() 362 | 363 | body, err := io.ReadAll(response.Body) 364 | if err != nil { 365 | return "", err 366 | } 367 | 368 | var found string 369 | for _, match := range hcRegex.FindAllStringSubmatch(string(body), -1) { 370 | found = match[0] 371 | break 372 | } 373 | 374 | if found == "" { 375 | return "", fmt.Errorf("cant find version") 376 | } 377 | 378 | version := strings.Split(strings.Split(found, "v1/")[1], "/static")[0] 379 | 380 | log.Println("load version", version) 381 | 382 | return version, nil 383 | } 384 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/fingerprint_test.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | const ( 10 | JWT_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmIjowLCJzIjoyLCJ0IjoidyIsImQiOiIrOVZwZU5nUUFwSUpXR2dhY0Y1LzlhTHhad0l5ZHpoQzVwMUw4QWh1cXRDTTJIQjlyOHpJeFNWczdGWW9HOU1uVW54U1I4aEkxanFoUG1YRE5pdGFaK2NDOUM0UWN2NlRTZUhISzI4Nzc5T0pTRVIyOUhDOTdUNysvVUFvQjVReXkzYi9XTkpjMnBUbk1hTDA1RkNYV2QzMmMwa1krdFYxVUlsMzdEekJpdUVQc1kzNDdPSHRYdmpXRHc9PVcrQTlSRTBnNlB4OGdVRlciLCJsIjoiaHR0cHM6Ly9uZXdhc3NldHMuaGNhcHRjaGEuY29tL2MvNzhlZTZmYyIsImkiOiJzaGEyNTYtdEs3YTVnbXE3Wjd1R0w2REh5OW9ReHUvRmsvdW1WdzNlTFBaWitlS2lkdz0iLCJlIjoxNjk3MjQxNzE3LCJuIjoiaHN3IiwiYyI6MTAwMH0.AbJ3rc1gTbM0ADacD-MdrcCyrShmMmhYi98juOCT1aEdTaZ5z5AQcNZIfMuMkxG3gAbsgrqeyK_OiTHeoNIw5hLXrDeDGihgHVAF2A0gBbNpWVI78rACHJQn4UyP1GHoYTTu3-HeAQXmVP0JEd6X7UgAWUeIn0IRKLlrNrmO0aI" 11 | ) 12 | 13 | func TestNewFingerprintBuilder(t *testing.T) { 14 | type args struct { 15 | useragent string 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | want *Builder 21 | wantErr bool 22 | }{ 23 | { 24 | name: "1", 25 | args: args{ 26 | useragent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60", 27 | }, 28 | }, 29 | } 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | got, err := NewFingerprintBuilder(tt.args.useragent, "https://discord.com") 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | if _, err := got.GenerateProfile(); err != nil { 38 | panic(err) 39 | } 40 | 41 | n, err := got.Build(JWT_TOKEN, false, true) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | out, err := json.Marshal(n) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | fmt.Println(string(out)) 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/jwt.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang-jwt/jwt/v5" 7 | ) 8 | 9 | type JwtData struct { 10 | FingerprintType float64 `json:"f,omitempty"` 11 | Difficuly float64 `json:"s,omitempty"` 12 | VmType string `json:"t,omitempty"` 13 | PowData string `json:"d,omitempty"` 14 | Location string `json:"l,omitempty"` 15 | Signature string `json:"i,omitempty"` 16 | Timestamp float64 `json:"e,omitempty"` 17 | N string `json:"n,omitempty"` 18 | TimeoutValue float64 `json:"c,omitempty"` 19 | } 20 | 21 | func validateClaimString(claims map[string]interface{}, key string) error { 22 | if value, ok := claims[key].(string); !ok || value == "" { 23 | return fmt.Errorf("can't parse jwt token at key %v", key) 24 | } 25 | return nil 26 | } 27 | 28 | func validateClaimInt(claims map[string]interface{}, key string) error { 29 | if value, ok := claims[key].(float64); !ok || value == 0 { 30 | return fmt.Errorf("can't parse jwt token at key %v", key) 31 | } 32 | 33 | return nil 34 | } 35 | 36 | func ValidateJwtClaims(claims map[string]interface{}) error { 37 | stringKeys := []string{"t", "d", "l", "i", "n"} 38 | intKeys := []string{"s", "e", "c"} 39 | 40 | for _, key := range stringKeys { 41 | if err := validateClaimString(claims, key); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | for _, key := range intKeys { 47 | if err := validateClaimInt(claims, key); err != nil { 48 | return err 49 | } 50 | } 51 | 52 | return nil 53 | } 54 | 55 | func ParseJWT(token string) (*JwtData, error) { 56 | payload, _ := jwt.Parse(token, nil) 57 | 58 | if payload == nil { 59 | return nil, fmt.Errorf("failed to parse jwt") 60 | } 61 | 62 | claims := payload.Claims.(jwt.MapClaims) 63 | 64 | if err := ValidateJwtClaims(claims); err != nil { 65 | return nil, err 66 | } 67 | 68 | return &JwtData{ 69 | FingerprintType: claims["f"].(float64), 70 | Difficuly: claims["s"].(float64), 71 | VmType: claims["t"].(string), 72 | PowData: claims["d"].(string), 73 | Location: claims["l"].(string), 74 | Signature: claims["i"].(string), 75 | Timestamp: claims["e"].(float64), 76 | N: claims["n"].(string), 77 | TimeoutValue: claims["c"].(float64), 78 | }, nil 79 | } 80 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/rand.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import ( 4 | "hash/crc32" 5 | "math/bits" 6 | ) 7 | 8 | func RandHash(input []byte) (uint32, float64) { 9 | crc := crc32.Checksum(input, crc32.MakeTable(bits.Reverse32(79764919))) 10 | 11 | return crc, float64(crc) * 2.3283064365386963e-10 12 | } -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/stamp.go: -------------------------------------------------------------------------------- 1 | /** 2 | https://crates.io/crates/rust-hashcash/0.3.3 3 | Thanks hcaptcha 1990 algo !! 4 | 5 | fork: github.com/catalinc/hashcash 6 | */ 7 | 8 | package fingerprint 9 | 10 | import ( 11 | "crypto/rand" 12 | "crypto/sha1" 13 | "encoding/base64" 14 | "encoding/binary" 15 | "fmt" 16 | "hash" 17 | "math" 18 | "strconv" 19 | "strings" 20 | "time" 21 | ) 22 | 23 | const dateFormat = "2006-01-02" 24 | 25 | type StampHash struct { 26 | hasher hash.Hash 27 | bits uint 28 | zeros uint 29 | saltLen uint 30 | extra string 31 | } 32 | 33 | func New(bits uint, saltLen uint, extra string) *StampHash { 34 | h := &StampHash{ 35 | hasher: sha1.New(), 36 | bits: bits, 37 | saltLen: saltLen, 38 | extra: extra} 39 | h.zeros = uint(math.Ceil(float64(h.bits) / 4.0)) 40 | return h 41 | } 42 | 43 | func NewStd(difficulty uint) *StampHash { 44 | return New(difficulty, 8, "") 45 | } 46 | 47 | func (h *StampHash) Mint(resource string) (string, error) { 48 | version := "1" 49 | 50 | salt, err := h.getSalt() 51 | if err != nil { 52 | return "", err 53 | } 54 | date := time.Now().Format(dateFormat) 55 | counter := 0 56 | 57 | var stamp string 58 | for { 59 | stamp = fmt.Sprintf("%s:%d:%s:%s:%s:%s:%x", 60 | version, h.bits/2, date, resource, h.extra, salt, counter) 61 | 62 | if h.checkZeros(stamp) { 63 | return stamp, nil 64 | } 65 | 66 | counter++ 67 | } 68 | } 69 | 70 | func (h *StampHash) Check(stamp string) bool { 71 | if h.checkDate(stamp) { 72 | return h.checkZeros(stamp) 73 | } 74 | return false 75 | } 76 | 77 | func (h *StampHash) CheckNoDate(stamp string) bool { 78 | return h.checkZeros(stamp) 79 | } 80 | 81 | func (h *StampHash) getSalt() (string, error) { 82 | buf := make([]byte, h.saltLen) 83 | _, err := rand.Read(buf) 84 | if err != nil { 85 | return "", err 86 | } 87 | salt := base64.StdEncoding.EncodeToString(buf) 88 | return salt[:h.saltLen], nil 89 | } 90 | 91 | func (h *StampHash) checkZeros(stamp string) bool { 92 | h.hasher.Reset() 93 | h.hasher.Write([]byte(stamp)) 94 | 95 | sum := h.hasher.Sum(nil) 96 | sumUint64 := binary.BigEndian.Uint64(sum) 97 | sumBits := strconv.FormatUint(sumUint64, 2) 98 | zeroes := 64 - len(sumBits) 99 | 100 | return uint(zeroes) >= h.bits 101 | } 102 | 103 | func (h *StampHash) checkDate(stamp string) bool { 104 | fields := strings.Split(stamp, ":") 105 | if len(fields) != 7 { 106 | return false 107 | } 108 | then, err := time.Parse(dateFormat, fields[2]) 109 | if err != nil { 110 | return false 111 | } 112 | duration := time.Since(then) 113 | return duration.Hours()*2 <= 48 114 | } 115 | 116 | func GetStamp(difficulty uint, data string) (string, error) { 117 | H := NewStd(difficulty*2) 118 | return H.Mint(data) 119 | } 120 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/stamp_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | https://crates.io/crates/rust-hashcash/0.3.3 3 | Thanks hcaptcha 1990 algo !! 4 | 5 | fork: github.com/catalinc/hashcash 6 | */ 7 | 8 | package fingerprint 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | ) 14 | 15 | func TestGetStamp(t *testing.T) { 16 | type args struct { 17 | data string 18 | } 19 | tests := []struct { 20 | name string 21 | args args 22 | }{ 23 | { 24 | name: "test", 25 | args: args{ 26 | data: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmIjowLCJzIjoyLCJ0IjoidyIsImQiOiJrcWU0Rk5kSmxqaFVsRVFMUVFoL3d2YlMrQWN2UC92d040OFN2ei9mRkQ4LzhGN0VwNHF4Mm5wRWpRNStWUE1CdVVQNFVmNFZBTVJGNVZWZzZRTjlMeHN3VXdoTzE5V0tHUDIxSTNKSGVFU1hYdTkwTlNmZmlZNFBuUU1mVmw0dG92MDN4SVk3aWtuQWFDUHdYMm5SeVVWMURrSzhsVzJ0djhPZzJqdjFFZHJQNmc0a2lYQUdxMm5pZ3c9PTVBaUxZZmZwREEwRFY3QXYiLCJsIjoiaHR0cHM6Ly9uZXdhc3NldHMuaGNhcHRjaGEuY29tL2MvNzhlZTZmYyIsImkiOiJzaGEyNTYtdEs3YTVnbXE3Wjd1R0w2REh5OW9ReHUvRmsvdW1WdzNlTFBaWitlS2lkdz0iLCJlIjoxNjk3NTc2MTk2LCJuIjoiaHN3IiwiYyI6MTAwMH0.n0lbRXee0rrYEiODWLmPneTi-lDMPZ6bGX7ar0lp5PzHfX6d_bIeV39WYnsv2V5aoDybj62KKzjIoUJJAkVbiGSmP-ngTgD8tb6PlAdzt-W48b1GMJbkkjXEP-UB4k8j9pmxSLgNegupvJi1XKfmnHh-R4k26cdz3yAUPQ19AD0", 27 | }, 28 | }, 29 | } 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | token, err := ParseJWT(tt.args.data) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | for i := 0; i < 10; i++ { 38 | got, err := GetStamp(uint(token.Difficuly), token.PowData) 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | fmt.Println(got) 44 | } 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/struct.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import "github.com/Implex-ltd/hcsolver/internal/hcaptcha/fingerprint/events" 4 | 5 | type Profile struct { 6 | UserAgent string 7 | Screen Screen 8 | Navigator Navigator 9 | Hash Hash 10 | Misc Misc 11 | } 12 | 13 | type Builder struct { 14 | Manager *events.EventManager 15 | Profile *Profile 16 | } 17 | 18 | type Misc struct { 19 | UniqueKeys, InvUniqueKeys, CommonKeysTails string 20 | } 21 | 22 | type Hash struct { 23 | Performance, Canvas, WebGL, WebRTC, Audio, ParrentWindow string 24 | CommonKeys uint32 25 | } 26 | 27 | type Ndata struct { 28 | ProofSpec ProofSpec `json:"proof_spec"` 29 | Rand []float64 `json:"rand"` 30 | Components Components `json:"components"` 31 | FingerprintEvents [][]interface{} `json:"fingerprint_events"` 32 | Messages interface{} `json:"messages"` 33 | StackData []string `json:"stack_data"` 34 | FingerprintSuspiciousEvents []string `json:"fingerprint_suspicious_events"` 35 | Stamp string `json:"stamp"` 36 | Href string `json:"href"` 37 | Errs Errs `json:"errs"` 38 | Perf [][]any `json:"perf"` 39 | } 40 | 41 | type Components struct { 42 | Version string `json:"version"` 43 | Navigator Navigator `json:"navigator"` 44 | Screen Screen `json:"screen"` 45 | DevicePixelRatio float64 `json:"device_pixel_ratio"` 46 | HasSessionStorage bool `json:"has_session_storage"` 47 | HasLocalStorage bool `json:"has_local_storage"` 48 | HasIndexedDB bool `json:"has_indexed_db"` 49 | WebGlHash string `json:"web_gl_hash"` 50 | CanvasHash string `json:"canvas_hash"` 51 | HasTouch bool `json:"has_touch"` 52 | NotificationAPIPermission string `json:"notification_api_permission"` 53 | Chrome bool `json:"chrome"` 54 | ToStringLength int64 `json:"to_string_length"` 55 | ErrFirefox interface{} `json:"err_firefox"` 56 | RBotScore int64 `json:"r_bot_score"` 57 | RBotScoreSuspiciousKeys []string `json:"r_bot_score_suspicious_keys"` 58 | RBotScore2 int64 `json:"r_bot_scorevents"` 59 | AudioHash string `json:"audio_hash"` 60 | Extensions []bool `json:"extensions"` 61 | ParentWinHash string `json:"parent_win_hash"` 62 | WebrtcHash string `json:"webrtc_hash"` 63 | PerformanceHash string `json:"performance_hash"` 64 | UniqueKeys string `json:"unique_keys"` 65 | InvUniqueKeys string `json:"inv_unique_keys"` 66 | CommonKeysHash uint32 `json:"common_keys_hash"` 67 | CommonKeysTail string `json:"common_keys_tail"` 68 | Features Features `json:"features"` 69 | } 70 | 71 | type Features struct { 72 | PerformanceEntries bool `json:"performance_entries"` 73 | WebAudio bool `json:"web_audio"` 74 | WebRTC bool `json:"web_rtc"` 75 | Canvas2D bool `json:"canvas_2d"` 76 | Fetch bool `json:"fetch"` 77 | } 78 | 79 | type Navigator struct { 80 | UserAgent string `json:"user_agent"` 81 | Language string `json:"language"` 82 | Languages []string `json:"languages"` 83 | Platform string `json:"platform"` 84 | MaxTouchPoints float64 `json:"max_touch_points"` 85 | Webdriver bool `json:"webdriver"` 86 | NotificationQueryPermission interface{} `json:"notification_query_permission"` 87 | PluginsUndefined bool `json:"plugins_undefined"` 88 | } 89 | 90 | type Screen struct { 91 | ColorDepth float64 `json:"color_depth"` 92 | PixelDepth float64 `json:"pixel_depth"` 93 | Width float64 `json:"width"` 94 | Height float64 `json:"height"` 95 | AvailWidth float64 `json:"avail_width"` 96 | AvailHeight float64 `json:"avail_height"` 97 | } 98 | 99 | type Errs struct { 100 | List []string `json:"list"` 101 | } 102 | 103 | type ProofSpec struct { 104 | Difficulty int64 `json:"difficulty"` 105 | FingerprintType int64 `json:"fingerprint_type"` 106 | Type string `json:"_type"` 107 | Data string `json:"data"` 108 | Location string `json:"_location"` 109 | TimeoutValue int64 `json:"timeout_value"` 110 | } 111 | -------------------------------------------------------------------------------- /internal/hcaptcha/fingerprint/xxhash.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/shivakar/xxhash" 7 | ) 8 | 9 | const ( 10 | seed = uint64(5575352424011909552) 11 | ) 12 | 13 | func HashString(content []byte) string { 14 | h := xxhash.NewSeedXXHash64(seed) 15 | defer h.Reset() 16 | 17 | h.Write(content) 18 | 19 | return fmt.Sprintf("%v", h.Uint64()) 20 | } 21 | -------------------------------------------------------------------------------- /internal/hcaptcha/hcaptcha.go: -------------------------------------------------------------------------------- 1 | package hcaptcha 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strings" 8 | "time" 9 | 10 | "github.com/Implex-ltd/cleanhttp/cleanhttp" 11 | "github.com/Implex-ltd/fingerprint-client/fpclient" 12 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha/fingerprint" 13 | "github.com/Implex-ltd/hcsolver/internal/recognizer" 14 | "github.com/Implex-ltd/hcsolver/internal/utils" 15 | ) 16 | 17 | const ( 18 | LANG = "fr" 19 | ) 20 | 21 | var ( 22 | RETRY = 0 23 | ) 24 | 25 | func NewHcaptcha(config *Config) (*Hcap, error) { 26 | builder, err := fingerprint.NewFingerprintBuilder(config.UserAgent, config.Href) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | if _, err := builder.GenerateProfile(); err != nil { 32 | return nil, err 33 | } 34 | 35 | config.UserAgent = builder.Manager.Fingerprint.Browser.UserAgent 36 | 37 | fp, err := ApplyFingerprint(config) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | // edit fp to match scrapped one 43 | fp.Navigator.Languages = builder.Profile.Navigator.Languages 44 | fp.Navigator.Language = builder.Profile.Navigator.Language 45 | fp.Navigator.Platform = builder.Profile.Navigator.Platform 46 | 47 | c, err := cleanhttp.NewFastCleanHttpClient(&cleanhttp.Config{ 48 | Proxy: config.Proxy, 49 | BrowserFp: fp, 50 | Timeout: 15, 51 | ReadTimeout: time.Second * 15, 52 | WriteTimeout: time.Second * 15, 53 | MaxIdleConnDuration: time.Minute, 54 | }) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | hc := &Hcap{ 60 | Fingerprint: fp, 61 | Config: config, 62 | Http: c, 63 | Logger: config.Logger, 64 | Manager: builder, 65 | Sessions: [][]string{}, 66 | } 67 | 68 | if _, err := hc.Manager.GenerateProfile(); err != nil { 69 | return nil, fmt.Errorf("cant generate fingerprint profile") 70 | } 71 | 72 | return hc, nil 73 | } 74 | 75 | func ApplyFingerprint(config *Config) (*fpclient.Fingerprint, error) { 76 | fp, err := fpclient.LoadFingerprint(&fpclient.LoadingConfig{ 77 | FilePath: "../../assets/chrome.json", 78 | }) 79 | 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | infos := cleanhttp.ParseUserAgent(config.UserAgent) 85 | 86 | fp.Navigator.UserAgent = config.UserAgent 87 | fp.Navigator.Platform = infos.OSName 88 | 89 | return fp, nil 90 | } 91 | 92 | func (c *Hcap) SolveImages(captcha *Captcha) (any, error) { 93 | retry := 0 94 | 95 | for retry <= RETRY { 96 | if len(captcha.Tasklist) <= 0 { 97 | return nil, fmt.Errorf("no images found") 98 | } 99 | 100 | r, err := recognizer.NewRecognizer(c.Config.Proxy, captcha.RequestType, captcha.RequesterQuestion.En, captcha.RequesterRestrictedAnswerSet, captcha.Tasklist) 101 | if err != nil { 102 | return map[string]any{}, err 103 | } 104 | 105 | response, err := r.Recognize() 106 | if err != nil { 107 | return map[string]any{}, err 108 | } 109 | 110 | if !response.Success { 111 | retry++ 112 | continue 113 | } 114 | 115 | return response.Data, nil 116 | } 117 | 118 | return nil, fmt.Errorf("max retry exceded") 119 | } 120 | 121 | func (c *Hcap) CheckSiteConfig() (*SiteConfig, error) { 122 | st := time.Now() 123 | 124 | body, err := c.Http.Do(cleanhttp.RequestOption{ 125 | Method: "POST", 126 | Url: fmt.Sprintf("https://%shcaptcha.com/checksiteconfig?v=%s&host=%s&sitekey=%s&sc=1&swa=1&spst=1", utils.RandomElementString([]string{"api2.", ""}), fingerprint.VERSION, c.Config.Domain, c.Config.SiteKey), 127 | Header: c.HeaderCheckSiteConfig(), 128 | }) 129 | c.SiteConfigProcessing = time.Since(st) 130 | if err != nil { 131 | return nil, err 132 | } 133 | 134 | var config SiteConfig 135 | if err := json.Unmarshal(body.Body(), &config); err != nil { 136 | return nil, err 137 | } 138 | 139 | if !config.Pass { 140 | return &config, fmt.Errorf("checksiteconfig pass is false: %v", config) 141 | } 142 | 143 | if !config.Features.A11YChallenge && c.Config.FreeTextEntry { 144 | return &config, fmt.Errorf("a11y_challenge is disabled on this website") 145 | } 146 | 147 | return &config, nil 148 | } 149 | 150 | func (c *Hcap) GetChallenge(config *SiteConfig) (*Captcha, error) { 151 | var pow string 152 | var err error 153 | 154 | st := time.Now() 155 | 156 | pow, err = c.GetHsw(config.C.Req, false, false) 157 | if err != nil { 158 | return nil, err 159 | } 160 | 161 | c.AnswerProcessing = time.Since(st) 162 | 163 | pdc, _ := json.Marshal(&Pdc{ 164 | S: st.UTC().UnixNano() / 1e6, 165 | N: 0, 166 | P: 0, 167 | Gcs: utils.RandomNumber(40, 110), //int(time.Since(st).Milliseconds()), 168 | }) 169 | 170 | motion := c.NewMotionData(&Motion{ 171 | IsCheck: false, 172 | }) 173 | 174 | C, _ := json.Marshal(&C{ 175 | Type: config.C.Type, 176 | Req: config.C.Req, 177 | }) 178 | 179 | payload := url.Values{} 180 | for name, value := range map[string]string{ 181 | `v`: fingerprint.VERSION, 182 | `sitekey`: c.Config.SiteKey, 183 | `host`: c.Config.Domain, 184 | `hl`: LANG, 185 | `motionData`: motion, 186 | `pdc`: string(pdc), 187 | `n`: pow, 188 | `c`: string(C), 189 | `pst`: `false`, 190 | } { 191 | payload.Set(name, value) 192 | } 193 | 194 | if c.Config.Rqdata != "" { 195 | payload.Set("rqdata", c.Config.Rqdata) 196 | } 197 | 198 | /*if c.Config.FreeTextEntry { 199 | payload.Set(`a11y_tfe`, `true`) 200 | }*/ 201 | 202 | header := c.HeaderGetCaptcha() 203 | if c.Config.HcAccessibility != "" { 204 | header.Add("cookie", fmt.Sprintf("hc_accessibility=%s", c.Config.HcAccessibility)) 205 | } 206 | 207 | t := time.Now() 208 | body, err := c.Http.Do(cleanhttp.RequestOption{ 209 | Method: "POST", 210 | Url: fmt.Sprintf("https://hcaptcha.com/getcaptcha/%s", c.Config.SiteKey), 211 | Body: strings.NewReader(payload.Encode()), 212 | Header: header, 213 | }) 214 | c.GetProcessing = time.Since(t) 215 | if err != nil { 216 | return nil, err 217 | } 218 | 219 | if body == nil { 220 | return nil, fmt.Errorf("GetChallenge body is nil") 221 | } 222 | 223 | if body.StatusCode() == 429 { 224 | return nil, fmt.Errorf("ip is ratelimited or site-key is geo-restricted, please switch your proxy") 225 | } 226 | 227 | var captcha Captcha 228 | if err := json.Unmarshal(body.Body(), &captcha); err != nil { 229 | return nil, err 230 | } 231 | 232 | return &captcha, nil 233 | } 234 | 235 | func (c *Hcap) GetChallengeFreeTextEntry(config *SiteConfig) (*Captcha, error) { 236 | var pow string 237 | var err error 238 | 239 | chall, err := c.GetChallenge(config) 240 | if err != nil { 241 | return nil, err 242 | } 243 | 244 | c.Sessions = append(c.Sessions, []string{ 245 | chall.Key, 246 | c.WidgetIDList[0], 247 | }) 248 | 249 | st := time.Now() 250 | 251 | pow, err = c.GetHsw(chall.C.Req, false, true) 252 | if err != nil { 253 | return nil, err 254 | } 255 | 256 | c.AnswerProcessing = time.Since(st) 257 | 258 | pdc, _ := json.Marshal(&Pdc{ 259 | S: st.UTC().UnixNano() / 1e6, 260 | N: 1, 261 | P: 2, 262 | Gcs: utils.RandomNumber(40, 110), //int(time.Since(st).Milliseconds()), 263 | }) 264 | 265 | motion := c.NewMotionData(&Motion{ 266 | IsCheck: false, 267 | }) 268 | 269 | C, _ := json.Marshal(&C{ 270 | Type: chall.C.Type, 271 | Req: chall.C.Req, 272 | }) 273 | 274 | TextChall, _ := json.Marshal(&chall) 275 | 276 | payload := url.Values{} 277 | for name, value := range map[string]string{ 278 | `v`: fingerprint.VERSION, 279 | `sitekey`: c.Config.SiteKey, 280 | `host`: c.Config.Domain, 281 | `hl`: LANG, 282 | `action`: `challenge-refresh`, 283 | `extraData`: string(TextChall), 284 | `motionData`: motion, 285 | `pdc`: string(pdc), 286 | `old_ekey`: chall.Key, 287 | `n`: pow, 288 | `c`: string(C), 289 | `pst`: `false`, 290 | } { 291 | payload.Set(name, value) 292 | } 293 | 294 | if c.Config.Rqdata != "" { 295 | payload.Set("rqdata", c.Config.Rqdata) 296 | } 297 | 298 | if c.Config.FreeTextEntry { 299 | payload.Set(`a11y_tfe`, `true`) 300 | } 301 | 302 | header := c.HeaderGetCaptcha() 303 | if c.Config.HcAccessibility != "" { 304 | header.Add("cookie", fmt.Sprintf("hc_accessibility=%s", c.Config.HcAccessibility)) 305 | } 306 | 307 | t := time.Now() 308 | body, err := c.Http.Do(cleanhttp.RequestOption{ 309 | Method: "POST", 310 | Url: fmt.Sprintf("https://api.hcaptcha.com/getcaptcha/%s", c.Config.SiteKey), 311 | Body: strings.NewReader(payload.Encode()), 312 | Header: header, 313 | }) 314 | c.GetProcessing = time.Since(t) 315 | if err != nil { 316 | return nil, err 317 | } 318 | 319 | if body == nil { 320 | return nil, fmt.Errorf("GetChallenge body is nil") 321 | } 322 | 323 | if body.StatusCode() == 429 { 324 | return nil, fmt.Errorf("ip is ratelimited or site-key is geo-restricted, please switch your proxy") 325 | } 326 | 327 | var captcha Captcha 328 | if err := json.Unmarshal(body.Body(), &captcha); err != nil { 329 | return nil, err 330 | } 331 | 332 | return &captcha, nil 333 | } 334 | 335 | func (c *Hcap) CheckCaptcha(captcha *Captcha) (*ResponseCheckCaptcha, error) { 336 | var answers any 337 | var payload []byte 338 | var pow string 339 | var err error 340 | 341 | resultChans := make(chan error) 342 | st := time.Now() 343 | 344 | go func() { 345 | answers, err = c.SolveImages(captcha) 346 | if err != nil { 347 | resultChans <- err 348 | return 349 | } 350 | 351 | c.AnswerProcessing = time.Since(st) 352 | resultChans <- nil 353 | }() 354 | 355 | go func() { 356 | pow, err = c.GetHsw(captcha.C.Req, true, captcha.RequestType == "text_free_entry") 357 | if err != nil { 358 | resultChans <- err 359 | return 360 | } 361 | 362 | c.HswProcessing = time.Since(st) 363 | resultChans <- nil 364 | }() 365 | 366 | for i := 0; i < 2; i++ { 367 | err := <-resultChans 368 | if err != nil { 369 | return nil, err 370 | } 371 | } 372 | 373 | motion := c.NewMotionData(&Motion{ 374 | IsCheck: true, 375 | Answers: map[string]string{"x": "true", "y": "true", "z": "true"}, 376 | }) 377 | 378 | C, _ := json.Marshal(&C{ 379 | Type: captcha.C.Type, 380 | Req: captcha.C.Req, 381 | }) 382 | 383 | payload, err = json.Marshal(&PayloadCheckChallenge{ 384 | V: fingerprint.VERSION, 385 | Sitekey: c.Config.SiteKey, 386 | Serverdomain: c.Config.Domain, 387 | JobMode: captcha.RequestType, 388 | MotionData: motion, 389 | N: pow, 390 | C: string(C), 391 | Answers: answers, 392 | }) 393 | 394 | if err != nil { 395 | return nil, err 396 | } 397 | 398 | time.Sleep((time.Millisecond * time.Duration(c.Config.TurboSt)) - time.Since(st)) 399 | 400 | t := time.Now() 401 | body, err := c.Http.Do(cleanhttp.RequestOption{ 402 | Url: fmt.Sprintf("https://api.hcaptcha.com/checkcaptcha/%s/%s", c.Config.SiteKey, captcha.Key), 403 | Body: strings.NewReader(string(payload)), 404 | Method: "POST", 405 | Header: c.HeaderCheckCaptcha(), 406 | }) 407 | c.CheckProcessing = time.Since(t) 408 | 409 | if err != nil { 410 | return nil, err 411 | } 412 | 413 | var Resp ResponseCheckCaptcha 414 | if json.Unmarshal([]byte(body.Body()), &Resp) != nil { 415 | return nil, fmt.Errorf("checkCaptcha-unmarshal: %+v", err) 416 | } 417 | 418 | if !Resp.Pass { 419 | return nil, fmt.Errorf("checkCaptcha: failed to pass: %+v", string(body.Body())) 420 | } 421 | 422 | return &Resp, nil 423 | } 424 | -------------------------------------------------------------------------------- /internal/hcaptcha/headers.go: -------------------------------------------------------------------------------- 1 | package hcaptcha 2 | 3 | import http "github.com/bogdanfinn/fhttp" 4 | 5 | var ( 6 | order = []string{ 7 | `accept`, 8 | `accept-encoding`, 9 | `accept-language`, 10 | `content-length`, 11 | `content-type`, 12 | `cookie`, 13 | `origin`, 14 | `referer`, 15 | `sec-ch-ua`, 16 | `sec-ch-ua-mobile`, 17 | `sec-ch-ua-platform`, 18 | `sec-fetch-dest`, 19 | `sec-fetch-mode`, 20 | `sec-fetch-site`, 21 | `user-agent`, 22 | } 23 | ) 24 | 25 | func (c *Hcap) HeaderCheckSiteConfig() http.Header { 26 | return http.Header{ 27 | `accept`: {`application/json`}, 28 | `accept-language`: {c.Http.BaseHeader.AcceptLanguage}, 29 | `content-type`: {`text/plain`}, 30 | `origin`: {`https://newassets.hcaptcha.com`}, 31 | `referer`: {`https://newassets.hcaptcha.com/`}, 32 | `sec-ch-ua`: {c.Http.BaseHeader.SecChUa}, 33 | `sec-ch-ua-mobile`: {c.Http.BaseHeader.SecChUaMobile}, 34 | `sec-ch-ua-platform`: {c.Http.BaseHeader.SecChUaPlatform}, 35 | `sec-fetch-dest`: {`empty`}, 36 | `sec-fetch-mode`: {`cors`}, 37 | `sec-fetch-site`: {`same-site`}, 38 | `user-agent`: {c.Manager.Manager.Fingerprint.Browser.UserAgent}, 39 | 40 | http.HeaderOrderKey: order, 41 | } 42 | } 43 | 44 | func (c *Hcap) HeaderGetCaptcha() http.Header { 45 | return http.Header{ 46 | `accept`: {`application/json`}, 47 | `accept-language`: {c.Http.BaseHeader.AcceptLanguage}, 48 | `content-type`: {`application/x-www-form-urlencoded`}, 49 | `origin`: {`https://newassets.hcaptcha.com`}, 50 | `referer`: {`https://newassets.hcaptcha.com/`}, 51 | `sec-ch-ua`: {c.Http.BaseHeader.SecChUa}, 52 | `sec-ch-ua-mobile`: {c.Http.BaseHeader.SecChUaMobile}, 53 | `sec-ch-ua-platform`: {c.Http.BaseHeader.SecChUaPlatform}, 54 | `sec-fetch-dest`: {`empty`}, 55 | `sec-fetch-mode`: {`cors`}, 56 | `sec-fetch-site`: {`same-site`}, 57 | `user-agent`: {c.Manager.Manager.Fingerprint.Browser.UserAgent}, 58 | 59 | http.HeaderOrderKey: order, 60 | } 61 | } 62 | 63 | func (c *Hcap) HeaderCheckCaptcha() http.Header { 64 | return http.Header{ 65 | `accept`: {`*/*`}, 66 | `accept-language`: {c.Http.BaseHeader.AcceptLanguage}, 67 | `content-type`: {`application/json;charset=UTF-8`}, 68 | `origin`: {`https://newassets.hcaptcha.com`}, 69 | `referer`: {`https://newassets.hcaptcha.com/`}, 70 | `sec-ch-ua`: {c.Http.BaseHeader.SecChUa}, 71 | `sec-ch-ua-mobile`: {c.Http.BaseHeader.SecChUaMobile}, 72 | `sec-ch-ua-platform`: {c.Http.BaseHeader.SecChUaPlatform}, 73 | `sec-fetch-dest`: {`empty`}, 74 | `sec-fetch-mode`: {`cors`}, 75 | `sec-fetch-site`: {`same-site`}, 76 | `user-agent`: {c.Manager.Manager.Fingerprint.Browser.UserAgent}, 77 | 78 | http.HeaderOrderKey: order, 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /internal/hcaptcha/hsw.go: -------------------------------------------------------------------------------- 1 | package hcaptcha 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/0xF7A4C6/GoCycle" 10 | "github.com/valyala/fasthttp" 11 | "github.com/zenthangplus/goccm" 12 | ) 13 | 14 | var ( 15 | NORMAL_ADDR = "http://127.0.0.1:3030" 16 | ) 17 | 18 | var ( 19 | TASKTYPE_ENTERPRISE = 0 20 | TASKTYPE_NORMAL = 1 21 | ) 22 | 23 | var ( 24 | cc = goccm.New(1500) 25 | 26 | readTimeout, _ = time.ParseDuration("10s") 27 | writeTimeout, _ = time.ParseDuration("10s") 28 | 29 | headerContentTypeJson = []byte("application/json") 30 | Client = &fasthttp.Client{ 31 | ReadTimeout: readTimeout, 32 | WriteTimeout: writeTimeout, 33 | MaxIdleConnDuration: time.Second * 15, 34 | NoDefaultUserAgentHeader: true, 35 | DisableHeaderNamesNormalizing: true, 36 | DisablePathNormalizing: true, 37 | Dial: (&fasthttp.TCPDialer{ 38 | Concurrency: 4096, 39 | DNSCacheDuration: time.Hour, 40 | }).Dial, 41 | } 42 | ) 43 | 44 | var ( 45 | Endpoints = GoCycle.New(&[]string{ 46 | "http://127.0.0.1:1235", 47 | "http://127.0.0.1:1236", 48 | "http://127.0.0.1:1237", 49 | "http://127.0.0.1:1238", 50 | "http://127.0.0.1:1239", 51 | "http://127.0.0.1:1240", 52 | "http://127.0.0.1:1241", 53 | "http://127.0.0.1:1242", 54 | "http://127.0.0.1:1243", 55 | "http://127.0.0.1:1244", 56 | "http://127.0.0.1:1245", 57 | "http://127.0.0.1:1246", 58 | }) 59 | ) 60 | 61 | func (c *Hcap) GetHsw(jwt string, isSubmit, isText bool) (string, error) { 62 | for i := 0; i < 10; i++ { 63 | req := fasthttp.AcquireRequest() 64 | 65 | switch c.Config.TaskType { 66 | case TASKTYPE_ENTERPRISE: 67 | n, err := c.Manager.Build(jwt, isSubmit, isText) 68 | if err != nil { 69 | fmt.Println(err) 70 | return "", fmt.Errorf("someone poop in the api and we got a error") 71 | } 72 | 73 | out, err := json.Marshal(n) 74 | if err != nil { 75 | return "", fmt.Errorf("someone poop in the api and we got a error") 76 | } 77 | 78 | fp := base64.StdEncoding.EncodeToString(out) 79 | 80 | end, _ := Endpoints.Next() 81 | req.Header.SetMethod(fasthttp.MethodPost) 82 | req.Header.SetContentTypeBytes(headerContentTypeJson) 83 | req.SetRequestURI(fmt.Sprintf("%s/n", end)) 84 | req.SetBodyRaw([]byte(fmt.Sprintf(`{"jwt": "%s", "fp": "%s"}`, jwt, fp))) 85 | case TASKTYPE_NORMAL: 86 | req.Header.SetMethod(fasthttp.MethodGet) 87 | req.SetRequestURI(fmt.Sprintf("%s/n?req=%s", NORMAL_ADDR, jwt)) 88 | } 89 | 90 | resp := fasthttp.AcquireResponse() 91 | 92 | cc.Wait() 93 | err := Client.Do(req, resp) 94 | cc.Done() 95 | 96 | fasthttp.ReleaseRequest(req) 97 | defer fasthttp.ReleaseResponse(resp) 98 | 99 | if err != nil { 100 | continue 101 | } 102 | 103 | return string(resp.Body()), nil 104 | } 105 | 106 | return "", fmt.Errorf("someone poop in the api and we got a error") 107 | } 108 | -------------------------------------------------------------------------------- /internal/hcaptcha/motiondata.go: -------------------------------------------------------------------------------- 1 | package hcaptcha 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "image/color" 7 | "math/rand" 8 | "time" 9 | 10 | "github.com/Implex-ltd/hcsolver/internal/utils" 11 | "gonum.org/v1/plot" 12 | "gonum.org/v1/plot/plotter" 13 | "gonum.org/v1/plot/vg" 14 | ) 15 | 16 | var boxes = map[int]Box{ 17 | 0: {Start: Point{131, 282, 0}, End: Point{177, 310, 0}}, 18 | 1: {Start: Point{250, 274, 0}, End: Point{313, 318, 0}}, 19 | 2: {Start: Point{390, 274, 0}, End: Point{438, 324, 0}}, 20 | 3: {Start: Point{122, 408, 0}, End: Point{187, 456, 0}}, 21 | 4: {Start: Point{250, 400, 0}, End: Point{314, 451, 0}}, 22 | 5: {Start: Point{386, 400, 0}, End: Point{448, 466, 0}}, 23 | 6: {Start: Point{124, 530, 0}, End: Point{188, 584, 0}}, 24 | 7: {Start: Point{250, 539, 0}, End: Point{313, 588, 0}}, 25 | 8: {Start: Point{387, 537, 0}, End: Point{446, 579, 0}}, 26 | } 27 | 28 | func RandomPointInBox(box Box) Point { 29 | const minDiff = 1 30 | xDiff := box.End.X - box.Start.X 31 | if xDiff < 0 { 32 | xDiff = -xDiff 33 | } 34 | yDiff := box.End.Y - box.Start.Y 35 | if yDiff < 0 { 36 | yDiff = -yDiff 37 | } 38 | return Point{ 39 | X: box.Start.X + rand.Int63n(xDiff+minDiff), 40 | Y: box.Start.Y + rand.Int63n(yDiff+minDiff), 41 | T: time.Now().UnixNano() / 1e6, 42 | } 43 | } 44 | 45 | func calculateBezier(WnTime float64, start, ctrl1, ctrl2, end int64) int64 { 46 | u := 1.0 - WnTime 47 | tt := WnTime * WnTime 48 | uu := u * u 49 | uuu := uu * u 50 | ttt := tt * WnTime 51 | 52 | res := uuu*float64(start) + 3*uu*WnTime*float64(ctrl1) + 3*u*tt*float64(ctrl2) + ttt*float64(end) 53 | return int64(res) 54 | } 55 | 56 | func GenerateMousePath(start, end Point, numPoints int) []Point { 57 | const minDiff = 1 58 | 59 | ctrl1 := Point{ 60 | X: start.X + max(rand.Int63n(max((end.X-start.X)/2, minDiff)), minDiff), 61 | Y: start.Y + max(rand.Int63n(max((end.Y-start.Y)/2, minDiff)), minDiff), 62 | T: start.T + (end.T-start.T)/4, 63 | } 64 | ctrl2 := Point{ 65 | X: ctrl1.X + max(rand.Int63n(max((end.X-ctrl1.X)/2, minDiff)), minDiff), 66 | Y: ctrl1.Y + max(rand.Int63n(max((end.Y-ctrl1.Y)/2, minDiff)), minDiff), 67 | T: ctrl1.T + (end.T-ctrl1.T)/2, 68 | } 69 | 70 | var path []Point 71 | for i := 0; i < numPoints; i++ { 72 | WnTime := float64(i) / float64(numPoints-1) 73 | 74 | path = append(path, Point{ 75 | X: calculateBezier(WnTime, start.X, ctrl1.X, ctrl2.X, end.X), 76 | Y: calculateBezier(WnTime, start.Y, ctrl1.Y, ctrl2.Y, end.Y), 77 | T: int64((1.0-WnTime)*float64(start.T) + WnTime*float64(end.T)), 78 | }) 79 | } 80 | 81 | return path 82 | } 83 | 84 | func max(a, b int64) int64 { 85 | if a > b { 86 | return a 87 | } 88 | return b 89 | } 90 | 91 | func PlotPoints(points [][]int64) { 92 | var path []Point 93 | 94 | for _, p := range points { 95 | path = append(path, Point{ 96 | X: p[0], 97 | Y: p[1], 98 | T: p[2], 99 | }) 100 | } 101 | 102 | p := plot.New() 103 | 104 | p.Title.Text = "Mouse Path" 105 | p.X.Label.Text = "X" 106 | p.Y.Label.Text = "Y" 107 | 108 | pts := make(plotter.XYs, len(path)) 109 | for i, point := range path { 110 | pts[i].X = float64(point.X) 111 | pts[i].Y = float64(point.Y) 112 | } 113 | 114 | minX, maxX, minY, maxY := pts[0].X, pts[0].X, pts[0].Y, pts[0].Y 115 | for _, point := range pts[1:] { 116 | if point.X < minX { 117 | minX = point.X 118 | } 119 | if point.X > maxX { 120 | maxX = point.X 121 | } 122 | if point.Y < minY { 123 | minY = point.Y 124 | } 125 | if point.Y > maxY { 126 | maxY = point.Y 127 | } 128 | } 129 | 130 | p.X.Min = minX - 1 131 | p.X.Max = maxX + 1 132 | p.Y.Min = minY - 1 133 | p.Y.Max = maxY + 1 134 | 135 | s, err := plotter.NewScatter(pts) 136 | if err != nil { 137 | panic(err) 138 | } 139 | 140 | s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255} 141 | 142 | l, err := plotter.NewLine(pts) 143 | if err != nil { 144 | panic(err) 145 | } 146 | 147 | l.LineStyle.Width = vg.Points(1) 148 | l.LineStyle.Dashes = []vg.Length{vg.Points(5), vg.Points(5)} 149 | l.LineStyle.Color = color.RGBA{B: 255, A: 255} 150 | 151 | p.Add(s, l) 152 | 153 | if err := p.Save(4*vg.Inch, 4*vg.Inch, fmt.Sprintf("./.tmp/%s.png", utils.RandomString(10))); err != nil { 154 | panic(err) 155 | } 156 | 157 | fmt.Println("Plot saved to mouse_path.png") 158 | } 159 | 160 | func Point2path(p []Point) [][]int64 { 161 | convertedPath := make([][]int64, len(p)) 162 | 163 | for i, point := range p { 164 | convertedPath[i] = []int64{point.X, point.Y, point.T} 165 | } 166 | 167 | return convertedPath 168 | } 169 | 170 | func addTime(st int64, toAdd time.Duration) int64 { 171 | return st + toAdd.Milliseconds() 172 | } 173 | 174 | func Click(boxesToClick []int, startTime, duration int64, curveAmount int) [][]int64 { 175 | var path []Point 176 | timeIncrement := duration / int64(len(boxesToClick)) 177 | 178 | for i, boxNum := range boxesToClick { 179 | box := boxes[boxNum] 180 | targetPoint := RandomPointInBox(box) 181 | targetPoint.T = startTime + timeIncrement*int64(i) 182 | 183 | if i > 0 { 184 | intermediatePath := GenerateMousePath(path[len(path)-1], targetPoint, curveAmount) 185 | timeDiff := targetPoint.T - path[len(path)-1].T 186 | 187 | for j, point := range intermediatePath { 188 | point.T = path[len(path)-1].T + timeDiff*int64(j)/int64(len(intermediatePath)) 189 | intermediatePath[j] = point 190 | } 191 | path = append(path, intermediatePath...) 192 | } 193 | 194 | path = append(path, targetPoint) 195 | } 196 | 197 | return Point2path(path) 198 | } 199 | 200 | func GetRandomBox() Box { 201 | boxIDs := make([]int, 0, len(boxes)) 202 | for boxID := range boxes { 203 | boxIDs = append(boxIDs, boxID) 204 | } 205 | 206 | rand.New(rand.NewSource(time.Now().UnixNano())) 207 | randomIndex := rand.Intn(len(boxIDs)) 208 | randomBoxID := boxIDs[randomIndex] 209 | 210 | return boxes[randomBoxID] 211 | } 212 | 213 | func calculateMeanPeriod(events [][]int64) float64 { 214 | var timeDifferences []int64 215 | for i := 0; i < len(events)-1; i++ { 216 | timeDifference := events[i+1][2] - events[i][2] 217 | timeDifferences = append(timeDifferences, timeDifference) 218 | } 219 | var sum int64 = 0 220 | for _, timeDifference := range timeDifferences { 221 | sum += timeDifference 222 | } 223 | meanPeriod := float64(sum) / float64(len(timeDifferences)) 224 | return meanPeriod 225 | } 226 | 227 | func genBoxToClick(answers map[string]string) []int { 228 | var num = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 229 | var items []int 230 | 231 | rand.Shuffle(len(num), func(i, j int) { 232 | num[i], num[j] = num[j], num[i] 233 | }) 234 | 235 | for i := 0; i < len(answers) && i < len(num); i++ { 236 | items = append(items, num[i]) 237 | } 238 | 239 | return items 240 | } 241 | 242 | /* 243 | - free text entry generation 244 | [key, timestamp] 245 | 246 | > 8 = return 247 | > 13 = enter 248 | > 73 = i 249 | > 78 = n 250 | > 79 = o 251 | > 85 = u 252 | */ 253 | 254 | const ( 255 | KEY_RETURN = 8 256 | KEY_ENTER = 13 257 | KEY_I = 73 258 | KEY_N = 78 259 | KEY_O = 79 260 | KEY_U = 85 261 | ) 262 | 263 | func genKd(answers []string) ([][]int64, float64) { 264 | out := [][]int64{} 265 | st := time.Now().UnixNano() / 1e6 266 | increment := 2 * time.Millisecond 267 | 268 | // Store times for each key press 269 | keyTimes := []int64{} 270 | 271 | getNo := func() []int64 { 272 | keyStrokes := []int64{KEY_N, KEY_O, KEY_N} 273 | 274 | for _, k := range keyStrokes { 275 | keyTime := addTime(st, increment) 276 | keyTimes = append(keyTimes, keyTime) 277 | out = append(out, []int64{k, keyTime}) 278 | increment += time.Duration(rand.Intn(500)+500) * time.Millisecond 279 | } 280 | 281 | return keyTimes 282 | } 283 | 284 | getYes := func() []int64 { 285 | keyStrokes := []int64{KEY_O, KEY_U, KEY_I} 286 | 287 | for _, k := range keyStrokes { 288 | keyTime := addTime(st, increment) 289 | keyTimes = append(keyTimes, keyTime) 290 | out = append(out, []int64{k, keyTime}) 291 | increment += time.Duration(rand.Intn(500)+500) * time.Millisecond 292 | } 293 | 294 | return keyTimes 295 | } 296 | 297 | for _, answer := range answers { 298 | switch answer { 299 | case "oui": 300 | keyTimes = append(keyTimes, getYes()...) 301 | case "non": 302 | keyTimes = append(keyTimes, getNo()...) 303 | } 304 | 305 | increment += time.Duration(rand.Intn(1000)+1000) * time.Millisecond 306 | out = append(out, []int64{KEY_ENTER, addTime(st, increment)}) 307 | keyTimes = append(keyTimes, addTime(st, increment)) 308 | } 309 | 310 | totalTime := keyTimes[len(keyTimes)-1] - keyTimes[0] 311 | meanPeriod := float64(totalTime) / float64(len(keyTimes)-1) 312 | 313 | return out, meanPeriod 314 | } 315 | 316 | /* 317 | * Todo: add right mouse moovements side, if box is lower/higher edit path to add right box 318 | */ 319 | func (c *Hcap) NewMotionData(m *Motion) string { 320 | st := time.Now().UnixNano() / 1e6 321 | duration := int64(utils.RandomNumber(100, 500)) 322 | 323 | if !m.IsCheck { 324 | m.Answers = map[string]string{"x": "x", "y": "y", "z": "z", "d": "z", "a": "z"} 325 | } 326 | 327 | for i := 0; i < utils.RandomNumber(1, 5); i++ { 328 | m.Answers[utils.RandomString(5)] = "x" 329 | } 330 | 331 | toClick := genBoxToClick(m.Answers) 332 | 333 | CaptchaPath := Click(toClick, st, duration, utils.RandomNumber(3, 6)) 334 | MdPath := Click([]int{utils.RandomNumber(0, 8), utils.RandomNumber(0, 8), utils.RandomNumber(0, 8)}, st, duration, utils.RandomNumber(8, 16)) 335 | MuPath := Click([]int{utils.RandomNumber(0, 8), utils.RandomNumber(0, 8), utils.RandomNumber(0, 8)}, st, duration, utils.RandomNumber(3, 10)) 336 | MmPath := Click([]int{utils.RandomNumber(0, 8), utils.RandomNumber(0, 8), utils.RandomNumber(0, 8)}, st, duration, utils.RandomNumber(10, 20)) 337 | 338 | //WnTime := time.Duration(utils.RandomNumber(20, 35)) * time.Millisecond 339 | //PlotPoints(CaptchaPath) 340 | 341 | topLevel := TopLevel{ 342 | St: st, 343 | Sc: Sc{ 344 | AvailWidth: int64(c.Manager.Profile.Screen.AvailWidth), 345 | AvailHeight: int64(c.Manager.Profile.Screen.AvailHeight), 346 | Width: int64(c.Manager.Profile.Screen.Width), 347 | Height: int64(c.Manager.Profile.Screen.Height), 348 | ColorDepth: int64(c.Manager.Profile.Screen.ColorDepth), 349 | PixelDepth: int64(c.Manager.Profile.Screen.PixelDepth), 350 | AvailLeft: int64(c.Fingerprint.Screen.AvailLeft), 351 | AvailTop: int64(c.Fingerprint.Screen.AvailTop), 352 | Onchange: nil, 353 | IsExtended: true, 354 | }, 355 | Nv: Nv{ 356 | HardwareConcurrency: int64(c.Manager.Manager.Fingerprint.Browser.HardwareConcurrency), 357 | DeviceMemory: int64(c.Manager.Manager.Fingerprint.Browser.DeviceMemory), 358 | Webdriver: false, 359 | MaxTouchPoints: c.Manager.Profile.Navigator.MaxTouchPoints, 360 | CookieEnabled: true, 361 | AppCodeName: c.Fingerprint.Navigator.AppCodeName, 362 | AppName: c.Fingerprint.Navigator.AppName, 363 | AppVersion: c.Manager.Manager.Fingerprint.Browser.AppVersion, 364 | Platform: c.Manager.Profile.Navigator.Platform, 365 | Product: c.Fingerprint.Navigator.Product, 366 | ProductSub: c.Fingerprint.Navigator.ProductSub, 367 | UserAgent: c.Manager.Manager.UserAgent, 368 | Vendor: c.Fingerprint.Navigator.Vendor, 369 | VendorSub: c.Fingerprint.Navigator.VendorSub, 370 | Language: c.Manager.Profile.Navigator.Language, 371 | Languages: c.Manager.Profile.Navigator.Languages, 372 | OnLine: true, 373 | PDFViewerEnabled: c.Manager.Manager.Fingerprint.Browser.PDFViewerEnabled, 374 | DoNotTrack: c.Fingerprint.Navigator.DoNotTrack, 375 | Plugins: []string{"internal-pdf-viewer", "internal-pdf-viewer", "internal-pdf-viewer", "internal-pdf-viewer", "internal-pdf-viewer"}, 376 | UserAgentData: UserAgentData{ 377 | Brands: []Brand{ 378 | { 379 | Brand: "Not=A?Brand", 380 | Version: "8", 381 | }, 382 | { 383 | Brand: "Google Chrome", 384 | Version: c.Http.BaseHeader.UaInfo.UaVersion, 385 | }, 386 | { 387 | Brand: "Chromium", 388 | Version: c.Http.BaseHeader.UaInfo.UaVersion, 389 | }, 390 | }, 391 | Mobile: c.Manager.Manager.Fingerprint.Browser.Mobile, 392 | Platform: c.Manager.Manager.Fingerprint.Events["702"].(map[string]interface{})["OsName"].(string), 393 | }, 394 | }, 395 | DR: c.Config.Dr, 396 | Inv: c.Config.Invisible, 397 | Exec: c.Config.Exec, 398 | Wn: [][]int64{ 399 | /*{ 400 | int64(c.Manager.Profile.Screen.AvailWidth), // mt.Browser.width() // ---> return window.innerWidth && window.document.documentElement.clientWidth ? Math.min(window.innerWidth, document.documentElement.clientWidth) : window.innerWidth || window.document.documentElement.clientWidth || document.body.clientWidth; 401 | int64(c.Manager.Profile.Screen.AvailHeight), // mt.Browser.height() // ---> return window.innerHeight || window.document.documentElement.clientHeight || document.body.clientHeight; 402 | 1, // mt.System.dpr() 403 | addTime(st, WnTime), // Date.now() 404 | },*/ 405 | }, 406 | WnMp: 0, 407 | Xy: [][]int64{ 408 | /*{ 409 | 0, // mt.Browser.scrollX(), // ---> return window.pageXOffset !== undefined ? window.pageXOffset : WnTime.isCSS1 ? document.documentElement.scrollLeft : document.body.scrollLeft; 410 | 0, // mt.Browser.scrollY(), // ---> return window.pageYOffset !== undefined ? window.pageYOffset : WnTime.isCSS1 ? document.documentElement.scrollTop : document.body.scrollTop; 411 | int64(c.Manager.Profile.Screen.AvailWidth) / (int64(c.Manager.Profile.Screen.AvailWidth) * 2), // document.documentElement.clientWidth / mt.Browser.width(), 412 | addTime(st, WnTime), // Date.now() 413 | },*/ 414 | }, 415 | XyMp: 0, 416 | Mm: MmPath, 417 | MmMp: calculateMeanPeriod(MmPath), 418 | } 419 | 420 | output := []byte{} 421 | 422 | switch m.IsCheck { 423 | case true: 424 | if c.Config.FreeTextEntry { 425 | keyData, meanPeriod := genKd(utils.ShuffleStrings([]string{"oui", "oui", "non"})) 426 | 427 | output, _ = json.Marshal(&CheckDataFreeTextEntry{ 428 | St: st, 429 | Dct: st, 430 | Kd: keyData, 431 | KdMp: meanPeriod, 432 | Ku: keyData, 433 | KuMp: meanPeriod, 434 | TopLevel: topLevel, 435 | V: 1, 436 | }) 437 | } else { 438 | output, _ = json.Marshal(&CheckData{ 439 | St: st, 440 | Dct: st, 441 | Mm: CaptchaPath, 442 | MmMp: calculateMeanPeriod(CaptchaPath), 443 | Md: MdPath, 444 | MdMp: calculateMeanPeriod(MdPath), 445 | Mu: MuPath, 446 | MuMp: calculateMeanPeriod(MuPath), 447 | TopLevel: topLevel, 448 | V: 1, 449 | }) 450 | } 451 | 452 | case false: 453 | widget := "0" + utils.RandomString(10) 454 | 455 | c.WidgetIDList = append(c.WidgetIDList, widget) 456 | 457 | output, _ = json.Marshal(&GetData{ 458 | St: st, 459 | Mm: CaptchaPath, 460 | MmMp: calculateMeanPeriod(CaptchaPath), 461 | Md: MdPath, 462 | MdMp: calculateMeanPeriod(MdPath), 463 | Mu: MuPath, 464 | MuMp: calculateMeanPeriod(MuPath), 465 | TopLevel: topLevel, 466 | V: 1, 467 | 468 | Session: c.Sessions, 469 | WidgetList: []string{ 470 | widget, 471 | }, 472 | WidgetID: widget, 473 | Href: c.Manager.Manager.Href, 474 | Prev: Prev{ 475 | Escaped: false, 476 | Passed: false, 477 | ExpiredChallenge: false, 478 | ExpiredResponse: false, 479 | }, 480 | }) 481 | } 482 | 483 | return string(output) 484 | } 485 | -------------------------------------------------------------------------------- /internal/hcaptcha/struct.go: -------------------------------------------------------------------------------- 1 | package hcaptcha 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Implex-ltd/cleanhttp/cleanhttp" 7 | "github.com/Implex-ltd/fingerprint-client/fpclient" 8 | "github.com/Implex-ltd/hcsolver/internal/hcaptcha/fingerprint" 9 | "github.com/Implex-ltd/hcsolver/internal/recognizer" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | type Config struct { 14 | UserAgent string 15 | SiteKey string 16 | Domain string 17 | Proxy string 18 | Rqdata string 19 | Invisible bool 20 | TaskType int 21 | FreeTextEntry bool 22 | Turbo bool 23 | TurboSt int 24 | HcAccessibility string 25 | OneClick bool 26 | Href string 27 | Exec bool 28 | Dr string 29 | 30 | Logger *zap.Logger 31 | } 32 | 33 | type Hcap struct { 34 | Config *Config 35 | Http *cleanhttp.FastCleanHttp 36 | Fingerprint *fpclient.Fingerprint 37 | 38 | // metrics 39 | AnswerProcessing time.Duration 40 | HswProcessing time.Duration 41 | CheckProcessing time.Duration 42 | GetProcessing time.Duration 43 | SiteConfigProcessing time.Duration 44 | 45 | Logger *zap.Logger 46 | Manager *fingerprint.Builder 47 | 48 | Sessions [][]string 49 | WidgetIDList []string 50 | } 51 | 52 | type Motion struct { 53 | IsCheck bool 54 | Answers map[string]string 55 | } 56 | 57 | /* 58 | Payloads 59 | */ 60 | 61 | type Pdc struct { 62 | S int64 `json:"s"` 63 | N int `json:"n"` 64 | P int `json:"p"` 65 | Gcs int `json:"gcs"` 66 | } 67 | 68 | /* 69 | general 70 | */ 71 | 72 | type C struct { 73 | Type string `json:"type"` 74 | Req string `json:"req"` 75 | } 76 | 77 | /* 78 | checksiteconfig 79 | */ 80 | 81 | type SiteConfig struct { 82 | Features Features `json:"features"` 83 | C C `json:"c"` 84 | Pass bool `json:"pass"` 85 | } 86 | 87 | type Features struct { 88 | A11YChallenge bool `json:"a11y_challenge"` 89 | } 90 | 91 | /* 92 | getcaptcha 93 | */ 94 | 95 | type Captcha struct { 96 | C C `json:"c"` 97 | ChallengeURI string `json:"challenge_uri"` 98 | Key string `json:"key"` 99 | RequestConfig RequestConfig `json:"request_config"` 100 | RequestType string `json:"request_type"` 101 | RequesterQuestion RequesterQuestion `json:"requester_question"` 102 | RequesterQuestionExample []string `json:"requester_question_example"` 103 | Tasklist []recognizer.TaskList `json:"tasklist"` 104 | BypassMessage string `json:"bypass-message"` 105 | 106 | RequesterRestrictedAnswerSet map[string]map[string]string `json:"requester_restricted_answer_set"` 107 | 108 | // one click 109 | Pass bool `json:"pass"` 110 | GeneratedPassUUID string `json:"generated_pass_UUID"` 111 | Expiration int `json:"expiration"` 112 | 113 | // a11y_challenge 114 | Rq bool `json:"rq"` 115 | } 116 | 117 | type RequestConfig struct { 118 | Version int `json:"version"` 119 | ShapeType string `json:"shape_type"` 120 | MinPoints int `json:"min_points"` 121 | MaxPoints int `json:"max_points"` 122 | MinShapesPerImage int `json:"min_shapes_per_image"` 123 | MaxShapesPerImage int `json:"max_shapes_per_image"` 124 | RestrictToCoords string `json:"restrict_to_coords"` 125 | MinimumSelectionAreaPerShape string `json:"minimum_selection_area_per_shape"` 126 | MultipleChoiceMaxChoices int `json:"multiple_choice_max_choices"` 127 | MultipleChoiceMinChoices int `json:"multiple_choice_min_choices"` 128 | OverlapThreshold string `json:"overlap_threshold"` 129 | AnswerType string `json:"answer_type"` 130 | MaxValue string `json:"max_value"` 131 | MinValue string `json:"min_value"` 132 | MaxLength string `json:"max_length"` 133 | MinLength string `json:"min_length"` 134 | SigFigs string `json:"sig_figs"` 135 | KeepAnswersOrder bool `json:"keep_answers_order"` 136 | } 137 | 138 | type RequesterQuestion struct { 139 | Fr string `json:"fr"` 140 | En string `json:"en"` 141 | } 142 | 143 | type Tasklist struct { 144 | DatapointURI string `json:"datapoint_uri"` 145 | TaskKey string `json:"task_key"` 146 | } 147 | 148 | /* 149 | motion data 150 | */ 151 | 152 | type Empty struct { 153 | } 154 | 155 | type CheckData struct { 156 | St int64 `json:"st"` 157 | Dct int64 `json:"dct"` 158 | 159 | Mm [][]int64 `json:"mm"` 160 | MmMp float64 `json:"mm-mp"` 161 | 162 | Md [][]int64 `json:"md"` 163 | MdMp float64 `json:"md-mp"` 164 | 165 | Mu [][]int64 `json:"mu"` 166 | MuMp float64 `json:"mu-mp"` 167 | 168 | TopLevel TopLevel `json:"topLevel"` 169 | V int64 `json:"v"` 170 | 171 | //Kd [][]int64 `json:"kd"` 172 | //KdMp int64 `json:"kd-mp"` 173 | //Ku [][]int64 `json:"ku"` 174 | //KuMp int64 `json:"ku-mp"` 175 | } 176 | 177 | type GetData struct { 178 | St int64 `json:"st"` 179 | V int64 `json:"v"` 180 | TopLevel TopLevel `json:"topLevel"` 181 | 182 | Mm [][]int64 `json:"mm"` 183 | MmMp float64 `json:"mm-mp"` 184 | 185 | Md [][]int64 `json:"md"` 186 | MdMp float64 `json:"md-mp"` 187 | 188 | Mu [][]int64 `json:"mu"` 189 | MuMp float64 `json:"mu-mp"` 190 | 191 | Session [][]string `json:"session"` 192 | WidgetList []string `json:"widgetList"` 193 | WidgetID string `json:"widgetId"` 194 | Href string `json:"href"` 195 | Prev Prev `json:"prev"` 196 | } 197 | 198 | type CheckDataFreeTextEntry struct { 199 | St int64 `json:"st"` 200 | Dct int64 `json:"dct"` 201 | 202 | Kd [][]int64 `json:"kd"` 203 | KdMp float64 `json:"kd-mp"` 204 | Ku [][]int64 `json:"ku"` 205 | KuMp float64 `json:"ku-mp"` 206 | 207 | TopLevel TopLevel `json:"topLevel"` 208 | V int64 `json:"v"` 209 | } 210 | 211 | type TopLevel struct { 212 | Inv bool `json:"inv"` 213 | St int64 `json:"st"` 214 | Sc Sc `json:"sc"` 215 | Nv Nv `json:"nv"` 216 | DR string `json:"dr"` 217 | Exec bool `json:"exec"` 218 | 219 | Wn [][]int64 `json:"wn"` 220 | WnMp float64 `json:"wn-mp"` 221 | 222 | Xy [][]int64 `json:"xy"` 223 | XyMp float64 `json:"xy-mp"` 224 | 225 | Mm [][]int64 `json:"mm"` 226 | MmMp float64 `json:"mm-mp"` 227 | 228 | //Md [][]int64 `json:"md"` 229 | //MdMp int64 `json:"md-mp"` 230 | //Mu [][]int64 `json:"mu"` 231 | //MuMp int64 `json:"mu-mp"` 232 | //Lpt int64 `json:"lpt"` 233 | } 234 | 235 | type Nv struct { 236 | /* 237 | Iphone params: 238 | - Standalone bool `json:"standalone"` 239 | */ 240 | 241 | Clipboard Empty `json:"clipboard"` 242 | VendorSub string `json:"vendorSub"` 243 | ProductSub string `json:"productSub"` 244 | Vendor string `json:"vendor"` 245 | MaxTouchPoints float64 `json:"maxTouchPoints"` 246 | Scheduling Empty `json:"scheduling"` 247 | UserActivation Empty `json:"userActivation"` 248 | DoNotTrack interface{} `json:"doNotTrack"` 249 | Geolocation Empty `json:"geolocation"` 250 | Connection Empty `json:"connection"` 251 | PDFViewerEnabled bool `json:"pdfViewerEnabled"` 252 | WebkitTemporaryStorage Empty `json:"webkitTemporaryStorage"` 253 | HardwareConcurrency int64 `json:"hardwareConcurrency"` 254 | CookieEnabled bool `json:"cookieEnabled"` 255 | AppCodeName string `json:"appCodeName"` 256 | AppName string `json:"appName"` 257 | AppVersion string `json:"appVersion"` 258 | Platform string `json:"platform"` 259 | Product string `json:"product"` 260 | UserAgent string `json:"userAgent"` 261 | Language string `json:"language"` 262 | Languages []string `json:"languages"` 263 | OnLine bool `json:"onLine"` 264 | Webdriver bool `json:"webdriver"` 265 | DeprecatedRunAdAuctionEnforcesKAnonymity bool `json:"deprecatedRunAdAuctionEnforcesKAnonymity"` 266 | Bluetooth Empty `json:"bluetooth"` 267 | Credentials Empty `json:"credentials"` 268 | Keyboard Empty `json:"keyboard"` 269 | Managed Empty `json:"managed"` 270 | MediaDevices Empty `json:"mediaDevices"` 271 | Storage Empty `json:"storage"` 272 | ServiceWorker Empty `json:"serviceWorker"` 273 | VirtualKeyboard Empty `json:"virtualKeyboard"` 274 | WakeLock Empty `json:"wakeLock"` 275 | DeviceMemory int64 `json:"deviceMemory"` 276 | Ink Empty `json:"ink"` 277 | HID Empty `json:"hid"` 278 | Locks Empty `json:"locks"` 279 | MediaCapabilities Empty `json:"mediaCapabilities"` 280 | MediaSession Empty `json:"mediaSession"` 281 | Permissions Empty `json:"permissions"` 282 | Presentation Empty `json:"presentation"` 283 | Serial Empty `json:"serial"` 284 | GPU Empty `json:"gpu"` 285 | USB Empty `json:"usb"` 286 | WindowControlsOverlay Empty `json:"windowControlsOverlay"` 287 | Xr Empty `json:"xr"` 288 | UserAgentData UserAgentData `json:"userAgentData"` 289 | Plugins []string `json:"plugins"` 290 | } 291 | 292 | type Brand struct { 293 | Brand string `json:"brand"` 294 | Version string `json:"version"` 295 | } 296 | 297 | type UserAgentData struct { 298 | Brands []Brand `json:"brands"` 299 | Mobile bool `json:"mobile"` 300 | Platform string `json:"platform"` 301 | } 302 | 303 | type Sc struct { 304 | AvailWidth int64 `json:"availWidth"` 305 | AvailHeight int64 `json:"availHeight"` 306 | Width int64 `json:"width"` 307 | Height int64 `json:"height"` 308 | ColorDepth int64 `json:"colorDepth"` 309 | PixelDepth int64 `json:"pixelDepth"` 310 | AvailLeft int64 `json:"availLeft"` 311 | AvailTop int64 `json:"availTop"` 312 | Onchange interface{} `json:"onchange"` 313 | IsExtended bool `json:"isExtended"` 314 | } 315 | 316 | type Prev struct { 317 | Escaped bool `json:"escaped"` 318 | Passed bool `json:"passed"` 319 | ExpiredChallenge bool `json:"expiredChallenge"` 320 | ExpiredResponse bool `json:"expiredResponse"` 321 | } 322 | 323 | /* 324 | Mouse curves 325 | */ 326 | 327 | type Box struct { 328 | Start, End Point 329 | } 330 | 331 | type Point struct { 332 | X, Y, T int64 333 | } 334 | 335 | /* 336 | Anwsers 337 | */ 338 | 339 | type LabelAreaSelect struct { 340 | TaskType string `json:"task_type"` 341 | Question string `json:"question"` 342 | EntityType string `json:"entity_type"` 343 | Tasklist []Tasklist `json:"tasklist"` 344 | } 345 | 346 | type LabelBinaryPayload struct { 347 | TaskType string `json:"task_type"` 348 | Question string `json:"question"` 349 | Tasklist []Tasklist `json:"tasklist"` 350 | } 351 | 352 | type AiSolverResponse struct { 353 | Success bool `json:"success"` 354 | Data map[string]any `json:"data"` 355 | } 356 | 357 | /* 358 | checkcaptcha 359 | */ 360 | 361 | type PayloadCheckChallenge struct { 362 | V string `json:"v"` 363 | JobMode string `json:"job_mode"` 364 | Answers any `json:"answers"` 365 | Serverdomain string `json:"serverdomain"` 366 | Sitekey string `json:"sitekey"` 367 | MotionData string `json:"motionData"` 368 | N string `json:"n"` 369 | C string `json:"c"` 370 | } 371 | 372 | type ResponseCheckCaptcha struct { 373 | C C `json:"c"` 374 | Pass bool `json:"pass"` 375 | GeneratedPassUUID string `json:"generated_pass_UUID"` 376 | Expiration int `json:"expiration"` 377 | } 378 | -------------------------------------------------------------------------------- /internal/model/task.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/surrealdb/surrealdb.go" 4 | 5 | type Task struct { 6 | surrealdb.Basemodel `table:"task"` 7 | 8 | ID string `json:"id,omitempty"` 9 | Status int `json:"status"` 10 | Token string `json:"token"` 11 | Error string `json:"error"` 12 | Success bool `json:"success"` 13 | Expiration int `json:"expiration"` 14 | UserAgent string `json:"user_agent"` 15 | Req string `json:"req"` 16 | } 17 | -------------------------------------------------------------------------------- /internal/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/surrealdb/surrealdb.go" 4 | 5 | type User struct { 6 | surrealdb.Basemodel `table:"user"` 7 | 8 | ID string `json:"id,omitempty"` 9 | Balance int `json:"balance"` 10 | 11 | // Permissions 12 | BypassRestrictedSites bool `json:"bypass_restricted_sites"` 13 | 14 | // Hcaptcha 15 | SolvedHcaptcha int `json:"solved_hcaptcha"` 16 | ThreadUsedHcaptcha int `json:"thread_used_hcaptcha"` 17 | ThreadMaxHcaptcha int `json:"thread_max_hcaptcha"` 18 | } 19 | -------------------------------------------------------------------------------- /internal/recognizer/nocap.go: -------------------------------------------------------------------------------- 1 | package recognizer 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "sync" 10 | "time" 11 | 12 | "github.com/Implex-ltd/hcsolver/internal/utils" 13 | ) 14 | 15 | var ( 16 | ImMut, SmMut, AsMut sync.RWMutex 17 | Client *http.Client 18 | ) 19 | 20 | var ( 21 | nocap = true 22 | apikey = "uwusogaylmaoowo-0a5418e7-6826-c2c0-889f-120216260b2d" //"rorm-5cd874d2-0d3b-e49c-f760-f1e74b316467" 23 | proapi = "https://pro.nocaptchaai.com/solve" 24 | ) 25 | 26 | type Base64JSON struct { 27 | Images map[string]string `json:"images"` 28 | Target string `json:"target"` 29 | Method string `json:"method"` 30 | Sitekey string `json:"sitekey"` 31 | Site string `json:"site"` 32 | Ln string `json:"ln"` 33 | Type string `json:"type"` 34 | } 35 | 36 | type NoCapAnswer struct { 37 | Answer []any `json:"answer"` 38 | ID string `json:"id"` 39 | Message string `json:"message"` 40 | ProcessingTime string `json:"processing_time"` 41 | Solution []int `json:"solution"` 42 | Status string `json:"status"` 43 | Target string `json:"target"` 44 | URL string `json:"url"` 45 | } 46 | 47 | type NoCapAnswerSelect struct { 48 | Answer []int `json:"answer"` 49 | Answers [][]int `json:"answers"` 50 | ID string `json:"id"` 51 | ProcessingTime string `json:"processing_time"` 52 | Solution []any `json:"solution"` 53 | Status string `json:"status"` 54 | Url string `json:"url"` 55 | } 56 | 57 | func SolvePic(toSolve map[string]map[string]string, prompt, target string) (map[string]string, error) { 58 | imgs := map[string]string{} 59 | out := map[string]string{} 60 | 61 | j := 0 62 | for _, v := range toSolve { 63 | imgs[fmt.Sprintf("%d", j)] = v["body"] 64 | j++ 65 | } 66 | 67 | jsonBody, err := json.Marshal(Base64JSON{ 68 | Images: imgs, 69 | Target: prompt, 70 | Method: "hcaptcha_base64", 71 | Sitekey: "4c672d35-0701-42b2-88c3-78380b0db560", 72 | Site: "discord.com", 73 | Ln: "en", 74 | Type: "grid", 75 | }) 76 | if err != nil { 77 | return imgs, err 78 | } 79 | 80 | req, err := http.NewRequest("POST", proapi, bytes.NewBuffer(jsonBody)) 81 | if err != nil { 82 | return imgs, err 83 | } 84 | 85 | req.Header.Set("Content-type", "application/json") 86 | req.Header.Set("apikey", apikey) 87 | 88 | resp, err := Client.Do(req) 89 | if err != nil { 90 | return imgs, err 91 | } 92 | defer func(Body io.ReadCloser) { 93 | err := Body.Close() 94 | if err != nil { 95 | fmt.Println("nocap", err) 96 | } 97 | }(resp.Body) 98 | 99 | bodyBytes, err := io.ReadAll(resp.Body) 100 | if err != nil { 101 | return imgs, err 102 | } 103 | 104 | var answer NoCapAnswer 105 | if err := json.Unmarshal(bodyBytes, &answer); err != nil { 106 | return imgs, err 107 | } 108 | 109 | i := 0 110 | for _, v := range toSolve { 111 | found := false 112 | 113 | for _, a := range answer.Solution { 114 | if a == i { 115 | found = true 116 | break 117 | } 118 | } 119 | 120 | go func(v map[string]string) { 121 | if found { 122 | SmMut.Lock() 123 | defer SmMut.Unlock() 124 | 125 | currentValue, _ := Hashlist.Load(target) 126 | 127 | var updatedValue []string 128 | if currentValue != nil { 129 | updatedValue = append(currentValue.([]string), v["hash"]) 130 | } else { 131 | updatedValue = []string{v["hash"]} 132 | } 133 | 134 | Hashlist.Store(target, updatedValue) 135 | utils.AppendLine(fmt.Sprintf("%s,%s", v["hash"], target), "hash.csv") 136 | } else { 137 | SmMut.Lock() 138 | defer SmMut.Unlock() 139 | currentValue, _ := Hashlist.Load(target) 140 | 141 | var updatedValue []string 142 | if currentValue != nil { 143 | updatedValue = append(currentValue.([]string), "noy_"+v["hash"]) 144 | } else { 145 | updatedValue = []string{"noy_" + v["hash"]} 146 | } 147 | 148 | Hashlist.Store(target, updatedValue) 149 | utils.AppendLine(fmt.Sprintf("%s,not_%s", v["hash"], target), "hash.csv") 150 | } 151 | }(v) 152 | 153 | ImMut.Lock() 154 | out[v["key"]] = fmt.Sprintf(`%v`, found) 155 | ImMut.Unlock() 156 | i++ 157 | } 158 | 159 | return out, nil 160 | } 161 | 162 | func SolvePicSelect(toSolve map[string]map[string]string, prompt, target string) (map[string]HashData, error) { 163 | imgs := map[string]string{} 164 | out := map[string]HashData{} 165 | 166 | j := 0 167 | for _, v := range toSolve { 168 | imgs[fmt.Sprintf("%d", j)] = v["body"] 169 | j++ 170 | } 171 | 172 | jsonBody, err := json.Marshal(Base64JSON{ 173 | Images: imgs, 174 | Target: prompt, 175 | Method: "hcaptcha_base64", 176 | Sitekey: "4c672d35-0701-42b2-88c3-78380b0db560", 177 | Site: "discord.com", 178 | Ln: "en", 179 | Type: "bbox", 180 | }) 181 | if err != nil { 182 | return out, err 183 | } 184 | 185 | req, err := http.NewRequest("POST", proapi, bytes.NewBuffer(jsonBody)) 186 | if err != nil { 187 | return out, err 188 | } 189 | 190 | req.Header.Set("Content-type", "application/json") 191 | req.Header.Set("apikey", apikey) 192 | 193 | resp, err := Client.Do(req) 194 | if err != nil { 195 | return out, err 196 | } 197 | defer func(Body io.ReadCloser) { 198 | err := Body.Close() 199 | if err != nil { 200 | fmt.Println("nocap2", err) 201 | } 202 | }(resp.Body) 203 | 204 | bodyBytes, err := io.ReadAll(resp.Body) 205 | if err != nil { 206 | return out, err 207 | } 208 | 209 | var answer NoCapAnswerSelect 210 | if err := json.Unmarshal(bodyBytes, &answer); err != nil { 211 | return out, err 212 | } 213 | 214 | if answer.Status == "new" { 215 | time.Sleep(time.Second) 216 | 217 | req, err := http.NewRequest("GET", answer.Url, nil) 218 | if err != nil { 219 | return out, err 220 | } 221 | 222 | req.Header.Set("Content-type", "application/json") 223 | req.Header.Set("apikey", apikey) 224 | 225 | resp, err := Client.Do(req) 226 | if err != nil { 227 | return out, err 228 | } 229 | defer func(Body io.ReadCloser) { 230 | err := Body.Close() 231 | if err != nil { 232 | fmt.Println("nocap2", err) 233 | } 234 | }(resp.Body) 235 | 236 | bodyBytes, err := io.ReadAll(resp.Body) 237 | if err != nil { 238 | return out, err 239 | } 240 | 241 | if err := json.Unmarshal(bodyBytes, &answer); err != nil { 242 | return out, err 243 | } 244 | } 245 | 246 | if answer.Status == "skip" { 247 | return out, fmt.Errorf("cant solve images (skip)") 248 | } 249 | 250 | if len(answer.Answers) == 0 { 251 | return out, fmt.Errorf("can solve images, (0 found)") 252 | } 253 | 254 | i := 0 255 | for _, v := range toSolve { 256 | go func(i int, v map[string]string) { 257 | AsMut.Lock() 258 | defer AsMut.Unlock() 259 | 260 | currentValue, _ := Selectlist.Load(target) 261 | newHashData := HashData{ 262 | Hash: v["hash"], 263 | X: answer.Answers[i][0], 264 | Y: answer.Answers[i][1], 265 | } 266 | var updatedValue []HashData 267 | if currentValue != nil { 268 | updatedValue = append(currentValue.([]HashData), newHashData) 269 | } else { 270 | updatedValue = []HashData{newHashData} 271 | } 272 | Selectlist.Store(target, updatedValue) 273 | 274 | utils.AppendLine(fmt.Sprintf("%s,%s,%d,%d", v["hash"], target, answer.Answers[i][0], answer.Answers[i][1]), "area_hash.csv") 275 | }(i, v) 276 | 277 | if i > len(answer.Answers) { 278 | fmt.Println("doesn't got all answers") 279 | break 280 | } 281 | 282 | ImMut.Lock() 283 | out[v["key"]] = HashData{ 284 | Hash: v["hash"], 285 | X: answer.Answers[i][0], 286 | Y: answer.Answers[i][1], 287 | } 288 | ImMut.Unlock() 289 | i++ 290 | 291 | if i > len(answer.Answers) { 292 | fmt.Println("doesn't got all answers") 293 | break 294 | } 295 | } 296 | 297 | return out, nil 298 | } 299 | -------------------------------------------------------------------------------- /internal/recognizer/recognizer.go: -------------------------------------------------------------------------------- 1 | package recognizer 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "io" 7 | "math/rand" 8 | "net/http" 9 | "net/url" 10 | 11 | "regexp" 12 | "strings" 13 | "sync" 14 | "time" 15 | 16 | "github.com/Implex-ltd/hcsolver/internal/utils" 17 | "github.com/cespare/xxhash" 18 | "github.com/zenthangplus/goccm" 19 | ) 20 | 21 | var ( 22 | re = regexp.MustCompile(`Please click (on all|each) image(s)? containing (a\s)?(.+)`) 23 | badCode = map[string]string{ 24 | "а": "a", 25 | "е": "e", 26 | "e": "e", 27 | "i": "i", 28 | "і": "i", 29 | "ο": "o", 30 | "с": "c", 31 | "ԁ": "d", 32 | "ѕ": "s", 33 | "һ": "h", 34 | "у": "y", 35 | "р": "p", 36 | "ϳ": "j", 37 | "х": "x", 38 | "ー": "一", 39 | "土": "士", 40 | 41 | "\u0405": "S", 42 | "\u0042": "B", 43 | "\u0052": "R", 44 | "\u0049": "I", 45 | "\u0043": "C", 46 | "\u004b": "K", 47 | "\u039a": "K", 48 | "\u0053": "S", 49 | "\u0421": "C", 50 | "\u006c": "l", 51 | "\u0399": "I", 52 | "\u0392": "B", 53 | } 54 | ) 55 | 56 | func NewRecognizer(Proxy, Type, Question string, Requester map[string]map[string]string, Task []TaskList) (*Recognizer, error) { 57 | proxy, err := url.Parse(Proxy) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | t := http.DefaultTransport.(*http.Transport).Clone() 63 | t.MaxIdleConns = 200 64 | t.MaxConnsPerHost = 500 65 | t.MaxIdleConnsPerHost = 200 66 | 67 | client := &http.Client{ 68 | Timeout: 10 * time.Second, 69 | Transport: &http.Transport{ 70 | Proxy: http.ProxyURL(proxy), 71 | }, 72 | } 73 | 74 | return &Recognizer{ 75 | TaskType: Type, 76 | Question: Question, 77 | TaskList: Task, 78 | Requester: Requester, 79 | OMut: sync.RWMutex{}, 80 | TMut: sync.RWMutex{}, 81 | Http: client, 82 | }, nil 83 | } 84 | 85 | func (R *Recognizer) ExtractTarget(question string) string { 86 | matches := re.FindStringSubmatch(question) 87 | out := R.Question 88 | 89 | if len(matches) >= 4 { 90 | out = strings.ReplaceAll(matches[4], " ", "_") 91 | } 92 | 93 | return out 94 | } 95 | 96 | func (R *Recognizer) LabelCleaning(rawLabel string) string { 97 | runes := []rune(rawLabel) 98 | 99 | for i, r := range runes { 100 | if replacement, ok := badCode[string(r)]; ok { 101 | runes[i] = []rune(replacement)[0] 102 | } 103 | } 104 | 105 | cleanLabel := string(runes) 106 | return cleanLabel 107 | } 108 | 109 | func (R *Recognizer) Recognize() (*SolveResponse, error) { 110 | var solved *SolveResponse 111 | var err error 112 | 113 | R.Question = R.LabelCleaning(R.Question) 114 | 115 | switch R.TaskType { 116 | case "image_label_binary": 117 | R.Target = R.ExtractTarget(R.Question) 118 | 119 | solved, err = R.LabelBinary() 120 | case "image_label_area_select": 121 | var entity string 122 | 123 | for _, innerMap := range R.Requester { 124 | if value, ok := innerMap["en"]; ok { 125 | entity = value 126 | } 127 | } 128 | 129 | if strings.Contains(R.Question, "Please click the") { 130 | R.Target = strings.ReplaceAll(strings.Split(R.Question, "Please click the ")[1], " ", "_") 131 | } else { 132 | R.Target = strings.ReplaceAll(strings.Split(R.Question, "Please click on the ")[1], " ", "_") 133 | } 134 | 135 | R.EntityType = entity 136 | 137 | solved, err = R.LabelAreaSelect() 138 | case "text_free_entry": 139 | solved, err = R.TextFreeEntry() 140 | default: 141 | err = fmt.Errorf("invalid task-type: %v", R.TaskType) 142 | } 143 | 144 | return solved, err 145 | } 146 | 147 | func (R *Recognizer) HashExistBinary(hashString string) (bool, error) { 148 | hashesInterface, _ := Hashlist.Load(R.Target) 149 | 150 | if hashesInterface != nil { 151 | hashes := hashesInterface.([]string) 152 | for _, h := range hashes { 153 | if h == hashString { 154 | return true, nil 155 | } 156 | } 157 | } 158 | 159 | Hashlist.Range(func(key, value interface{}) bool { 160 | prompt := key.(string) 161 | if prompt != R.Target { 162 | hashes := value.([]string) 163 | for _, h := range hashes { 164 | if h == hashString { 165 | return false 166 | } 167 | } 168 | } 169 | return true 170 | }) 171 | 172 | return false, fmt.Errorf("hash not solved yet") 173 | } 174 | 175 | func (R *Recognizer) DownloadAndCheckBinary(Url string) (bool, string, error) { 176 | resp, err := R.Http.Get(Url) 177 | 178 | if err != nil { 179 | return false, "", err 180 | } 181 | defer func(Body io.ReadCloser) { 182 | err := Body.Close() 183 | if err != nil { 184 | fmt.Println("dl_and_check", err) 185 | } 186 | }(resp.Body) 187 | 188 | buff := make([]byte, 650) 189 | _, err = io.ReadFull(resp.Body, buff) 190 | if err != nil { 191 | return false, "", err 192 | } 193 | 194 | hash := xxhash.Sum64(buff) 195 | hashString := fmt.Sprintf("%x", hash) 196 | 197 | exist, err := R.HashExistBinary(hashString) 198 | if err != nil { 199 | return false, hashString, err 200 | } 201 | 202 | return exist, hashString, nil 203 | } 204 | 205 | func (R *Recognizer) LabelBinary() (*SolveResponse, error) { 206 | out := map[string]any{} 207 | toResolve := map[string]map[string]string{} 208 | 209 | c := goccm.New(len(R.TaskList)) 210 | 211 | for _, task := range R.TaskList { 212 | c.Wait() 213 | 214 | go func(t TaskList) { 215 | defer c.Done() 216 | 217 | var is bool 218 | var err error 219 | var hash string 220 | 221 | for { 222 | is, hash, err = R.DownloadAndCheckBinary(t.DatapointURI) 223 | if err != nil && err.Error() != "hash not solved yet" { 224 | fmt.Println("blank hash", err) 225 | time.Sleep(time.Second) 226 | continue 227 | } 228 | 229 | if err != nil { 230 | if err.Error() == "hash not solved yet" && nocap { 231 | for { 232 | resp, err := R.Http.Get(t.DatapointURI) 233 | 234 | if err != nil { 235 | continue 236 | } 237 | defer func(Body io.ReadCloser) { 238 | err := Body.Close() 239 | if err != nil { 240 | fmt.Println("xd1", err) 241 | } 242 | }(resp.Body) 243 | 244 | body, err := io.ReadAll(resp.Body) 245 | if err != nil { 246 | continue 247 | } 248 | 249 | R.TMut.Lock() 250 | toResolve[t.TaskKey] = map[string]string{ 251 | "body": base64.StdEncoding.EncodeToString(body), 252 | "hash": hash, 253 | "key": t.TaskKey, 254 | } 255 | R.TMut.Unlock() 256 | break 257 | } 258 | } 259 | } 260 | break 261 | } 262 | 263 | if is { 264 | R.OMut.Lock() 265 | out[t.TaskKey] = fmt.Sprintf(`%v`, is) 266 | R.OMut.Unlock() 267 | } 268 | }(task) 269 | } 270 | 271 | c.WaitAllDone() 272 | 273 | if nocap && len(toResolve) > 1 { 274 | response, err := SolvePic(toResolve, R.Question, R.Target) 275 | if err != nil { 276 | return nil, err 277 | } 278 | 279 | R.OMut.Lock() 280 | for k, v := range response { 281 | out[k] = v 282 | } 283 | R.OMut.Unlock() 284 | } 285 | 286 | return &SolveResponse{ 287 | Success: true, 288 | Data: out, 289 | }, nil 290 | } 291 | 292 | func (R *Recognizer) HashExistSelect(hashString string) (*HashData, error) { 293 | value, _ := Selectlist.Load(R.Target) 294 | 295 | if value != nil { 296 | hashDataList := value.([]HashData) 297 | for _, k := range hashDataList { 298 | if hashString == k.Hash { 299 | return &k, nil 300 | } 301 | } 302 | } 303 | 304 | Selectlist.Range(func(key, value interface{}) bool { 305 | prompt := key.(string) 306 | if prompt != R.Target { 307 | hashDataList := value.([]HashData) 308 | for _, k := range hashDataList { 309 | if hashString == k.Hash { 310 | return false 311 | } 312 | } 313 | } 314 | return true 315 | }) 316 | 317 | return nil, fmt.Errorf("hash not solved yet") 318 | } 319 | 320 | func (R *Recognizer) DownloadAndCheckSelect(Url string) (*HashData, string, error) { 321 | resp, err := R.Http.Get(Url) 322 | 323 | if err != nil { 324 | return nil, "", err 325 | } 326 | defer func(Body io.ReadCloser) { 327 | err := Body.Close() 328 | if err != nil { 329 | fmt.Println("dl_and_check0", err) 330 | } 331 | }(resp.Body) 332 | 333 | buff := make([]byte, 650) 334 | _, err = io.ReadFull(resp.Body, buff) 335 | if err != nil { 336 | fmt.Println(resp.StatusCode) 337 | return nil, "", err 338 | } 339 | 340 | hash := xxhash.Sum64(buff) 341 | hashString := fmt.Sprintf("%x", hash) 342 | 343 | data, err := R.HashExistSelect(hashString) 344 | if err != nil { 345 | return nil, hashString, err 346 | } 347 | 348 | return data, hashString, nil 349 | } 350 | 351 | func (R *Recognizer) LabelAreaSelect() (*SolveResponse, error) { 352 | out := make(map[string][]map[string]interface{}) 353 | toResolve := map[string]map[string]string{} 354 | 355 | c := goccm.New(len(R.TaskList)) 356 | 357 | for _, task := range R.TaskList { 358 | c.Wait() 359 | 360 | go func(t TaskList) { 361 | defer c.Done() 362 | 363 | var data *HashData 364 | var err error 365 | var hash string 366 | 367 | for { 368 | data, hash, err = R.DownloadAndCheckSelect(t.DatapointURI) 369 | 370 | if err != nil && err.Error() != "hash not solved yet" { 371 | fmt.Println("blank hash", err) 372 | time.Sleep(time.Second) 373 | continue 374 | } 375 | 376 | if err != nil { 377 | if err.Error() == "hash not solved yet" && nocap { 378 | fmt.Println("nocap") 379 | for { 380 | resp, err := R.Http.Get(t.DatapointURI) 381 | if err != nil { 382 | continue 383 | } 384 | defer func(Body io.ReadCloser) { 385 | err := Body.Close() 386 | if err != nil { 387 | fmt.Println("nocap2", err) 388 | } 389 | }(resp.Body) 390 | 391 | body, err := io.ReadAll(resp.Body) 392 | if err != nil { 393 | continue 394 | } 395 | 396 | R.TMut.Lock() 397 | toResolve[t.TaskKey] = map[string]string{ 398 | "body": base64.StdEncoding.EncodeToString(body), 399 | "hash": hash, 400 | "key": t.TaskKey, 401 | } 402 | R.TMut.Unlock() 403 | break 404 | } 405 | } 406 | } 407 | 408 | break 409 | } 410 | 411 | if data != nil { 412 | R.OMut.Lock() 413 | out[t.TaskKey] = []map[string]interface{}{ 414 | { 415 | "entity_name": 0, 416 | "entity_type": R.EntityType, 417 | "entity_coords": []int{ 418 | data.X, 419 | data.Y, 420 | }, 421 | }, 422 | } 423 | R.OMut.Unlock() 424 | } 425 | }(task) 426 | } 427 | 428 | c.WaitAllDone() 429 | 430 | if nocap && len(toResolve) > 1 { 431 | response, err := SolvePicSelect(toResolve, R.Question, R.Target) 432 | if err != nil { 433 | return nil, err 434 | } 435 | 436 | if len(response) < 1 { 437 | return &SolveResponse{ 438 | Success: false, 439 | }, fmt.Errorf("cant recognize") 440 | } 441 | 442 | R.OMut.Lock() 443 | for k, v := range response { 444 | out[k] = []map[string]interface{}{ 445 | { 446 | "entity_name": 0, 447 | "entity_type": R.EntityType, 448 | "entity_coords": []int{ 449 | v.X, 450 | v.Y, 451 | }, 452 | }, 453 | } 454 | } 455 | R.OMut.Unlock() 456 | } 457 | 458 | return &SolveResponse{ 459 | Success: true, 460 | Data: out, 461 | }, nil 462 | } 463 | 464 | func (R *Recognizer) TextFreeEntry() (*SolveResponse, error) { 465 | answers := map[string]AnswerStruct{} 466 | resp := []string{"oui", "non"} 467 | 468 | if len(R.TaskList) == 0 { 469 | return nil, fmt.Errorf("no prompt found") 470 | } 471 | 472 | for _, questions := range R.TaskList { 473 | res := resp[rand.Int()%len(resp)] 474 | question := strings.ReplaceAll(questions.DatapointText["fr"], " ", "_") 475 | 476 | val, ok := Answerlist.Load(question) 477 | 478 | if ok { 479 | res = val.(string) 480 | } else { 481 | utils.AppendLine(fmt.Sprintf("%s|%s", question, res), "nop.txt") 482 | } 483 | 484 | answers[questions.TaskKey] = AnswerStruct{ 485 | Text: res, 486 | } 487 | } 488 | 489 | // Todo: check if yes/yes/no or no/no/yes 490 | 491 | return &SolveResponse{ 492 | Success: true, 493 | Data: answers, 494 | }, nil 495 | } 496 | -------------------------------------------------------------------------------- /internal/recognizer/struct.go: -------------------------------------------------------------------------------- 1 | package recognizer 2 | 3 | import ( 4 | "net/http" 5 | "sync" 6 | ) 7 | 8 | var ( 9 | Hashlist = new(sync.Map) 10 | Selectlist = new(sync.Map) 11 | Answerlist = new(sync.Map) 12 | ) 13 | 14 | type HashData struct { 15 | X int 16 | Y int 17 | Hash string 18 | } 19 | 20 | type Recognizer struct { 21 | Http *http.Client 22 | TaskType string 23 | Question string 24 | Target string 25 | TaskList []TaskList 26 | EntityType string 27 | Requester map[string]map[string]string 28 | OMut, TMut sync.RWMutex 29 | } 30 | 31 | type TaskList struct { 32 | DatapointURI string `json:"datapoint_uri"` 33 | TaskKey string `json:"task_key"` 34 | 35 | // a11y_challenge 36 | DatapointHash string `json:"datapoint_hash"` 37 | DatapointText map[string]string `json:"datapoint_text"` 38 | } 39 | 40 | type SolveResponse struct { 41 | Success bool `json:"success"` 42 | Data any `json:"data"` 43 | } 44 | 45 | type AnswerStruct struct { 46 | Text string `json:"text"` 47 | } 48 | -------------------------------------------------------------------------------- /internal/recognizer/utils.go: -------------------------------------------------------------------------------- 1 | package recognizer 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | func LoadHashSelect(path string) (int, error) { 11 | file, err := os.Open(path) 12 | if err != nil { 13 | return 0, err 14 | } 15 | defer file.Close() 16 | 17 | scanner := bufio.NewScanner(file) 18 | count := 0 19 | 20 | for scanner.Scan() { 21 | line := scanner.Text() 22 | parts := strings.Split(line, ",") 23 | 24 | if len(parts) != 4 { 25 | continue 26 | } 27 | 28 | hash, prompt, _x, _y := parts[0], parts[1], parts[2], parts[3] 29 | 30 | x, _ := strconv.Atoi(_x) 31 | y, _ := strconv.Atoi(_y) 32 | 33 | currentValue, _ := Selectlist.Load(prompt) 34 | newHashData := HashData{ 35 | Hash: hash, 36 | X: x, 37 | Y: y, 38 | } 39 | 40 | var updatedValue []HashData 41 | if currentValue != nil { 42 | updatedValue = append(currentValue.([]HashData), newHashData) 43 | } else { 44 | updatedValue = []HashData{newHashData} 45 | } 46 | 47 | Selectlist.Store(prompt, updatedValue) 48 | 49 | count++ 50 | } 51 | 52 | if err := scanner.Err(); err != nil { 53 | return 0, err 54 | } 55 | 56 | return count, nil 57 | } 58 | 59 | func LoadHash(path string) (int, error) { 60 | file, err := os.Open(path) 61 | if err != nil { 62 | return 0, err 63 | } 64 | defer file.Close() 65 | 66 | scanner := bufio.NewScanner(file) 67 | count := 0 68 | 69 | for scanner.Scan() { 70 | line := scanner.Text() 71 | parts := strings.Split(line, ",") 72 | 73 | if len(parts) != 2 { 74 | continue 75 | } 76 | 77 | hash, prompt := parts[0], parts[1] 78 | currentValue, _ := Hashlist.Load(prompt) 79 | 80 | var updatedValue []string 81 | if currentValue != nil { 82 | updatedValue = append(currentValue.([]string), hash) 83 | } else { 84 | updatedValue = []string{hash} 85 | } 86 | 87 | Hashlist.Store(prompt, updatedValue) 88 | count++ 89 | } 90 | 91 | if err := scanner.Err(); err != nil { 92 | return 0, err 93 | } 94 | 95 | return count, nil 96 | } 97 | 98 | func LoadAnswer(path string) (int, error) { 99 | file, err := os.Open(path) 100 | if err != nil { 101 | return 0, err 102 | } 103 | defer file.Close() 104 | 105 | scanner := bufio.NewScanner(file) 106 | count := 0 107 | 108 | for scanner.Scan() { 109 | line := scanner.Text() 110 | parts := strings.Split(line, "|") 111 | 112 | if len(parts) != 2 { 113 | continue 114 | } 115 | 116 | Answerlist.Store(parts[0], parts[1]) 117 | } 118 | 119 | if err := scanner.Err(); err != nil { 120 | return 0, err 121 | } 122 | 123 | return count, nil 124 | } -------------------------------------------------------------------------------- /internal/routes/task/task.go: -------------------------------------------------------------------------------- 1 | package taskRoutes 2 | 3 | import ( 4 | taskHandler "github.com/Implex-ltd/hcsolver/internal/handlers/task" 5 | "github.com/gofiber/fiber/v2" 6 | ) 7 | 8 | func SetupTaskRoutes(router fiber.Router) { 9 | user := router.Group("/task") 10 | 11 | user.Post("/new", taskHandler.CreateTask) 12 | user.Get("/:taskId", taskHandler.GetTask) 13 | 14 | misc := router.Group("/misc") 15 | misc.Get("/check/:siteKey", taskHandler.GetSitekeySettings) 16 | } 17 | -------------------------------------------------------------------------------- /internal/routes/user/user.go: -------------------------------------------------------------------------------- 1 | package userRoutes 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/subtle" 6 | 7 | userHandler "github.com/Implex-ltd/hcsolver/internal/handlers/user" 8 | "github.com/gofiber/fiber/v2" 9 | "github.com/gofiber/fiber/v2/middleware/keyauth" 10 | "github.com/gofiber/fiber/v2/middleware/logger" 11 | ) 12 | 13 | /* 14 | - Authorization header required 15 | - /api/internal/user 16 | */ 17 | 18 | var ( 19 | apiKey = "Cr4p0nT0pD54sdljn4D" 20 | ) 21 | 22 | func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { 23 | hashedAPIKey := sha256.Sum256([]byte(apiKey)) 24 | hashedKey := sha256.Sum256([]byte(key)) 25 | 26 | if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { 27 | return true, nil 28 | } 29 | return false, keyauth.ErrMissingOrMalformedAPIKey 30 | } 31 | 32 | func SetupUserRoutes(router fiber.Router) { 33 | user := router.Group("/user") 34 | 35 | user.Get("/:userId", userHandler.GetUser) 36 | 37 | internalApi := user.Group("/internal", logger.New()) 38 | internalApi.Use(keyauth.New(keyauth.Config{ 39 | KeyLookup: "header:Authorization", 40 | Validator: validateAPIKey, 41 | })) 42 | 43 | internalApi.Post("/new", userHandler.CreateUser) 44 | internalApi.Post("/refill", userHandler.AddBalance) 45 | internalApi.Post("/set-bypass", userHandler.SetypassRestricted) 46 | } 47 | -------------------------------------------------------------------------------- /internal/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | ) 8 | 9 | const ( 10 | charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 11 | hash_charset = "0123456789" 12 | 13 | precision = 10000000000000.0 14 | basePath = "../../assets" 15 | ) 16 | 17 | func RandomNumber(a, b int) int { 18 | if a >= b { 19 | panic("Invalid range: a must be less than b") 20 | } 21 | 22 | return rand.Intn(b-a+1) + a 23 | } 24 | 25 | func RandomFloat64Precission(a, b float64, prec float64) float64 { 26 | if a >= b { 27 | panic("Invalid range: a must be less than b") 28 | } 29 | 30 | randomFloat := a + rand.Float64()*(b-a) 31 | randomFloat *= prec 32 | randomFloat = float64(int(randomFloat)) / prec 33 | 34 | return randomFloat 35 | } 36 | 37 | func RandomFloat64(a, b float64) float64 { 38 | if a >= b { 39 | panic("Invalid range: a must be less than b") 40 | } 41 | 42 | randomFloat := a + rand.Float64()*(b-a) 43 | randomFloat *= precision 44 | randomFloat = float64(int(randomFloat)) / precision 45 | 46 | return randomFloat 47 | } 48 | 49 | func RandomString(length int) string { 50 | result := make([]byte, length) 51 | for i := range result { 52 | result[i] = charset[rand.Intn(len(charset))] 53 | } 54 | 55 | return string(result) 56 | } 57 | 58 | func RandomHash(length int) string { 59 | result := make([]byte, length) 60 | for i := range result { 61 | result[i] = hash_charset[rand.Intn(len(hash_charset))] 62 | } 63 | 64 | return string(result) 65 | } 66 | 67 | func RandomElementInt(slice []int) int { 68 | index := rand.Intn(len(slice)) 69 | return slice[index] 70 | } 71 | 72 | func RandomElementString(slice []string) string { 73 | index := rand.Intn(len(slice)) 74 | return slice[index] 75 | } 76 | 77 | func AppendLine(text, fileName string) error { 78 | file, err := os.OpenFile(fmt.Sprintf("%s/%s", basePath, fileName), os.O_APPEND|os.O_WRONLY, 0644) 79 | if err != nil { 80 | return err 81 | } 82 | defer file.Close() 83 | 84 | if _, err := file.WriteString(text + "\n"); err != nil { 85 | return err 86 | } 87 | 88 | return nil 89 | } 90 | 91 | func ShuffleStrings(strings []string) []string { 92 | for i := range strings { 93 | j := rand.Intn(i + 1) 94 | strings[i], strings[j] = strings[j], strings[i] 95 | } 96 | 97 | return strings 98 | } 99 | 100 | func ShuffleSlice(slice [][]interface{}) { 101 | for i := range slice { 102 | j := rand.Intn(i + 1) 103 | slice[i], slice[j] = slice[j], slice[i] 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /scripts/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all run 2 | 3 | clear: 4 | cls||clear 5 | 6 | all: clear 7 | @echo "Specify a target: run" 8 | 9 | run: clear 10 | cd ../cmd/hcsolver/ && go run . 11 | -------------------------------------------------------------------------------- /scripts/config.toml: -------------------------------------------------------------------------------- 1 | [api] 2 | port = 80 3 | 4 | [ratelimit] 5 | api_max = 250 6 | api_expiration = 1 7 | 8 | [database] 9 | username = "root" 10 | password = "56484qsd844qsdq48sd4" # rootnikoontoplmao5245 11 | ip = "144.172.76.66" 12 | port = 88 #8000 13 | -------------------------------------------------------------------------------- /scripts/loop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | EXECUTABLE="." 4 | 5 | run_program() { 6 | while true; do 7 | echo "Starting the API..." 8 | go run "$EXECUTABLE" 9 | echo "API crashed. Restarting in 1 second..." 10 | sleep 1 11 | done 12 | } 13 | 14 | run_program -------------------------------------------------------------------------------- /scripts/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | make run 4 | pause 5 | --------------------------------------------------------------------------------