Spaces:
Running
Running
Upload 42 files
Browse files- Dockerfile +17 -0
- License +201 -0
- README.md +172 -10
- getHar.md +15 -0
- go.mod +59 -0
- go.sum +185 -0
- harPool/put_har_in_this_folder +0 -0
- main.go +78 -0
- pkg/common/processBody.go +34 -0
- pkg/common/request.go +147 -0
- pkg/db/db.go +7 -0
- pkg/db/redis.go +27 -0
- pkg/env/env.go +92 -0
- pkg/funcaptcha/api.go +169 -0
- pkg/funcaptcha/challenge.go +356 -0
- pkg/funcaptcha/constants.go +241 -0
- pkg/funcaptcha/crypt.go +234 -0
- pkg/funcaptcha/fingerprint.go +63 -0
- pkg/funcaptcha/funcaptcha.go +187 -0
- pkg/funcaptcha/get.go +31 -0
- pkg/funcaptcha/hashing.go +1380 -0
- pkg/funcaptcha/murmur.go +149 -0
- pkg/funcaptcha/util.go +75 -0
- pkg/funcaptcha/webgl.go +58 -0
- pkg/logger/logger.go +25 -0
- pkg/plugins/api/arkosetoken/arkosetoken.go +73 -0
- pkg/plugins/api/backendapi/backend.go +288 -0
- pkg/plugins/api/officialapi/officialApi.go +155 -0
- pkg/plugins/api/publicapi/publicapi.go +152 -0
- pkg/plugins/api/rapi/api.go +152 -0
- pkg/plugins/api/session/session.go +116 -0
- pkg/plugins/api/unofficialapi/chatrsp.go +508 -0
- pkg/plugins/api/unofficialapi/unofficialapi.go +530 -0
- pkg/plugins/plugins.go +20 -0
- pkg/plugins/service/proxypool/proxypool.go +160 -0
- pkg/plugins/service/wsstostream/wsstostream.go +199 -0
- pkg/tools/auth.go +559 -0
- pkg/tools/cache.go +44 -0
- pkg/tools/sseclient.go +109 -0
- static/img1.png +0 -0
- static/img2.png +0 -0
- static/img3.png +0 -0
Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM golang:1.21-alpine
|
2 |
+
LABEL authors="oliverkirk-sudo"
|
3 |
+
|
4 |
+
RUN apk add --update redis
|
5 |
+
|
6 |
+
WORKDIR /app
|
7 |
+
|
8 |
+
COPY go.mod ./
|
9 |
+
COPY go.sum ./
|
10 |
+
|
11 |
+
RUN go mod download
|
12 |
+
|
13 |
+
COPY . .
|
14 |
+
|
15 |
+
RUN go build -o warpgpt
|
16 |
+
|
17 |
+
CMD redis-server & sleep 3 & ./warpgpt
|
License
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
README.md
CHANGED
@@ -1,10 +1,172 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Warp-GPT
|
2 |
+
作为刚学go的一个练手项目,自用
|
3 |
+
|
4 |
+
- 将chatgpt前端进行逆向,实现绕过cloudflare
|
5 |
+
- 对官方api进行代理
|
6 |
+
- 实现前端接口转标准api(通过access_token实现标准api传入访问)
|
7 |
+
|
8 |
+
端口列表
|
9 |
+
```
|
10 |
+
/backend-api/* (前端逆向接口)
|
11 |
+
/backend-api/conversation/ws (前端WS逆向为原数据流格式)
|
12 |
+
/api/* (前端逆向接口)
|
13 |
+
/public-api/* (前端逆向接口)
|
14 |
+
/v1/* (官方api代理)
|
15 |
+
/r/v1/chat/completions (前端接口转标准api,支持流式)
|
16 |
+
/r/v1/chat/completions/ws (前端WS转标准api,支持流式)
|
17 |
+
/r/ws/v1/chat/completions (功能相同,提供兼容)
|
18 |
+
/r/v1/images/generations (前端接口转标准api,不支持流式,只支持gpt-4的账户)
|
19 |
+
/getsession (实现__Secure-next-auth.session-token刷新session,返回session,或输入username与password输出session)
|
20 |
+
/token (获取ArkoseToken)
|
21 |
+
```
|
22 |
+
目前ws逆向仅支持3.5,4有些问题,看ip纯度,纯度高就能输出
|
23 |
+
```
|
24 |
+
/r/v1/chat/completions method:["GET", "POST", "OPTIONS"]
|
25 |
+
input:
|
26 |
+
{
|
27 |
+
"model": "gpt-3.5-turbo-16k",
|
28 |
+
"messages": [
|
29 |
+
{
|
30 |
+
"role": "user",
|
31 |
+
"content": "what can you do"
|
32 |
+
}
|
33 |
+
]
|
34 |
+
}
|
35 |
+
output:
|
36 |
+
{
|
37 |
+
"id": "chatcmpl-m3mYrjKTZuoNARfQerON95UJlA9XSWBi",
|
38 |
+
"object": "chat.completion",
|
39 |
+
"created": 1701011706,
|
40 |
+
"model": "gpt-3.5-turbo-16k",
|
41 |
+
"choices": [
|
42 |
+
{
|
43 |
+
"index": 0,
|
44 |
+
"message": {
|
45 |
+
"role": "assistant",
|
46 |
+
"content": "I can do a wide range of tasks and provide information on various topics. Here are some of the things I can do:\n\n1. Answer Questions: I can provide information on a wide range of topics, including science, history, technology, mathematics, and more.\n\n2. Generate Text: I can generate text for various purposes, such as writing essays, creating stories, composing emails, and more.\n\n3. Language Translation: I can translate text from one language to another.\n\n4. Math Assistance: I can help with mathematical calculations, equations, and explanations.\n\n5. Programming Help: I can assist with coding and programming-related questions and problems.\n\n6. Writing Assistance: I can help with grammar and writing suggestions, including editing and proofreading.\n\n7. General Knowledge: I can provide general knowledge and facts on a wide variety of subjects.\n\n8. Recommendations: I can offer recommendations for books, movies, music, travel destinations, and more.\n\n9. Conversation and Chat: I can engage in casual conversation and chat on a variety of topics.\n\n10. Learning and Education: I can assist with learning and provide explanations on academic subjects.\n\n11. Problem Solving: I can help you brainstorm ideas, solve problems, and make decisions.\n\n12. Trivia and Quizzes: I can create and participate in trivia quizzes and answer trivia questions.\n\nPlease keep in mind that I do not have access to real-time information beyond my last knowledge update in January 2022, so some information may be outdated, and I cannot provide current news or events. If you have a specific task or question in mind, feel free to ask, and I'll do my best to assist you!"
|
47 |
+
},
|
48 |
+
"finish_reason": "stop"
|
49 |
+
}
|
50 |
+
],
|
51 |
+
"usage": {
|
52 |
+
"prompt_tokens": 0,
|
53 |
+
"completion_tokens": 0,
|
54 |
+
"total_tokens": 0
|
55 |
+
}
|
56 |
+
}
|
57 |
+
```
|
58 |
+
```
|
59 |
+
/r/v1/images/generations method:["GET", "POST", "OPTIONS"]
|
60 |
+
input:
|
61 |
+
{
|
62 |
+
"model": "dall-e-3",
|
63 |
+
"prompt": "A cute baby sea otter",
|
64 |
+
"n": 1,
|
65 |
+
"size": "1024x1024"
|
66 |
+
}
|
67 |
+
output:
|
68 |
+
{
|
69 |
+
"created": 1701014049,
|
70 |
+
"data": [
|
71 |
+
{
|
72 |
+
"revised_prompt": "A cute baby sea otter, looking fluffy and adorable, with big, curious eyes, floating on its back in a calm blue ocean. The otter is holding a small shell in its tiny paws, and its fur is wet, giving it a shiny appearance under the sun. The background features a serene seascape with gentle waves and a clear sky.",
|
73 |
+
"url": "https://files.oaiusercontent.com/file-fqEmsHBijHGkBKo0CnYIAfCJ?se=2023-11-26T16%3A54%3A09Z&sp=r&sv=2021-08-06&sr=b&rscc=max-age%3D31536000%2C%20immutable&rscd=attachment%3B%20filename%3Daa87dac2-8142-419d-9fe1-afa90c0a376e.webp&sig=xjwmZhzC3fZSF7V6TJ5hTWkmxBOMiVQKs0v/wTJRvAM%3D"
|
74 |
+
}
|
75 |
+
]
|
76 |
+
}
|
77 |
+
```
|
78 |
+
```
|
79 |
+
/getsession methods:["POST"]
|
80 |
+
input:
|
81 |
+
{
|
82 |
+
"refreshCookie":""
|
83 |
+
}
|
84 |
+
or
|
85 |
+
{
|
86 |
+
"username":"",
|
87 |
+
"password":""
|
88 |
+
}
|
89 |
+
output:
|
90 |
+
{
|
91 |
+
"user": {
|
92 |
+
"id": "",
|
93 |
+
"name": "",
|
94 |
+
"email": "",
|
95 |
+
"image": "",
|
96 |
+
"picture": "",
|
97 |
+
"idp": "auth0",
|
98 |
+
"iat": 1701014297,
|
99 |
+
"mfa": false,
|
100 |
+
"groups": [],
|
101 |
+
"intercom_hash": ""
|
102 |
+
},
|
103 |
+
"expires": "2024-02-24T15:58:17.821Z",
|
104 |
+
"accessToken": "",
|
105 |
+
"authProvider": "auth0",
|
106 |
+
"models": [
|
107 |
+
{
|
108 |
+
"slug": "text-davinci-002-render-sha",
|
109 |
+
"max_tokens": 8191,
|
110 |
+
"title": "Default (GPT-3.5)",
|
111 |
+
"description": "Our fastest model, great for most everyday tasks.",
|
112 |
+
"tags": [
|
113 |
+
"gpt3.5"
|
114 |
+
],
|
115 |
+
"capabilities": {},
|
116 |
+
"product_features": {}
|
117 |
+
}
|
118 |
+
],
|
119 |
+
"refreshCookie": ""
|
120 |
+
}
|
121 |
+
```
|
122 |
+
```
|
123 |
+
/token/:id methods:["GET"]
|
124 |
+
eg: /token/0A1D34FC-659D-4E23-B17B-694DCFCF6A6C
|
125 |
+
output:
|
126 |
+
{
|
127 |
+
"token": ""
|
128 |
+
}
|
129 |
+
```
|
130 |
+
|
131 |
+
## 代码部署
|
132 |
+
### 配置文件
|
133 |
+
- 在harPool目录中加入har文件,实现登录验证与gpt4对话验证([获取har教程](./getHar.md))
|
134 |
+
- 复制一份.env.temp,并修改名称为.env,修改配置项后保存
|
135 |
+
``` python
|
136 |
+
proxy = "http://127.0.0.1:10809" #代理地址 (选填)
|
137 |
+
port = 5000 #程序运行端口
|
138 |
+
host = '127.0.0.1' #可访问ip,0.0.0.0允许所有ip
|
139 |
+
verify = false #是否对访问进行验证
|
140 |
+
auth_key = "" #若开启访问验证,则需要在Header中添加AuthKey字段,且值为auth_key的值才能访问 (选填)
|
141 |
+
arkose_must = false #是否强行gpt3.5进行验证
|
142 |
+
OpenAI_HOST = "chat.openai.com" #openai网页api接口地址 (选填)
|
143 |
+
openai_api_host = "api.openai.com" #openai官方api接口 (选填)
|
144 |
+
proxy_pool_url="" #ipidea代理池链接 (选填)
|
145 |
+
#示例http://api.proxy.ipidea.io/getProxyIp?num=10&return_type=json&lb=1&sb=0&flow=1®ions=us&protocol=http,根据访问频次设置num值
|
146 |
+
log_level = "debug" #日志等级
|
147 |
+
|
148 |
+
redis_address = "127.0.0.1:6379" #redis地址(若不开启代理池可选填)
|
149 |
+
redis_passwd = "" #redis密码
|
150 |
+
redis_db = 0 #选择的redis数据库
|
151 |
+
```
|
152 |
+
其中proxy_pool_url使用的是[ipidea](https://share.ipidea.net/8hPKah)的代理池,注册送100M流量,无限ip,一个月,测试足够
|
153 |
+
使用代理池后需要填写redis信息,redis版本需要7以上
|
154 |
+
### 运行
|
155 |
+
|
156 |
+
`go build && ./WarpGPT`
|
157 |
+
|
158 |
+
## Docker部署
|
159 |
+
首先克隆代码
|
160 |
+
```shell
|
161 |
+
git clone https://github.com/oliverkirk-sudo/WarpGPT.git
|
162 |
+
cd WarpGPT
|
163 |
+
```
|
164 |
+
正确配置.env文件,在harPool中放入har文件
|
165 |
+
(其中host应该为0.0.0.0)
|
166 |
+
```shell
|
167 |
+
docker build -t warpgpt .
|
168 |
+
docker run -d -p 5000:5000 warpgpt
|
169 |
+
```
|
170 |
+
|
171 |
+
## License
|
172 |
+
Apache-2.0
|
getHar.md
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 获取har文件
|
2 |
+
|
3 |
+
1. 首先使用具**有gpt4资格**的帐号登录openai<br>
|
4 |
+
打开开发者控制台(F12 或Ctrl+Shift+I)
|
5 |
+
![img.png](static/img1.png)
|
6 |
+
|
7 |
+
2. 随便问一个问题<br>
|
8 |
+
确保请求中出现![img.png](static/img2.png)
|
9 |
+
https://tcr9i.chat.openai.com/fc/gt2/public_key/35536E1E-65B4-4D96-9D97-6ADB7EFF8147
|
10 |
+
这个请求
|
11 |
+
|
12 |
+
3. 右键这个请求,选择`以HAR格式保存所有内容`
|
13 |
+
![img.png](static/img3.png)
|
14 |
+
|
15 |
+
保存的内容就是har文件
|
go.mod
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module WarpGPT
|
2 |
+
|
3 |
+
go 1.21
|
4 |
+
|
5 |
+
require (
|
6 |
+
github.com/EDDYCJY/fake-useragent v0.2.0
|
7 |
+
github.com/bogdanfinn/fhttp v0.5.27
|
8 |
+
github.com/bogdanfinn/tls-client v1.7.2
|
9 |
+
github.com/gin-gonic/gin v1.9.1
|
10 |
+
github.com/google/uuid v1.6.0
|
11 |
+
github.com/gorilla/websocket v1.5.1
|
12 |
+
github.com/joho/godotenv v1.5.1
|
13 |
+
github.com/pkoukk/tiktoken-go v0.1.6
|
14 |
+
github.com/redis/go-redis/v9 v9.4.0
|
15 |
+
github.com/sirupsen/logrus v1.9.3
|
16 |
+
golang.org/x/net v0.21.0
|
17 |
+
)
|
18 |
+
|
19 |
+
require (
|
20 |
+
github.com/PuerkitoBio/goquery v1.8.1 // indirect
|
21 |
+
github.com/andybalholm/brotli v1.1.0 // indirect
|
22 |
+
github.com/andybalholm/cascadia v1.3.1 // indirect
|
23 |
+
github.com/bogdanfinn/utls v1.6.1 // indirect
|
24 |
+
github.com/bytedance/sonic v1.10.2 // indirect
|
25 |
+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
26 |
+
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
27 |
+
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
28 |
+
github.com/cloudflare/circl v1.3.7 // indirect
|
29 |
+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
30 |
+
github.com/dlclark/regexp2 v1.10.0 // indirect
|
31 |
+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
32 |
+
github.com/gin-contrib/sse v0.1.0 // indirect
|
33 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
34 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
35 |
+
github.com/go-playground/validator/v10 v10.18.0 // indirect
|
36 |
+
github.com/goccy/go-json v0.10.2 // indirect
|
37 |
+
github.com/google/go-cmp v0.6.0 // indirect
|
38 |
+
github.com/json-iterator/go v1.1.12 // indirect
|
39 |
+
github.com/klauspost/compress v1.17.6 // indirect
|
40 |
+
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
41 |
+
github.com/kr/text v0.2.0 // indirect
|
42 |
+
github.com/leodido/go-urn v1.4.0 // indirect
|
43 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
44 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
45 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
46 |
+
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
47 |
+
github.com/quic-go/quic-go v0.41.0 // indirect
|
48 |
+
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
49 |
+
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
|
50 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
51 |
+
github.com/ugorji/go/codec v1.2.12 // indirect
|
52 |
+
golang.org/x/arch v0.7.0 // indirect
|
53 |
+
golang.org/x/crypto v0.19.0 // indirect
|
54 |
+
golang.org/x/sys v0.17.0 // indirect
|
55 |
+
golang.org/x/text v0.14.0 // indirect
|
56 |
+
google.golang.org/protobuf v1.32.0 // indirect
|
57 |
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
58 |
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
59 |
+
)
|
go.sum
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
github.com/EDDYCJY/fake-useragent v0.2.0 h1:Jcnkk2bgXmDpX0z+ELlUErTkoLb/mxFBNd2YdcpvJBs=
|
2 |
+
github.com/EDDYCJY/fake-useragent v0.2.0/go.mod h1:5wn3zzlDxhKW6NYknushqinPcAqZcAPHy8lLczCdJdc=
|
3 |
+
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
4 |
+
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
5 |
+
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
6 |
+
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
7 |
+
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
8 |
+
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
9 |
+
github.com/bogdanfinn/fhttp v0.5.27 h1:+glR3k8v5nxfUSk7+J3M246zEQ2yadhS0vLq1utK71A=
|
10 |
+
github.com/bogdanfinn/fhttp v0.5.27/go.mod h1:oJiYPG3jQTKzk/VFmogH8jxjH5yiv2rrOH48Xso2lrE=
|
11 |
+
github.com/bogdanfinn/tls-client v1.7.2 h1:vpL5qBYUfT9ueygEf1yLfymrXyUEZQatL25amfqGV8M=
|
12 |
+
github.com/bogdanfinn/tls-client v1.7.2/go.mod h1:pOGa2euqTbEkGNqE5idx5jKKfs9ytlyn3fwEw8RSP+g=
|
13 |
+
github.com/bogdanfinn/utls v1.6.1 h1:dKDYAcXEyFFJ3GaWaN89DEyjyRraD1qb4osdEK89ass=
|
14 |
+
github.com/bogdanfinn/utls v1.6.1/go.mod h1:VXIbRZaiY/wHZc6Hu+DZ4O2CgTzjhjCg/Ou3V4r/39Y=
|
15 |
+
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
16 |
+
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
17 |
+
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
18 |
+
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
19 |
+
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
20 |
+
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
21 |
+
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
22 |
+
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
23 |
+
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
24 |
+
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
25 |
+
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
26 |
+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
27 |
+
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
28 |
+
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
29 |
+
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
30 |
+
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
31 |
+
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
32 |
+
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
33 |
+
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
34 |
+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
35 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
36 |
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
37 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
38 |
+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
39 |
+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
40 |
+
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
41 |
+
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
42 |
+
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
43 |
+
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
44 |
+
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
45 |
+
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
46 |
+
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
47 |
+
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
48 |
+
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
49 |
+
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
50 |
+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
51 |
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
52 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
53 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
54 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
55 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
56 |
+
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
57 |
+
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
58 |
+
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
59 |
+
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
60 |
+
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
61 |
+
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
62 |
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
63 |
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
64 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
65 |
+
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
66 |
+
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
67 |
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
68 |
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
69 |
+
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
70 |
+
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
71 |
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
72 |
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
73 |
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
74 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
75 |
+
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
76 |
+
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
77 |
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
78 |
+
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
79 |
+
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
80 |
+
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
81 |
+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
82 |
+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
83 |
+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
84 |
+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
85 |
+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
86 |
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
87 |
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
88 |
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
89 |
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
90 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
91 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
92 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
93 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
94 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
95 |
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
96 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
97 |
+
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
98 |
+
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
99 |
+
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
100 |
+
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
101 |
+
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
102 |
+
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
103 |
+
github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw=
|
104 |
+
github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg=
|
105 |
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
106 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
107 |
+
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
|
108 |
+
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
|
109 |
+
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
|
110 |
+
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
111 |
+
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
112 |
+
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
113 |
+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
114 |
+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
115 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
116 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
117 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
118 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
119 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
120 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
121 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
122 |
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
123 |
+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
124 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
125 |
+
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc=
|
126 |
+
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng=
|
127 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
128 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
129 |
+
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
130 |
+
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
131 |
+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
132 |
+
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
133 |
+
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
134 |
+
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
135 |
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
136 |
+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
137 |
+
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
138 |
+
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
139 |
+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
140 |
+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
141 |
+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
142 |
+
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
143 |
+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
144 |
+
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
145 |
+
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
146 |
+
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
147 |
+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
148 |
+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
149 |
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
150 |
+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
151 |
+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
152 |
+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
153 |
+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
154 |
+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
155 |
+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
156 |
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
157 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
158 |
+
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
159 |
+
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
160 |
+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
161 |
+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
162 |
+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
163 |
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
164 |
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
165 |
+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
166 |
+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
167 |
+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
168 |
+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
169 |
+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
170 |
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
171 |
+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
172 |
+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
173 |
+
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
174 |
+
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
175 |
+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
176 |
+
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
177 |
+
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
178 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
179 |
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
180 |
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
181 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
182 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
183 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
184 |
+
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
185 |
+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
harPool/put_har_in_this_folder
ADDED
File without changes
|
main.go
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package main
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/db"
|
5 |
+
"WarpGPT/pkg/env"
|
6 |
+
"WarpGPT/pkg/funcaptcha"
|
7 |
+
"WarpGPT/pkg/logger"
|
8 |
+
"WarpGPT/pkg/plugins"
|
9 |
+
"WarpGPT/pkg/plugins/api/arkosetoken"
|
10 |
+
"WarpGPT/pkg/plugins/api/backendapi"
|
11 |
+
"WarpGPT/pkg/plugins/api/officialapi"
|
12 |
+
"WarpGPT/pkg/plugins/api/publicapi"
|
13 |
+
"WarpGPT/pkg/plugins/api/rapi"
|
14 |
+
"WarpGPT/pkg/plugins/api/session"
|
15 |
+
"WarpGPT/pkg/plugins/api/unofficialapi"
|
16 |
+
"WarpGPT/pkg/plugins/service/proxypool"
|
17 |
+
"github.com/bogdanfinn/fhttp"
|
18 |
+
"github.com/gin-gonic/gin"
|
19 |
+
"strconv"
|
20 |
+
)
|
21 |
+
|
22 |
+
func CORSMiddleware() gin.HandlerFunc {
|
23 |
+
return func(c *gin.Context) {
|
24 |
+
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
25 |
+
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
26 |
+
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
|
27 |
+
c.Writer.Header().Set("Access-Control-Allow-Methods", "*")
|
28 |
+
|
29 |
+
if c.Request.Method == "OPTIONS" {
|
30 |
+
c.AbortWithStatus(http.StatusNoContent)
|
31 |
+
return
|
32 |
+
}
|
33 |
+
|
34 |
+
c.Next()
|
35 |
+
}
|
36 |
+
}
|
37 |
+
func AuthMiddleware() gin.HandlerFunc {
|
38 |
+
return func(c *gin.Context) {
|
39 |
+
apiKey := c.GetHeader("AuthKey")
|
40 |
+
if apiKey != env.E.AuthKey {
|
41 |
+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
42 |
+
return
|
43 |
+
}
|
44 |
+
c.Next()
|
45 |
+
}
|
46 |
+
}
|
47 |
+
func main() {
|
48 |
+
var router = gin.Default()
|
49 |
+
if env.E.Verify {
|
50 |
+
router.Use(AuthMiddleware())
|
51 |
+
}
|
52 |
+
router.Use(CORSMiddleware())
|
53 |
+
component := &plugins.Component{
|
54 |
+
Engine: router,
|
55 |
+
Db: db.DB{
|
56 |
+
GetRedisClient: db.GetRedisClient,
|
57 |
+
},
|
58 |
+
Logger: logger.Log,
|
59 |
+
Env: &env.E,
|
60 |
+
Auth: funcaptcha.GetOpenAIArkoseToken,
|
61 |
+
}
|
62 |
+
var plugin_list []plugins.Plugin
|
63 |
+
plugin_list = append(
|
64 |
+
plugin_list,
|
65 |
+
&arkosetoken.ArkoseTokenInstance,
|
66 |
+
&session.SessionTokenInstance,
|
67 |
+
&backendapi.BackendProcessInstance,
|
68 |
+
&officialapi.OfficialApiProcessInstance,
|
69 |
+
&unofficialapi.UnofficialApiProcessInstance,
|
70 |
+
&publicapi.PublicApiProcessInstance,
|
71 |
+
&rapi.ApiProcessInstance,
|
72 |
+
&proxypool.ProxyPoolInstance,
|
73 |
+
)
|
74 |
+
for _, plugin := range plugin_list {
|
75 |
+
plugin.Run(component)
|
76 |
+
}
|
77 |
+
router.Run(env.E.Host + ":" + strconv.Itoa(env.E.Port))
|
78 |
+
}
|
pkg/common/processBody.go
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package common
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/logger"
|
5 |
+
fhttp "github.com/bogdanfinn/fhttp"
|
6 |
+
"github.com/gin-gonic/gin"
|
7 |
+
)
|
8 |
+
|
9 |
+
type ContextProcessor[T any] interface {
|
10 |
+
SetContext(conversation T)
|
11 |
+
GetContext() T
|
12 |
+
ProcessMethod()
|
13 |
+
}
|
14 |
+
|
15 |
+
func Do[T any](p ContextProcessor[T], conversation T) {
|
16 |
+
p.SetContext(conversation)
|
17 |
+
p.ProcessMethod()
|
18 |
+
}
|
19 |
+
|
20 |
+
func CopyResponseHeaders(response *fhttp.Response, ctx *gin.Context) {
|
21 |
+
logger.Log.Debug("CopyResponseHeaders")
|
22 |
+
if response == nil {
|
23 |
+
ctx.JSON(400, gin.H{"error": "response is empty"})
|
24 |
+
logger.Log.Warning("response is empty")
|
25 |
+
}
|
26 |
+
skipHeaders := map[string]bool{"Content-Encoding": true, "Content-Length": true, "transfer-encoding": true, "connection": true}
|
27 |
+
for name, values := range response.Header {
|
28 |
+
if !skipHeaders[name] {
|
29 |
+
for _, value := range values {
|
30 |
+
ctx.Writer.Header().Set(name, value)
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
}
|
pkg/common/request.go
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package common
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/env"
|
5 |
+
"WarpGPT/pkg/logger"
|
6 |
+
"WarpGPT/pkg/plugins/service/proxypool"
|
7 |
+
"encoding/json"
|
8 |
+
browser "github.com/EDDYCJY/fake-useragent"
|
9 |
+
http "github.com/bogdanfinn/fhttp"
|
10 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
11 |
+
"github.com/bogdanfinn/tls-client/profiles"
|
12 |
+
"github.com/gin-gonic/gin"
|
13 |
+
"io"
|
14 |
+
"fmt"
|
15 |
+
"math/rand"
|
16 |
+
"sync"
|
17 |
+
)
|
18 |
+
|
19 |
+
type Context struct {
|
20 |
+
GinContext *gin.Context
|
21 |
+
RequestUrl string
|
22 |
+
RequestClient tls_client.HttpClient
|
23 |
+
RequestBody io.ReadCloser
|
24 |
+
RequestParam string
|
25 |
+
RequestMethod string
|
26 |
+
RequestHeaders http.Header
|
27 |
+
}
|
28 |
+
|
29 |
+
type APIError struct {
|
30 |
+
AccessToken string
|
31 |
+
StatusCode int
|
32 |
+
}
|
33 |
+
|
34 |
+
func (e *APIError) Error() string {
|
35 |
+
return fmt.Sprintf("HTTP status %d, AccessToken: %s", e.StatusCode, e.AccessToken)
|
36 |
+
}
|
37 |
+
|
38 |
+
var tu sync.Mutex
|
39 |
+
|
40 |
+
type RequestUrl interface {
|
41 |
+
Generate(path string, rawquery string) string
|
42 |
+
}
|
43 |
+
|
44 |
+
func GetContextPack[T RequestUrl](ctx *gin.Context, reqUrl T) Context {
|
45 |
+
conversation := Context{}
|
46 |
+
conversation.GinContext = ctx
|
47 |
+
conversation.RequestUrl = reqUrl.Generate(ctx.Param("path"), ctx.Request.URL.RawQuery)
|
48 |
+
conversation.RequestMethod = ctx.Request.Method
|
49 |
+
conversation.RequestBody = ctx.Request.Body
|
50 |
+
conversation.RequestParam = ctx.Param("path")
|
51 |
+
conversation.RequestClient = GetHttpClient()
|
52 |
+
conversation.RequestHeaders = http.Header(ctx.Request.Header)
|
53 |
+
return conversation
|
54 |
+
}
|
55 |
+
func getUserAgent() string {
|
56 |
+
tu.Lock()
|
57 |
+
defer tu.Unlock()
|
58 |
+
return browser.Safari()
|
59 |
+
}
|
60 |
+
|
61 |
+
func GetHttpClient() tls_client.HttpClient {
|
62 |
+
jar := tls_client.NewCookieJar()
|
63 |
+
userAgent := map[int]profiles.ClientProfile{
|
64 |
+
1: profiles.Safari_15_6_1,
|
65 |
+
2: profiles.Safari_16_0,
|
66 |
+
3: profiles.Safari_IOS_15_5,
|
67 |
+
4: profiles.Safari_IOS_15_6,
|
68 |
+
5: profiles.Safari_IOS_16_0,
|
69 |
+
}
|
70 |
+
|
71 |
+
options := []tls_client.HttpClientOption{
|
72 |
+
tls_client.WithTimeoutSeconds(120),
|
73 |
+
tls_client.WithClientProfile(userAgent[rand.Intn(5)+1]),
|
74 |
+
tls_client.WithNotFollowRedirects(),
|
75 |
+
tls_client.WithCookieJar(jar),
|
76 |
+
tls_client.WithRandomTLSExtensionOrder(),
|
77 |
+
}
|
78 |
+
if env.E.ProxyPoolUrl != "" {
|
79 |
+
ip, err := proxypool.ProxyPoolInstance.GetIpInRedis()
|
80 |
+
if err != nil {
|
81 |
+
logger.Log.Warning(err.Error())
|
82 |
+
return nil
|
83 |
+
}
|
84 |
+
options = append(options, tls_client.WithProxyUrl(ip))
|
85 |
+
} else {
|
86 |
+
options = append(options, tls_client.WithProxyUrl(env.E.Proxy))
|
87 |
+
}
|
88 |
+
client, err := tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
|
89 |
+
if err != nil {
|
90 |
+
logger.Log.Error("Error creating http client:", err)
|
91 |
+
return nil
|
92 |
+
}
|
93 |
+
return client
|
94 |
+
}
|
95 |
+
|
96 |
+
func RequestOpenAI[T any](path string, body io.Reader, accessToken string, requestMethod string) (*T, error) {
|
97 |
+
url := "https://" + env.E.OpenaiHost + path
|
98 |
+
req, err := http.NewRequest(requestMethod, url, body)
|
99 |
+
if err != nil {
|
100 |
+
logger.Log.Error("Error creating request:", err)
|
101 |
+
return nil, err
|
102 |
+
}
|
103 |
+
userAgentStr := getUserAgent()
|
104 |
+
headers := map[string]string{
|
105 |
+
"Host": env.E.OpenaiHost,
|
106 |
+
"Origin": "https://" + env.E.OpenaiHost,
|
107 |
+
"Authorization": accessToken,
|
108 |
+
"Connection": "keep-alive",
|
109 |
+
"User-Agent": userAgentStr,
|
110 |
+
"Referer": "https://" + env.E.OpenaiHost,
|
111 |
+
"Content-Type": "application/json",
|
112 |
+
"Accept": "*/*",
|
113 |
+
"sec-fetch-dest": "empty",
|
114 |
+
"sec-fetch-site": "same-origin",
|
115 |
+
}
|
116 |
+
for key, value := range headers {
|
117 |
+
req.Header.Set(key, value)
|
118 |
+
}
|
119 |
+
resp, err := GetHttpClient().Do(req)
|
120 |
+
if err != nil {
|
121 |
+
logger.Log.Error("Error sending request:", err)
|
122 |
+
return nil, err
|
123 |
+
}
|
124 |
+
defer resp.Body.Close()
|
125 |
+
if resp.StatusCode != http.StatusOK {
|
126 |
+
apiError := &APIError{
|
127 |
+
AccessToken: accessToken,
|
128 |
+
StatusCode: resp.StatusCode,
|
129 |
+
}
|
130 |
+
return nil, apiError
|
131 |
+
}
|
132 |
+
var data T
|
133 |
+
readAll, err := io.ReadAll(resp.Body)
|
134 |
+
if err != nil {
|
135 |
+
logger.Log.Error("Read error:", err)
|
136 |
+
return nil, err
|
137 |
+
}
|
138 |
+
if readAll == nil {
|
139 |
+
return nil, nil
|
140 |
+
}
|
141 |
+
err = json.Unmarshal(readAll, &data)
|
142 |
+
if err != nil {
|
143 |
+
logger.Log.Error("Unmarshal error:", err)
|
144 |
+
return nil, err
|
145 |
+
}
|
146 |
+
return &data, nil
|
147 |
+
}
|
pkg/db/db.go
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package db
|
2 |
+
|
3 |
+
import "github.com/redis/go-redis/v9"
|
4 |
+
|
5 |
+
type DB struct {
|
6 |
+
GetRedisClient func() (*redis.Client, error)
|
7 |
+
}
|
pkg/db/redis.go
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package db
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/env"
|
5 |
+
"WarpGPT/pkg/logger"
|
6 |
+
"context"
|
7 |
+
"github.com/redis/go-redis/v9"
|
8 |
+
)
|
9 |
+
|
10 |
+
func GetRedisClient() (*redis.Client, error) {
|
11 |
+
|
12 |
+
redisClient := redis.NewClient(&redis.Options{
|
13 |
+
Addr: env.E.RedisAddress,
|
14 |
+
Password: env.E.RedisPasswd,
|
15 |
+
DB: env.E.RedisDB,
|
16 |
+
MaxRetries: 3,
|
17 |
+
MaxActiveConns: 20,
|
18 |
+
})
|
19 |
+
|
20 |
+
_, err := redisClient.Ping(context.Background()).Result()
|
21 |
+
if err != nil {
|
22 |
+
return nil, err
|
23 |
+
}
|
24 |
+
logger.Log.Info("成功连接到Redis")
|
25 |
+
|
26 |
+
return redisClient, nil
|
27 |
+
}
|
pkg/env/env.go
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package env
|
2 |
+
|
3 |
+
import (
|
4 |
+
"flag"
|
5 |
+
"github.com/joho/godotenv"
|
6 |
+
"os"
|
7 |
+
"strconv"
|
8 |
+
)
|
9 |
+
|
10 |
+
type ENV struct {
|
11 |
+
Proxy string
|
12 |
+
Port int
|
13 |
+
Host string
|
14 |
+
Verify bool
|
15 |
+
AuthKey string
|
16 |
+
ArkoseMust bool
|
17 |
+
OpenaiHost string
|
18 |
+
OpenaiApiHost string
|
19 |
+
ProxyPoolUrl string
|
20 |
+
UserAgent string
|
21 |
+
LogLevel string
|
22 |
+
RedisAddress string
|
23 |
+
RedisPasswd string
|
24 |
+
RedisDB int
|
25 |
+
PostgreSQLDBURI string
|
26 |
+
CapSolver string
|
27 |
+
CapClientID string
|
28 |
+
}
|
29 |
+
|
30 |
+
var E ENV
|
31 |
+
var EnvFile string
|
32 |
+
|
33 |
+
func init() {
|
34 |
+
flag.StringVar(&EnvFile, "e", ".env", "The env file path")
|
35 |
+
flag.Parse()
|
36 |
+
err := godotenv.Load(EnvFile)
|
37 |
+
if err != nil {
|
38 |
+
return
|
39 |
+
}
|
40 |
+
port, err := strconv.Atoi(os.Getenv("port"))
|
41 |
+
if err != nil {
|
42 |
+
port = 5000
|
43 |
+
}
|
44 |
+
verify, err := strconv.ParseBool(os.Getenv("verify"))
|
45 |
+
if err != nil {
|
46 |
+
verify = false
|
47 |
+
}
|
48 |
+
arkoseMust, err := strconv.ParseBool(os.Getenv("verify"))
|
49 |
+
if err != nil {
|
50 |
+
arkoseMust = false
|
51 |
+
}
|
52 |
+
OpenaiHost := os.Getenv("openai_host")
|
53 |
+
if OpenaiHost == "" {
|
54 |
+
OpenaiHost = "chat.openai.com"
|
55 |
+
}
|
56 |
+
openaiApiHost := os.Getenv("openai_api_host")
|
57 |
+
if openaiApiHost == "" {
|
58 |
+
openaiApiHost = "api.openai.com"
|
59 |
+
}
|
60 |
+
loglevel := os.Getenv("log_level")
|
61 |
+
if loglevel == "" {
|
62 |
+
loglevel = "info"
|
63 |
+
}
|
64 |
+
proxyPoolUrl := os.Getenv("proxy_pool_url")
|
65 |
+
redisAddress := os.Getenv("redis_address")
|
66 |
+
if proxyPoolUrl != "" && redisAddress == "" {
|
67 |
+
panic("配置proxyPoolUrl后未配置redis_address")
|
68 |
+
}
|
69 |
+
redisDb, err := strconv.Atoi(os.Getenv("redis_db"))
|
70 |
+
if err != nil && proxyPoolUrl != "" {
|
71 |
+
panic("DB填写出现问题")
|
72 |
+
}
|
73 |
+
E = ENV{
|
74 |
+
Proxy: os.Getenv("proxy"),
|
75 |
+
Port: port,
|
76 |
+
Host: os.Getenv("host"),
|
77 |
+
Verify: verify,
|
78 |
+
AuthKey: os.Getenv("auth_key"),
|
79 |
+
ArkoseMust: arkoseMust,
|
80 |
+
OpenaiHost: OpenaiHost,
|
81 |
+
OpenaiApiHost: openaiApiHost,
|
82 |
+
ProxyPoolUrl: proxyPoolUrl,
|
83 |
+
UserAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15",
|
84 |
+
LogLevel: loglevel,
|
85 |
+
RedisAddress: redisAddress,
|
86 |
+
RedisPasswd: os.Getenv("redis_passwd"),
|
87 |
+
RedisDB: redisDb,
|
88 |
+
PostgreSQLDBURI: os.Getenv("postgreSQL_db_URI"),
|
89 |
+
CapSolver: os.Getenv("cap_solver"),
|
90 |
+
CapClientID: os.Getenv("cap_client_id"),
|
91 |
+
}
|
92 |
+
}
|
pkg/funcaptcha/api.go
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/base64"
|
5 |
+
"encoding/json"
|
6 |
+
"errors"
|
7 |
+
"fmt"
|
8 |
+
"math/rand"
|
9 |
+
"net/url"
|
10 |
+
"regexp"
|
11 |
+
"strconv"
|
12 |
+
"strings"
|
13 |
+
"time"
|
14 |
+
|
15 |
+
http "github.com/bogdanfinn/fhttp"
|
16 |
+
)
|
17 |
+
|
18 |
+
const arkPreURL = "https://tcr9i.chat.openai.com/fc/gt2/"
|
19 |
+
const arkAuthPreURL = "https://tcr9i.openai.com/fc/gt2/"
|
20 |
+
|
21 |
+
var arkURLIns, _ = url.Parse(arkPreURL)
|
22 |
+
|
23 |
+
type arkReq struct {
|
24 |
+
arkURL string
|
25 |
+
arkBx string
|
26 |
+
arkHeader http.Header
|
27 |
+
arkBody url.Values
|
28 |
+
arkCookies []*http.Cookie
|
29 |
+
userAgent string
|
30 |
+
}
|
31 |
+
|
32 |
+
type kvPair struct {
|
33 |
+
Name string `json:"name"`
|
34 |
+
Value string `json:"value"`
|
35 |
+
}
|
36 |
+
type cookie struct {
|
37 |
+
Name string `json:"name"`
|
38 |
+
Value string `json:"value"`
|
39 |
+
Expires string `json:"expires"`
|
40 |
+
}
|
41 |
+
type postBody struct {
|
42 |
+
Params []kvPair `json:"params"`
|
43 |
+
}
|
44 |
+
type request struct {
|
45 |
+
URL string `json:"url"`
|
46 |
+
Headers []kvPair `json:"headers,omitempty"`
|
47 |
+
PostData postBody `json:"postData,omitempty"`
|
48 |
+
Cookies []cookie `json:"cookies,omitempty"`
|
49 |
+
}
|
50 |
+
type entries struct {
|
51 |
+
StartedDateTime string `json:"startedDateTime"`
|
52 |
+
Request request `json:"request"`
|
53 |
+
}
|
54 |
+
type logData struct {
|
55 |
+
Entries []entries `json:"entries"`
|
56 |
+
}
|
57 |
+
type HARData struct {
|
58 |
+
Log logData `json:"log"`
|
59 |
+
}
|
60 |
+
|
61 |
+
func (s *Solver) GetOpenAIToken(version arkVer, puid string) (string, error) {
|
62 |
+
token, err := s.sendRequest(version, "", puid)
|
63 |
+
return token, err
|
64 |
+
}
|
65 |
+
|
66 |
+
func (s *Solver) GetOpenAITokenWithBx(version arkVer, bx string, puid string) (string, error) {
|
67 |
+
token, err := s.sendRequest(version, getBdaWitBx(bx), puid)
|
68 |
+
return token, err
|
69 |
+
}
|
70 |
+
|
71 |
+
func (s *Solver) sendRequest(arkType arkVer, bda string, puid string) (string, error) {
|
72 |
+
if len(s.arks[arkType]) == 0 {
|
73 |
+
return "", errors.New("a valid HAR file with arkType " + strconv.Itoa(int(arkType)) + " required")
|
74 |
+
}
|
75 |
+
var tmpArk *arkReq = &s.arks[arkType][0]
|
76 |
+
s.arks[arkType] = append(s.arks[arkType][1:], s.arks[arkType][0])
|
77 |
+
if tmpArk == nil || tmpArk.arkBx == "" || len(tmpArk.arkBody) == 0 || len(tmpArk.arkHeader) == 0 {
|
78 |
+
return "", errors.New("a valid HAR file required")
|
79 |
+
}
|
80 |
+
if bda == "" {
|
81 |
+
bda = s.getBDA(tmpArk)
|
82 |
+
}
|
83 |
+
tmpArk.arkBody.Set("bda", base64.StdEncoding.EncodeToString([]byte(bda)))
|
84 |
+
tmpArk.arkBody.Set("rnd", strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
|
85 |
+
req, _ := http.NewRequest(http.MethodPost, tmpArk.arkURL, strings.NewReader(tmpArk.arkBody.Encode()))
|
86 |
+
req.Header = tmpArk.arkHeader.Clone()
|
87 |
+
(*s.client).GetCookieJar().SetCookies(arkURLIns, tmpArk.arkCookies)
|
88 |
+
if puid != "" {
|
89 |
+
req.Header.Set("cookie", "_puid="+puid+";")
|
90 |
+
}
|
91 |
+
resp, err := (*s.client).Do(req)
|
92 |
+
if err != nil {
|
93 |
+
return "", err
|
94 |
+
}
|
95 |
+
defer resp.Body.Close()
|
96 |
+
if resp.StatusCode != 200 {
|
97 |
+
return "", errors.New("status code " + resp.Status)
|
98 |
+
}
|
99 |
+
|
100 |
+
type arkoseResponse struct {
|
101 |
+
Token string `json:"token"`
|
102 |
+
}
|
103 |
+
var arkose arkoseResponse
|
104 |
+
err = json.NewDecoder(resp.Body).Decode(&arkose)
|
105 |
+
if err != nil {
|
106 |
+
return "", err
|
107 |
+
}
|
108 |
+
// Check if rid is empty
|
109 |
+
if !strings.Contains(arkose.Token, "pk=") {
|
110 |
+
return arkose.Token, errors.New("captcha required")
|
111 |
+
}
|
112 |
+
|
113 |
+
return arkose.Token, nil
|
114 |
+
}
|
115 |
+
|
116 |
+
//goland:noinspection SpellCheckingInspection
|
117 |
+
func (s *Solver) getBDA(arkReq *arkReq) string {
|
118 |
+
var bx string = arkReq.arkBx
|
119 |
+
if bx == "" {
|
120 |
+
bx = fmt.Sprintf(bx_template,
|
121 |
+
getF(),
|
122 |
+
getN(),
|
123 |
+
getWh(),
|
124 |
+
webglExtensions,
|
125 |
+
getWebglExtensionsHash(),
|
126 |
+
webglRenderer,
|
127 |
+
webglVendor,
|
128 |
+
webglVersion,
|
129 |
+
webglShadingLanguageVersion,
|
130 |
+
webglAliasedLineWidthRange,
|
131 |
+
webglAliasedPointSizeRange,
|
132 |
+
webglAntialiasing,
|
133 |
+
webglBits,
|
134 |
+
webglMaxParams,
|
135 |
+
webglMaxViewportDims,
|
136 |
+
webglUnmaskedVendor,
|
137 |
+
webglUnmaskedRenderer,
|
138 |
+
webglVsfParams,
|
139 |
+
webglVsiParams,
|
140 |
+
webglFsfParams,
|
141 |
+
webglFsiParams,
|
142 |
+
getWebglHashWebgl(),
|
143 |
+
s.initVer,
|
144 |
+
s.initHex,
|
145 |
+
getFe(),
|
146 |
+
getIfeHash(),
|
147 |
+
)
|
148 |
+
} else {
|
149 |
+
re := regexp.MustCompile(`"key"\:"n","value"\:"\S*?"`)
|
150 |
+
bx = re.ReplaceAllString(bx, `"key":"n","value":"`+getN()+`"`)
|
151 |
+
}
|
152 |
+
bt := getBt()
|
153 |
+
bw := getBw(bt)
|
154 |
+
return Encrypt(bx, arkReq.userAgent+bw)
|
155 |
+
}
|
156 |
+
|
157 |
+
func getBt() int64 {
|
158 |
+
return time.Now().UnixMicro() / 1000000
|
159 |
+
}
|
160 |
+
|
161 |
+
func getBw(bt int64) string {
|
162 |
+
return strconv.FormatInt(bt-(bt%21600), 10)
|
163 |
+
}
|
164 |
+
|
165 |
+
func getBdaWitBx(bx string) string {
|
166 |
+
bt := getBt()
|
167 |
+
bw := getBw(bt)
|
168 |
+
return Encrypt(bx, bv+bw)
|
169 |
+
}
|
pkg/funcaptcha/challenge.go
ADDED
@@ -0,0 +1,356 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/json"
|
5 |
+
"errors"
|
6 |
+
"fmt"
|
7 |
+
"io"
|
8 |
+
"log"
|
9 |
+
"math/rand"
|
10 |
+
"strconv"
|
11 |
+
"strings"
|
12 |
+
"time"
|
13 |
+
|
14 |
+
http "github.com/bogdanfinn/fhttp"
|
15 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
16 |
+
"github.com/bogdanfinn/tls-client/profiles"
|
17 |
+
)
|
18 |
+
|
19 |
+
type Session struct {
|
20 |
+
Sid string `json:"sid"`
|
21 |
+
SessionToken string `json:"session_token"`
|
22 |
+
Hex string `json:"hex"`
|
23 |
+
ChallengeLogger challengeLogger `json:"challenge_logger"`
|
24 |
+
Challenge Challenge `json:"challenge"`
|
25 |
+
ConciseChallenge ConciseChallenge `json:"concise_challenge"`
|
26 |
+
Headers http.Header `json:"headers"`
|
27 |
+
Client *tls_client.HttpClient `json:"-"`
|
28 |
+
options []tls_client.HttpClientOption `json:"-"`
|
29 |
+
}
|
30 |
+
|
31 |
+
type ConciseChallenge struct {
|
32 |
+
GameType string `json:"game_type"`
|
33 |
+
URLs []string `json:"urls"`
|
34 |
+
Instructions string `json:"instructions"`
|
35 |
+
}
|
36 |
+
type Input struct {
|
37 |
+
Index int
|
38 |
+
}
|
39 |
+
type ValueFunc func(Input) Input
|
40 |
+
type KeyFunc func(Input) interface{}
|
41 |
+
|
42 |
+
var Yz = map[int]struct {
|
43 |
+
Value map[string]ValueFunc
|
44 |
+
Key map[string]KeyFunc
|
45 |
+
}{
|
46 |
+
4: {
|
47 |
+
Value: map[string]ValueFunc{
|
48 |
+
"alpha": func(c Input) Input {
|
49 |
+
yValueStr := strconv.Itoa(c.Index) // 转换为字符串
|
50 |
+
combinedStr := yValueStr + strconv.Itoa(1) // 加1
|
51 |
+
combinedInt, _ := strconv.Atoi(combinedStr) // 将合并后的字符串转回为整数
|
52 |
+
return Input{Index: combinedInt - 2}
|
53 |
+
},
|
54 |
+
"beta": func(c Input) Input { return Input{Index: -c.Index} },
|
55 |
+
"gamma": func(c Input) Input { return Input{Index: 3 * (3 - c.Index)} },
|
56 |
+
"delta": func(c Input) Input { return Input{Index: 7 * c.Index} },
|
57 |
+
"epsilon": func(c Input) Input { return Input{Index: 2 * c.Index} },
|
58 |
+
"zeta": func(c Input) Input {
|
59 |
+
if c.Index != 0 {
|
60 |
+
return Input{Index: 100 / c.Index}
|
61 |
+
}
|
62 |
+
return Input{Index: c.Index}
|
63 |
+
},
|
64 |
+
},
|
65 |
+
Key: map[string]KeyFunc{
|
66 |
+
"alpha": func(c Input) interface{} {
|
67 |
+
return []int{rand.Intn(100), c.Index, rand.Intn(100)}
|
68 |
+
},
|
69 |
+
"beta": func(c Input) interface{} {
|
70 |
+
return map[string]int{
|
71 |
+
"size": 50 - c.Index,
|
72 |
+
"id": c.Index,
|
73 |
+
"limit": 10 * c.Index,
|
74 |
+
"req_timestamp": int(time.Now().UnixNano() / int64(time.Millisecond)),
|
75 |
+
}
|
76 |
+
},
|
77 |
+
"gamma": func(c Input) interface{} {
|
78 |
+
return c.Index
|
79 |
+
},
|
80 |
+
"delta": func(c Input) interface{} {
|
81 |
+
return map[string]int{"index": c.Index}
|
82 |
+
},
|
83 |
+
"epsilon": func(c Input) interface{} {
|
84 |
+
arr := make([]int, rand.Intn(5)+1)
|
85 |
+
randIndex := rand.Intn(len(arr))
|
86 |
+
for i := range arr {
|
87 |
+
if i == randIndex {
|
88 |
+
arr[i] = c.Index
|
89 |
+
} else {
|
90 |
+
arr[i] = rand.Intn(10)
|
91 |
+
}
|
92 |
+
}
|
93 |
+
return append(arr, randIndex)
|
94 |
+
},
|
95 |
+
"zeta": func(c Input) interface{} {
|
96 |
+
return append(make([]int, rand.Intn(5)+1), c.Index)
|
97 |
+
},
|
98 |
+
},
|
99 |
+
},
|
100 |
+
}
|
101 |
+
|
102 |
+
func YB(gameType int, apiBreaker *ApiBreaker) func(Input) interface{} {
|
103 |
+
return func(input Input) interface{} {
|
104 |
+
for _, valueFuncName := range apiBreaker.Value {
|
105 |
+
input = Yz[gameType].Value[valueFuncName](input)
|
106 |
+
}
|
107 |
+
return Yz[gameType].Key[apiBreaker.Key](input)
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
type Challenge struct {
|
112 |
+
SessionToken string `json:"session_token"`
|
113 |
+
ChallengeID string `json:"challengeID"`
|
114 |
+
ChallengeURL string `json:"challengeURL"`
|
115 |
+
AudioChallengeURLs []string `json:"audio_challenge_urls"`
|
116 |
+
AudioGameRateLimited interface{} `json:"audio_game_rate_limited"`
|
117 |
+
Sec int `json:"sec"`
|
118 |
+
EndURL interface{} `json:"end_url"`
|
119 |
+
GameData struct {
|
120 |
+
GameType int `json:"gameType"`
|
121 |
+
GameVariant string `json:"game_variant"`
|
122 |
+
InstructionString string `json:"instruction_string"`
|
123 |
+
CustomGUI struct {
|
124 |
+
ChallengeIMGs []string `json:"_challenge_imgs"`
|
125 |
+
ApiBreaker *ApiBreaker `json:"api_breaker"`
|
126 |
+
ApiBreakerV2Enabled int `json:"api_breaker_v2_enabled"`
|
127 |
+
} `json:"customGUI"`
|
128 |
+
} `json:"game_data"`
|
129 |
+
GameSID string `json:"game_sid"`
|
130 |
+
SID string `json:"sid"`
|
131 |
+
Lang string `json:"lang"`
|
132 |
+
StringTablePrefixes []interface{} `json:"string_table_prefixes"`
|
133 |
+
StringTable map[string]string `json:"string_table"`
|
134 |
+
EarlyVictoryMessage interface{} `json:"earlyVictoryMessage"`
|
135 |
+
FontSizeAdjustments interface{} `json:"font_size_adjustments"`
|
136 |
+
StyleTheme string `json:"style_theme"`
|
137 |
+
}
|
138 |
+
|
139 |
+
type challengeLogger struct {
|
140 |
+
Sid string `json:"sid"`
|
141 |
+
SessionToken string `json:"session_token"`
|
142 |
+
AnalyticsTier int `json:"analytics_tier"`
|
143 |
+
RenderType string `json:"render_type"`
|
144 |
+
Category string `json:"category"`
|
145 |
+
Action string `json:"action"`
|
146 |
+
// Omit if empty
|
147 |
+
GameToken string `json:"game_token,omitempty"`
|
148 |
+
GameType string `json:"game_type,omitempty"`
|
149 |
+
}
|
150 |
+
|
151 |
+
type requestChallenge struct {
|
152 |
+
Sid string `json:"sid"`
|
153 |
+
Token string `json:"token"`
|
154 |
+
AnalyticsTier int `json:"analytics_tier"`
|
155 |
+
RenderType string `json:"render_type"`
|
156 |
+
Lang string `json:"lang"`
|
157 |
+
IsAudioGame bool `json:"isAudioGame"`
|
158 |
+
APIBreakerVersion string `json:"apiBreakerVersion"`
|
159 |
+
}
|
160 |
+
|
161 |
+
type submitChallenge struct {
|
162 |
+
SessionToken string `json:"session_token"`
|
163 |
+
Sid string `json:"sid"`
|
164 |
+
GameToken string `json:"game_token"`
|
165 |
+
Guess string `json:"guess"`
|
166 |
+
RenderType string `json:"render_type"`
|
167 |
+
AnalyticsTier int `json:"analytics_tier"`
|
168 |
+
Bio string `json:"bio"`
|
169 |
+
}
|
170 |
+
|
171 |
+
type ApiBreaker struct {
|
172 |
+
Key string `json:"key"`
|
173 |
+
Value []string `json:"value"`
|
174 |
+
}
|
175 |
+
|
176 |
+
func StartChallenge(full_session, hex string) (*Session, error) {
|
177 |
+
fields := strings.Split(full_session, "|")
|
178 |
+
session_token := fields[0]
|
179 |
+
sid := strings.Split(fields[1], "=")[1]
|
180 |
+
|
181 |
+
session := Session{
|
182 |
+
Sid: sid,
|
183 |
+
SessionToken: session_token,
|
184 |
+
Hex: hex,
|
185 |
+
}
|
186 |
+
session.Headers = headers
|
187 |
+
session.Headers.Set("Referer", fmt.Sprintf("https://client-api.arkoselabs.com/fc/assets/ec-game-core/game-core/1.15.0/standard/index.html?session=%s", strings.Replace(full_session, "|", "&", -1)))
|
188 |
+
session.ChallengeLogger = challengeLogger{
|
189 |
+
Sid: sid,
|
190 |
+
SessionToken: session_token,
|
191 |
+
AnalyticsTier: 40,
|
192 |
+
RenderType: "canvas",
|
193 |
+
}
|
194 |
+
err := session.log("", 0, "Site URL", fmt.Sprintf("https://client-api.arkoselabs.com/v2/1.5.5/enforcement.%s.html", hex))
|
195 |
+
jar := tls_client.NewCookieJar()
|
196 |
+
session.options = []tls_client.HttpClientOption{
|
197 |
+
tls_client.WithTimeoutSeconds(360),
|
198 |
+
tls_client.WithClientProfile(profiles.Chrome_117),
|
199 |
+
tls_client.WithRandomTLSExtensionOrder(),
|
200 |
+
tls_client.WithNotFollowRedirects(),
|
201 |
+
tls_client.WithCookieJar(jar),
|
202 |
+
}
|
203 |
+
client, _ := tls_client.NewHttpClient(tls_client.NewNoopLogger(), session.options...)
|
204 |
+
session.Client = &client
|
205 |
+
return &session, err
|
206 |
+
}
|
207 |
+
|
208 |
+
func (c *Session) RequestChallenge(isAudioGame bool) (*ApiBreaker, error) {
|
209 |
+
challenge_request := requestChallenge{
|
210 |
+
Sid: c.Sid,
|
211 |
+
Token: c.SessionToken,
|
212 |
+
AnalyticsTier: 40,
|
213 |
+
RenderType: "canvas",
|
214 |
+
Lang: "en-us",
|
215 |
+
IsAudioGame: isAudioGame,
|
216 |
+
APIBreakerVersion: "green",
|
217 |
+
}
|
218 |
+
payload := jsonToForm(toJSON(challenge_request))
|
219 |
+
|
220 |
+
req, _ := http.NewRequest(http.MethodPost, "https://client-api.arkoselabs.com/fc/gfct/", strings.NewReader(payload))
|
221 |
+
req.Header = c.Headers
|
222 |
+
req.Header.Set("X-NewRelic-Timestamp", getTimeStamp())
|
223 |
+
resp, err := (*c.Client).Do(req)
|
224 |
+
if err != nil {
|
225 |
+
return nil, err
|
226 |
+
}
|
227 |
+
defer resp.Body.Close()
|
228 |
+
|
229 |
+
if resp.StatusCode != 200 {
|
230 |
+
return nil, fmt.Errorf("status code %d", resp.StatusCode)
|
231 |
+
}
|
232 |
+
|
233 |
+
body, _ := io.ReadAll(resp.Body)
|
234 |
+
var challenge_data Challenge
|
235 |
+
err = json.Unmarshal(body, &challenge_data)
|
236 |
+
if err != nil {
|
237 |
+
return nil, err
|
238 |
+
}
|
239 |
+
err = c.log(challenge_data.ChallengeID, challenge_data.GameData.GameType, "loaded", "game loaded")
|
240 |
+
c.Challenge = challenge_data
|
241 |
+
// Build concise challenge
|
242 |
+
var challenge_type string
|
243 |
+
var challenge_urls []string
|
244 |
+
var key string
|
245 |
+
var apiBreaker *ApiBreaker
|
246 |
+
switch challenge_data.GameData.GameType {
|
247 |
+
case 4:
|
248 |
+
challenge_type = "image"
|
249 |
+
challenge_urls = challenge_data.GameData.CustomGUI.ChallengeIMGs
|
250 |
+
instruction_string := challenge_data.GameData.InstructionString
|
251 |
+
key = fmt.Sprintf("4.instructions-%s", instruction_string)
|
252 |
+
if challenge_data.GameData.CustomGUI.ApiBreakerV2Enabled == 1 {
|
253 |
+
apiBreaker = challenge_data.GameData.CustomGUI.ApiBreaker
|
254 |
+
}
|
255 |
+
case 101:
|
256 |
+
challenge_type = "audio"
|
257 |
+
challenge_urls = challenge_data.AudioChallengeURLs
|
258 |
+
instruction_string := challenge_data.GameData.GameVariant
|
259 |
+
key = fmt.Sprintf("audio_game.instructions-%s", instruction_string)
|
260 |
+
|
261 |
+
default:
|
262 |
+
challenge_type = "unknown"
|
263 |
+
challenge_urls = []string{}
|
264 |
+
}
|
265 |
+
|
266 |
+
c.ConciseChallenge = ConciseChallenge{
|
267 |
+
GameType: challenge_type,
|
268 |
+
URLs: challenge_urls,
|
269 |
+
Instructions: strings.ReplaceAll(strings.ReplaceAll(challenge_data.StringTable[key], "<strong>", ""), "</strong>", ""),
|
270 |
+
}
|
271 |
+
return apiBreaker, err
|
272 |
+
}
|
273 |
+
|
274 |
+
func (c *Session) SubmitAnswer(indices []int, isAudio bool, apiBreaker *ApiBreaker) error {
|
275 |
+
submission := submitChallenge{
|
276 |
+
SessionToken: c.SessionToken,
|
277 |
+
Sid: c.Sid,
|
278 |
+
GameToken: c.Challenge.ChallengeID,
|
279 |
+
RenderType: "canvas",
|
280 |
+
AnalyticsTier: 40,
|
281 |
+
Bio: "eyJtYmlvIjoiMTUwLDAsMTE3LDIzOTszMDAsMCwxMjEsMjIxOzMxNywwLDEyNCwyMTY7NTUwLDAsMTI5LDIxMDs1NjcsMCwxMzQsMjA3OzYxNywwLDE0NCwyMDU7NjUwLDAsMTU1LDIwNTs2NjcsMCwxNjUsMjA1OzY4NCwwLDE3MywyMDc7NzAwLDAsMTc4LDIxMjs4MzQsMCwyMjEsMjI4OzI2MDY3LDAsMTkzLDM1MTsyNjEwMSwwLDE4NSwzNTM7MjYxMDEsMCwxODAsMzU3OzI2MTM0LDAsMTcyLDM2MTsyNjE4NCwwLDE2NywzNjM7MjYyMTcsMCwxNjEsMzY1OzI2MzM0LDAsMTU2LDM2NDsyNjM1MSwwLDE1MiwzNTQ7MjYzNjcsMCwxNTIsMzQzOzI2Mzg0LDAsMTUyLDMzMTsyNjQ2NywwLDE1MSwzMjU7MjY0NjcsMCwxNTEsMzE3OzI2NTAxLDAsMTQ5LDMxMTsyNjY4NCwxLDE0NywzMDc7MjY3NTEsMiwxNDcsMzA3OzMwNDUxLDAsMzcsNDM3OzMwNDY4LDAsNTcsNDI0OzMwNDg0LDAsNjYsNDE0OzMwNTAxLDAsODgsMzkwOzMwNTAxLDAsMTA0LDM2OTszMDUxOCwwLDEyMSwzNDk7MzA1MzQsMCwxNDEsMzI0OzMwNTUxLDAsMTQ5LDMxNDszMDU4NCwwLDE1MywzMDQ7MzA2MTgsMCwxNTUsMjk2OzMwNzUxLDAsMTU5LDI4OTszMDc2OCwwLDE2NywyODA7MzA3ODQsMCwxNzcsMjc0OzMwODE4LDAsMTgzLDI3MDszMDg1MSwwLDE5MSwyNzA7MzA4ODQsMCwyMDEsMjY4OzMwOTE4LDAsMjA4LDI2ODszMTIzNCwwLDIwNCwyNjM7MzEyNTEsMCwyMDAsMjU3OzMxMzg0LDAsMTk1LDI1MTszMTQxOCwwLDE4OSwyNDk7MzE1NTEsMSwxODksMjQ5OzMxNjM0LDIsMTg5LDI0OTszMTcxOCwxLDE4OSwyNDk7MzE3ODQsMiwxODksMjQ5OzMxODg0LDEsMTg5LDI0OTszMTk2OCwyLDE4OSwyNDk7MzIyODQsMCwyMDIsMjQ5OzMyMzE4LDAsMjE2LDI0NzszMjMxOCwwLDIzNCwyNDU7MzIzMzQsMCwyNjksMjQ1OzMyMzUxLDAsMzAwLDI0NTszMjM2OCwwLDMzOSwyNDE7MzIzODQsMCwzODgsMjM5OzMyNjE4LDAsMzkwLDI0NzszMjYzNCwwLDM3NCwyNTM7MzI2NTEsMCwzNjUsMjU1OzMyNjY4LDAsMzUzLDI1NzszMjk1MSwxLDM0OCwyNTc7MzMwMDEsMiwzNDgsMjU3OzMzNTY4LDAsMzI4LDI3MjszMzU4NCwwLDMxOSwyNzg7MzM2MDEsMCwzMDcsMjg2OzMzNjUxLDAsMjk1LDI5NjszMzY1MSwwLDI5MSwzMDA7MzM2ODQsMCwyODEsMzA5OzMzNjg0LDAsMjcyLDMxNTszMzcxOCwwLDI2NiwzMTc7MzM3MzQsMCwyNTgsMzIzOzMzNzUxLDAsMjUyLDMyNzszMzc1MSwwLDI0NiwzMzM7MzM3NjgsMCwyNDAsMzM3OzMzNzg0LDAsMjM2LDM0MTszMzgxOCwwLDIyNywzNDc7MzM4MzQsMCwyMjEsMzUzOzM0MDUxLDAsMjE2LDM1NDszNDA2OCwwLDIxMCwzNDg7MzQwODQsMCwyMDQsMzQ0OzM0MTAxLDAsMTk4LDM0MDszNDEzNCwwLDE5NCwzMzY7MzQ1ODQsMSwxOTIsMzM0OzM0NjUxLDIsMTkyLDMzNDsiLCJ0YmlvIjoiIiwia2JpbyI6IiJ9",
|
282 |
+
}
|
283 |
+
var answerIndex []string
|
284 |
+
if isAudio {
|
285 |
+
for _, answer := range indices {
|
286 |
+
answerIndex = append(answerIndex, strconv.Itoa(answer))
|
287 |
+
}
|
288 |
+
} else {
|
289 |
+
for _, answer := range indices {
|
290 |
+
input := Input{Index: answer}
|
291 |
+
encoder := YB(4, apiBreaker)
|
292 |
+
result := encoder(input)
|
293 |
+
marshal, _ := json.Marshal(result)
|
294 |
+
answerIndex = append(answerIndex, string(marshal))
|
295 |
+
}
|
296 |
+
}
|
297 |
+
answer := "[" + strings.Join(answerIndex, ",") + "]"
|
298 |
+
submission.Guess = Encrypt(answer, c.SessionToken)
|
299 |
+
payload := jsonToForm(toJSON(submission))
|
300 |
+
req, _ := http.NewRequest(http.MethodPost, "https://client-api.arkoselabs.com/fc/ca/", strings.NewReader(payload))
|
301 |
+
req.Header = c.Headers
|
302 |
+
req.Header.Set("X-Requested-ID", getRequestId(c.SessionToken))
|
303 |
+
req.Header.Set("X-NewRelic-Timestamp", getTimeStamp())
|
304 |
+
|
305 |
+
resp, err := (*c.Client).Do(req)
|
306 |
+
if err != nil {
|
307 |
+
return err
|
308 |
+
}
|
309 |
+
defer resp.Body.Close()
|
310 |
+
body, _ := io.ReadAll(resp.Body)
|
311 |
+
var response struct {
|
312 |
+
Error string `json:"error"`
|
313 |
+
Response string `json:"response"`
|
314 |
+
Solved bool `json:"solved"`
|
315 |
+
IncorrectGuess string `json:"incorrect_guess"`
|
316 |
+
Score int `json:"score"`
|
317 |
+
}
|
318 |
+
log.Println(string(body))
|
319 |
+
err = json.Unmarshal(body, &response)
|
320 |
+
if err != nil {
|
321 |
+
return err
|
322 |
+
}
|
323 |
+
if response.Error != "" {
|
324 |
+
return errors.New(response.Error)
|
325 |
+
}
|
326 |
+
if !response.Solved {
|
327 |
+
return fmt.Errorf("incorrect guess: %s", response.IncorrectGuess)
|
328 |
+
}
|
329 |
+
// Set new client
|
330 |
+
cli, _ := tls_client.NewHttpClient(tls_client.NewNoopLogger(), c.options...)
|
331 |
+
c.Client = &cli
|
332 |
+
return nil
|
333 |
+
}
|
334 |
+
|
335 |
+
func (c *Session) log(game_token string, game_type int, category, action string) error {
|
336 |
+
v := c.ChallengeLogger
|
337 |
+
v.GameToken = game_token
|
338 |
+
if game_type != 0 {
|
339 |
+
v.GameType = fmt.Sprintf("%d", game_type)
|
340 |
+
}
|
341 |
+
v.Category = category
|
342 |
+
v.Action = action
|
343 |
+
|
344 |
+
request, _ := http.NewRequest(http.MethodPost, "https://client-api.arkoselabs.com/fc/a/", strings.NewReader(jsonToForm(toJSON(v))))
|
345 |
+
request.Header = headers
|
346 |
+
resp, err := (*c.Client).Do(request)
|
347 |
+
if err != nil {
|
348 |
+
return err
|
349 |
+
}
|
350 |
+
defer resp.Body.Close()
|
351 |
+
|
352 |
+
if resp.StatusCode != 200 {
|
353 |
+
return fmt.Errorf("status code %d", resp.StatusCode)
|
354 |
+
}
|
355 |
+
return nil
|
356 |
+
}
|
pkg/funcaptcha/constants.go
ADDED
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import http "github.com/bogdanfinn/fhttp"
|
4 |
+
|
5 |
+
var bv = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
|
6 |
+
var headers = http.Header{
|
7 |
+
"Accept": []string{"*/*"},
|
8 |
+
"Accept-Encoding": []string{"gzip, deflate, br"},
|
9 |
+
"Accept-Language": []string{"en-US,en;q=0.5"},
|
10 |
+
"Cache-Control": []string{"no-cache"},
|
11 |
+
"Connection": []string{"keep-alive"},
|
12 |
+
"Content-Type": []string{"application/x-www-form-urlencoded; charset=UTF-8"},
|
13 |
+
"Host": []string{"client-api.arkoselabs.com"},
|
14 |
+
"Origin": []string{"https://client-api.arkoselabs.com"},
|
15 |
+
"User-Agent": []string{bv},
|
16 |
+
"X-Requested-With": []string{"XMLHttpRequest"},
|
17 |
+
}
|
18 |
+
|
19 |
+
const bx_template string = `
|
20 |
+
[{
|
21 |
+
"key": "api_type",
|
22 |
+
"value": "js"
|
23 |
+
}, {
|
24 |
+
"key": "p",
|
25 |
+
"value": 1
|
26 |
+
}, {
|
27 |
+
"key": "f",
|
28 |
+
"value": "%s"
|
29 |
+
}, {
|
30 |
+
"key": "n",
|
31 |
+
"value": "%s"
|
32 |
+
}, {
|
33 |
+
"key": "wh",
|
34 |
+
"value": "%s"
|
35 |
+
}, {
|
36 |
+
"key": "enhanced_fp",
|
37 |
+
"value": [{
|
38 |
+
"key": "webgl_extensions",
|
39 |
+
"value": "%s"
|
40 |
+
}, {
|
41 |
+
"key": "webgl_extensions_hash",
|
42 |
+
"value": "%s"
|
43 |
+
}, {
|
44 |
+
"key": "webgl_renderer",
|
45 |
+
"value": "%s"
|
46 |
+
}, {
|
47 |
+
"key": "webgl_vendor",
|
48 |
+
"value": "%s"
|
49 |
+
}, {
|
50 |
+
"key": "webgl_version",
|
51 |
+
"value": "%s"
|
52 |
+
}, {
|
53 |
+
"key": "webgl_shading_language_version",
|
54 |
+
"value": "%s"
|
55 |
+
}, {
|
56 |
+
"key": "webgl_aliased_line_width_range",
|
57 |
+
"value": "%s"
|
58 |
+
}, {
|
59 |
+
"key": "webgl_aliased_point_size_range",
|
60 |
+
"value": "%s"
|
61 |
+
}, {
|
62 |
+
"key": "webgl_antialiasing",
|
63 |
+
"value": "%s"
|
64 |
+
}, {
|
65 |
+
"key": "webgl_bits",
|
66 |
+
"value": "%s"
|
67 |
+
}, {
|
68 |
+
"key": "webgl_max_params",
|
69 |
+
"value": "%s"
|
70 |
+
}, {
|
71 |
+
"key": "webgl_max_viewport_dims",
|
72 |
+
"value": "%s"
|
73 |
+
}, {
|
74 |
+
"key": "webgl_unmasked_vendor",
|
75 |
+
"value": "%s"
|
76 |
+
}, {
|
77 |
+
"key": "webgl_unmasked_renderer",
|
78 |
+
"value": "%s"
|
79 |
+
}, {
|
80 |
+
"key": "webgl_vsf_params",
|
81 |
+
"value": "%s"
|
82 |
+
}, {
|
83 |
+
"key": "webgl_vsi_params",
|
84 |
+
"value": "%s"
|
85 |
+
}, {
|
86 |
+
"key": "webgl_fsf_params",
|
87 |
+
"value": "%s"
|
88 |
+
}, {
|
89 |
+
"key": "webgl_fsi_params",
|
90 |
+
"value": "%s"
|
91 |
+
}, {
|
92 |
+
"key": "webgl_hash_webgl",
|
93 |
+
"value": "%s"
|
94 |
+
}, {
|
95 |
+
"key": "user_agent_data_brands",
|
96 |
+
"value": "Not.A/Brand,Chromium,Google Chrome"
|
97 |
+
}, {
|
98 |
+
"key": "user_agent_data_mobile",
|
99 |
+
"value": false
|
100 |
+
}, {
|
101 |
+
"key": "navigator_connection_downlink",
|
102 |
+
"value": 10.0
|
103 |
+
}, {
|
104 |
+
"key": "navigator_connection_downlink_max",
|
105 |
+
"value": null
|
106 |
+
}, {
|
107 |
+
"key": "network_info_rtt",
|
108 |
+
"value": 150
|
109 |
+
}, {
|
110 |
+
"key": "network_info_save_data",
|
111 |
+
"value": false
|
112 |
+
}, {
|
113 |
+
"key": "network_info_rtt_type",
|
114 |
+
"value": null
|
115 |
+
}, {
|
116 |
+
"key": "screen_pixel_depth",
|
117 |
+
"value": 24
|
118 |
+
}, {
|
119 |
+
"key": "navigator_device_memory",
|
120 |
+
"value": 8
|
121 |
+
}, {
|
122 |
+
"key": "navigator_languages",
|
123 |
+
"value": "zh-CN,en"
|
124 |
+
}, {
|
125 |
+
"key": "window_inner_width",
|
126 |
+
"value": 0
|
127 |
+
}, {
|
128 |
+
"key": "window_inner_height",
|
129 |
+
"value": 0
|
130 |
+
}, {
|
131 |
+
"key": "window_outer_width",
|
132 |
+
"value": 1920
|
133 |
+
}, {
|
134 |
+
"key": "window_outer_height",
|
135 |
+
"value": 1057
|
136 |
+
}, {
|
137 |
+
"key": "browser_detection_firefox",
|
138 |
+
"value": false
|
139 |
+
}, {
|
140 |
+
"key": "browser_detection_brave",
|
141 |
+
"value": false
|
142 |
+
}, {
|
143 |
+
"key": "audio_codecs",
|
144 |
+
"value": "{\"ogg\":\"probably\",\"mp3\":\"probably\",\"wav\":\"probably\",\"m4a\":\"maybe\",\"aac\":\"probably\"}"
|
145 |
+
}, {
|
146 |
+
"key": "video_codecs",
|
147 |
+
"value": "{\"ogg\":\"probably\",\"h264\":\"probably\",\"webm\":\"probably\",\"mpeg4v\":\"\",\"mpeg4a\":\"\",\"theora\":\"\"}"
|
148 |
+
}, {
|
149 |
+
"key": "media_query_dark_mode",
|
150 |
+
"value": true
|
151 |
+
}, {
|
152 |
+
"key": "headless_browser_phantom",
|
153 |
+
"value": false
|
154 |
+
}, {
|
155 |
+
"key": "headless_browser_selenium",
|
156 |
+
"value": false
|
157 |
+
}, {
|
158 |
+
"key": "headless_browser_nightmare_js",
|
159 |
+
"value": false
|
160 |
+
}, {
|
161 |
+
"key": "document__referrer",
|
162 |
+
"value": ""
|
163 |
+
}, {
|
164 |
+
"key": "window__ancestor_origins",
|
165 |
+
"value": ["https://chat.openai.com"]
|
166 |
+
}, {
|
167 |
+
"key": "window__tree_index",
|
168 |
+
"value": [2]
|
169 |
+
}, {
|
170 |
+
"key": "window__tree_structure",
|
171 |
+
"value": "[[],[],[]]"
|
172 |
+
}, {
|
173 |
+
"key": "window__location_href",
|
174 |
+
"value": "https://tcr9i.chat.openai.com/v2/%s/enforcement.%s.html#35536E1E-65B4-4D96-9D97-6ADB7EFF8147"
|
175 |
+
}, {
|
176 |
+
"key": "client_config__sitedata_location_href",
|
177 |
+
"value": "https://chat.openai.com"
|
178 |
+
}, {
|
179 |
+
"key": "client_config__surl",
|
180 |
+
"value": "https://tcr9i.chat.openai.com"
|
181 |
+
}, {
|
182 |
+
"key": "mobile_sdk__is_sdk"
|
183 |
+
}, {
|
184 |
+
"key": "client_config__language",
|
185 |
+
"value": null
|
186 |
+
}, {
|
187 |
+
"key": "navigator_battery_charging",
|
188 |
+
"value": true
|
189 |
+
}, {
|
190 |
+
"key": "audio_fingerprint",
|
191 |
+
"value": "124.04347527516074"
|
192 |
+
}]
|
193 |
+
}, {
|
194 |
+
"key": "fe",
|
195 |
+
"value": %s
|
196 |
+
}, {
|
197 |
+
"key": "ife_hash",
|
198 |
+
"value": "%s"
|
199 |
+
}, {
|
200 |
+
"key": "cs",
|
201 |
+
"value": 1
|
202 |
+
}, {
|
203 |
+
"key": "jsbd",
|
204 |
+
"value": "{\"HL\":5,\"NCE\":true,\"DT\":\"\",\"NWD\":\"false\",\"DOTO\":1,\"DMTO\":1}"
|
205 |
+
}]
|
206 |
+
`
|
207 |
+
|
208 |
+
// LinkedHashMap
|
209 |
+
var fe = []map[string]interface{}{
|
210 |
+
{"DNT": "1"},
|
211 |
+
{"L": "zh-CN"},
|
212 |
+
{"D": 24},
|
213 |
+
{"PR": 1}, // this.getPixelRatio();
|
214 |
+
{"S": "1920;1080"},
|
215 |
+
{"AS": "1920;1080"},
|
216 |
+
{"TO": -480},
|
217 |
+
{"SS": true},
|
218 |
+
{"LS": true},
|
219 |
+
{"IDB": true},
|
220 |
+
{"B": false},
|
221 |
+
{"ODB": true},
|
222 |
+
{"CPUC": "unknown"},
|
223 |
+
{"PK": "Linux x86_64"},
|
224 |
+
{"CFP": cfp},
|
225 |
+
{"FR": false},
|
226 |
+
{"FOS": false},
|
227 |
+
{"FB": false},
|
228 |
+
{"JSF": "Arial;Courier;Courier New;Helvetica;Times;Times New Roman"},
|
229 |
+
{"P": p},
|
230 |
+
{"T": "0;false;false"},
|
231 |
+
{"H": 16},
|
232 |
+
{"SWF": false},
|
233 |
+
}
|
234 |
+
|
235 |
+
const (
|
236 |
+
p = "Chrome PDF Viewer::Portable Document Format::application/pdf~pdf,text/pdf~pdf;Chromium PDF Viewer::Portable Document Format::application/pdf~pdf,text/pdf~pdf;Microsoft Edge PDF Viewer::Portable Document Format::application/pdf~pdf,text/pdf~pdf;PDF Viewer::Portable Document Format::application/pdf~pdf,text/pdf~pdf;WebKit built-in PDF::Portable Document Format::application/pdf~pdf,text/pdf~pdf"
|
237 |
+
)
|
238 |
+
|
239 |
+
const (
|
240 |
+
cfp = "canvas winding:yes~canvas fp:data:image/png;base64,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kd/P/miR4vw+1VKGAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECQyqwoQC9esx3dQu995vjBx1/b352xW5DulOL/05g6nG/zs2X79EHjiWt2+fL+lBLCQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECAy5wHoD9KqzMuXZSab33OVL/35hFp3tBnrPkH0o8LKzFmTRZ/txA31GkeKsPnSkBAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBIaFwGAB+u6tW+ije+p26ysfzJPH7thTDZP7IzD6igfy5LE79VhsRev2+fwe65hOgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBYSOwwQC96rJMeWGSD/bUcbHk2aycsEVPNUzuj8Coxc+mHN/rWXylSHFafxpShQABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAsNDoJ0AfXKSWUle2FPLN271YA55xi30nhB7nHzTlg/m0Kd7PYMnqzepFynm9NiN6QQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEBhWAoMG6FW3ZcrqPejV+9C7//zNa+blkl/s3X0BM3sWOOnV8/LPP+/1DM4qUszouRcFCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgMMwE2g3QN2vdQp/Sdf87nbs49585oev5JvYusPM5i/PAJ3s5g9mt2+fP996MCgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEBheAm0F6FXLZcqjk1zZffvzkv/dJ+klvu1+cTMXJ3npXUl6uoB+TJHiKpgECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBDYFAXaDtCrzZcp/ynJqV1DfGHsAzlj+U5dzzexe4HzxzyQjy7rxf4bRYr3dN+AmQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEBjeAp0G6BNbj3If19W2Dnjn3fnFd/bqaq5JvQm8+h1357Zvd2u/tPXo9gW9NWE2AQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEhq9ARwF6tY0y5WlJLuhqS5vf/Gienrp9RnU126RuBVYm2WrWo3nu4O27LHF6keLCLueaRoAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgUYIdByg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ECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECDQF4ERE6APpFWm3DbJgdlpzhEp7jo0y5fumz/59cqMv+eZ7H5fmT2WvyB7P7pddk1WfVWfha2veds/ll+P+W3mv6TIkj22zP/tOSpjxt2Z7HNj7p9cvcv81iLF4305JUUIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAoHaBER2g165rAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBBojIAAvTFHpVECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFNAgF6nrtoECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0BgBAXpjjkqjBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCngAC9Tl21CRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKAxAgL0xhyVRgkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgTgEBep26ahMgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAYwQE6I05Ko0SIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQJ0CAvQ6ddUmQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgcYICNAbc1QaJUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIE6BQTodeqqTYAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNERCgN+aoNEqAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECdQoI0OvUVZsAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEGiMgQG/MUWmUAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOoUEKDXqas2AQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECDRGQIDemKPSKAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUKSBAr1NXbQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBBojIAAvTFHpVECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFNAgF6nrtoECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0BgBAXpjjkqjBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCngAC9Tl21CRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKAxAgL0xhyVRgkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgTgEBep26ahMgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAYwQE6I05Ko0SIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQJ0CAvQ6ddUmQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgcYICNAbc1QaJUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIE6BQTodeqqTYAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNERCgN+aoNEqAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECdQoI0OvUVZsAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEGiMgQG/MUWmUAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOoUEKDXqas2AQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECDRGQIDemKPSKAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUKSBAr1NXbQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBBojIAAvTFHpVECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFNAgF6nrtoECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0BgBAXpjjkqjBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFCngAC9Tl21CRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKAxAgL0xhyVRgkQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgTgEBep26ahMgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAYwQE6I05Ko0SIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQJ0CAvQ6ddUmQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgcYICNAbc1QaJUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIE6BQTodeqqTYAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQKNERCgN+aoNEqAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECdQoI0OvUVZsAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEGiMgQG/MUWmUAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBOoUEKDXqas2AQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECDRGQIDemKPSKAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAjUKSBAr1NXbQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBBojIAAvTFHpVECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQqFPg/wGfCrReV85ybQAAAABJRU5ErkJggg=="
|
241 |
+
)
|
pkg/funcaptcha/crypt.go
ADDED
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"bytes"
|
5 |
+
"crypto/aes"
|
6 |
+
"crypto/cipher"
|
7 |
+
"crypto/md5"
|
8 |
+
"crypto/rand"
|
9 |
+
"encoding/base64"
|
10 |
+
"encoding/hex"
|
11 |
+
"encoding/json"
|
12 |
+
"errors"
|
13 |
+
"hash"
|
14 |
+
)
|
15 |
+
|
16 |
+
type encryptionData struct {
|
17 |
+
Ct string `json:"ct"`
|
18 |
+
Iv string `json:"iv"`
|
19 |
+
S string `json:"s"`
|
20 |
+
}
|
21 |
+
|
22 |
+
func Encrypt(data string, key string) string {
|
23 |
+
encData, _ := aesEncrypt(data, key)
|
24 |
+
|
25 |
+
encDataJson, err := json.Marshal(encData)
|
26 |
+
if err != nil {
|
27 |
+
panic(err)
|
28 |
+
}
|
29 |
+
|
30 |
+
return string(encDataJson)
|
31 |
+
}
|
32 |
+
|
33 |
+
func aesEncrypt(content string, password string) (*encryptionData, error) {
|
34 |
+
salt := make([]byte, 8)
|
35 |
+
_, err := rand.Read(salt)
|
36 |
+
if err != nil {
|
37 |
+
return nil, err
|
38 |
+
}
|
39 |
+
key, iv, err := defaultEvpKDF([]byte(password), salt)
|
40 |
+
|
41 |
+
if err != nil {
|
42 |
+
return nil, err
|
43 |
+
}
|
44 |
+
|
45 |
+
block, err := aes.NewCipher(key)
|
46 |
+
if err != nil {
|
47 |
+
return nil, err
|
48 |
+
}
|
49 |
+
|
50 |
+
mode := cipher.NewCBCEncrypter(block, iv)
|
51 |
+
cipherBytes := pKCS5Padding([]byte(content), aes.BlockSize)
|
52 |
+
mode.CryptBlocks(cipherBytes, cipherBytes)
|
53 |
+
|
54 |
+
//TODO: remove redundant code
|
55 |
+
md5Hash := md5.New()
|
56 |
+
salted := ""
|
57 |
+
var dx []byte
|
58 |
+
|
59 |
+
for i := 0; i < 3; i++ {
|
60 |
+
md5Hash.Write(dx)
|
61 |
+
md5Hash.Write([]byte(password))
|
62 |
+
md5Hash.Write(salt)
|
63 |
+
|
64 |
+
dx = md5Hash.Sum(nil)
|
65 |
+
md5Hash.Reset()
|
66 |
+
|
67 |
+
salted += hex.EncodeToString(dx)
|
68 |
+
}
|
69 |
+
|
70 |
+
cipherText := base64.StdEncoding.EncodeToString(cipherBytes)
|
71 |
+
encData := &encryptionData{
|
72 |
+
Ct: cipherText,
|
73 |
+
Iv: salted[64 : 64+32],
|
74 |
+
S: hex.EncodeToString(salt),
|
75 |
+
}
|
76 |
+
return encData, nil
|
77 |
+
}
|
78 |
+
|
79 |
+
// https://stackoverflow.com/questions/27677236/encryption-in-javascript-and-decryption-with-php/27678978#27678978
|
80 |
+
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/evpkdf.js#L55
|
81 |
+
func evpKDF(password []byte, salt []byte, keySize int, iterations int, hashAlgorithm string) ([]byte, error) {
|
82 |
+
var block []byte
|
83 |
+
var hasher hash.Hash
|
84 |
+
derivedKeyBytes := make([]byte, 0)
|
85 |
+
switch hashAlgorithm {
|
86 |
+
case "md5":
|
87 |
+
hasher = md5.New()
|
88 |
+
default:
|
89 |
+
return []byte{}, errors.New("not implement hasher algorithm")
|
90 |
+
}
|
91 |
+
for len(derivedKeyBytes) < keySize*4 {
|
92 |
+
if len(block) > 0 {
|
93 |
+
hasher.Write(block)
|
94 |
+
}
|
95 |
+
hasher.Write(password)
|
96 |
+
hasher.Write(salt)
|
97 |
+
block = hasher.Sum([]byte{})
|
98 |
+
hasher.Reset()
|
99 |
+
|
100 |
+
for i := 1; i < iterations; i++ {
|
101 |
+
hasher.Write(block)
|
102 |
+
block = hasher.Sum([]byte{})
|
103 |
+
hasher.Reset()
|
104 |
+
}
|
105 |
+
derivedKeyBytes = append(derivedKeyBytes, block...)
|
106 |
+
}
|
107 |
+
return derivedKeyBytes[:keySize*4], nil
|
108 |
+
}
|
109 |
+
|
110 |
+
func defaultEvpKDF(password []byte, salt []byte) (key []byte, iv []byte, err error) {
|
111 |
+
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/cipher-core.js#L775
|
112 |
+
keySize := 256 / 32
|
113 |
+
ivSize := 128 / 32
|
114 |
+
derivedKeyBytes, err := evpKDF(password, salt, keySize+ivSize, 1, "md5")
|
115 |
+
if err != nil {
|
116 |
+
return []byte{}, []byte{}, err
|
117 |
+
}
|
118 |
+
return derivedKeyBytes[:keySize*4], derivedKeyBytes[keySize*4:], nil
|
119 |
+
}
|
120 |
+
func pKCS5Padding(src []byte, blockSize int) []byte {
|
121 |
+
padding := blockSize - len(src)%blockSize
|
122 |
+
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
123 |
+
return append(src, padtext...)
|
124 |
+
}
|
125 |
+
|
126 |
+
func Decrypt(data string, password string, fallbackPass string) string {
|
127 |
+
decDataText, err := AesDecrypt(data, password, fallbackPass)
|
128 |
+
|
129 |
+
if err != nil {
|
130 |
+
println(err.Error())
|
131 |
+
}
|
132 |
+
|
133 |
+
return decDataText
|
134 |
+
}
|
135 |
+
|
136 |
+
func AesDecrypt(baseText string, password string, fallbackPass string) (string, error) {
|
137 |
+
encBytes, err := base64.StdEncoding.DecodeString(baseText)
|
138 |
+
if err != nil {
|
139 |
+
return "", err
|
140 |
+
}
|
141 |
+
var encData encryptionData
|
142 |
+
err = json.Unmarshal(encBytes, &encData)
|
143 |
+
if err != nil {
|
144 |
+
return "", err
|
145 |
+
}
|
146 |
+
cipherBytes, err := base64.StdEncoding.DecodeString(encData.Ct)
|
147 |
+
if err != nil {
|
148 |
+
return "", err
|
149 |
+
}
|
150 |
+
salt, err := hex.DecodeString(encData.S)
|
151 |
+
if err != nil {
|
152 |
+
return "", err
|
153 |
+
}
|
154 |
+
dstBytes := make([]byte, len(cipherBytes))
|
155 |
+
decrypt:
|
156 |
+
key, _, err := DefaultEvpKDF([]byte(password), salt)
|
157 |
+
iv, _ := hex.DecodeString(encData.Iv)
|
158 |
+
if err != nil {
|
159 |
+
return "", err
|
160 |
+
}
|
161 |
+
|
162 |
+
block, err := aes.NewCipher(key)
|
163 |
+
if err != nil {
|
164 |
+
return "", err
|
165 |
+
}
|
166 |
+
|
167 |
+
mode := cipher.NewCBCDecrypter(block, iv)
|
168 |
+
mode.CryptBlocks(dstBytes, cipherBytes)
|
169 |
+
result := PKCS5UnPadding(dstBytes)
|
170 |
+
if !json.Valid(result) {
|
171 |
+
if password == fallbackPass {
|
172 |
+
return "", errors.New("decryption can't get the correct result")
|
173 |
+
} else {
|
174 |
+
password = fallbackPass
|
175 |
+
goto decrypt
|
176 |
+
}
|
177 |
+
}
|
178 |
+
return string(result), nil
|
179 |
+
}
|
180 |
+
|
181 |
+
// https://stackoverflow.com/questions/27677236/encryption-in-javascript-and-decryption-with-php/27678978#27678978
|
182 |
+
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/evpkdf.js#L55
|
183 |
+
func EvpKDF(password []byte, salt []byte, keySize int, iterations int, hashAlgorithm string) ([]byte, error) {
|
184 |
+
var block []byte
|
185 |
+
var hasher hash.Hash
|
186 |
+
derivedKeyBytes := make([]byte, 0)
|
187 |
+
switch hashAlgorithm {
|
188 |
+
case "md5":
|
189 |
+
hasher = md5.New()
|
190 |
+
default:
|
191 |
+
return []byte{}, errors.New("not implement hasher algorithm")
|
192 |
+
}
|
193 |
+
for len(derivedKeyBytes) < keySize*4 {
|
194 |
+
if len(block) > 0 {
|
195 |
+
hasher.Write(block)
|
196 |
+
}
|
197 |
+
hasher.Write(password)
|
198 |
+
hasher.Write(salt)
|
199 |
+
block = hasher.Sum([]byte{})
|
200 |
+
hasher.Reset()
|
201 |
+
|
202 |
+
for i := 1; i < iterations; i++ {
|
203 |
+
hasher.Write(block)
|
204 |
+
block = hasher.Sum([]byte{})
|
205 |
+
hasher.Reset()
|
206 |
+
}
|
207 |
+
derivedKeyBytes = append(derivedKeyBytes, block...)
|
208 |
+
}
|
209 |
+
return derivedKeyBytes[:keySize*4], nil
|
210 |
+
}
|
211 |
+
|
212 |
+
func DefaultEvpKDF(password []byte, salt []byte) (key []byte, iv []byte, err error) {
|
213 |
+
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/cipher-core.js#L775
|
214 |
+
keySize := 256 / 32
|
215 |
+
ivSize := 128 / 32
|
216 |
+
derivedKeyBytes, err := EvpKDF(password, salt, keySize+ivSize, 1, "md5")
|
217 |
+
if err != nil {
|
218 |
+
return []byte{}, []byte{}, err
|
219 |
+
}
|
220 |
+
return derivedKeyBytes[:keySize*4], derivedKeyBytes[keySize*4:], nil
|
221 |
+
}
|
222 |
+
|
223 |
+
// https://stackoverflow.com/questions/41579325/golang-how-do-i-decrypt-with-des-cbc-and-pkcs7
|
224 |
+
func PKCS5UnPadding(src []byte) []byte {
|
225 |
+
length := len(src)
|
226 |
+
unpadding := int(src[length-1])
|
227 |
+
return src[:(length - unpadding)]
|
228 |
+
}
|
229 |
+
|
230 |
+
func PKCS5Padding(src []byte, blockSize int) []byte {
|
231 |
+
padding := blockSize - len(src)%blockSize
|
232 |
+
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
233 |
+
return append(src, padtext...)
|
234 |
+
}
|
pkg/funcaptcha/fingerprint.go
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/base64"
|
5 |
+
"encoding/json"
|
6 |
+
"fmt"
|
7 |
+
"strings"
|
8 |
+
"time"
|
9 |
+
)
|
10 |
+
|
11 |
+
func getF() string {
|
12 |
+
var res []string
|
13 |
+
for _, val := range fe {
|
14 |
+
for _, v := range val {
|
15 |
+
res = append(res, fmt.Sprintf("%v", v))
|
16 |
+
}
|
17 |
+
}
|
18 |
+
return getMurmur128String(strings.Join(res, "~~~"), 31)
|
19 |
+
}
|
20 |
+
|
21 |
+
func getN() string {
|
22 |
+
timestamp := fmt.Sprintf("%d", time.Now().UnixNano()/1000000000)
|
23 |
+
return base64.StdEncoding.EncodeToString([]byte(timestamp))
|
24 |
+
}
|
25 |
+
|
26 |
+
func getWh() string {
|
27 |
+
return fmt.Sprintf("%s|%s", getWindowHash(), getWindowProtoChainHash())
|
28 |
+
}
|
29 |
+
|
30 |
+
func getFe() string {
|
31 |
+
fe, _ := json.Marshal(getFeList())
|
32 |
+
return string(fe)
|
33 |
+
}
|
34 |
+
|
35 |
+
func getFeList() []string {
|
36 |
+
// var b6 = [];
|
37 |
+
var feList []string
|
38 |
+
for _, feMap := range fe {
|
39 |
+
for k, v := range feMap {
|
40 |
+
if k == "S" ||
|
41 |
+
k == "AS" ||
|
42 |
+
k == "JSF" ||
|
43 |
+
k == "T" {
|
44 |
+
v = strings.ReplaceAll(v.(string), ";", ",")
|
45 |
+
} else if k == "CFP" { // case dH(f_a_iI.X):
|
46 |
+
v = getCFPHash(cfp)
|
47 |
+
} else if k == "P" { // case 'P':
|
48 |
+
v = getP(p)
|
49 |
+
}
|
50 |
+
feList = append(feList, fmt.Sprintf("%v:%v", k, v))
|
51 |
+
}
|
52 |
+
}
|
53 |
+
return feList
|
54 |
+
}
|
55 |
+
|
56 |
+
func getP(p string) string {
|
57 |
+
var pList []string
|
58 |
+
for _, s := range strings.Split(p, ";") {
|
59 |
+
split := strings.Split(s, "::")
|
60 |
+
pList = append(pList, split[0])
|
61 |
+
}
|
62 |
+
return strings.Join(pList, ",")
|
63 |
+
}
|
pkg/funcaptcha/funcaptcha.go
ADDED
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/env"
|
5 |
+
"encoding/json"
|
6 |
+
http "github.com/bogdanfinn/fhttp"
|
7 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
8 |
+
"github.com/bogdanfinn/tls-client/profiles"
|
9 |
+
"net/url"
|
10 |
+
"os"
|
11 |
+
"path"
|
12 |
+
"path/filepath"
|
13 |
+
"strings"
|
14 |
+
"time"
|
15 |
+
)
|
16 |
+
|
17 |
+
type arkVer int
|
18 |
+
|
19 |
+
const (
|
20 |
+
ArkVerAuth arkVer = 0
|
21 |
+
ArkVerReg arkVer = 1
|
22 |
+
ArkVerChat3 arkVer = 3
|
23 |
+
ArkVerChat4 arkVer = 4
|
24 |
+
)
|
25 |
+
|
26 |
+
type Solver struct {
|
27 |
+
initVer string
|
28 |
+
initHex string
|
29 |
+
arks map[arkVer][]arkReq
|
30 |
+
client *tls_client.HttpClient
|
31 |
+
}
|
32 |
+
|
33 |
+
type solverArg func(*Solver)
|
34 |
+
|
35 |
+
func NewSolver(args ...solverArg) *Solver {
|
36 |
+
var (
|
37 |
+
jar = tls_client.NewCookieJar()
|
38 |
+
options = []tls_client.HttpClientOption{
|
39 |
+
tls_client.WithTimeoutSeconds(360),
|
40 |
+
tls_client.WithClientProfile(profiles.Chrome_117),
|
41 |
+
tls_client.WithRandomTLSExtensionOrder(),
|
42 |
+
tls_client.WithNotFollowRedirects(),
|
43 |
+
tls_client.WithCookieJar(jar),
|
44 |
+
}
|
45 |
+
client, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
|
46 |
+
)
|
47 |
+
s := &Solver{
|
48 |
+
arks: make(map[arkVer][]arkReq),
|
49 |
+
client: &client,
|
50 |
+
initVer: "1.5.4",
|
51 |
+
initHex: "cd12da708fe6cbe6e068918c38de2ad9",
|
52 |
+
}
|
53 |
+
for _, arg := range args {
|
54 |
+
arg(s)
|
55 |
+
}
|
56 |
+
return s
|
57 |
+
}
|
58 |
+
|
59 |
+
func WithInitVer(ver string) solverArg {
|
60 |
+
return func(s *Solver) {
|
61 |
+
s.initVer = ver
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
func WithProxy(proxy string) solverArg {
|
66 |
+
return func(s *Solver) {
|
67 |
+
(*s.client).SetProxy(proxy)
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
func WithInitHex(hex string) solverArg {
|
72 |
+
return func(s *Solver) {
|
73 |
+
s.initHex = hex
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
func WithClient(client *tls_client.HttpClient) solverArg {
|
78 |
+
return func(s *Solver) {
|
79 |
+
s.client = client
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
func WithHarData(harData HARData) solverArg {
|
84 |
+
return func(s *Solver) {
|
85 |
+
for _, v := range harData.Log.Entries {
|
86 |
+
if strings.HasPrefix(v.Request.URL, arkPreURL) || strings.HasPrefix(v.Request.URL, arkAuthPreURL) {
|
87 |
+
var tmpArk arkReq
|
88 |
+
tmpArk.arkURL = v.Request.URL
|
89 |
+
if v.StartedDateTime == "" {
|
90 |
+
println("Error: no arkose request!")
|
91 |
+
continue
|
92 |
+
}
|
93 |
+
t, _ := time.Parse(time.RFC3339, v.StartedDateTime)
|
94 |
+
bw := getBw(t.Unix())
|
95 |
+
fallbackBw := getBw(t.Unix() - 21600)
|
96 |
+
tmpArk.arkHeader = make(http.Header)
|
97 |
+
for _, h := range v.Request.Headers {
|
98 |
+
if !strings.EqualFold(h.Name, "content-length") && !strings.EqualFold(h.Name, "cookie") && !strings.HasPrefix(h.Name, ":") {
|
99 |
+
tmpArk.arkHeader.Set(h.Name, h.Value)
|
100 |
+
if strings.EqualFold(h.Name, "user-agent") {
|
101 |
+
tmpArk.userAgent = h.Value
|
102 |
+
}
|
103 |
+
}
|
104 |
+
}
|
105 |
+
tmpArk.arkCookies = []*http.Cookie{}
|
106 |
+
for _, cookie := range v.Request.Cookies {
|
107 |
+
expire, _ := time.Parse(time.RFC3339, cookie.Expires)
|
108 |
+
if expire.After(time.Now()) {
|
109 |
+
tmpArk.arkCookies = append(tmpArk.arkCookies, &http.Cookie{Name: cookie.Name, Value: cookie.Value, Expires: expire.UTC()})
|
110 |
+
}
|
111 |
+
}
|
112 |
+
var arkType string
|
113 |
+
tmpArk.arkBody = make(url.Values)
|
114 |
+
for _, p := range v.Request.PostData.Params {
|
115 |
+
if p.Name == "bda" {
|
116 |
+
cipher, err := url.QueryUnescape(p.Value)
|
117 |
+
if err != nil {
|
118 |
+
panic(err)
|
119 |
+
}
|
120 |
+
tmpArk.arkBx = Decrypt(cipher, tmpArk.userAgent+bw, tmpArk.userAgent+fallbackBw)
|
121 |
+
} else if p.Name != "rnd" {
|
122 |
+
query, err := url.QueryUnescape(p.Value)
|
123 |
+
if err != nil {
|
124 |
+
panic(err)
|
125 |
+
}
|
126 |
+
tmpArk.arkBody.Set(p.Name, query)
|
127 |
+
if p.Name == "public_key" {
|
128 |
+
if query == "0A1D34FC-659D-4E23-B17B-694DCFCF6A6C" {
|
129 |
+
arkType = "auth"
|
130 |
+
s.arks[ArkVerAuth] = append(s.arks[ArkVerAuth], tmpArk)
|
131 |
+
} else if query == "3D86FBBA-9D22-402A-B512-3420086BA6CC" {
|
132 |
+
arkType = "chat3"
|
133 |
+
s.arks[ArkVerChat3] = append(s.arks[ArkVerChat3], tmpArk)
|
134 |
+
} else if query == "35536E1E-65B4-4D96-9D97-6ADB7EFF8147" {
|
135 |
+
arkType = "chat4"
|
136 |
+
s.arks[ArkVerChat4] = append(s.arks[ArkVerChat4], tmpArk)
|
137 |
+
} else if query == "0655BC92-82E1-43D9-B32E-9DF9B01AF50C" {
|
138 |
+
arkType = "reg"
|
139 |
+
s.arks[ArkVerReg] = append(s.arks[ArkVerReg], tmpArk)
|
140 |
+
}
|
141 |
+
}
|
142 |
+
}
|
143 |
+
}
|
144 |
+
if tmpArk.arkBx != "" {
|
145 |
+
println("success read " + arkType + " arkose")
|
146 |
+
} else {
|
147 |
+
println("failed to decrypt HAR file")
|
148 |
+
}
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
func WithHarpool(s *Solver) {
|
156 |
+
dirPath := path.Join(path.Dir(env.EnvFile), "harPool")
|
157 |
+
var harPath []string
|
158 |
+
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
159 |
+
if err != nil {
|
160 |
+
return err
|
161 |
+
}
|
162 |
+
if !info.IsDir() {
|
163 |
+
ext := filepath.Ext(info.Name())
|
164 |
+
if ext == ".har" {
|
165 |
+
harPath = append(harPath, path)
|
166 |
+
}
|
167 |
+
}
|
168 |
+
return nil
|
169 |
+
})
|
170 |
+
if err != nil {
|
171 |
+
println("Error: please put HAR files in harPool directory!")
|
172 |
+
}
|
173 |
+
for _, path := range harPath {
|
174 |
+
file, err := os.ReadFile(path)
|
175 |
+
if err != nil {
|
176 |
+
return
|
177 |
+
}
|
178 |
+
var harFile HARData
|
179 |
+
err = json.Unmarshal(file, &harFile)
|
180 |
+
if err != nil {
|
181 |
+
println("Error: not a HAR file!")
|
182 |
+
return
|
183 |
+
}
|
184 |
+
WithHarData(harFile)(s)
|
185 |
+
}
|
186 |
+
|
187 |
+
}
|
pkg/funcaptcha/get.go
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/env"
|
5 |
+
"WarpGPT/pkg/logger"
|
6 |
+
"WarpGPT/pkg/plugins/service/proxypool"
|
7 |
+
)
|
8 |
+
|
9 |
+
func GetOpenAIArkoseToken(arkType int, puid string) (string, error) {
|
10 |
+
logger.Log.Debug("GetArkoseToken")
|
11 |
+
var proxyArg solverArg
|
12 |
+
if env.E.ProxyPoolUrl != "" {
|
13 |
+
ip, err := proxypool.ProxyPoolInstance.GetIpInRedis()
|
14 |
+
if err != nil {
|
15 |
+
logger.Log.Warning(err.Error())
|
16 |
+
return "", nil
|
17 |
+
}
|
18 |
+
proxyArg = WithProxy(ip)
|
19 |
+
} else {
|
20 |
+
proxyArg = WithProxy(env.E.Proxy)
|
21 |
+
}
|
22 |
+
|
23 |
+
solver := NewSolver(proxyArg)
|
24 |
+
WithHarpool(solver)
|
25 |
+
token, err := solver.GetOpenAIToken(arkVer(arkType), puid)
|
26 |
+
if err != nil {
|
27 |
+
logger.Log.Warning(err)
|
28 |
+
return "", err
|
29 |
+
}
|
30 |
+
return token, nil
|
31 |
+
}
|
pkg/funcaptcha/hashing.go
ADDED
@@ -0,0 +1,1380 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// https://github.com/fingerprintjs/fingerprintjs/blob/master/src/utils/hashing.ts
|
2 |
+
|
3 |
+
package funcaptcha
|
4 |
+
|
5 |
+
import (
|
6 |
+
"fmt"
|
7 |
+
"sort"
|
8 |
+
"strings"
|
9 |
+
)
|
10 |
+
|
11 |
+
//goland:noinspection SpellCheckingInspection
|
12 |
+
func getWindowHash() string { // return aA(b1[df(f_a_hT.e)]()[df(f_a_hT.f)]('|'), 0x1a4);
|
13 |
+
// Object.getOwnPropertyNames(window);
|
14 |
+
b1 := []string{
|
15 |
+
"ALFCCJS",
|
16 |
+
"AbortController",
|
17 |
+
"AbortSignal",
|
18 |
+
"AbsoluteOrientationSensor",
|
19 |
+
"AbstractRange",
|
20 |
+
"Accelerometer",
|
21 |
+
"AggregateError",
|
22 |
+
"AnalyserNode",
|
23 |
+
"Animation",
|
24 |
+
"AnimationEffect",
|
25 |
+
"AnimationEvent",
|
26 |
+
"AnimationPlaybackEvent",
|
27 |
+
"AnimationTimeline",
|
28 |
+
"ArkoseEnforcement",
|
29 |
+
"Array",
|
30 |
+
"ArrayBuffer",
|
31 |
+
"Atomics",
|
32 |
+
"Attr",
|
33 |
+
"Audio",
|
34 |
+
"AudioBuffer",
|
35 |
+
"AudioBufferSourceNode",
|
36 |
+
"AudioContext",
|
37 |
+
"AudioData",
|
38 |
+
"AudioDecoder",
|
39 |
+
"AudioDestinationNode",
|
40 |
+
"AudioEncoder",
|
41 |
+
"AudioListener",
|
42 |
+
"AudioNode",
|
43 |
+
"AudioParam",
|
44 |
+
"AudioParamMap",
|
45 |
+
"AudioProcessingEvent",
|
46 |
+
"AudioScheduledSourceNode",
|
47 |
+
"AudioSinkInfo",
|
48 |
+
"AudioWorklet",
|
49 |
+
"AudioWorkletNode",
|
50 |
+
"AuthenticatorAssertionResponse",
|
51 |
+
"AuthenticatorAttestationResponse",
|
52 |
+
"AuthenticatorResponse",
|
53 |
+
"BackgroundFetchManager",
|
54 |
+
"BackgroundFetchRecord",
|
55 |
+
"BackgroundFetchRegistration",
|
56 |
+
"BarProp",
|
57 |
+
"BaseAudioContext",
|
58 |
+
"BatteryManager",
|
59 |
+
"BeforeInstallPromptEvent",
|
60 |
+
"BeforeUnloadEvent",
|
61 |
+
"BigInt",
|
62 |
+
"BigInt64Array",
|
63 |
+
"BigUint64Array",
|
64 |
+
"BiquadFilterNode",
|
65 |
+
"Blob",
|
66 |
+
"BlobEvent",
|
67 |
+
"Boolean",
|
68 |
+
"BroadcastChannel",
|
69 |
+
"BrowserCaptureMediaStreamTrack",
|
70 |
+
"ByteLengthQueuingStrategy",
|
71 |
+
"CDATASection",
|
72 |
+
"CSS",
|
73 |
+
"CSSAnimation",
|
74 |
+
"CSSConditionRule",
|
75 |
+
"CSSContainerRule",
|
76 |
+
"CSSCounterStyleRule",
|
77 |
+
"CSSFontFaceRule",
|
78 |
+
"CSSFontPaletteValuesRule",
|
79 |
+
"CSSGroupingRule",
|
80 |
+
"CSSImageValue",
|
81 |
+
"CSSImportRule",
|
82 |
+
"CSSKeyframeRule",
|
83 |
+
"CSSKeyframesRule",
|
84 |
+
"CSSKeywordValue",
|
85 |
+
"CSSLayerBlockRule",
|
86 |
+
"CSSLayerStatementRule",
|
87 |
+
"CSSMathClamp",
|
88 |
+
"CSSMathInvert",
|
89 |
+
"CSSMathMax",
|
90 |
+
"CSSMathMin",
|
91 |
+
"CSSMathNegate",
|
92 |
+
"CSSMathProduct",
|
93 |
+
"CSSMathSum",
|
94 |
+
"CSSMathValue",
|
95 |
+
"CSSMatrixComponent",
|
96 |
+
"CSSMediaRule",
|
97 |
+
"CSSNamespaceRule",
|
98 |
+
"CSSNumericArray",
|
99 |
+
"CSSNumericValue",
|
100 |
+
"CSSPageRule",
|
101 |
+
"CSSPerspective",
|
102 |
+
"CSSPositionValue",
|
103 |
+
"CSSPropertyRule",
|
104 |
+
"CSSRotate",
|
105 |
+
"CSSRule",
|
106 |
+
"CSSRuleList",
|
107 |
+
"CSSScale",
|
108 |
+
"CSSSkew",
|
109 |
+
"CSSSkewX",
|
110 |
+
"CSSSkewY",
|
111 |
+
"CSSStyleDeclaration",
|
112 |
+
"CSSStyleRule",
|
113 |
+
"CSSStyleSheet",
|
114 |
+
"CSSStyleValue",
|
115 |
+
"CSSSupportsRule",
|
116 |
+
"CSSTransformComponent",
|
117 |
+
"CSSTransformValue",
|
118 |
+
"CSSTransition",
|
119 |
+
"CSSTranslate",
|
120 |
+
"CSSUnitValue",
|
121 |
+
"CSSUnparsedValue",
|
122 |
+
"CSSVariableReferenceValue",
|
123 |
+
"Cache",
|
124 |
+
"CacheStorage",
|
125 |
+
"CanvasCaptureMediaStreamTrack",
|
126 |
+
"CanvasGradient",
|
127 |
+
"CanvasPattern",
|
128 |
+
"CanvasRenderingContext2D",
|
129 |
+
"CaptureController",
|
130 |
+
"ChannelMergerNode",
|
131 |
+
"ChannelSplitterNode",
|
132 |
+
"CharacterData",
|
133 |
+
"Clipboard",
|
134 |
+
"ClipboardEvent",
|
135 |
+
"ClipboardItem",
|
136 |
+
"CloseEvent",
|
137 |
+
"Comment",
|
138 |
+
"CompositionEvent",
|
139 |
+
"CompressionStream",
|
140 |
+
"ConstantSourceNode",
|
141 |
+
"ContentVisibilityAutoStateChangeEvent",
|
142 |
+
"ConvolverNode",
|
143 |
+
"CookieChangeEvent",
|
144 |
+
"CookieStore",
|
145 |
+
"CookieStoreManager",
|
146 |
+
"CountQueuingStrategy",
|
147 |
+
"Credential",
|
148 |
+
"CredentialsContainer",
|
149 |
+
"CropTarget",
|
150 |
+
"Crypto",
|
151 |
+
"CryptoKey",
|
152 |
+
"CustomElementRegistry",
|
153 |
+
"CustomEvent",
|
154 |
+
"CustomStateSet",
|
155 |
+
"DOMError",
|
156 |
+
"DOMException",
|
157 |
+
"DOMImplementation",
|
158 |
+
"DOMMatrix",
|
159 |
+
"DOMMatrixReadOnly",
|
160 |
+
"DOMParser",
|
161 |
+
"DOMPoint",
|
162 |
+
"DOMPointReadOnly",
|
163 |
+
"DOMQuad",
|
164 |
+
"DOMRect",
|
165 |
+
"DOMRectList",
|
166 |
+
"DOMRectReadOnly",
|
167 |
+
"DOMStringList",
|
168 |
+
"DOMStringMap",
|
169 |
+
"DOMTokenList",
|
170 |
+
"DataTransfer",
|
171 |
+
"DataTransferItem",
|
172 |
+
"DataTransferItemList",
|
173 |
+
"DataView",
|
174 |
+
"Date",
|
175 |
+
"DecompressionStream",
|
176 |
+
"DelayNode",
|
177 |
+
"DelegatedInkTrailPresenter",
|
178 |
+
"DeviceMotionEvent",
|
179 |
+
"DeviceMotionEventAcceleration",
|
180 |
+
"DeviceMotionEventRotationRate",
|
181 |
+
"DeviceOrientationEvent",
|
182 |
+
"Document",
|
183 |
+
"DocumentFragment",
|
184 |
+
"DocumentTimeline",
|
185 |
+
"DocumentType",
|
186 |
+
"DragEvent",
|
187 |
+
"DynamicsCompressorNode",
|
188 |
+
"Element",
|
189 |
+
"ElementInternals",
|
190 |
+
"EncodedAudioChunk",
|
191 |
+
"EncodedVideoChunk",
|
192 |
+
"Error",
|
193 |
+
"ErrorEvent",
|
194 |
+
"EvalError",
|
195 |
+
"Event",
|
196 |
+
"EventCounts",
|
197 |
+
"EventSource",
|
198 |
+
"EventTarget",
|
199 |
+
"External",
|
200 |
+
"EyeDropper",
|
201 |
+
"FeaturePolicy",
|
202 |
+
"FederatedCredential",
|
203 |
+
"File",
|
204 |
+
"FileList",
|
205 |
+
"FileReader",
|
206 |
+
"FileSystemDirectoryHandle",
|
207 |
+
"FileSystemFileHandle",
|
208 |
+
"FileSystemHandle",
|
209 |
+
"FileSystemWritableFileStream",
|
210 |
+
"FinalizationRegistry",
|
211 |
+
"Float32Array",
|
212 |
+
"Float64Array",
|
213 |
+
"FocusEvent",
|
214 |
+
"FontData",
|
215 |
+
"FontFace",
|
216 |
+
"FontFaceSetLoadEvent",
|
217 |
+
"FormData",
|
218 |
+
"FormDataEvent",
|
219 |
+
"FragmentDirective",
|
220 |
+
"FunCaptcha",
|
221 |
+
"Function",
|
222 |
+
"GPU",
|
223 |
+
"GPUAdapter",
|
224 |
+
"GPUAdapterInfo",
|
225 |
+
"GPUBindGroup",
|
226 |
+
"GPUBindGroupLayout",
|
227 |
+
"GPUBuffer",
|
228 |
+
"GPUBufferUsage",
|
229 |
+
"GPUCanvasContext",
|
230 |
+
"GPUColorWrite",
|
231 |
+
"GPUCommandBuffer",
|
232 |
+
"GPUCommandEncoder",
|
233 |
+
"GPUCompilationInfo",
|
234 |
+
"GPUCompilationMessage",
|
235 |
+
"GPUComputePassEncoder",
|
236 |
+
"GPUComputePipeline",
|
237 |
+
"GPUDevice",
|
238 |
+
"GPUDeviceLostInfo",
|
239 |
+
"GPUError",
|
240 |
+
"GPUExternalTexture",
|
241 |
+
"GPUInternalError",
|
242 |
+
"GPUMapMode",
|
243 |
+
"GPUOutOfMemoryError",
|
244 |
+
"GPUPipelineError",
|
245 |
+
"GPUPipelineLayout",
|
246 |
+
"GPUQuerySet",
|
247 |
+
"GPUQueue",
|
248 |
+
"GPURenderBundle",
|
249 |
+
"GPURenderBundleEncoder",
|
250 |
+
"GPURenderPassEncoder",
|
251 |
+
"GPURenderPipeline",
|
252 |
+
"GPUSampler",
|
253 |
+
"GPUShaderModule",
|
254 |
+
"GPUShaderStage",
|
255 |
+
"GPUSupportedFeatures",
|
256 |
+
"GPUSupportedLimits",
|
257 |
+
"GPUTexture",
|
258 |
+
"GPUTextureUsage",
|
259 |
+
"GPUTextureView",
|
260 |
+
"GPUUncapturedErrorEvent",
|
261 |
+
"GPUValidationError",
|
262 |
+
"GainNode",
|
263 |
+
"Gamepad",
|
264 |
+
"GamepadButton",
|
265 |
+
"GamepadEvent",
|
266 |
+
"GamepadHapticActuator",
|
267 |
+
"Geolocation",
|
268 |
+
"GeolocationCoordinates",
|
269 |
+
"GeolocationPosition",
|
270 |
+
"GeolocationPositionError",
|
271 |
+
"GravitySensor",
|
272 |
+
"Gyroscope",
|
273 |
+
"HID",
|
274 |
+
"HIDConnectionEvent",
|
275 |
+
"HIDDevice",
|
276 |
+
"HIDInputReportEvent",
|
277 |
+
"HTMLAllCollection",
|
278 |
+
"HTMLAnchorElement",
|
279 |
+
"HTMLAreaElement",
|
280 |
+
"HTMLAudioElement",
|
281 |
+
"HTMLBRElement",
|
282 |
+
"HTMLBaseElement",
|
283 |
+
"HTMLBodyElement",
|
284 |
+
"HTMLButtonElement",
|
285 |
+
"HTMLCanvasElement",
|
286 |
+
"HTMLCollection",
|
287 |
+
"HTMLDListElement",
|
288 |
+
"HTMLDataElement",
|
289 |
+
"HTMLDataListElement",
|
290 |
+
"HTMLDetailsElement",
|
291 |
+
"HTMLDialogElement",
|
292 |
+
"HTMLDirectoryElement",
|
293 |
+
"HTMLDivElement",
|
294 |
+
"HTMLDocument",
|
295 |
+
"HTMLElement",
|
296 |
+
"HTMLEmbedElement",
|
297 |
+
"HTMLFieldSetElement",
|
298 |
+
"HTMLFontElement",
|
299 |
+
"HTMLFormControlsCollection",
|
300 |
+
"HTMLFormElement",
|
301 |
+
"HTMLFrameElement",
|
302 |
+
"HTMLFrameSetElement",
|
303 |
+
"HTMLHRElement",
|
304 |
+
"HTMLHeadElement",
|
305 |
+
"HTMLHeadingElement",
|
306 |
+
"HTMLHtmlElement",
|
307 |
+
"HTMLIFrameElement",
|
308 |
+
"HTMLImageElement",
|
309 |
+
"HTMLInputElement",
|
310 |
+
"HTMLLIElement",
|
311 |
+
"HTMLLabelElement",
|
312 |
+
"HTMLLegendElement",
|
313 |
+
"HTMLLinkElement",
|
314 |
+
"HTMLMapElement",
|
315 |
+
"HTMLMarqueeElement",
|
316 |
+
"HTMLMediaElement",
|
317 |
+
"HTMLMenuElement",
|
318 |
+
"HTMLMetaElement",
|
319 |
+
"HTMLMeterElement",
|
320 |
+
"HTMLModElement",
|
321 |
+
"HTMLOListElement",
|
322 |
+
"HTMLObjectElement",
|
323 |
+
"HTMLOptGroupElement",
|
324 |
+
"HTMLOptionElement",
|
325 |
+
"HTMLOptionsCollection",
|
326 |
+
"HTMLOutputElement",
|
327 |
+
"HTMLParagraphElement",
|
328 |
+
"HTMLParamElement",
|
329 |
+
"HTMLPictureElement",
|
330 |
+
"HTMLPreElement",
|
331 |
+
"HTMLProgressElement",
|
332 |
+
"HTMLQuoteElement",
|
333 |
+
"HTMLScriptElement",
|
334 |
+
"HTMLSelectElement",
|
335 |
+
"HTMLSlotElement",
|
336 |
+
"HTMLSourceElement",
|
337 |
+
"HTMLSpanElement",
|
338 |
+
"HTMLStyleElement",
|
339 |
+
"HTMLTableCaptionElement",
|
340 |
+
"HTMLTableCellElement",
|
341 |
+
"HTMLTableColElement",
|
342 |
+
"HTMLTableElement",
|
343 |
+
"HTMLTableRowElement",
|
344 |
+
"HTMLTableSectionElement",
|
345 |
+
"HTMLTemplateElement",
|
346 |
+
"HTMLTextAreaElement",
|
347 |
+
"HTMLTimeElement",
|
348 |
+
"HTMLTitleElement",
|
349 |
+
"HTMLTrackElement",
|
350 |
+
"HTMLUListElement",
|
351 |
+
"HTMLUnknownElement",
|
352 |
+
"HTMLVideoElement",
|
353 |
+
"HashChangeEvent",
|
354 |
+
"Headers",
|
355 |
+
"Highlight",
|
356 |
+
"HighlightRegistry",
|
357 |
+
"History",
|
358 |
+
"IDBCursor",
|
359 |
+
"IDBCursorWithValue",
|
360 |
+
"IDBDatabase",
|
361 |
+
"IDBFactory",
|
362 |
+
"IDBIndex",
|
363 |
+
"IDBKeyRange",
|
364 |
+
"IDBObjectStore",
|
365 |
+
"IDBOpenDBRequest",
|
366 |
+
"IDBRequest",
|
367 |
+
"IDBTransaction",
|
368 |
+
"IDBVersionChangeEvent",
|
369 |
+
"IIRFilterNode",
|
370 |
+
"IdentityCredential",
|
371 |
+
"IdleDeadline",
|
372 |
+
"IdleDetector",
|
373 |
+
"Image",
|
374 |
+
"ImageBitmap",
|
375 |
+
"ImageBitmapRenderingContext",
|
376 |
+
"ImageCapture",
|
377 |
+
"ImageData",
|
378 |
+
"ImageDecoder",
|
379 |
+
"ImageTrack",
|
380 |
+
"ImageTrackList",
|
381 |
+
"Infinity",
|
382 |
+
"Ink",
|
383 |
+
"InputDeviceCapabilities",
|
384 |
+
"InputDeviceInfo",
|
385 |
+
"InputEvent",
|
386 |
+
"Int16Array",
|
387 |
+
"Int32Array",
|
388 |
+
"Int8Array",
|
389 |
+
"IntersectionObserver",
|
390 |
+
"IntersectionObserverEntry",
|
391 |
+
"Intl",
|
392 |
+
"JSON",
|
393 |
+
"Keyboard",
|
394 |
+
"KeyboardEvent",
|
395 |
+
"KeyboardLayoutMap",
|
396 |
+
"KeyframeEffect",
|
397 |
+
"LargestContentfulPaint",
|
398 |
+
"LaunchParams",
|
399 |
+
"LaunchQueue",
|
400 |
+
"LayoutShift",
|
401 |
+
"LayoutShiftAttribution",
|
402 |
+
"LinearAccelerationSensor",
|
403 |
+
"Location",
|
404 |
+
"Lock",
|
405 |
+
"LockManager",
|
406 |
+
"MIDIAccess",
|
407 |
+
"MIDIConnectionEvent",
|
408 |
+
"MIDIInput",
|
409 |
+
"MIDIInputMap",
|
410 |
+
"MIDIMessageEvent",
|
411 |
+
"MIDIOutput",
|
412 |
+
"MIDIOutputMap",
|
413 |
+
"MIDIPort",
|
414 |
+
"Map",
|
415 |
+
"Math",
|
416 |
+
"MathMLElement",
|
417 |
+
"MediaCapabilities",
|
418 |
+
"MediaDeviceInfo",
|
419 |
+
"MediaDevices",
|
420 |
+
"MediaElementAudioSourceNode",
|
421 |
+
"MediaEncryptedEvent",
|
422 |
+
"MediaError",
|
423 |
+
"MediaKeyMessageEvent",
|
424 |
+
"MediaKeySession",
|
425 |
+
"MediaKeyStatusMap",
|
426 |
+
"MediaKeySystemAccess",
|
427 |
+
"MediaKeys",
|
428 |
+
"MediaList",
|
429 |
+
"MediaMetadata",
|
430 |
+
"MediaQueryList",
|
431 |
+
"MediaQueryListEvent",
|
432 |
+
"MediaRecorder",
|
433 |
+
"MediaSession",
|
434 |
+
"MediaSource",
|
435 |
+
"MediaSourceHandle",
|
436 |
+
"MediaStream",
|
437 |
+
"MediaStreamAudioDestinationNode",
|
438 |
+
"MediaStreamAudioSourceNode",
|
439 |
+
"MediaStreamEvent",
|
440 |
+
"MediaStreamTrack",
|
441 |
+
"MediaStreamTrackEvent",
|
442 |
+
"MediaStreamTrackGenerator",
|
443 |
+
"MediaStreamTrackProcessor",
|
444 |
+
"MessageChannel",
|
445 |
+
"MessageEvent",
|
446 |
+
"MessagePort",
|
447 |
+
"MimeType",
|
448 |
+
"MimeTypeArray",
|
449 |
+
"MouseEvent",
|
450 |
+
"MutationEvent",
|
451 |
+
"MutationObserver",
|
452 |
+
"MutationRecord",
|
453 |
+
"NaN",
|
454 |
+
"NamedNodeMap",
|
455 |
+
"NavigateEvent",
|
456 |
+
"Navigation",
|
457 |
+
"NavigationCurrentEntryChangeEvent",
|
458 |
+
"NavigationDestination",
|
459 |
+
"NavigationHistoryEntry",
|
460 |
+
"NavigationPreloadManager",
|
461 |
+
"NavigationTransition",
|
462 |
+
"Navigator",
|
463 |
+
"NavigatorManagedData",
|
464 |
+
"NavigatorUAData",
|
465 |
+
"NetworkInformation",
|
466 |
+
"Node",
|
467 |
+
"NodeFilter",
|
468 |
+
"NodeIterator",
|
469 |
+
"NodeList",
|
470 |
+
"Notification",
|
471 |
+
"Number",
|
472 |
+
"OTPCredential",
|
473 |
+
"Object",
|
474 |
+
"OfflineAudioCompletionEvent",
|
475 |
+
"OfflineAudioContext",
|
476 |
+
"OffscreenCanvas",
|
477 |
+
"OffscreenCanvasRenderingContext2D",
|
478 |
+
"Option",
|
479 |
+
"OrientationSensor",
|
480 |
+
"OscillatorNode",
|
481 |
+
"OverconstrainedError",
|
482 |
+
"PageTransitionEvent",
|
483 |
+
"PannerNode",
|
484 |
+
"PasswordCredential",
|
485 |
+
"Path2D",
|
486 |
+
"PaymentAddress",
|
487 |
+
"PaymentManager",
|
488 |
+
"PaymentMethodChangeEvent",
|
489 |
+
"PaymentRequest",
|
490 |
+
"PaymentRequestUpdateEvent",
|
491 |
+
"PaymentResponse",
|
492 |
+
"Performance",
|
493 |
+
"PerformanceElementTiming",
|
494 |
+
"PerformanceEntry",
|
495 |
+
"PerformanceEventTiming",
|
496 |
+
"PerformanceLongTaskTiming",
|
497 |
+
"PerformanceMark",
|
498 |
+
"PerformanceMeasure",
|
499 |
+
"PerformanceNavigation",
|
500 |
+
"PerformanceNavigationTiming",
|
501 |
+
"PerformanceObserver",
|
502 |
+
"PerformanceObserverEntryList",
|
503 |
+
"PerformancePaintTiming",
|
504 |
+
"PerformanceResourceTiming",
|
505 |
+
"PerformanceServerTiming",
|
506 |
+
"PerformanceTiming",
|
507 |
+
"PeriodicSyncManager",
|
508 |
+
"PeriodicWave",
|
509 |
+
"PermissionStatus",
|
510 |
+
"Permissions",
|
511 |
+
"PictureInPictureEvent",
|
512 |
+
"PictureInPictureWindow",
|
513 |
+
"Plugin",
|
514 |
+
"PluginArray",
|
515 |
+
"PointerEvent",
|
516 |
+
"PopStateEvent",
|
517 |
+
"Presentation",
|
518 |
+
"PresentationAvailability",
|
519 |
+
"PresentationConnection",
|
520 |
+
"PresentationConnectionAvailableEvent",
|
521 |
+
"PresentationConnectionCloseEvent",
|
522 |
+
"PresentationConnectionList",
|
523 |
+
"PresentationReceiver",
|
524 |
+
"PresentationRequest",
|
525 |
+
"ProcessingInstruction",
|
526 |
+
"Profiler",
|
527 |
+
"ProgressEvent",
|
528 |
+
"Promise",
|
529 |
+
"PromiseRejectionEvent",
|
530 |
+
"Proxy",
|
531 |
+
"PublicKeyCredential",
|
532 |
+
"PushManager",
|
533 |
+
"PushSubscription",
|
534 |
+
"PushSubscriptionOptions",
|
535 |
+
"RTCCertificate",
|
536 |
+
"RTCDTMFSender",
|
537 |
+
"RTCDTMFToneChangeEvent",
|
538 |
+
"RTCDataChannel",
|
539 |
+
"RTCDataChannelEvent",
|
540 |
+
"RTCDtlsTransport",
|
541 |
+
"RTCEncodedAudioFrame",
|
542 |
+
"RTCEncodedVideoFrame",
|
543 |
+
"RTCError",
|
544 |
+
"RTCErrorEvent",
|
545 |
+
"RTCIceCandidate",
|
546 |
+
"RTCIceTransport",
|
547 |
+
"RTCPeerConnection",
|
548 |
+
"RTCPeerConnectionIceErrorEvent",
|
549 |
+
"RTCPeerConnectionIceEvent",
|
550 |
+
"RTCRtpReceiver",
|
551 |
+
"RTCRtpSender",
|
552 |
+
"RTCRtpTransceiver",
|
553 |
+
"RTCSctpTransport",
|
554 |
+
"RTCSessionDescription",
|
555 |
+
"RTCStatsReport",
|
556 |
+
"RTCTrackEvent",
|
557 |
+
"RadioNodeList",
|
558 |
+
"Range",
|
559 |
+
"RangeError",
|
560 |
+
"ReadableByteStreamController",
|
561 |
+
"ReadableStream",
|
562 |
+
"ReadableStreamBYOBReader",
|
563 |
+
"ReadableStreamBYOBRequest",
|
564 |
+
"ReadableStreamDefaultController",
|
565 |
+
"ReadableStreamDefaultReader",
|
566 |
+
"ReferenceError",
|
567 |
+
"Reflect",
|
568 |
+
"RegExp",
|
569 |
+
"RelativeOrientationSensor",
|
570 |
+
"RemotePlayback",
|
571 |
+
"ReportingObserver",
|
572 |
+
"Request",
|
573 |
+
"ResizeObserver",
|
574 |
+
"ResizeObserverEntry",
|
575 |
+
"ResizeObserverSize",
|
576 |
+
"Response",
|
577 |
+
"SVGAElement",
|
578 |
+
"SVGAngle",
|
579 |
+
"SVGAnimateElement",
|
580 |
+
"SVGAnimateMotionElement",
|
581 |
+
"SVGAnimateTransformElement",
|
582 |
+
"SVGAnimatedAngle",
|
583 |
+
"SVGAnimatedBoolean",
|
584 |
+
"SVGAnimatedEnumeration",
|
585 |
+
"SVGAnimatedInteger",
|
586 |
+
"SVGAnimatedLength",
|
587 |
+
"SVGAnimatedLengthList",
|
588 |
+
"SVGAnimatedNumber",
|
589 |
+
"SVGAnimatedNumberList",
|
590 |
+
"SVGAnimatedPreserveAspectRatio",
|
591 |
+
"SVGAnimatedRect",
|
592 |
+
"SVGAnimatedString",
|
593 |
+
"SVGAnimatedTransformList",
|
594 |
+
"SVGAnimationElement",
|
595 |
+
"SVGCircleElement",
|
596 |
+
"SVGClipPathElement",
|
597 |
+
"SVGComponentTransferFunctionElement",
|
598 |
+
"SVGDefsElement",
|
599 |
+
"SVGDescElement",
|
600 |
+
"SVGElement",
|
601 |
+
"SVGEllipseElement",
|
602 |
+
"SVGFEBlendElement",
|
603 |
+
"SVGFEColorMatrixElement",
|
604 |
+
"SVGFEComponentTransferElement",
|
605 |
+
"SVGFECompositeElement",
|
606 |
+
"SVGFEConvolveMatrixElement",
|
607 |
+
"SVGFEDiffuseLightingElement",
|
608 |
+
"SVGFEDisplacementMapElement",
|
609 |
+
"SVGFEDistantLightElement",
|
610 |
+
"SVGFEDropShadowElement",
|
611 |
+
"SVGFEFloodElement",
|
612 |
+
"SVGFEFuncAElement",
|
613 |
+
"SVGFEFuncBElement",
|
614 |
+
"SVGFEFuncGElement",
|
615 |
+
"SVGFEFuncRElement",
|
616 |
+
"SVGFEGaussianBlurElement",
|
617 |
+
"SVGFEImageElement",
|
618 |
+
"SVGFEMergeElement",
|
619 |
+
"SVGFEMergeNodeElement",
|
620 |
+
"SVGFEMorphologyElement",
|
621 |
+
"SVGFEOffsetElement",
|
622 |
+
"SVGFEPointLightElement",
|
623 |
+
"SVGFESpecularLightingElement",
|
624 |
+
"SVGFESpotLightElement",
|
625 |
+
"SVGFETileElement",
|
626 |
+
"SVGFETurbulenceElement",
|
627 |
+
"SVGFilterElement",
|
628 |
+
"SVGForeignObjectElement",
|
629 |
+
"SVGGElement",
|
630 |
+
"SVGGeometryElement",
|
631 |
+
"SVGGradientElement",
|
632 |
+
"SVGGraphicsElement",
|
633 |
+
"SVGImageElement",
|
634 |
+
"SVGLength",
|
635 |
+
"SVGLengthList",
|
636 |
+
"SVGLineElement",
|
637 |
+
"SVGLinearGradientElement",
|
638 |
+
"SVGMPathElement",
|
639 |
+
"SVGMarkerElement",
|
640 |
+
"SVGMaskElement",
|
641 |
+
"SVGMatrix",
|
642 |
+
"SVGMetadataElement",
|
643 |
+
"SVGNumber",
|
644 |
+
"SVGNumberList",
|
645 |
+
"SVGPathElement",
|
646 |
+
"SVGPatternElement",
|
647 |
+
"SVGPoint",
|
648 |
+
"SVGPointList",
|
649 |
+
"SVGPolygonElement",
|
650 |
+
"SVGPolylineElement",
|
651 |
+
"SVGPreserveAspectRatio",
|
652 |
+
"SVGRadialGradientElement",
|
653 |
+
"SVGRect",
|
654 |
+
"SVGRectElement",
|
655 |
+
"SVGSVGElement",
|
656 |
+
"SVGScriptElement",
|
657 |
+
"SVGSetElement",
|
658 |
+
"SVGStopElement",
|
659 |
+
"SVGStringList",
|
660 |
+
"SVGStyleElement",
|
661 |
+
"SVGSwitchElement",
|
662 |
+
"SVGSymbolElement",
|
663 |
+
"SVGTSpanElement",
|
664 |
+
"SVGTextContentElement",
|
665 |
+
"SVGTextElement",
|
666 |
+
"SVGTextPathElement",
|
667 |
+
"SVGTextPositioningElement",
|
668 |
+
"SVGTitleElement",
|
669 |
+
"SVGTransform",
|
670 |
+
"SVGTransformList",
|
671 |
+
"SVGUnitTypes",
|
672 |
+
"SVGUseElement",
|
673 |
+
"SVGViewElement",
|
674 |
+
"Sanitizer",
|
675 |
+
"Scheduler",
|
676 |
+
"Scheduling",
|
677 |
+
"Screen",
|
678 |
+
"ScreenDetailed",
|
679 |
+
"ScreenDetails",
|
680 |
+
"ScreenOrientation",
|
681 |
+
"ScriptProcessorNode",
|
682 |
+
"SecurityPolicyViolationEvent",
|
683 |
+
"Selection",
|
684 |
+
"Sensor",
|
685 |
+
"SensorErrorEvent",
|
686 |
+
"Serial",
|
687 |
+
"SerialPort",
|
688 |
+
"ServiceWorker",
|
689 |
+
"ServiceWorkerContainer",
|
690 |
+
"ServiceWorkerRegistration",
|
691 |
+
"Set",
|
692 |
+
"ShadowRoot",
|
693 |
+
"SharedWorker",
|
694 |
+
"SourceBuffer",
|
695 |
+
"SourceBufferList",
|
696 |
+
"SpeechSynthesisErrorEvent",
|
697 |
+
"SpeechSynthesisEvent",
|
698 |
+
"SpeechSynthesisUtterance",
|
699 |
+
"StaticRange",
|
700 |
+
"StereoPannerNode",
|
701 |
+
"Storage",
|
702 |
+
"StorageEvent",
|
703 |
+
"StorageManager",
|
704 |
+
"String",
|
705 |
+
"StylePropertyMap",
|
706 |
+
"StylePropertyMapReadOnly",
|
707 |
+
"StyleSheet",
|
708 |
+
"StyleSheetList",
|
709 |
+
"SubmitEvent",
|
710 |
+
"SubtleCrypto",
|
711 |
+
"Symbol",
|
712 |
+
"SyncManager",
|
713 |
+
"SyntaxError",
|
714 |
+
"TaskAttributionTiming",
|
715 |
+
"TaskController",
|
716 |
+
"TaskPriorityChangeEvent",
|
717 |
+
"TaskSignal",
|
718 |
+
"Text",
|
719 |
+
"TextDecoder",
|
720 |
+
"TextDecoderStream",
|
721 |
+
"TextEncoder",
|
722 |
+
"TextEncoderStream",
|
723 |
+
"TextEvent",
|
724 |
+
"TextMetrics",
|
725 |
+
"TextTrack",
|
726 |
+
"TextTrackCue",
|
727 |
+
"TextTrackCueList",
|
728 |
+
"TextTrackList",
|
729 |
+
"TimeRanges",
|
730 |
+
"ToggleEvent",
|
731 |
+
"Touch",
|
732 |
+
"TouchEvent",
|
733 |
+
"TouchList",
|
734 |
+
"TrackEvent",
|
735 |
+
"TransformStream",
|
736 |
+
"TransformStreamDefaultController",
|
737 |
+
"TransitionEvent",
|
738 |
+
"TreeWalker",
|
739 |
+
"TrustedHTML",
|
740 |
+
"TrustedScript",
|
741 |
+
"TrustedScriptURL",
|
742 |
+
"TrustedTypePolicy",
|
743 |
+
"TrustedTypePolicyFactory",
|
744 |
+
"TypeError",
|
745 |
+
"UIEvent",
|
746 |
+
"URIError",
|
747 |
+
"URL",
|
748 |
+
"URLPattern",
|
749 |
+
"URLSearchParams",
|
750 |
+
"USB",
|
751 |
+
"USBAlternateInterface",
|
752 |
+
"USBConfiguration",
|
753 |
+
"USBConnectionEvent",
|
754 |
+
"USBDevice",
|
755 |
+
"USBEndpoint",
|
756 |
+
"USBInTransferResult",
|
757 |
+
"USBInterface",
|
758 |
+
"USBIsochronousInTransferPacket",
|
759 |
+
"USBIsochronousInTransferResult",
|
760 |
+
"USBIsochronousOutTransferPacket",
|
761 |
+
"USBIsochronousOutTransferResult",
|
762 |
+
"USBOutTransferResult",
|
763 |
+
"Uint16Array",
|
764 |
+
"Uint32Array",
|
765 |
+
"Uint8Array",
|
766 |
+
"Uint8ClampedArray",
|
767 |
+
"UserActivation",
|
768 |
+
"VTTCue",
|
769 |
+
"ValidityState",
|
770 |
+
"VideoColorSpace",
|
771 |
+
"VideoDecoder",
|
772 |
+
"VideoEncoder",
|
773 |
+
"VideoFrame",
|
774 |
+
"VideoPlaybackQuality",
|
775 |
+
"ViewTransition",
|
776 |
+
"VirtualKeyboard",
|
777 |
+
"VirtualKeyboardGeometryChangeEvent",
|
778 |
+
"VisualViewport",
|
779 |
+
"WakeLock",
|
780 |
+
"WakeLockSentinel",
|
781 |
+
"WaveShaperNode",
|
782 |
+
"WeakMap",
|
783 |
+
"WeakRef",
|
784 |
+
"WeakSet",
|
785 |
+
"WebAssembly",
|
786 |
+
"WebGL2RenderingContext",
|
787 |
+
"WebGLActiveInfo",
|
788 |
+
"WebGLBuffer",
|
789 |
+
"WebGLContextEvent",
|
790 |
+
"WebGLFramebuffer",
|
791 |
+
"WebGLProgram",
|
792 |
+
"WebGLQuery",
|
793 |
+
"WebGLRenderbuffer",
|
794 |
+
"WebGLRenderingContext",
|
795 |
+
"WebGLSampler",
|
796 |
+
"WebGLShader",
|
797 |
+
"WebGLShaderPrecisionFormat",
|
798 |
+
"WebGLSync",
|
799 |
+
"WebGLTexture",
|
800 |
+
"WebGLTransformFeedback",
|
801 |
+
"WebGLUniformLocation",
|
802 |
+
"WebGLVertexArrayObject",
|
803 |
+
"WebKitCSSMatrix",
|
804 |
+
"WebKitMutationObserver",
|
805 |
+
"WebSocket",
|
806 |
+
"WebTransport",
|
807 |
+
"WebTransportBidirectionalStream",
|
808 |
+
"WebTransportDatagramDuplexStream",
|
809 |
+
"WebTransportError",
|
810 |
+
"WheelEvent",
|
811 |
+
"Window",
|
812 |
+
"WindowControlsOverlay",
|
813 |
+
"WindowControlsOverlayGeometryChangeEvent",
|
814 |
+
"Worker",
|
815 |
+
"Worklet",
|
816 |
+
"WritableStream",
|
817 |
+
"WritableStreamDefaultController",
|
818 |
+
"WritableStreamDefaultWriter",
|
819 |
+
"XMLDocument",
|
820 |
+
"XMLHttpRequest",
|
821 |
+
"XMLHttpRequestEventTarget",
|
822 |
+
"XMLHttpRequestUpload",
|
823 |
+
"XMLSerializer",
|
824 |
+
"XPathEvaluator",
|
825 |
+
"XPathExpression",
|
826 |
+
"XPathResult",
|
827 |
+
"XRAnchor",
|
828 |
+
"XRAnchorSet",
|
829 |
+
"XRBoundedReferenceSpace",
|
830 |
+
"XRCPUDepthInformation",
|
831 |
+
"XRCamera",
|
832 |
+
"XRDOMOverlayState",
|
833 |
+
"XRDepthInformation",
|
834 |
+
"XRFrame",
|
835 |
+
"XRHitTestResult",
|
836 |
+
"XRHitTestSource",
|
837 |
+
"XRInputSource",
|
838 |
+
"XRInputSourceArray",
|
839 |
+
"XRInputSourceEvent",
|
840 |
+
"XRInputSourcesChangeEvent",
|
841 |
+
"XRLayer",
|
842 |
+
"XRLightEstimate",
|
843 |
+
"XRLightProbe",
|
844 |
+
"XRPose",
|
845 |
+
"XRRay",
|
846 |
+
"XRReferenceSpace",
|
847 |
+
"XRReferenceSpaceEvent",
|
848 |
+
"XRRenderState",
|
849 |
+
"XRRigidTransform",
|
850 |
+
"XRSession",
|
851 |
+
"XRSessionEvent",
|
852 |
+
"XRSpace",
|
853 |
+
"XRSystem",
|
854 |
+
"XRTransientInputHitTestResult",
|
855 |
+
"XRTransientInputHitTestSource",
|
856 |
+
"XRView",
|
857 |
+
"XRViewerPose",
|
858 |
+
"XRViewport",
|
859 |
+
"XRWebGLBinding",
|
860 |
+
"XRWebGLDepthInformation",
|
861 |
+
"XRWebGLLayer",
|
862 |
+
"XSLTProcessor",
|
863 |
+
"__core-js_shared__",
|
864 |
+
"_countAA",
|
865 |
+
"ae",
|
866 |
+
"alert",
|
867 |
+
"api_target",
|
868 |
+
"api_target_sri",
|
869 |
+
"ark",
|
870 |
+
"async_fingerprints",
|
871 |
+
"atob",
|
872 |
+
"blur",
|
873 |
+
"btoa",
|
874 |
+
"caches",
|
875 |
+
"cancelAnimationFrame",
|
876 |
+
"cancelIdleCallback",
|
877 |
+
"capiMode",
|
878 |
+
"capiSettings",
|
879 |
+
"capiVersion",
|
880 |
+
"captureEvents",
|
881 |
+
"cdn",
|
882 |
+
"chrome",
|
883 |
+
"clearInterval",
|
884 |
+
"clearTimeout",
|
885 |
+
"clientInformation",
|
886 |
+
"close",
|
887 |
+
"closed",
|
888 |
+
"confirm",
|
889 |
+
"console",
|
890 |
+
"cookieStore",
|
891 |
+
"createImageBitmap",
|
892 |
+
"credentialless",
|
893 |
+
"crossOriginIsolated",
|
894 |
+
"crypto",
|
895 |
+
"customElements",
|
896 |
+
"decodeURI",
|
897 |
+
"decodeURIComponent",
|
898 |
+
"devicePixelRatio",
|
899 |
+
"doBBBd",
|
900 |
+
"document",
|
901 |
+
"encodeURI",
|
902 |
+
"encodeURIComponent",
|
903 |
+
"escape",
|
904 |
+
"eval",
|
905 |
+
"event",
|
906 |
+
"extended_fingerprinting_enabled",
|
907 |
+
"external",
|
908 |
+
"fc_api_server",
|
909 |
+
"fc_fp",
|
910 |
+
"fc_obj",
|
911 |
+
"fetch",
|
912 |
+
"find",
|
913 |
+
"find_onload",
|
914 |
+
"fingerprinting_enabled",
|
915 |
+
"focus",
|
916 |
+
"fp_result",
|
917 |
+
"frameElement",
|
918 |
+
"frames",
|
919 |
+
"getComputedStyle",
|
920 |
+
"getScreenDetails",
|
921 |
+
"getSelection",
|
922 |
+
"get_outer_html",
|
923 |
+
"get_query_data",
|
924 |
+
"globalThis",
|
925 |
+
"history",
|
926 |
+
"indexedDB",
|
927 |
+
"innerHeight",
|
928 |
+
"innerWidth",
|
929 |
+
"isFinite",
|
930 |
+
"isNaN",
|
931 |
+
"isSecureContext",
|
932 |
+
"launchQueue",
|
933 |
+
"length",
|
934 |
+
"loadedWithData",
|
935 |
+
"localStorage",
|
936 |
+
"location",
|
937 |
+
"locationbar",
|
938 |
+
"log",
|
939 |
+
"matchMedia",
|
940 |
+
"menubar",
|
941 |
+
"moveBy",
|
942 |
+
"moveTo",
|
943 |
+
"msie",
|
944 |
+
"name",
|
945 |
+
"navigation",
|
946 |
+
"navigator",
|
947 |
+
"offscreenBuffering",
|
948 |
+
"onabort",
|
949 |
+
"onafterprint",
|
950 |
+
"onanimationend",
|
951 |
+
"onanimationiteration",
|
952 |
+
"onanimationstart",
|
953 |
+
"onappinstalled",
|
954 |
+
"onauxclick",
|
955 |
+
"onbeforeinput",
|
956 |
+
"onbeforeinstallprompt",
|
957 |
+
"onbeforematch",
|
958 |
+
"onbeforeprint",
|
959 |
+
"onbeforetoggle",
|
960 |
+
"onbeforeunload",
|
961 |
+
"onbeforexrselect",
|
962 |
+
"onblur",
|
963 |
+
"oncancel",
|
964 |
+
"oncanplay",
|
965 |
+
"oncanplaythrough",
|
966 |
+
"onchange",
|
967 |
+
"onclick",
|
968 |
+
"onclose",
|
969 |
+
"oncontentvisibilityautostatechange",
|
970 |
+
"oncontextlost",
|
971 |
+
"oncontextmenu",
|
972 |
+
"oncontextrestored",
|
973 |
+
"oncuechange",
|
974 |
+
"ondblclick",
|
975 |
+
"ondevicemotion",
|
976 |
+
"ondeviceorientation",
|
977 |
+
"ondeviceorientationabsolute",
|
978 |
+
"ondrag",
|
979 |
+
"ondragend",
|
980 |
+
"ondragenter",
|
981 |
+
"ondragleave",
|
982 |
+
"ondragover",
|
983 |
+
"ondragstart",
|
984 |
+
"ondrop",
|
985 |
+
"ondurationchange",
|
986 |
+
"onemptied",
|
987 |
+
"onended",
|
988 |
+
"onerror",
|
989 |
+
"onfocus",
|
990 |
+
"onformdata",
|
991 |
+
"ongotpointercapture",
|
992 |
+
"onhashchange",
|
993 |
+
"oninput",
|
994 |
+
"oninvalid",
|
995 |
+
"onkeydown",
|
996 |
+
"onkeypress",
|
997 |
+
"onkeyup",
|
998 |
+
"onlanguagechange",
|
999 |
+
"onload",
|
1000 |
+
"onload_retry",
|
1001 |
+
"onloadeddata",
|
1002 |
+
"onloadedmetadata",
|
1003 |
+
"onloadstart",
|
1004 |
+
"onlostpointercapture",
|
1005 |
+
"onmessage",
|
1006 |
+
"onmessageerror",
|
1007 |
+
"onmousedown",
|
1008 |
+
"onmouseenter",
|
1009 |
+
"onmouseleave",
|
1010 |
+
"onmousemove",
|
1011 |
+
"onmouseout",
|
1012 |
+
"onmouseover",
|
1013 |
+
"onmouseup",
|
1014 |
+
"onmousewheel",
|
1015 |
+
"onoffline",
|
1016 |
+
"ononline",
|
1017 |
+
"onpagehide",
|
1018 |
+
"onpageshow",
|
1019 |
+
"onpause",
|
1020 |
+
"onplay",
|
1021 |
+
"onplaying",
|
1022 |
+
"onpointercancel",
|
1023 |
+
"onpointerdown",
|
1024 |
+
"onpointerenter",
|
1025 |
+
"onpointerleave",
|
1026 |
+
"onpointermove",
|
1027 |
+
"onpointerout",
|
1028 |
+
"onpointerover",
|
1029 |
+
"onpointerrawupdate",
|
1030 |
+
"onpointerup",
|
1031 |
+
"onpopstate",
|
1032 |
+
"onprogress",
|
1033 |
+
"onratechange",
|
1034 |
+
"onrejectionhandled",
|
1035 |
+
"onreset",
|
1036 |
+
"onresize",
|
1037 |
+
"onscroll",
|
1038 |
+
"onscrollend",
|
1039 |
+
"onsearch",
|
1040 |
+
"onsecuritypolicyviolation",
|
1041 |
+
"onseeked",
|
1042 |
+
"onseeking",
|
1043 |
+
"onselect",
|
1044 |
+
"onselectionchange",
|
1045 |
+
"onselectstart",
|
1046 |
+
"onslotchange",
|
1047 |
+
"onstalled",
|
1048 |
+
"onstorage",
|
1049 |
+
"onsubmit",
|
1050 |
+
"onsuspend",
|
1051 |
+
"ontimeupdate",
|
1052 |
+
"ontoggle",
|
1053 |
+
"ontransitioncancel",
|
1054 |
+
"ontransitionend",
|
1055 |
+
"ontransitionrun",
|
1056 |
+
"ontransitionstart",
|
1057 |
+
"onunhandledrejection",
|
1058 |
+
"onunload",
|
1059 |
+
"onvolumechange",
|
1060 |
+
"onwaiting",
|
1061 |
+
"onwebkitanimationend",
|
1062 |
+
"onwebkitanimationiteration",
|
1063 |
+
"onwebkitanimationstart",
|
1064 |
+
"onwebkittransitionend",
|
1065 |
+
"onwheel",
|
1066 |
+
"open",
|
1067 |
+
"openDatabase",
|
1068 |
+
"opener",
|
1069 |
+
"origin",
|
1070 |
+
"originAgentCluster",
|
1071 |
+
"outerHeight",
|
1072 |
+
"outerWidth",
|
1073 |
+
"pageXOffset",
|
1074 |
+
"pageYOffset",
|
1075 |
+
"parent",
|
1076 |
+
"parseFloat",
|
1077 |
+
"parseInt",
|
1078 |
+
"performance",
|
1079 |
+
"personalbar",
|
1080 |
+
"postMessage",
|
1081 |
+
"print",
|
1082 |
+
"prompt",
|
1083 |
+
"public_key",
|
1084 |
+
"queryLocalFonts",
|
1085 |
+
"query_data",
|
1086 |
+
"queueMicrotask",
|
1087 |
+
"releaseEvents",
|
1088 |
+
"reportError",
|
1089 |
+
"requestAnimationFrame",
|
1090 |
+
"requestIdleCallback",
|
1091 |
+
"resizeBy",
|
1092 |
+
"resizeTo",
|
1093 |
+
"scheduler",
|
1094 |
+
"screen",
|
1095 |
+
"screenLeft",
|
1096 |
+
"screenTop",
|
1097 |
+
"screenX",
|
1098 |
+
"screenY",
|
1099 |
+
"scroll",
|
1100 |
+
"scrollBy",
|
1101 |
+
"scrollTo",
|
1102 |
+
"scrollX",
|
1103 |
+
"scrollY",
|
1104 |
+
"scrollbars",
|
1105 |
+
"self",
|
1106 |
+
"sessionStorage",
|
1107 |
+
"setAPIInput",
|
1108 |
+
"setInterval",
|
1109 |
+
"setQueryDataInput",
|
1110 |
+
"setTimeout",
|
1111 |
+
"showDirectoryPicker",
|
1112 |
+
"showOpenFilePicker",
|
1113 |
+
"showSaveFilePicker",
|
1114 |
+
"siteData",
|
1115 |
+
"speechSynthesis",
|
1116 |
+
"startArkoseEnforcement",
|
1117 |
+
"status",
|
1118 |
+
"statusbar",
|
1119 |
+
"stop",
|
1120 |
+
"stringifyWithFloat",
|
1121 |
+
"structuredClone",
|
1122 |
+
"styleMedia",
|
1123 |
+
"target",
|
1124 |
+
"toolbar",
|
1125 |
+
"top",
|
1126 |
+
"trustedTypes",
|
1127 |
+
"undefined",
|
1128 |
+
"unescape",
|
1129 |
+
"visualViewport",
|
1130 |
+
"webkitCancelAnimationFrame",
|
1131 |
+
"webkitMediaStream",
|
1132 |
+
"webkitRTCPeerConnection",
|
1133 |
+
"webkitRequestAnimationFrame",
|
1134 |
+
"webkitRequestFileSystem",
|
1135 |
+
"webkitResolveLocalFileSystemURL",
|
1136 |
+
"webkitSpeechGrammar",
|
1137 |
+
"webkitSpeechGrammarList",
|
1138 |
+
"webkitSpeechRecognition",
|
1139 |
+
"webkitSpeechRecognitionError",
|
1140 |
+
"webkitSpeechRecognitionEvent",
|
1141 |
+
"webkitURL",
|
1142 |
+
"window",
|
1143 |
+
}
|
1144 |
+
sort.Strings(b1)
|
1145 |
+
result := strings.Join(b1, "|")
|
1146 |
+
return getMurmur128String(result, 420)
|
1147 |
+
}
|
1148 |
+
|
1149 |
+
func getWindowProtoChainHash() string { // return this[dh(f_a_hU.f)](b0[dh(f_a_hU.g)]('|'), 0x1a4);
|
1150 |
+
// Object.getPrototypeOf(window);
|
1151 |
+
b0 := []string{
|
1152 |
+
"TEMPORARY",
|
1153 |
+
"PERSISTENT",
|
1154 |
+
"constructor",
|
1155 |
+
"addEventListener",
|
1156 |
+
"dispatchEvent",
|
1157 |
+
"removeEventListener",
|
1158 |
+
"constructor",
|
1159 |
+
"constructor",
|
1160 |
+
"__defineGetter__",
|
1161 |
+
"__defineSetter__",
|
1162 |
+
"hasOwnProperty",
|
1163 |
+
"__lookupGetter__",
|
1164 |
+
"__lookupSetter__",
|
1165 |
+
"isPrototypeOf",
|
1166 |
+
"propertyIsEnumerable",
|
1167 |
+
"toString",
|
1168 |
+
"valueOf",
|
1169 |
+
"__proto__",
|
1170 |
+
"toLocaleString",
|
1171 |
+
}
|
1172 |
+
result2 := strings.Join(b0, "|")
|
1173 |
+
return x64hash128(result2, 420)
|
1174 |
+
}
|
1175 |
+
|
1176 |
+
func x64Add(m []uint32, n []uint32) []uint32 {
|
1177 |
+
m = []uint32{m[0] >> 16, m[0] & 0xffff, m[1] >> 16, m[1] & 0xffff}
|
1178 |
+
n = []uint32{n[0] >> 16, n[0] & 0xffff, n[1] >> 16, n[1] & 0xffff}
|
1179 |
+
o := []uint32{0, 0, 0, 0}
|
1180 |
+
o[3] += m[3] + n[3]
|
1181 |
+
o[2] += o[3] >> 16
|
1182 |
+
o[3] &= 0xffff
|
1183 |
+
o[2] += m[2] + n[2]
|
1184 |
+
o[1] += o[2] >> 16
|
1185 |
+
o[2] &= 0xffff
|
1186 |
+
o[1] += m[1] + n[1]
|
1187 |
+
o[0] += o[1] >> 16
|
1188 |
+
o[1] &= 0xffff
|
1189 |
+
o[0] += m[0] + n[0]
|
1190 |
+
o[0] &= 0xffff
|
1191 |
+
return []uint32{(o[0] << 16) | o[1], (o[2] << 16) | o[3]}
|
1192 |
+
}
|
1193 |
+
|
1194 |
+
func x64Multiply(m []uint32, n []uint32) []uint32 {
|
1195 |
+
m = []uint32{m[0] >> 16, m[0] & 0xffff, m[1] >> 16, m[1] & 0xffff}
|
1196 |
+
n = []uint32{n[0] >> 16, n[0] & 0xffff, n[1] >> 16, n[1] & 0xffff}
|
1197 |
+
o := []uint32{0, 0, 0, 0}
|
1198 |
+
o[3] += m[3] * n[3]
|
1199 |
+
o[2] += o[3] >> 16
|
1200 |
+
o[3] &= 0xffff
|
1201 |
+
o[2] += m[2] * n[3]
|
1202 |
+
o[1] += o[2] >> 16
|
1203 |
+
o[2] &= 0xffff
|
1204 |
+
o[2] += m[3] * n[2]
|
1205 |
+
o[1] += o[2] >> 16
|
1206 |
+
o[2] &= 0xffff
|
1207 |
+
o[1] += m[1] * n[3]
|
1208 |
+
o[0] += o[1] >> 16
|
1209 |
+
o[1] &= 0xffff
|
1210 |
+
o[1] += m[2] * n[2]
|
1211 |
+
o[0] += o[1] >> 16
|
1212 |
+
o[1] &= 0xffff
|
1213 |
+
o[1] += m[3] * n[1]
|
1214 |
+
o[0] += o[1] >> 16
|
1215 |
+
o[1] &= 0xffff
|
1216 |
+
o[0] += m[0]*n[3] + m[1]*n[2] + m[2]*n[1] + m[3]*n[0]
|
1217 |
+
o[0] &= 0xffff
|
1218 |
+
return []uint32{(o[0] << 16) | o[1], (o[2] << 16) | o[3]}
|
1219 |
+
}
|
1220 |
+
|
1221 |
+
//goland:noinspection SpellCheckingInspection
|
1222 |
+
func x64Rotl(m []uint32, n uint32) []uint32 {
|
1223 |
+
n %= 64
|
1224 |
+
if n == 32 {
|
1225 |
+
return []uint32{m[1], m[0]}
|
1226 |
+
} else if n < 32 {
|
1227 |
+
return []uint32{(m[0] << n) | (m[1] >> (32 - n)), (m[1] << n) | (m[0] >> (32 - n))}
|
1228 |
+
} else {
|
1229 |
+
n -= 32
|
1230 |
+
return []uint32{(m[1] << n) | (m[0] >> (32 - n)), (m[0] << n) | (m[1] >> (32 - n))}
|
1231 |
+
}
|
1232 |
+
}
|
1233 |
+
|
1234 |
+
func x64LeftShift(m []uint32, n uint32) []uint32 {
|
1235 |
+
n %= 64
|
1236 |
+
if n == 0 {
|
1237 |
+
return m
|
1238 |
+
} else if n < 32 {
|
1239 |
+
return []uint32{(m[0] << n) | (m[1] >> (32 - n)), m[1] << n}
|
1240 |
+
} else {
|
1241 |
+
return []uint32{m[1] << (n - 32), 0}
|
1242 |
+
}
|
1243 |
+
}
|
1244 |
+
|
1245 |
+
func x64Xor(m []uint32, n []uint32) []uint32 {
|
1246 |
+
return []uint32{m[0] ^ n[0], m[1] ^ n[1]}
|
1247 |
+
}
|
1248 |
+
|
1249 |
+
//goland:noinspection SpellCheckingInspection
|
1250 |
+
func x64Fmix(h []uint32) []uint32 {
|
1251 |
+
h = x64Xor(h, []uint32{0, h[0] >> 1})
|
1252 |
+
h = x64Multiply(h, []uint32{0xff51afd7, 0xed558ccd})
|
1253 |
+
h = x64Xor(h, []uint32{0, h[0] >> 1})
|
1254 |
+
h = x64Multiply(h, []uint32{0xc4ceb9fe, 0x1a85ec53})
|
1255 |
+
h = x64Xor(h, []uint32{0, h[0] >> 1})
|
1256 |
+
return h
|
1257 |
+
}
|
1258 |
+
|
1259 |
+
func x64hash128(key string, seed uint32) string {
|
1260 |
+
keyLength := len(key)
|
1261 |
+
remainder := keyLength % 16
|
1262 |
+
bytes := keyLength - remainder
|
1263 |
+
|
1264 |
+
var h1 = []uint32{0, seed}
|
1265 |
+
var h2 = []uint32{0, seed}
|
1266 |
+
var k1 = []uint32{0, 0}
|
1267 |
+
var k2 = []uint32{0, 0}
|
1268 |
+
var c1 = []uint32{0x87c37b91, 0x114253d5}
|
1269 |
+
var c2 = []uint32{0x4cf5ad43, 0x2745937f}
|
1270 |
+
|
1271 |
+
for i := 0; i < bytes; i += 16 {
|
1272 |
+
k1[0] = uint32(key[i+4])&0xff | (uint32(key[i+5])&0xff)<<8 | (uint32(key[i+6])&0xff)<<16 | (uint32(key[i+7])&0xff)<<24
|
1273 |
+
k1[1] = uint32(key[i])&0xff | (uint32(key[i+1])&0xff)<<8 | (uint32(key[i+2])&0xff)<<16 | (uint32(key[i+3])&0xff)<<24
|
1274 |
+
|
1275 |
+
k2[0] = uint32(key[i+12])&0xff | (uint32(key[i+13])&0xff)<<8 | (uint32(key[i+14])&0xff)<<16 | (uint32(key[i+15])&0xff)<<24
|
1276 |
+
k2[1] = uint32(key[i+8])&0xff | (uint32(key[i+9])&0xff)<<8 | (uint32(key[i+10])&0xff)<<16 | (uint32(key[i+11])&0xff)<<24
|
1277 |
+
|
1278 |
+
k1 = x64Multiply(k1, c1)
|
1279 |
+
k1 = x64Rotl(k1, 31)
|
1280 |
+
k1 = x64Multiply(k1, c2)
|
1281 |
+
h1 = x64Xor(h1, k1)
|
1282 |
+
h1 = x64Rotl(h1, 27)
|
1283 |
+
h1 = x64Add(h1, h2)
|
1284 |
+
h1 = x64Add(x64Multiply(h1, []uint32{0, 5}), []uint32{0, 0x52dce729})
|
1285 |
+
|
1286 |
+
k2 = x64Multiply(k2, c2)
|
1287 |
+
k2 = x64Rotl(k2, 33)
|
1288 |
+
k2 = x64Multiply(k2, c1)
|
1289 |
+
h2 = x64Xor(h2, k2)
|
1290 |
+
h2 = x64Rotl(h2, 31)
|
1291 |
+
h2 = x64Add(h2, h1)
|
1292 |
+
h2 = x64Add(x64Multiply(h2, []uint32{0, 5}), []uint32{0, 0x38495ab5})
|
1293 |
+
}
|
1294 |
+
|
1295 |
+
k1 = []uint32{0, 0}
|
1296 |
+
k2 = []uint32{0, 0}
|
1297 |
+
|
1298 |
+
switch remainder {
|
1299 |
+
case 15:
|
1300 |
+
k2 = x64Xor(k2, x64LeftShift([]uint32{0, uint32(key[bytes+14])}, 48))
|
1301 |
+
fallthrough
|
1302 |
+
case 14:
|
1303 |
+
k2 = x64Xor(k2, x64LeftShift([]uint32{0, uint32(key[bytes+13])}, 40))
|
1304 |
+
fallthrough
|
1305 |
+
case 13:
|
1306 |
+
k2 = x64Xor(k2, x64LeftShift([]uint32{0, uint32(key[bytes+12])}, 32))
|
1307 |
+
fallthrough
|
1308 |
+
case 12:
|
1309 |
+
k2 = x64Xor(k2, x64LeftShift([]uint32{0, uint32(key[bytes+11])}, 24))
|
1310 |
+
fallthrough
|
1311 |
+
case 11:
|
1312 |
+
k2 = x64Xor(k2, x64LeftShift([]uint32{0, uint32(key[bytes+10])}, 16))
|
1313 |
+
fallthrough
|
1314 |
+
case 10:
|
1315 |
+
k2 = x64Xor(k2, x64LeftShift([]uint32{0, uint32(key[bytes+9])}, 8))
|
1316 |
+
fallthrough
|
1317 |
+
case 9:
|
1318 |
+
k2 = x64Xor(k2, []uint32{0, uint32(key[bytes+8])})
|
1319 |
+
k2 = x64Multiply(k2, c2)
|
1320 |
+
k2 = x64Rotl(k2, 33)
|
1321 |
+
k2 = x64Multiply(k2, c1)
|
1322 |
+
h2 = x64Xor(h2, k2)
|
1323 |
+
fallthrough
|
1324 |
+
case 8:
|
1325 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+7])}, 56))
|
1326 |
+
fallthrough
|
1327 |
+
case 7:
|
1328 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+6])}, 48))
|
1329 |
+
fallthrough
|
1330 |
+
case 6:
|
1331 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+5])}, 40))
|
1332 |
+
fallthrough
|
1333 |
+
case 5:
|
1334 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+4])}, 32))
|
1335 |
+
fallthrough
|
1336 |
+
case 4:
|
1337 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+3])}, 24))
|
1338 |
+
fallthrough
|
1339 |
+
case 3:
|
1340 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+2])}, 16))
|
1341 |
+
fallthrough
|
1342 |
+
case 2:
|
1343 |
+
k1 = x64Xor(k1, x64LeftShift([]uint32{0, uint32(key[bytes+1])}, 8))
|
1344 |
+
fallthrough
|
1345 |
+
case 1:
|
1346 |
+
k1 = x64Xor(k1, []uint32{0, uint32(key[bytes])})
|
1347 |
+
k1 = x64Multiply(k1, c1)
|
1348 |
+
k1 = x64Rotl(k1, 31)
|
1349 |
+
k1 = x64Multiply(k1, c2)
|
1350 |
+
h1 = x64Xor(h1, k1)
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
h1 = x64Xor(h1, []uint32{0, uint32(keyLength)})
|
1354 |
+
h2 = x64Xor(h2, []uint32{0, uint32(keyLength)})
|
1355 |
+
h1 = x64Add(h1, h2)
|
1356 |
+
h2 = x64Add(h2, h1)
|
1357 |
+
h1 = x64Fmix(h1)
|
1358 |
+
h2 = x64Fmix(h2)
|
1359 |
+
h1 = x64Add(h1, h2)
|
1360 |
+
h2 = x64Add(h2, h1)
|
1361 |
+
|
1362 |
+
return fmt.Sprintf("%08x%08x%08x%08x", h1[0], h1[1], h2[0], h2[1])
|
1363 |
+
}
|
1364 |
+
|
1365 |
+
func getCFPHash(cfp string) uint32 {
|
1366 |
+
//'this is the cfp: canvas xxx base64 image'.split('').reduce((b5, b6) => {
|
1367 |
+
// return b5 = (b5 << 5) - b5 + b6.charCodeAt(0), b5 & b5;
|
1368 |
+
//}, 0);
|
1369 |
+
|
1370 |
+
var b5 uint32
|
1371 |
+
for _, b6 := range cfp {
|
1372 |
+
b5 = (b5 << 5) - b5 + uint32(b6)
|
1373 |
+
b5 &= b5
|
1374 |
+
}
|
1375 |
+
return b5
|
1376 |
+
}
|
1377 |
+
|
1378 |
+
func getIfeHash() string {
|
1379 |
+
return x64hash128(strings.Join(getFeList(), ", "), 38)
|
1380 |
+
}
|
pkg/funcaptcha/murmur.go
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/binary"
|
5 |
+
"fmt"
|
6 |
+
)
|
7 |
+
|
8 |
+
type digest struct {
|
9 |
+
h1, h2 uint64
|
10 |
+
length int
|
11 |
+
seed uint64
|
12 |
+
}
|
13 |
+
|
14 |
+
func getMurmur128String(input string, seed uint64) string {
|
15 |
+
d := newWithSeed(seed)
|
16 |
+
d.Write([]byte(input))
|
17 |
+
h1, h2 := d.Sum()
|
18 |
+
return fmt.Sprintf("%016x%016x", h1, h2)
|
19 |
+
}
|
20 |
+
|
21 |
+
func newWithSeed(seed uint64) *digest {
|
22 |
+
d := new(digest)
|
23 |
+
d.seed = seed
|
24 |
+
d.h1 = seed
|
25 |
+
d.h2 = seed
|
26 |
+
return d
|
27 |
+
}
|
28 |
+
|
29 |
+
func (d *digest) Write(data []byte) {
|
30 |
+
length := len(data)
|
31 |
+
d.length += length
|
32 |
+
|
33 |
+
var (
|
34 |
+
h1 = d.h1
|
35 |
+
h2 = d.h2
|
36 |
+
c1 = uint64(0x87c37b91114253d5)
|
37 |
+
c2 = uint64(0x4cf5ad432745937f)
|
38 |
+
)
|
39 |
+
|
40 |
+
for len(data) >= 16 {
|
41 |
+
k1 := binary.LittleEndian.Uint64(data)
|
42 |
+
k2 := binary.LittleEndian.Uint64(data[8:])
|
43 |
+
|
44 |
+
k1 *= c1
|
45 |
+
k1 = (k1 << 31) | (k1 >> (64 - 31))
|
46 |
+
k1 *= c2
|
47 |
+
h1 ^= k1
|
48 |
+
|
49 |
+
h1 = (h1 << 27) | (h1 >> (64 - 27))
|
50 |
+
h1 += h2
|
51 |
+
h1 = h1*5 + 0x52dce729
|
52 |
+
|
53 |
+
k2 *= c2
|
54 |
+
k2 = (k2 << 33) | (k2 >> (64 - 33))
|
55 |
+
k2 *= c1
|
56 |
+
h2 ^= k2
|
57 |
+
|
58 |
+
h2 = (h2 << 31) | (h2 >> (64 - 31))
|
59 |
+
h2 += h1
|
60 |
+
h2 = h2*5 + 0x38495ab5
|
61 |
+
|
62 |
+
data = data[16:]
|
63 |
+
}
|
64 |
+
|
65 |
+
var k1, k2 uint64
|
66 |
+
|
67 |
+
switch len(data) {
|
68 |
+
case 15:
|
69 |
+
k2 ^= uint64(data[14]) << 48
|
70 |
+
fallthrough
|
71 |
+
case 14:
|
72 |
+
k2 ^= uint64(data[13]) << 40
|
73 |
+
fallthrough
|
74 |
+
case 13:
|
75 |
+
k2 ^= uint64(data[12]) << 32
|
76 |
+
fallthrough
|
77 |
+
case 12:
|
78 |
+
k2 ^= uint64(data[11]) << 24
|
79 |
+
fallthrough
|
80 |
+
case 11:
|
81 |
+
k2 ^= uint64(data[10]) << 16
|
82 |
+
fallthrough
|
83 |
+
case 10:
|
84 |
+
k2 ^= uint64(data[9]) << 8
|
85 |
+
fallthrough
|
86 |
+
case 9:
|
87 |
+
k2 ^= uint64(data[8])
|
88 |
+
k2 *= c2
|
89 |
+
k2 = (k2 << 33) | (k2 >> (64 - 33))
|
90 |
+
k2 *= c1
|
91 |
+
h2 ^= k2
|
92 |
+
|
93 |
+
fallthrough
|
94 |
+
case 8:
|
95 |
+
k1 ^= uint64(data[7]) << 56
|
96 |
+
fallthrough
|
97 |
+
case 7:
|
98 |
+
k1 ^= uint64(data[6]) << 48
|
99 |
+
fallthrough
|
100 |
+
case 6:
|
101 |
+
k1 ^= uint64(data[5]) << 40
|
102 |
+
fallthrough
|
103 |
+
case 5:
|
104 |
+
k1 ^= uint64(data[4]) << 32
|
105 |
+
fallthrough
|
106 |
+
case 4:
|
107 |
+
k1 ^= uint64(data[3]) << 24
|
108 |
+
fallthrough
|
109 |
+
case 3:
|
110 |
+
k1 ^= uint64(data[2]) << 16
|
111 |
+
fallthrough
|
112 |
+
case 2:
|
113 |
+
k1 ^= uint64(data[1]) << 8
|
114 |
+
fallthrough
|
115 |
+
case 1:
|
116 |
+
k1 ^= uint64(data[0])
|
117 |
+
k1 *= c1
|
118 |
+
k1 = (k1 << 31) | (k1 >> (64 - 31))
|
119 |
+
k1 *= c2
|
120 |
+
h1 ^= k1
|
121 |
+
}
|
122 |
+
|
123 |
+
h1 ^= uint64(length)
|
124 |
+
h2 ^= uint64(length)
|
125 |
+
|
126 |
+
h1 += h2
|
127 |
+
h2 += h1
|
128 |
+
|
129 |
+
h1 = fmix(h1)
|
130 |
+
h2 = fmix(h2)
|
131 |
+
|
132 |
+
h1 += h2
|
133 |
+
h2 += h1
|
134 |
+
d.h1 = h1
|
135 |
+
d.h2 = h2
|
136 |
+
}
|
137 |
+
|
138 |
+
func (d *digest) Sum() (h1, h2 uint64) {
|
139 |
+
return d.h1, d.h2
|
140 |
+
}
|
141 |
+
|
142 |
+
func fmix(k uint64) uint64 {
|
143 |
+
k ^= k >> 33
|
144 |
+
k *= 0xff51afd7ed558ccd
|
145 |
+
k ^= k >> 33
|
146 |
+
k *= 0xc4ceb9fe1a85ec53
|
147 |
+
k ^= k >> 33
|
148 |
+
return k
|
149 |
+
}
|
pkg/funcaptcha/util.go
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/base64"
|
5 |
+
"encoding/json"
|
6 |
+
"fmt"
|
7 |
+
"io"
|
8 |
+
"net/url"
|
9 |
+
"os"
|
10 |
+
"strings"
|
11 |
+
"time"
|
12 |
+
|
13 |
+
http "github.com/bogdanfinn/fhttp"
|
14 |
+
)
|
15 |
+
|
16 |
+
func toJSON(data interface{}) string {
|
17 |
+
str, _ := json.Marshal(data)
|
18 |
+
return string(str)
|
19 |
+
}
|
20 |
+
|
21 |
+
func jsonToForm(data string) string {
|
22 |
+
// Unmarshal into map
|
23 |
+
var form_data map[string]interface{}
|
24 |
+
json.Unmarshal([]byte(data), &form_data)
|
25 |
+
// Use reflection to convert to form data
|
26 |
+
var form url.Values = url.Values{}
|
27 |
+
for k, v := range form_data {
|
28 |
+
form.Add(k, fmt.Sprintf("%v", v))
|
29 |
+
}
|
30 |
+
return form.Encode()
|
31 |
+
}
|
32 |
+
|
33 |
+
func (s *Session) DownloadChallenge(urls []string, b64 bool) ([]string, error) {
|
34 |
+
var b64_imgs []string = make([]string, len(urls))
|
35 |
+
for i, url := range urls {
|
36 |
+
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
37 |
+
req.Header = headers
|
38 |
+
resp, err := (*s.Client).Do(req)
|
39 |
+
if err != nil {
|
40 |
+
return nil, err
|
41 |
+
}
|
42 |
+
defer resp.Body.Close()
|
43 |
+
|
44 |
+
if resp.StatusCode != 200 {
|
45 |
+
return nil, fmt.Errorf("status code %d", resp.StatusCode)
|
46 |
+
}
|
47 |
+
|
48 |
+
body, _ := io.ReadAll(resp.Body)
|
49 |
+
// Figure out filename from URL
|
50 |
+
url_paths := strings.Split(url, "/")
|
51 |
+
if !b64 {
|
52 |
+
filename := strings.Split(url_paths[len(url_paths)-1], "?")[0]
|
53 |
+
if filename == "image" {
|
54 |
+
filename = fmt.Sprintf("image_%s.png", getTimeStamp())
|
55 |
+
}
|
56 |
+
err = os.WriteFile(filename, body, 0644)
|
57 |
+
if err != nil {
|
58 |
+
return nil, err
|
59 |
+
}
|
60 |
+
} else {
|
61 |
+
// base64 encode body
|
62 |
+
b64_imgs[i] = base64.StdEncoding.EncodeToString(body)
|
63 |
+
}
|
64 |
+
}
|
65 |
+
return b64_imgs, nil
|
66 |
+
}
|
67 |
+
|
68 |
+
func getTimeStamp() string {
|
69 |
+
return fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond))
|
70 |
+
}
|
71 |
+
|
72 |
+
func getRequestId(sessionId string) string {
|
73 |
+
pwd := fmt.Sprintf("REQUESTED%sID", sessionId)
|
74 |
+
return Encrypt(`{"sc":[147,307]}`, pwd)
|
75 |
+
}
|
pkg/funcaptcha/webgl.go
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package funcaptcha
|
2 |
+
|
3 |
+
import "strings"
|
4 |
+
|
5 |
+
const (
|
6 |
+
webglExtensions = "ANGLE_instanced_arrays;EXT_blend_minmax;EXT_color_buffer_half_float;EXT_disjoint_timer_query;EXT_float_blend;EXT_frag_depth;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw" // this.getWebGLKeys();
|
7 |
+
webglRenderer = "WebKit WebGL"
|
8 |
+
webglVendor = "WebKit"
|
9 |
+
webglVersion = "WebGL 1.0 (OpenGL ES 2.0 Chromium)"
|
10 |
+
webglShadingLanguageVersion = "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)"
|
11 |
+
webglAliasedLineWidthRange = "[1, 10]"
|
12 |
+
webglAliasedPointSizeRange = "[1, 2047]"
|
13 |
+
webglAntialiasing = "yes"
|
14 |
+
webglBits = "8,8,24,8,8,0"
|
15 |
+
webglMaxParams = "16,64,32768,1024,32768,32,32768,31,16,32,1024"
|
16 |
+
webglMaxViewportDims = "[32768, 32768]"
|
17 |
+
webglUnmaskedVendor = "Google Inc. (NVIDIA Corporation)"
|
18 |
+
webglUnmaskedRenderer = "ANGLE (NVIDIA Corporation, NVIDIA GeForce RTX 3060 Ti/PCIe/SSE2, OpenGL 4.5.0)"
|
19 |
+
webglFsfParams = "23,127,127,10,15,15,10,15,15"
|
20 |
+
webglFsiParams = "0,31,30,0,31,30,0,31,30"
|
21 |
+
webglVsfParams = "23,127,127,10,15,15,10,15,15"
|
22 |
+
webglVsiParams = "0,31,30,0,31,30,0,31,30"
|
23 |
+
)
|
24 |
+
|
25 |
+
var (
|
26 |
+
webglExtensionsHash = getWebglExtensionsHash()
|
27 |
+
)
|
28 |
+
|
29 |
+
func getWebglExtensionsHash() string {
|
30 |
+
return x64hash128(webglExtensions, 0)
|
31 |
+
}
|
32 |
+
|
33 |
+
func getWebglHashWebgl() string {
|
34 |
+
//aZ['webgl_hash' + cr(f_a_gY.X)] = this['x64hash128'](aC(aZ, function(b3) {
|
35 |
+
// return b3;
|
36 |
+
//})[cr(f_a_gY.Y)](','));
|
37 |
+
|
38 |
+
var webglList []string
|
39 |
+
webglList = append(webglList, webglExtensions)
|
40 |
+
webglList = append(webglList, webglExtensionsHash)
|
41 |
+
webglList = append(webglList, webglRenderer)
|
42 |
+
webglList = append(webglList, webglVendor)
|
43 |
+
webglList = append(webglList, webglVersion)
|
44 |
+
webglList = append(webglList, webglShadingLanguageVersion)
|
45 |
+
webglList = append(webglList, webglAliasedLineWidthRange)
|
46 |
+
webglList = append(webglList, webglAliasedPointSizeRange)
|
47 |
+
webglList = append(webglList, webglAntialiasing)
|
48 |
+
webglList = append(webglList, webglBits)
|
49 |
+
webglList = append(webglList, webglMaxParams)
|
50 |
+
webglList = append(webglList, webglMaxViewportDims)
|
51 |
+
webglList = append(webglList, webglUnmaskedVendor)
|
52 |
+
webglList = append(webglList, webglUnmaskedRenderer)
|
53 |
+
webglList = append(webglList, webglFsfParams)
|
54 |
+
webglList = append(webglList, webglFsiParams)
|
55 |
+
webglList = append(webglList, webglVsfParams)
|
56 |
+
webglList = append(webglList, webglVsiParams)
|
57 |
+
return x64hash128(strings.Join(webglList, ","), 0)
|
58 |
+
}
|
pkg/logger/logger.go
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package logger
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/env"
|
5 |
+
"github.com/sirupsen/logrus"
|
6 |
+
"os"
|
7 |
+
)
|
8 |
+
|
9 |
+
var Log *logrus.Logger
|
10 |
+
|
11 |
+
func init() {
|
12 |
+
Log = logrus.New()
|
13 |
+
level, err := logrus.ParseLevel(env.E.LogLevel)
|
14 |
+
if err != nil {
|
15 |
+
return
|
16 |
+
}
|
17 |
+
Log.SetLevel(level)
|
18 |
+
|
19 |
+
Log.SetOutput(os.Stdout)
|
20 |
+
|
21 |
+
Log.SetFormatter(&logrus.TextFormatter{
|
22 |
+
FullTimestamp: true,
|
23 |
+
TimestampFormat: "2006-01-02 15:04:05",
|
24 |
+
})
|
25 |
+
}
|
pkg/plugins/api/arkosetoken/arkosetoken.go
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package arkosetoken
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/funcaptcha"
|
6 |
+
"WarpGPT/pkg/plugins"
|
7 |
+
http "github.com/bogdanfinn/fhttp"
|
8 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
9 |
+
"github.com/gin-gonic/gin"
|
10 |
+
"io"
|
11 |
+
)
|
12 |
+
|
13 |
+
var context *plugins.Component
|
14 |
+
var ArkoseTokenInstance ArkoseToken
|
15 |
+
|
16 |
+
type Context struct {
|
17 |
+
GinContext *gin.Context
|
18 |
+
RequestUrl string
|
19 |
+
RequestClient tls_client.HttpClient
|
20 |
+
RequestBody io.ReadCloser
|
21 |
+
RequestParam string
|
22 |
+
RequestMethod string
|
23 |
+
RequestHeaders http.Header
|
24 |
+
}
|
25 |
+
type ArkoseToken struct {
|
26 |
+
Context Context
|
27 |
+
}
|
28 |
+
|
29 |
+
func (p *ArkoseToken) GetContext() Context {
|
30 |
+
p.Context.RequestClient = common.GetHttpClient()
|
31 |
+
return p.Context
|
32 |
+
}
|
33 |
+
func (p *ArkoseToken) SetContext(conversation Context) {
|
34 |
+
p.Context = conversation
|
35 |
+
}
|
36 |
+
|
37 |
+
func (p *ArkoseToken) ProcessMethod() {
|
38 |
+
context.Logger.Debug("ArkoseToken")
|
39 |
+
id := p.GetContext().GinContext.Param("id")
|
40 |
+
var (
|
41 |
+
token string
|
42 |
+
err error
|
43 |
+
)
|
44 |
+
if id == "35536E1E-65B4-4D96-9D97-6ADB7EFF8147" {
|
45 |
+
token, err = funcaptcha.GetOpenAIArkoseToken(4, p.GetContext().RequestHeaders.Get("puid"))
|
46 |
+
} else if id == "0A1D34FC-659D-4E23-B17B-694DCFCF6A6C" {
|
47 |
+
token, err = funcaptcha.GetOpenAIArkoseToken(0, p.GetContext().RequestHeaders.Get("puid"))
|
48 |
+
} else if id == "3D86FBBA-9D22-402A-B512-3420086BA6CC" {
|
49 |
+
token, err = funcaptcha.GetOpenAIArkoseToken(3, p.GetContext().RequestHeaders.Get("puid"))
|
50 |
+
} else {
|
51 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Invalid id"})
|
52 |
+
return
|
53 |
+
}
|
54 |
+
if err != nil {
|
55 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Unable to generate ArkoseToken"})
|
56 |
+
}
|
57 |
+
p.GetContext().GinContext.Header("Content-Type", "application/json")
|
58 |
+
p.GetContext().GinContext.JSON(200, gin.H{"token": token})
|
59 |
+
}
|
60 |
+
|
61 |
+
type NotHaveUrl struct {
|
62 |
+
}
|
63 |
+
|
64 |
+
func (u NotHaveUrl) Generate(path string, rawquery string) string {
|
65 |
+
return ""
|
66 |
+
}
|
67 |
+
func (p *ArkoseToken) Run(com *plugins.Component) {
|
68 |
+
context = com
|
69 |
+
context.Engine.GET("/token/:id", func(c *gin.Context) {
|
70 |
+
conversation := common.GetContextPack(c, NotHaveUrl{})
|
71 |
+
common.Do[Context](new(ArkoseToken), Context(conversation))
|
72 |
+
})
|
73 |
+
}
|
pkg/plugins/api/backendapi/backend.go
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package backendapi
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/funcaptcha"
|
6 |
+
"WarpGPT/pkg/logger"
|
7 |
+
"WarpGPT/pkg/plugins"
|
8 |
+
"WarpGPT/pkg/plugins/service/wsstostream"
|
9 |
+
"WarpGPT/pkg/tools"
|
10 |
+
"bytes"
|
11 |
+
"encoding/json"
|
12 |
+
http "github.com/bogdanfinn/fhttp"
|
13 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
14 |
+
"github.com/gin-gonic/gin"
|
15 |
+
"io"
|
16 |
+
shttp "net/http"
|
17 |
+
"strings"
|
18 |
+
"time"
|
19 |
+
)
|
20 |
+
|
21 |
+
var context *plugins.Component
|
22 |
+
var BackendProcessInstance BackendProcess
|
23 |
+
|
24 |
+
type Context struct {
|
25 |
+
GinContext *gin.Context
|
26 |
+
RequestUrl string
|
27 |
+
RequestClient tls_client.HttpClient
|
28 |
+
RequestBody io.ReadCloser
|
29 |
+
RequestParam string
|
30 |
+
RequestMethod string
|
31 |
+
RequestHeaders http.Header
|
32 |
+
}
|
33 |
+
|
34 |
+
type WsResponse struct {
|
35 |
+
ConversationId string `json:"conversation_id"`
|
36 |
+
ExpiresAt time.Time `json:"expires_at"`
|
37 |
+
ResponseId string `json:"response_id"`
|
38 |
+
WssUrl string `json:"wss_url"`
|
39 |
+
}
|
40 |
+
|
41 |
+
type BackendProcess struct {
|
42 |
+
ConversationId string
|
43 |
+
Context Context
|
44 |
+
}
|
45 |
+
|
46 |
+
func (p *BackendProcess) GetContext() Context {
|
47 |
+
return p.Context
|
48 |
+
}
|
49 |
+
func (p *BackendProcess) SetContext(conversation Context) {
|
50 |
+
p.Context = conversation
|
51 |
+
}
|
52 |
+
|
53 |
+
func (p *BackendProcess) ProcessMethod() {
|
54 |
+
context.Logger.Debug("ProcessBackendProcess")
|
55 |
+
var requestBody map[string]interface{}
|
56 |
+
var ws *wsstostream.WssToStream
|
57 |
+
err := p.decodeRequestBody(&requestBody)
|
58 |
+
if err != nil {
|
59 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Request json decode error"})
|
60 |
+
context.Logger.Error(err)
|
61 |
+
return
|
62 |
+
}
|
63 |
+
request, err := p.createRequest(requestBody)
|
64 |
+
if err != nil {
|
65 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Server error"})
|
66 |
+
context.Logger.Error(err)
|
67 |
+
return
|
68 |
+
}
|
69 |
+
if strings.Contains(p.Context.RequestParam, "/conversation/ws") {
|
70 |
+
ws = wsstostream.NewWssToStream(p.GetContext().RequestHeaders.Get("Authorization"))
|
71 |
+
err = ws.InitConnect()
|
72 |
+
}
|
73 |
+
if err != nil {
|
74 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": err.Error()})
|
75 |
+
context.Logger.Error(err)
|
76 |
+
return
|
77 |
+
}
|
78 |
+
context.Logger.Debug("Requesting to ", p.GetContext().RequestUrl)
|
79 |
+
response, err := p.GetContext().RequestClient.Do(request)
|
80 |
+
if err != nil {
|
81 |
+
var jsonData interface{}
|
82 |
+
err = json.NewDecoder(response.Body).Decode(&jsonData)
|
83 |
+
if err != nil {
|
84 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Request json decode error"})
|
85 |
+
context.Logger.Error(err)
|
86 |
+
return
|
87 |
+
}
|
88 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
89 |
+
context.Logger.Error(err)
|
90 |
+
return
|
91 |
+
}
|
92 |
+
|
93 |
+
common.CopyResponseHeaders(response, p.GetContext().GinContext)
|
94 |
+
|
95 |
+
if strings.Contains(response.Header.Get("Content-Type"), "text/event-stream") {
|
96 |
+
err = p.streamResponse(response)
|
97 |
+
if err != nil {
|
98 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": err.Error()})
|
99 |
+
context.Logger.Error(err)
|
100 |
+
return
|
101 |
+
}
|
102 |
+
}
|
103 |
+
if strings.Contains(response.Header.Get("Content-Type"), "application/json") {
|
104 |
+
if strings.Contains(p.Context.RequestParam, "/conversation/ws") {
|
105 |
+
context.Logger.Debug("WsToStreamResponse")
|
106 |
+
p.WsToStreamResponse(ws, response)
|
107 |
+
} else {
|
108 |
+
err = p.jsonResponse(response)
|
109 |
+
if err != nil {
|
110 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": err.Error()})
|
111 |
+
context.Logger.Error(err)
|
112 |
+
return
|
113 |
+
}
|
114 |
+
}
|
115 |
+
}
|
116 |
+
}
|
117 |
+
func (p *BackendProcess) WsToStreamResponse(ws *wsstostream.WssToStream, response *http.Response) {
|
118 |
+
var jsonData WsResponse
|
119 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
120 |
+
if err != nil {
|
121 |
+
context.Logger.Error(err)
|
122 |
+
}
|
123 |
+
ws.ResponseId = jsonData.ResponseId
|
124 |
+
ws.ConversationId = jsonData.ConversationId
|
125 |
+
p.GetContext().GinContext.Writer.Header().Set("Content-Type", "text/event-stream")
|
126 |
+
p.GetContext().GinContext.Writer.Header().Set("Cache-Control", "no-cache")
|
127 |
+
p.GetContext().GinContext.Writer.Header().Set("Connection", "keep-alive")
|
128 |
+
ctx := p.GetContext().GinContext.Request.Context()
|
129 |
+
for {
|
130 |
+
select {
|
131 |
+
case <-p.GetContext().GinContext.Writer.CloseNotify():
|
132 |
+
logger.Log.Debug("WsToStreamResponse Writer.CloseNotify")
|
133 |
+
return
|
134 |
+
case <-ctx.Done():
|
135 |
+
logger.Log.Debug("WsToStreamResponse ctx.Done")
|
136 |
+
return
|
137 |
+
default:
|
138 |
+
message, err := ws.ReadMessage()
|
139 |
+
if err != nil {
|
140 |
+
context.Logger.Error(err)
|
141 |
+
break
|
142 |
+
}
|
143 |
+
if message != nil {
|
144 |
+
data, err := io.ReadAll(message)
|
145 |
+
if err != nil {
|
146 |
+
context.Logger.Error(err)
|
147 |
+
return
|
148 |
+
}
|
149 |
+
_, writeErr := p.GetContext().GinContext.Writer.Write(data)
|
150 |
+
if writeErr != nil {
|
151 |
+
return
|
152 |
+
}
|
153 |
+
p.GetContext().GinContext.Writer.Flush()
|
154 |
+
if strings.Contains(string(data), "data: [DONE]") {
|
155 |
+
return
|
156 |
+
}
|
157 |
+
}
|
158 |
+
}
|
159 |
+
}
|
160 |
+
}
|
161 |
+
func (p *BackendProcess) createRequest(requestBody map[string]interface{}) (*http.Request, error) {
|
162 |
+
context.Logger.Debug("BackendProcess createRequest")
|
163 |
+
var request *http.Request
|
164 |
+
if p.Context.RequestBody == shttp.NoBody {
|
165 |
+
request, _ = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, nil)
|
166 |
+
} else {
|
167 |
+
token, err := p.addArkoseTokenIfNeeded(&requestBody)
|
168 |
+
if err != nil {
|
169 |
+
return nil, err
|
170 |
+
}
|
171 |
+
bodyBytes, err := json.Marshal(requestBody)
|
172 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, bytes.NewBuffer(bodyBytes))
|
173 |
+
if token != "" {
|
174 |
+
p.addArkoseTokenInHeaderIfNeeded(request, token)
|
175 |
+
}
|
176 |
+
if err != nil {
|
177 |
+
return nil, err
|
178 |
+
}
|
179 |
+
}
|
180 |
+
p.buildHeaders(request)
|
181 |
+
p.setCookies(request)
|
182 |
+
return request, nil
|
183 |
+
}
|
184 |
+
func (p *BackendProcess) buildHeaders(request *http.Request) {
|
185 |
+
context.Logger.Debug("BackendProcess buildHeaders")
|
186 |
+
headers := map[string]string{
|
187 |
+
"Host": context.Env.OpenaiHost,
|
188 |
+
"Origin": "https://" + context.Env.OpenaiHost + "/chat",
|
189 |
+
"Authorization": p.GetContext().GinContext.Request.Header.Get("Authorization"),
|
190 |
+
"Connection": "keep-alive",
|
191 |
+
"User-Agent": context.Env.UserAgent,
|
192 |
+
"Content-Type": p.GetContext().GinContext.Request.Header.Get("Content-Type"),
|
193 |
+
}
|
194 |
+
|
195 |
+
for key, value := range headers {
|
196 |
+
request.Header.Set(key, value)
|
197 |
+
}
|
198 |
+
|
199 |
+
if puid := p.GetContext().GinContext.Request.Header.Get("PUID"); puid != "" {
|
200 |
+
request.Header.Set("cookie", "_puid="+puid+";")
|
201 |
+
}
|
202 |
+
}
|
203 |
+
func (p *BackendProcess) jsonResponse(response *http.Response) error {
|
204 |
+
context.Logger.Debug("BackendProcess jsonResponse")
|
205 |
+
var jsonData interface{}
|
206 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
207 |
+
if err != nil {
|
208 |
+
return err
|
209 |
+
}
|
210 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
211 |
+
return nil
|
212 |
+
}
|
213 |
+
|
214 |
+
func (p *BackendProcess) streamResponse(response *http.Response) error {
|
215 |
+
context.Logger.Debug("BackendProcess streamResponse")
|
216 |
+
client := tools.NewSSEClient(response.Body)
|
217 |
+
events := client.Read()
|
218 |
+
for event := range events {
|
219 |
+
if _, err := p.GetContext().GinContext.Writer.Write([]byte("data: " + event.Data + "\n\n")); err != nil {
|
220 |
+
return err
|
221 |
+
}
|
222 |
+
p.GetContext().GinContext.Writer.Flush()
|
223 |
+
}
|
224 |
+
defer client.Close()
|
225 |
+
return nil
|
226 |
+
}
|
227 |
+
func (p *BackendProcess) addArkoseTokenInHeaderIfNeeded(request *http.Request, token string) {
|
228 |
+
context.Logger.Debug("BackendProcess addArkoseTokenInHeaderIfNeeded")
|
229 |
+
request.Header.Set("Openai-Sentinel-Arkose-Token", token)
|
230 |
+
}
|
231 |
+
func (p *BackendProcess) addArkoseTokenIfNeeded(requestBody *map[string]interface{}) (string, error) {
|
232 |
+
context.Logger.Debug("BackendProcess addArkoseTokenIfNeeded")
|
233 |
+
model, exists := (*requestBody)["model"]
|
234 |
+
if !exists {
|
235 |
+
return "", nil
|
236 |
+
}
|
237 |
+
if strings.HasPrefix(model.(string), "gpt-4") || context.Env.ArkoseMust {
|
238 |
+
token, err := funcaptcha.GetOpenAIArkoseToken(4, p.GetContext().RequestHeaders.Get("puid"))
|
239 |
+
if err != nil {
|
240 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Get ArkoseToken Failed"})
|
241 |
+
return "", err
|
242 |
+
}
|
243 |
+
(*requestBody)["arkose_token"] = token
|
244 |
+
return token, nil
|
245 |
+
}
|
246 |
+
return "", nil
|
247 |
+
}
|
248 |
+
func (p *BackendProcess) setCookies(request *http.Request) {
|
249 |
+
context.Logger.Debug("BackendProcess setCookies")
|
250 |
+
for _, cookie := range p.GetContext().GinContext.Request.Cookies() {
|
251 |
+
request.AddCookie(&http.Cookie{
|
252 |
+
Name: cookie.Name,
|
253 |
+
Value: cookie.Value,
|
254 |
+
})
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
+
func (p *BackendProcess) decodeRequestBody(requestBody *map[string]interface{}) error {
|
259 |
+
conversation := p.GetContext()
|
260 |
+
if conversation.RequestBody != shttp.NoBody {
|
261 |
+
if err := json.NewDecoder(conversation.RequestBody).Decode(requestBody); err != nil {
|
262 |
+
conversation.GinContext.JSON(400, gin.H{"error": "JSON invalid"})
|
263 |
+
return err
|
264 |
+
}
|
265 |
+
}
|
266 |
+
return nil
|
267 |
+
}
|
268 |
+
|
269 |
+
type ReverseBackendRequestUrl struct {
|
270 |
+
}
|
271 |
+
|
272 |
+
func (u ReverseBackendRequestUrl) Generate(path string, rawquery string) string {
|
273 |
+
if strings.Contains(path, "/ws") {
|
274 |
+
path = strings.ReplaceAll(path, "/ws", "")
|
275 |
+
}
|
276 |
+
if rawquery == "" {
|
277 |
+
return "https://" + context.Env.OpenaiHost + "/backend-api" + path
|
278 |
+
}
|
279 |
+
return "https://" + context.Env.OpenaiHost + "/backend-api" + path + "?" + rawquery
|
280 |
+
}
|
281 |
+
|
282 |
+
func (p *BackendProcess) Run(com *plugins.Component) {
|
283 |
+
context = com
|
284 |
+
context.Engine.Any("/backend-api/*path", func(c *gin.Context) {
|
285 |
+
conversation := common.GetContextPack(c, ReverseBackendRequestUrl{})
|
286 |
+
common.Do[Context](new(BackendProcess), Context(conversation))
|
287 |
+
})
|
288 |
+
}
|
pkg/plugins/api/officialapi/officialApi.go
ADDED
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package officialapi
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/plugins"
|
6 |
+
"WarpGPT/pkg/tools"
|
7 |
+
"bytes"
|
8 |
+
"encoding/json"
|
9 |
+
http "github.com/bogdanfinn/fhttp"
|
10 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
11 |
+
"github.com/gin-gonic/gin"
|
12 |
+
"io"
|
13 |
+
fhttp "net/http"
|
14 |
+
shttp "net/http"
|
15 |
+
"strings"
|
16 |
+
)
|
17 |
+
|
18 |
+
var context *plugins.Component
|
19 |
+
var OfficialApiProcessInstance OfficialApiProcess
|
20 |
+
|
21 |
+
type Context struct {
|
22 |
+
GinContext *gin.Context
|
23 |
+
RequestUrl string
|
24 |
+
RequestClient tls_client.HttpClient
|
25 |
+
RequestBody io.ReadCloser
|
26 |
+
RequestParam string
|
27 |
+
RequestMethod string
|
28 |
+
RequestHeaders http.Header
|
29 |
+
}
|
30 |
+
type OfficialApiProcess struct {
|
31 |
+
Context Context
|
32 |
+
}
|
33 |
+
|
34 |
+
func (p *OfficialApiProcess) SetContext(conversation Context) {
|
35 |
+
p.Context = conversation
|
36 |
+
}
|
37 |
+
func (p *OfficialApiProcess) GetContext() Context {
|
38 |
+
return p.Context
|
39 |
+
}
|
40 |
+
func (p *OfficialApiProcess) ProcessMethod() {
|
41 |
+
context.Logger.Debug("officialApi")
|
42 |
+
var requestBody map[string]interface{}
|
43 |
+
err := p.decodeRequestBody(&requestBody) //解析请求体
|
44 |
+
if err != nil {
|
45 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Incorrect json format"})
|
46 |
+
return
|
47 |
+
}
|
48 |
+
|
49 |
+
request, err := p.createRequest(requestBody) //创建请求
|
50 |
+
if err != nil {
|
51 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Server error"})
|
52 |
+
return
|
53 |
+
}
|
54 |
+
|
55 |
+
response, err := p.GetContext().RequestClient.Do(request) //发送请求
|
56 |
+
if err != nil {
|
57 |
+
context.Logger.Error(err)
|
58 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Server Error"})
|
59 |
+
return
|
60 |
+
}
|
61 |
+
|
62 |
+
common.CopyResponseHeaders(response, p.GetContext().GinContext) //设置响应头
|
63 |
+
|
64 |
+
if strings.Contains(response.Header.Get("Content-Type"), "text/event-stream") {
|
65 |
+
err = p.streamResponse(response)
|
66 |
+
if err != nil {
|
67 |
+
return
|
68 |
+
}
|
69 |
+
}
|
70 |
+
if strings.Contains(response.Header.Get("Content-Type"), "application/json") {
|
71 |
+
err = p.jsonResponse(response)
|
72 |
+
if err != nil {
|
73 |
+
context.Logger.Warning(err)
|
74 |
+
}
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
78 |
+
func (p *OfficialApiProcess) createRequest(requestBody map[string]interface{}) (*http.Request, error) {
|
79 |
+
context.Logger.Debug("officialApi createRequest")
|
80 |
+
bodyBytes, err := json.Marshal(requestBody)
|
81 |
+
if err != nil {
|
82 |
+
return nil, err
|
83 |
+
}
|
84 |
+
var request *http.Request
|
85 |
+
if p.Context.RequestBody == shttp.NoBody {
|
86 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, nil)
|
87 |
+
} else {
|
88 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, bytes.NewBuffer(bodyBytes))
|
89 |
+
}
|
90 |
+
if err != nil {
|
91 |
+
return nil, err
|
92 |
+
}
|
93 |
+
p.WithHeaders(request)
|
94 |
+
return request, nil
|
95 |
+
}
|
96 |
+
|
97 |
+
func (p *OfficialApiProcess) WithHeaders(rsq *http.Request) {
|
98 |
+
rsq.Header.Set("Authorization", p.Context.RequestHeaders.Get("Authorization"))
|
99 |
+
rsq.Header.Set("Content-Type", p.Context.RequestHeaders.Get("Content-Type"))
|
100 |
+
}
|
101 |
+
|
102 |
+
func (p *OfficialApiProcess) jsonResponse(response *http.Response) error {
|
103 |
+
context.Logger.Debug("officialApi jsonResponse")
|
104 |
+
var jsonData interface{}
|
105 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
106 |
+
if err != nil {
|
107 |
+
return err
|
108 |
+
}
|
109 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
110 |
+
return nil
|
111 |
+
}
|
112 |
+
|
113 |
+
func (p *OfficialApiProcess) streamResponse(response *http.Response) error {
|
114 |
+
context.Logger.Debug("officialApi streamResponse")
|
115 |
+
context.Logger.Infoln("officialApiProcess stream Request")
|
116 |
+
client := tools.NewSSEClient(response.Body)
|
117 |
+
events := client.Read()
|
118 |
+
for event := range events {
|
119 |
+
if _, err := p.GetContext().GinContext.Writer.Write([]byte("data: " + event.Data + "\n\n")); err != nil {
|
120 |
+
return err
|
121 |
+
}
|
122 |
+
p.GetContext().GinContext.Writer.Flush()
|
123 |
+
}
|
124 |
+
defer client.Close()
|
125 |
+
return nil
|
126 |
+
}
|
127 |
+
|
128 |
+
type OfficialApiRequestUrl struct {
|
129 |
+
}
|
130 |
+
|
131 |
+
func (u OfficialApiRequestUrl) Generate(path string, rawquery string) string {
|
132 |
+
if rawquery == "" {
|
133 |
+
return "https://" + context.Env.OpenaiApiHost + "/v1" + path
|
134 |
+
}
|
135 |
+
return "https://" + context.Env.OpenaiApiHost + "/v1" + path + "?" + rawquery
|
136 |
+
}
|
137 |
+
func (p *OfficialApiProcess) decodeRequestBody(requestBody *map[string]interface{}) error {
|
138 |
+
context.Logger.Debug("officialApi decodeRequestBody")
|
139 |
+
conversation := p.GetContext()
|
140 |
+
if conversation.RequestBody != fhttp.NoBody {
|
141 |
+
if err := json.NewDecoder(conversation.RequestBody).Decode(requestBody); err != nil {
|
142 |
+
conversation.GinContext.JSON(400, gin.H{"error": "JSON invalid"})
|
143 |
+
return err
|
144 |
+
}
|
145 |
+
}
|
146 |
+
return nil
|
147 |
+
}
|
148 |
+
|
149 |
+
func (p *OfficialApiProcess) Run(com *plugins.Component) {
|
150 |
+
context = com
|
151 |
+
context.Engine.Any("/v1/*path", func(c *gin.Context) {
|
152 |
+
conversation := common.GetContextPack(c, OfficialApiRequestUrl{})
|
153 |
+
common.Do[Context](new(OfficialApiProcess), Context(conversation))
|
154 |
+
})
|
155 |
+
}
|
pkg/plugins/api/publicapi/publicapi.go
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package publicapi
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/plugins"
|
6 |
+
"bytes"
|
7 |
+
"encoding/json"
|
8 |
+
http "github.com/bogdanfinn/fhttp"
|
9 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
10 |
+
"github.com/gin-gonic/gin"
|
11 |
+
"io"
|
12 |
+
shttp "net/http"
|
13 |
+
"strings"
|
14 |
+
)
|
15 |
+
|
16 |
+
var context *plugins.Component
|
17 |
+
var PublicApiProcessInstance PublicApiProcess
|
18 |
+
|
19 |
+
type Context struct {
|
20 |
+
GinContext *gin.Context
|
21 |
+
RequestUrl string
|
22 |
+
RequestClient tls_client.HttpClient
|
23 |
+
RequestBody io.ReadCloser
|
24 |
+
RequestParam string
|
25 |
+
RequestMethod string
|
26 |
+
RequestHeaders http.Header
|
27 |
+
}
|
28 |
+
type PublicApiProcess struct {
|
29 |
+
Context Context
|
30 |
+
}
|
31 |
+
|
32 |
+
func (p *PublicApiProcess) SetContext(conversation Context) {
|
33 |
+
p.Context = conversation
|
34 |
+
}
|
35 |
+
func (p *PublicApiProcess) GetContext() Context {
|
36 |
+
return p.Context
|
37 |
+
}
|
38 |
+
func (p *PublicApiProcess) ProcessMethod() {
|
39 |
+
context.Logger.Debug("PublicApiProcess")
|
40 |
+
var requestBody map[string]interface{}
|
41 |
+
err := p.decodeRequestBody(&requestBody) //解析请求体
|
42 |
+
if err != nil {
|
43 |
+
return
|
44 |
+
}
|
45 |
+
request, err := p.createRequest(requestBody) //创建请求
|
46 |
+
if err != nil {
|
47 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Server error"})
|
48 |
+
return
|
49 |
+
}
|
50 |
+
response, err := p.GetContext().RequestClient.Do(request) //发送请求
|
51 |
+
if err != nil {
|
52 |
+
var jsonData interface{}
|
53 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
54 |
+
if err != nil {
|
55 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Request json decode error"})
|
56 |
+
return
|
57 |
+
}
|
58 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
59 |
+
return
|
60 |
+
}
|
61 |
+
if strings.Contains(response.Header.Get("Content-Type"), "application/json") {
|
62 |
+
err := p.jsonResponse(response)
|
63 |
+
if err != nil {
|
64 |
+
context.Logger.Warning(err)
|
65 |
+
}
|
66 |
+
}
|
67 |
+
common.CopyResponseHeaders(response, p.GetContext().GinContext) //设置响应头
|
68 |
+
}
|
69 |
+
func (p *PublicApiProcess) createRequest(requestBody map[string]interface{}) (*http.Request, error) {
|
70 |
+
context.Logger.Debug("PublicApiProcess createRequest")
|
71 |
+
bodyBytes, err := json.Marshal(requestBody)
|
72 |
+
if err != nil {
|
73 |
+
return nil, err
|
74 |
+
}
|
75 |
+
bodyReader := bytes.NewReader(bodyBytes)
|
76 |
+
var request *http.Request
|
77 |
+
if p.Context.RequestBody == shttp.NoBody {
|
78 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, nil)
|
79 |
+
} else {
|
80 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, bodyReader)
|
81 |
+
}
|
82 |
+
if err != nil {
|
83 |
+
return nil, err
|
84 |
+
}
|
85 |
+
p.buildHeaders(request)
|
86 |
+
p.setCookies(request)
|
87 |
+
return request, nil
|
88 |
+
}
|
89 |
+
func (p *PublicApiProcess) setCookies(request *http.Request) {
|
90 |
+
context.Logger.Debug("PublicApiProcess setCookies")
|
91 |
+
for _, cookie := range p.GetContext().GinContext.Request.Cookies() {
|
92 |
+
request.AddCookie(&http.Cookie{
|
93 |
+
Name: cookie.Name,
|
94 |
+
Value: cookie.Value,
|
95 |
+
})
|
96 |
+
}
|
97 |
+
}
|
98 |
+
func (p *PublicApiProcess) buildHeaders(request *http.Request) {
|
99 |
+
context.Logger.Debug("PublicApiProcess buildHeaders")
|
100 |
+
headers := map[string]string{
|
101 |
+
"Host": context.Env.OpenaiHost,
|
102 |
+
"Origin": "https://" + context.Env.OpenaiHost + "/chat",
|
103 |
+
"Authorization": p.GetContext().GinContext.Request.Header.Get("Authorization"),
|
104 |
+
"Connection": "keep-alive",
|
105 |
+
"User-Agent": context.Env.UserAgent,
|
106 |
+
"Content-Type": p.GetContext().GinContext.Request.Header.Get("Content-Type"),
|
107 |
+
}
|
108 |
+
for key, value := range headers {
|
109 |
+
request.Header.Set(key, value)
|
110 |
+
}
|
111 |
+
if puid := p.GetContext().GinContext.Request.Header.Get("PUID"); puid != "" {
|
112 |
+
request.Header.Set("cookie", "_puid="+puid+";")
|
113 |
+
}
|
114 |
+
}
|
115 |
+
func (p *PublicApiProcess) jsonResponse(response *http.Response) error {
|
116 |
+
context.Logger.Debug("PublicApiProcess jsonResponse")
|
117 |
+
var jsonData interface{}
|
118 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
119 |
+
if err != nil {
|
120 |
+
return err
|
121 |
+
}
|
122 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
123 |
+
return nil
|
124 |
+
}
|
125 |
+
func (p *PublicApiProcess) decodeRequestBody(requestBody *map[string]interface{}) error {
|
126 |
+
conversation := p.GetContext()
|
127 |
+
if conversation.RequestBody != shttp.NoBody {
|
128 |
+
if err := json.NewDecoder(conversation.RequestBody).Decode(requestBody); err != nil {
|
129 |
+
conversation.GinContext.JSON(400, gin.H{"error": "JSON invalid"})
|
130 |
+
return err
|
131 |
+
}
|
132 |
+
}
|
133 |
+
return nil
|
134 |
+
}
|
135 |
+
|
136 |
+
type ReversePublicApiRequestUrl struct {
|
137 |
+
}
|
138 |
+
|
139 |
+
func (u ReversePublicApiRequestUrl) Generate(path string, rawquery string) string {
|
140 |
+
if rawquery == "" {
|
141 |
+
return "https://" + context.Env.OpenaiHost + "/public-api" + path
|
142 |
+
}
|
143 |
+
return "https://" + context.Env.OpenaiHost + "/public-api" + path + "?" + rawquery
|
144 |
+
}
|
145 |
+
|
146 |
+
func (p *PublicApiProcess) Run(com *plugins.Component) {
|
147 |
+
context = com
|
148 |
+
context.Engine.Any("/public-api/*path", func(c *gin.Context) {
|
149 |
+
conversation := common.GetContextPack(c, ReversePublicApiRequestUrl{})
|
150 |
+
common.Do[Context](new(PublicApiProcess), Context(conversation))
|
151 |
+
})
|
152 |
+
}
|
pkg/plugins/api/rapi/api.go
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package rapi
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/plugins"
|
6 |
+
"bytes"
|
7 |
+
"encoding/json"
|
8 |
+
http "github.com/bogdanfinn/fhttp"
|
9 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
10 |
+
"github.com/gin-gonic/gin"
|
11 |
+
"io"
|
12 |
+
shttp "net/http"
|
13 |
+
"strings"
|
14 |
+
)
|
15 |
+
|
16 |
+
var context *plugins.Component
|
17 |
+
var ApiProcessInstance ApiProcess
|
18 |
+
|
19 |
+
type Context struct {
|
20 |
+
GinContext *gin.Context
|
21 |
+
RequestUrl string
|
22 |
+
RequestClient tls_client.HttpClient
|
23 |
+
RequestBody io.ReadCloser
|
24 |
+
RequestParam string
|
25 |
+
RequestMethod string
|
26 |
+
RequestHeaders http.Header
|
27 |
+
}
|
28 |
+
type ApiProcess struct {
|
29 |
+
Context Context
|
30 |
+
}
|
31 |
+
|
32 |
+
func (p *ApiProcess) SetContext(conversation Context) {
|
33 |
+
p.Context = conversation
|
34 |
+
}
|
35 |
+
func (p *ApiProcess) GetContext() Context {
|
36 |
+
return p.Context
|
37 |
+
}
|
38 |
+
func (p *ApiProcess) ProcessMethod() {
|
39 |
+
context.Logger.Debug("ApiProcess")
|
40 |
+
var requestBody map[string]interface{}
|
41 |
+
err := p.decodeRequestBody(&requestBody) //解析请求体
|
42 |
+
if err != nil {
|
43 |
+
return
|
44 |
+
}
|
45 |
+
request, err := p.createRequest(requestBody) //创建请求
|
46 |
+
if err != nil {
|
47 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Server error"})
|
48 |
+
return
|
49 |
+
}
|
50 |
+
response, err := p.GetContext().RequestClient.Do(request) //发送请求
|
51 |
+
if err != nil {
|
52 |
+
var jsonData interface{}
|
53 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
54 |
+
if err != nil {
|
55 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Request json decode error"})
|
56 |
+
return
|
57 |
+
}
|
58 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
59 |
+
return
|
60 |
+
}
|
61 |
+
if strings.Contains(response.Header.Get("Content-Type"), "application/json") {
|
62 |
+
err := p.jsonResponse(response)
|
63 |
+
if err != nil {
|
64 |
+
context.Logger.Warning(err)
|
65 |
+
}
|
66 |
+
}
|
67 |
+
common.CopyResponseHeaders(response, p.GetContext().GinContext) //设置响应头
|
68 |
+
}
|
69 |
+
func (p *ApiProcess) createRequest(requestBody map[string]interface{}) (*http.Request, error) {
|
70 |
+
context.Logger.Debug("ApiProcess createRequest")
|
71 |
+
bodyBytes, err := json.Marshal(requestBody)
|
72 |
+
if err != nil {
|
73 |
+
return nil, err
|
74 |
+
}
|
75 |
+
bodyReader := bytes.NewReader(bodyBytes)
|
76 |
+
var request *http.Request
|
77 |
+
if p.Context.RequestBody == shttp.NoBody {
|
78 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, nil)
|
79 |
+
} else {
|
80 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, bodyReader)
|
81 |
+
}
|
82 |
+
if err != nil {
|
83 |
+
return nil, err
|
84 |
+
}
|
85 |
+
p.buildHeaders(request)
|
86 |
+
p.setCookies(request)
|
87 |
+
return request, nil
|
88 |
+
}
|
89 |
+
func (p *ApiProcess) setCookies(request *http.Request) {
|
90 |
+
context.Logger.Debug("ApiProcess setCookies")
|
91 |
+
for _, cookie := range p.GetContext().GinContext.Request.Cookies() {
|
92 |
+
request.AddCookie(&http.Cookie{
|
93 |
+
Name: cookie.Name,
|
94 |
+
Value: cookie.Value,
|
95 |
+
})
|
96 |
+
}
|
97 |
+
}
|
98 |
+
func (p *ApiProcess) buildHeaders(request *http.Request) {
|
99 |
+
context.Logger.Debug("ApiProcess buildHeaders")
|
100 |
+
headers := map[string]string{
|
101 |
+
"Host": context.Env.OpenaiHost,
|
102 |
+
"Origin": "https://" + context.Env.OpenaiHost + "/chat",
|
103 |
+
"Authorization": p.GetContext().GinContext.Request.Header.Get("Authorization"),
|
104 |
+
"Connection": "keep-alive",
|
105 |
+
"User-Agent": context.Env.UserAgent,
|
106 |
+
"Content-Type": p.GetContext().GinContext.Request.Header.Get("Content-Type"),
|
107 |
+
}
|
108 |
+
for key, value := range headers {
|
109 |
+
request.Header.Set(key, value)
|
110 |
+
}
|
111 |
+
if puid := p.GetContext().GinContext.Request.Header.Get("PUID"); puid != "" {
|
112 |
+
request.Header.Set("cookie", "_puid="+puid+";")
|
113 |
+
}
|
114 |
+
}
|
115 |
+
func (p *ApiProcess) jsonResponse(response *http.Response) error {
|
116 |
+
context.Logger.Debug("ApiProcess jsonResponse")
|
117 |
+
var jsonData interface{}
|
118 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
119 |
+
if err != nil {
|
120 |
+
return err
|
121 |
+
}
|
122 |
+
p.GetContext().GinContext.JSON(response.StatusCode, jsonData)
|
123 |
+
return nil
|
124 |
+
}
|
125 |
+
func (p *ApiProcess) decodeRequestBody(requestBody *map[string]interface{}) error {
|
126 |
+
conversation := p.GetContext()
|
127 |
+
if conversation.RequestBody != shttp.NoBody {
|
128 |
+
if err := json.NewDecoder(conversation.RequestBody).Decode(requestBody); err != nil {
|
129 |
+
conversation.GinContext.JSON(400, gin.H{"error": "JSON invalid"})
|
130 |
+
return err
|
131 |
+
}
|
132 |
+
}
|
133 |
+
return nil
|
134 |
+
}
|
135 |
+
|
136 |
+
type ReverseApiRequestUrl struct {
|
137 |
+
}
|
138 |
+
|
139 |
+
func (u ReverseApiRequestUrl) Generate(path string, rawquery string) string {
|
140 |
+
if rawquery == "" {
|
141 |
+
return "https://" + context.Env.OpenaiHost + "/api" + path
|
142 |
+
}
|
143 |
+
return "https://" + context.Env.OpenaiHost + "/api" + path + "?" + rawquery
|
144 |
+
}
|
145 |
+
|
146 |
+
func (p *ApiProcess) Run(com *plugins.Component) {
|
147 |
+
context = com
|
148 |
+
context.Engine.Any("/api/*path", func(c *gin.Context) {
|
149 |
+
conversation := common.GetContextPack(c, ReverseApiRequestUrl{})
|
150 |
+
common.Do[Context](new(ApiProcess), Context(conversation))
|
151 |
+
})
|
152 |
+
}
|
pkg/plugins/api/session/session.go
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package session
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/plugins"
|
6 |
+
"WarpGPT/pkg/tools"
|
7 |
+
"encoding/json"
|
8 |
+
http "github.com/bogdanfinn/fhttp"
|
9 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
10 |
+
"github.com/gin-gonic/gin"
|
11 |
+
"io"
|
12 |
+
shttp "net/http"
|
13 |
+
)
|
14 |
+
|
15 |
+
var context *plugins.Component
|
16 |
+
var SessionTokenInstance SessionToken
|
17 |
+
|
18 |
+
type Context struct {
|
19 |
+
GinContext *gin.Context
|
20 |
+
RequestUrl string
|
21 |
+
RequestClient tls_client.HttpClient
|
22 |
+
RequestBody io.ReadCloser
|
23 |
+
RequestParam string
|
24 |
+
RequestMethod string
|
25 |
+
RequestHeaders http.Header
|
26 |
+
}
|
27 |
+
type SessionToken struct {
|
28 |
+
Context Context
|
29 |
+
}
|
30 |
+
|
31 |
+
func (p *SessionToken) GetContext() Context {
|
32 |
+
return p.Context
|
33 |
+
}
|
34 |
+
func (p *SessionToken) SetContext(conversation Context) {
|
35 |
+
p.Context = conversation
|
36 |
+
}
|
37 |
+
|
38 |
+
func (p *SessionToken) ProcessMethod() {
|
39 |
+
context.Logger.Debug("SessionToken")
|
40 |
+
var requestBody map[string]interface{}
|
41 |
+
if err := p.decodeRequestBody(&requestBody); err != nil {
|
42 |
+
return
|
43 |
+
}
|
44 |
+
var auth *tools.Authenticator
|
45 |
+
username, usernameExists := requestBody["username"]
|
46 |
+
password, passwordExists := requestBody["password"]
|
47 |
+
puid, puidExists := requestBody["puid"]
|
48 |
+
refreshCookie, refreshCookieExists := requestBody["refreshCookie"]
|
49 |
+
if !refreshCookieExists {
|
50 |
+
if usernameExists && passwordExists {
|
51 |
+
if puidExists {
|
52 |
+
auth = tools.NewAuthenticator(username.(string), password.(string), puid.(string))
|
53 |
+
} else {
|
54 |
+
auth = tools.NewAuthenticator(username.(string), password.(string), "")
|
55 |
+
}
|
56 |
+
if err := auth.Begin(); err != nil {
|
57 |
+
p.GetContext().GinContext.JSON(400, err)
|
58 |
+
return
|
59 |
+
}
|
60 |
+
auth.GetModels()
|
61 |
+
all := auth.GetAuthResult()
|
62 |
+
var result map[string]interface{}
|
63 |
+
accessToken := all.AccessToken
|
64 |
+
model := all.Model
|
65 |
+
refreshToken := all.FreshToken
|
66 |
+
result = accessToken
|
67 |
+
result["refreshCookie"] = refreshToken
|
68 |
+
result["models"] = model["models"]
|
69 |
+
p.GetContext().GinContext.JSON(200, result)
|
70 |
+
} else {
|
71 |
+
p.GetContext().GinContext.JSON(400, gin.H{"error": "Please provide a refreshCookie or username and password."})
|
72 |
+
return
|
73 |
+
}
|
74 |
+
} else {
|
75 |
+
auth = tools.NewAuthenticator("", "", "")
|
76 |
+
err := auth.GetAccessTokenByRefreshToken(refreshCookie.(string))
|
77 |
+
if err != nil {
|
78 |
+
p.GetContext().GinContext.JSON(400, err)
|
79 |
+
return
|
80 |
+
}
|
81 |
+
auth.GetModels()
|
82 |
+
all := auth.GetAuthResult()
|
83 |
+
var result map[string]interface{}
|
84 |
+
accessToken := all.AccessToken
|
85 |
+
model := all.Model
|
86 |
+
refreshToken := all.FreshToken
|
87 |
+
result = accessToken
|
88 |
+
result["refreshCookie"] = refreshToken
|
89 |
+
result["models"] = model["models"]
|
90 |
+
p.GetContext().GinContext.JSON(200, result)
|
91 |
+
}
|
92 |
+
}
|
93 |
+
func (p *SessionToken) decodeRequestBody(requestBody *map[string]interface{}) error {
|
94 |
+
conversation := p.GetContext()
|
95 |
+
if conversation.RequestBody != shttp.NoBody {
|
96 |
+
if err := json.NewDecoder(conversation.RequestBody).Decode(requestBody); err != nil {
|
97 |
+
conversation.GinContext.JSON(400, gin.H{"error": "JSON invalid"})
|
98 |
+
return err
|
99 |
+
}
|
100 |
+
}
|
101 |
+
return nil
|
102 |
+
}
|
103 |
+
|
104 |
+
type NotHaveUrl struct {
|
105 |
+
}
|
106 |
+
|
107 |
+
func (u NotHaveUrl) Generate(path string, rawquery string) string {
|
108 |
+
return ""
|
109 |
+
}
|
110 |
+
func (p *SessionToken) Run(com *plugins.Component) {
|
111 |
+
context = com
|
112 |
+
context.Engine.POST("/getsession", func(c *gin.Context) {
|
113 |
+
conversation := common.GetContextPack(c, NotHaveUrl{})
|
114 |
+
common.Do[Context](new(SessionToken), Context(conversation))
|
115 |
+
})
|
116 |
+
}
|
pkg/plugins/api/unofficialapi/chatrsp.go
ADDED
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package unofficialapi
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/json"
|
5 |
+
"fmt"
|
6 |
+
"log"
|
7 |
+
"math/rand"
|
8 |
+
"strconv"
|
9 |
+
"strings"
|
10 |
+
"time"
|
11 |
+
|
12 |
+
"github.com/google/uuid"
|
13 |
+
)
|
14 |
+
|
15 |
+
type ChatReqTemplate struct {
|
16 |
+
Id string `json:"id"`
|
17 |
+
Author struct {
|
18 |
+
Role string `json:"role"`
|
19 |
+
} `json:"author"`
|
20 |
+
Content struct {
|
21 |
+
ContentType string `json:"content_type"`
|
22 |
+
Parts []interface{} `json:"parts"`
|
23 |
+
} `json:"content"`
|
24 |
+
Metadata interface{} `json:"metadata"`
|
25 |
+
}
|
26 |
+
type ChatReqStr struct {
|
27 |
+
Action string `json:"action"`
|
28 |
+
Messages []ChatReqTemplate `json:"messages"`
|
29 |
+
ParentMessageId string `json:"parent_message_id"`
|
30 |
+
Model string `json:"model"`
|
31 |
+
TimezoneOffsetMin int `json:"timezone_offset_min"`
|
32 |
+
HistoryAndTrainingDisabled bool `json:"history_and_training_disabled"`
|
33 |
+
ArkoseToken string `json:"arkose_token"`
|
34 |
+
ConversationMode struct {
|
35 |
+
Kind string `json:"kind"`
|
36 |
+
} `json:"conversation_mode"`
|
37 |
+
ForceParagen bool `json:"force_paragen"`
|
38 |
+
ForceRateLimit bool `json:"force_rate_limit"`
|
39 |
+
}
|
40 |
+
|
41 |
+
type ChatRespStr struct {
|
42 |
+
Message struct {
|
43 |
+
Id string `json:"id"`
|
44 |
+
Author struct {
|
45 |
+
Role string `json:"role"`
|
46 |
+
Name interface{} `json:"name"`
|
47 |
+
Metadata struct {
|
48 |
+
} `json:"metadata"`
|
49 |
+
} `json:"author"`
|
50 |
+
CreateTime float64 `json:"create_time"`
|
51 |
+
UpdateTime interface{} `json:"update_time"`
|
52 |
+
Content struct {
|
53 |
+
ContentType string `json:"content_type"`
|
54 |
+
Parts []string `json:"parts"`
|
55 |
+
} `json:"content"`
|
56 |
+
Status string `json:"status"`
|
57 |
+
EndTurn bool `json:"end_turn"`
|
58 |
+
Weight float64 `json:"weight"`
|
59 |
+
Metadata struct {
|
60 |
+
FinishDetails struct {
|
61 |
+
Type string `json:"type"`
|
62 |
+
StopTokens []int `json:"stop_tokens"`
|
63 |
+
} `json:"finish_details"`
|
64 |
+
IsComplete bool `json:"is_complete"`
|
65 |
+
MessageType string `json:"message_type"`
|
66 |
+
ModelSlug string `json:"model_slug"`
|
67 |
+
ParentId string `json:"parent_id"`
|
68 |
+
Timestamp string `json:"timestamp_"`
|
69 |
+
IsUserSystemMessage bool `json:"is_user_system_message"`
|
70 |
+
UserContextMessageData struct {
|
71 |
+
AboutModelMessage string `json:"about_model_message"`
|
72 |
+
} `json:"user_context_message_data"`
|
73 |
+
} `json:"metadata"`
|
74 |
+
Recipient string `json:"recipient"`
|
75 |
+
} `json:"message"`
|
76 |
+
ConversationId string `json:"conversation_id"`
|
77 |
+
Error interface{} `json:"error"`
|
78 |
+
}
|
79 |
+
type ChatEndRespStr struct {
|
80 |
+
ConversationId string `json:"conversation_id"`
|
81 |
+
MessageId string `json:"message_id"`
|
82 |
+
IsCompletion bool `json:"is_completion"`
|
83 |
+
ModerationResponse struct {
|
84 |
+
Flagged bool `json:"flagged"`
|
85 |
+
Blocked bool `json:"blocked"`
|
86 |
+
ModerationId string `json:"moderation_id"`
|
87 |
+
} `json:"moderation_response"`
|
88 |
+
}
|
89 |
+
type ChatUserSystemMsgReqStr struct {
|
90 |
+
AboutUserMessage string `json:"about_user_message"`
|
91 |
+
AboutModelMessage string `json:"about_model_message"`
|
92 |
+
Enabled bool `json:"enabled"`
|
93 |
+
}
|
94 |
+
type ChatUserSystemMsgRespStr struct {
|
95 |
+
Object string `json:"object"`
|
96 |
+
Enabled bool `json:"enabled"`
|
97 |
+
AboutUserMessage string `json:"about_user_message"`
|
98 |
+
AboutModelMessage string `json:"about_model_message"`
|
99 |
+
}
|
100 |
+
type ChatDetectedErrorRespStr struct {
|
101 |
+
Message interface{} `json:"message"`
|
102 |
+
ConversationId string `json:"conversation_id"`
|
103 |
+
Error string `json:"error"`
|
104 |
+
}
|
105 |
+
type DALLERespStr struct {
|
106 |
+
Message struct {
|
107 |
+
Id string `json:"id"`
|
108 |
+
Author struct {
|
109 |
+
Role string `json:"role"`
|
110 |
+
Name string `json:"name"`
|
111 |
+
Metadata struct {
|
112 |
+
} `json:"metadata"`
|
113 |
+
} `json:"author"`
|
114 |
+
CreateTime interface{} `json:"create_time"`
|
115 |
+
UpdateTime interface{} `json:"update_time"`
|
116 |
+
Content struct {
|
117 |
+
ContentType string `json:"content_type"`
|
118 |
+
Parts []struct {
|
119 |
+
ContentType string `json:"content_type"`
|
120 |
+
AssetPointer string `json:"asset_pointer"`
|
121 |
+
SizeBytes int `json:"size_bytes"`
|
122 |
+
Width int `json:"width"`
|
123 |
+
Height int `json:"height"`
|
124 |
+
Fovea int `json:"fovea"`
|
125 |
+
Metadata struct {
|
126 |
+
Dalle struct {
|
127 |
+
GenId string `json:"gen_id"`
|
128 |
+
Prompt string `json:"prompt"`
|
129 |
+
Seed int64 `json:"seed"`
|
130 |
+
SerializationTitle string `json:"serialization_title"`
|
131 |
+
} `json:"dalle"`
|
132 |
+
} `json:"metadata"`
|
133 |
+
} `json:"parts"`
|
134 |
+
} `json:"content"`
|
135 |
+
Status string `json:"status"`
|
136 |
+
EndTurn interface{} `json:"end_turn"`
|
137 |
+
Weight float64 `json:"weight"`
|
138 |
+
Metadata struct {
|
139 |
+
MessageType string `json:"message_type"`
|
140 |
+
ModelSlug string `json:"model_slug"`
|
141 |
+
ParentId string `json:"parent_id"`
|
142 |
+
} `json:"metadata"`
|
143 |
+
Recipient string `json:"recipient"`
|
144 |
+
} `json:"message"`
|
145 |
+
ConversationId string `json:"conversation_id"`
|
146 |
+
Error interface{} `json:"error"`
|
147 |
+
}
|
148 |
+
type StrChoices struct {
|
149 |
+
Index int `json:"index"`
|
150 |
+
Message struct {
|
151 |
+
Role string `json:"role"`
|
152 |
+
Content string `json:"content"`
|
153 |
+
} `json:"message"`
|
154 |
+
FinishReason string `json:"finish_reason"`
|
155 |
+
}
|
156 |
+
type ApiRespStr struct {
|
157 |
+
Id string `json:"id"`
|
158 |
+
Object string `json:"object"`
|
159 |
+
Created int64 `json:"created"`
|
160 |
+
Model string `json:"model"`
|
161 |
+
SystemFingerprint string `json:"system_fingerprint"`
|
162 |
+
Choices []StrChoices `json:"choices"`
|
163 |
+
Usage struct {
|
164 |
+
PromptTokens int `json:"prompt_tokens"`
|
165 |
+
CompletionTokens int `json:"completion_tokens"`
|
166 |
+
TotalTokens int `json:"total_tokens"`
|
167 |
+
} `json:"usage"`
|
168 |
+
}
|
169 |
+
type StreamChoice struct {
|
170 |
+
Delta struct {
|
171 |
+
Content string `json:"content"`
|
172 |
+
} `json:"delta"`
|
173 |
+
Index int `json:"index"`
|
174 |
+
FinishReason interface{} `json:"finish_reason"`
|
175 |
+
}
|
176 |
+
type ApiRespStrStream struct {
|
177 |
+
Id string `json:"id"`
|
178 |
+
Object string `json:"object"`
|
179 |
+
Created int64 `json:"created"`
|
180 |
+
Model string `json:"model"`
|
181 |
+
SystemFingerprint string `json:"system_fingerprint"`
|
182 |
+
Choices []StreamChoice `json:"choices"`
|
183 |
+
}
|
184 |
+
type ApiRespStrStreamEnd struct {
|
185 |
+
Id string `json:"id"`
|
186 |
+
Object string `json:"object"`
|
187 |
+
Created int64 `json:"created"`
|
188 |
+
Model string `json:"model"`
|
189 |
+
SystemFingerprint interface{} `json:"system_fingerprint"`
|
190 |
+
Choices []struct {
|
191 |
+
Index int `json:"index"`
|
192 |
+
Delta struct {
|
193 |
+
} `json:"delta"`
|
194 |
+
FinishReason string `json:"finish_reason"`
|
195 |
+
} `json:"choices"`
|
196 |
+
}
|
197 |
+
type ApiImageGenerationRespStr struct {
|
198 |
+
Created int64 `json:"created"`
|
199 |
+
Data []ApiImageItem `json:"data"`
|
200 |
+
}
|
201 |
+
type ApiImageItem struct {
|
202 |
+
RevisedPrompt string `json:"revised_prompt"`
|
203 |
+
Url string `json:"url"`
|
204 |
+
}
|
205 |
+
type ApiImageGenerationErrorRespStr struct {
|
206 |
+
Error struct {
|
207 |
+
Code interface{} `json:"code"`
|
208 |
+
Message string `json:"message"`
|
209 |
+
Param interface{} `json:"param"`
|
210 |
+
Type string `json:"type"`
|
211 |
+
} `json:"error"`
|
212 |
+
}
|
213 |
+
type ImageDownloadUrl struct {
|
214 |
+
Status string `json:"status"`
|
215 |
+
DownloadUrl string `json:"download_url"`
|
216 |
+
Metadata struct {
|
217 |
+
} `json:"metadata"`
|
218 |
+
FileName string `json:"file_name"`
|
219 |
+
CreationTime string `json:"creation_time"`
|
220 |
+
}
|
221 |
+
|
222 |
+
func GetChatReqStr(model string) *ChatReqStr {
|
223 |
+
jsonStr := `{
|
224 |
+
"action": "next",
|
225 |
+
"messages": [],
|
226 |
+
"parent_message_id": "",
|
227 |
+
"model": "gpt-4-code-interpreter",
|
228 |
+
"timezone_offset_min": -480,
|
229 |
+
"history_and_training_disabled": true,
|
230 |
+
"arkose_token": "",
|
231 |
+
"conversation_mode": {
|
232 |
+
"kind": "primary_assistant"
|
233 |
+
},
|
234 |
+
"force_paragen": false,
|
235 |
+
"force_rate_limit": false
|
236 |
+
}`
|
237 |
+
|
238 |
+
t := new(ChatReqStr)
|
239 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
240 |
+
t.ParentMessageId = uuid.New().String()
|
241 |
+
t.Model = model
|
242 |
+
if err != nil {
|
243 |
+
log.Printf("Error parsing JSON: %v", err)
|
244 |
+
}
|
245 |
+
return t
|
246 |
+
}
|
247 |
+
func GetChatReqTemplate() *ChatReqTemplate {
|
248 |
+
jsonStr := `{
|
249 |
+
"id": "",
|
250 |
+
"author": {
|
251 |
+
"role": ""
|
252 |
+
},
|
253 |
+
"content": {
|
254 |
+
"content_type": "text",
|
255 |
+
"parts": []
|
256 |
+
},
|
257 |
+
"metadata": {}
|
258 |
+
}`
|
259 |
+
t := new(ChatReqTemplate)
|
260 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
261 |
+
t.Id = uuid.New().String()
|
262 |
+
if err != nil {
|
263 |
+
log.Printf("Error parsing JSON: %v", err)
|
264 |
+
}
|
265 |
+
return t
|
266 |
+
}
|
267 |
+
func GetChatFileReqTemplate() *ChatReqTemplate {
|
268 |
+
jsonStr := `{
|
269 |
+
"id": "",
|
270 |
+
"author": {
|
271 |
+
"role": ""
|
272 |
+
},
|
273 |
+
"content": {
|
274 |
+
"content_type": "multimodal_text",
|
275 |
+
"parts": [
|
276 |
+
]
|
277 |
+
},
|
278 |
+
"metadata": {
|
279 |
+
"attachments": [
|
280 |
+
]
|
281 |
+
}
|
282 |
+
}`
|
283 |
+
t := new(ChatReqTemplate)
|
284 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
285 |
+
t.Id = uuid.New().String()
|
286 |
+
if err != nil {
|
287 |
+
log.Printf("Error parsing JSON: %v", err)
|
288 |
+
}
|
289 |
+
return t
|
290 |
+
}
|
291 |
+
|
292 |
+
func GetChatRespStr() *ChatRespStr {
|
293 |
+
jsonStr := `{
|
294 |
+
"message":
|
295 |
+
{
|
296 |
+
"id": "",
|
297 |
+
"author":
|
298 |
+
{
|
299 |
+
"role": "assistant",
|
300 |
+
"name": null,
|
301 |
+
"metadata": {}
|
302 |
+
},
|
303 |
+
"create_time": 1699032699.636848,
|
304 |
+
"update_time": null,
|
305 |
+
"content": {
|
306 |
+
"content_type": "text",
|
307 |
+
"parts": []
|
308 |
+
},
|
309 |
+
"status": "finished_successfully",
|
310 |
+
"end_turn": true,
|
311 |
+
"weight": 1.0,
|
312 |
+
"metadata": {
|
313 |
+
"finish_details":
|
314 |
+
{
|
315 |
+
"type": "stop",
|
316 |
+
"stop_tokens": [100260]
|
317 |
+
},
|
318 |
+
"is_complete": true,
|
319 |
+
"message_type": "next",
|
320 |
+
"timestamp_": "absolute",
|
321 |
+
"message_type": null,
|
322 |
+
"is_user_system_message": true,
|
323 |
+
"user_context_message_data": {
|
324 |
+
"about_model_message": "Strict adherence to Instructions"
|
325 |
+
}
|
326 |
+
}, "recipient": "all"
|
327 |
+
},
|
328 |
+
"conversation_id": "611228f2-94fd-44ed-b5d9-4f229ef3c400",
|
329 |
+
"error": null
|
330 |
+
}`
|
331 |
+
t := new(ChatRespStr)
|
332 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
333 |
+
nowTime := fmt.Sprintf("%.6f\n", float64(time.Now().UnixNano())/1e9)
|
334 |
+
floatTime, _ := strconv.ParseFloat(nowTime, 64)
|
335 |
+
t.Message.CreateTime = floatTime
|
336 |
+
if err != nil {
|
337 |
+
log.Printf("Error parsing JSON: %v", err)
|
338 |
+
}
|
339 |
+
return t
|
340 |
+
}
|
341 |
+
func GetChatEndRespStr() *ChatEndRespStr {
|
342 |
+
jsonStr := `{
|
343 |
+
"conversation_id": "",
|
344 |
+
"message_id": "",
|
345 |
+
"is_completion": true,
|
346 |
+
"moderation_response": {
|
347 |
+
"flagged": false,
|
348 |
+
"blocked": false,
|
349 |
+
"moderation_id": ""
|
350 |
+
}
|
351 |
+
}`
|
352 |
+
t := new(ChatEndRespStr)
|
353 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
354 |
+
if err != nil {
|
355 |
+
log.Printf("Error parsing JSON: %v", err)
|
356 |
+
}
|
357 |
+
return t
|
358 |
+
}
|
359 |
+
func GetChatUserSystemMsgReqStr() *ChatUserSystemMsgReqStr {
|
360 |
+
jsonStr := `{
|
361 |
+
"about_user_message": "",
|
362 |
+
"about_model_message": "",
|
363 |
+
"enabled": true
|
364 |
+
}`
|
365 |
+
t := new(ChatUserSystemMsgReqStr)
|
366 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
367 |
+
if err != nil {
|
368 |
+
log.Printf("Error parsing JSON: %v", err)
|
369 |
+
}
|
370 |
+
return t
|
371 |
+
}
|
372 |
+
func GetApiRespStr(id string) *ApiRespStr {
|
373 |
+
jsonStr := `{
|
374 |
+
"id": "",
|
375 |
+
"object": "chat.completion",
|
376 |
+
"created": 1699074998,
|
377 |
+
"model": "",
|
378 |
+
"system_fingerprint": null,
|
379 |
+
"choices": [
|
380 |
+
],
|
381 |
+
"usage": {
|
382 |
+
"prompt_tokens": 0,
|
383 |
+
"completion_tokens": 0,
|
384 |
+
"total_tokens": 0
|
385 |
+
}
|
386 |
+
}`
|
387 |
+
t := new(ApiRespStr)
|
388 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
389 |
+
t.Id = id
|
390 |
+
t.Created = time.Now().Unix()
|
391 |
+
if err != nil {
|
392 |
+
log.Printf("Error parsing JSON: %v", err)
|
393 |
+
}
|
394 |
+
return t
|
395 |
+
}
|
396 |
+
func IdGenerator() string {
|
397 |
+
const prefix = "chatcmpl-"
|
398 |
+
const characters = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
399 |
+
var uniqueString strings.Builder
|
400 |
+
|
401 |
+
rand.Seed(time.Now().UnixNano()) // 初始化随机数生成器
|
402 |
+
for i := 0; i < 29; i++ {
|
403 |
+
uniqueString.WriteByte(characters[rand.Intn(len(characters))])
|
404 |
+
}
|
405 |
+
|
406 |
+
log.Println("id_generator")
|
407 |
+
return prefix + uniqueString.String()
|
408 |
+
}
|
409 |
+
func GetApiRespStrStream(id string) *ApiRespStrStream {
|
410 |
+
jsonStr := `{
|
411 |
+
"id": "",
|
412 |
+
"object": "chat.completion.chunk",
|
413 |
+
"created": 1701705204,
|
414 |
+
"model": "",
|
415 |
+
"system_fingerprint": null,
|
416 |
+
"choices": [
|
417 |
+
{
|
418 |
+
"index": 0,
|
419 |
+
"delta": {
|
420 |
+
"role": "assistant",
|
421 |
+
"content": ""
|
422 |
+
},
|
423 |
+
"finish_reason": null
|
424 |
+
}
|
425 |
+
]
|
426 |
+
}`
|
427 |
+
t := new(ApiRespStrStream)
|
428 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
429 |
+
t.Id = id
|
430 |
+
t.Created = time.Now().Unix()
|
431 |
+
if err != nil {
|
432 |
+
log.Printf("Error parsing JSON: %v", err)
|
433 |
+
}
|
434 |
+
return t
|
435 |
+
}
|
436 |
+
func GetApiRespStrStreamEnd(id string) *ApiRespStrStreamEnd {
|
437 |
+
jsonStr := `{
|
438 |
+
"id": "",
|
439 |
+
"object": "chat.completion.chunk",
|
440 |
+
"created": 1701705204,
|
441 |
+
"model": "",
|
442 |
+
"system_fingerprint": null,
|
443 |
+
"choices": [
|
444 |
+
{
|
445 |
+
"index": 0,
|
446 |
+
"delta": {},
|
447 |
+
"finish_reason": "stop"
|
448 |
+
}
|
449 |
+
]
|
450 |
+
}`
|
451 |
+
t := new(ApiRespStrStreamEnd)
|
452 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
453 |
+
t.Id = id
|
454 |
+
t.Created = time.Now().Unix()
|
455 |
+
if err != nil {
|
456 |
+
log.Printf("Error parsing JSON: %v", err)
|
457 |
+
}
|
458 |
+
return t
|
459 |
+
}
|
460 |
+
func GetApiImageGenerationRespStr() *ApiImageGenerationRespStr {
|
461 |
+
jsonStr := `{
|
462 |
+
"created": 1700809991,
|
463 |
+
"data": [
|
464 |
+
{
|
465 |
+
"revised_prompt": "",
|
466 |
+
"url": ""
|
467 |
+
}
|
468 |
+
]
|
469 |
+
}`
|
470 |
+
t := new(ApiImageGenerationRespStr)
|
471 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
472 |
+
t.Created = time.Now().Unix()
|
473 |
+
if err != nil {
|
474 |
+
log.Printf("Error parsing JSON: %v", err)
|
475 |
+
}
|
476 |
+
return t
|
477 |
+
}
|
478 |
+
func GetStreamChoice() *StreamChoice {
|
479 |
+
jsonStr := ` {
|
480 |
+
"index": 0,
|
481 |
+
"delta": {
|
482 |
+
"content": ""
|
483 |
+
},
|
484 |
+
"finish_reason": null
|
485 |
+
}`
|
486 |
+
t := new(StreamChoice)
|
487 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
488 |
+
if err != nil {
|
489 |
+
log.Printf("Error parsing JSON: %v", err)
|
490 |
+
}
|
491 |
+
return t
|
492 |
+
}
|
493 |
+
func GetStrChoices() *StrChoices {
|
494 |
+
jsonStr := `{
|
495 |
+
"finish_reason": "stop",
|
496 |
+
"index": 0,
|
497 |
+
"message": {
|
498 |
+
"content": "",
|
499 |
+
"role": "assistant"
|
500 |
+
}
|
501 |
+
}`
|
502 |
+
t := new(StrChoices)
|
503 |
+
err := json.Unmarshal([]byte(jsonStr), &t)
|
504 |
+
if err != nil {
|
505 |
+
log.Printf("Error parsing JSON: %v", err)
|
506 |
+
}
|
507 |
+
return t
|
508 |
+
}
|
pkg/plugins/api/unofficialapi/unofficialapi.go
ADDED
@@ -0,0 +1,530 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package unofficialapi
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/funcaptcha"
|
6 |
+
"WarpGPT/pkg/plugins"
|
7 |
+
"WarpGPT/pkg/tools"
|
8 |
+
"bytes"
|
9 |
+
"encoding/json"
|
10 |
+
"errors"
|
11 |
+
"fmt"
|
12 |
+
http "github.com/bogdanfinn/fhttp"
|
13 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
14 |
+
"github.com/pkoukk/tiktoken-go"
|
15 |
+
"io"
|
16 |
+
shttp "net/http"
|
17 |
+
"strings"
|
18 |
+
"time"
|
19 |
+
|
20 |
+
"WarpGPT/pkg/logger"
|
21 |
+
"WarpGPT/pkg/plugins/service/wsstostream"
|
22 |
+
"github.com/gin-gonic/gin"
|
23 |
+
)
|
24 |
+
|
25 |
+
var context *plugins.Component
|
26 |
+
var UnofficialApiProcessInstance UnofficialApiProcess
|
27 |
+
var tke, _ = tiktoken.GetEncoding("cl100k_base")
|
28 |
+
|
29 |
+
type WsResponse struct {
|
30 |
+
ConversationId string `json:"conversation_id"`
|
31 |
+
ExpiresAt time.Time `json:"expires_at"`
|
32 |
+
ResponseId string `json:"response_id"`
|
33 |
+
WssUrl string `json:"wss_url"`
|
34 |
+
}
|
35 |
+
type Context struct {
|
36 |
+
GinContext *gin.Context
|
37 |
+
RequestUrl string
|
38 |
+
RequestClient tls_client.HttpClient
|
39 |
+
RequestBody io.ReadCloser
|
40 |
+
RequestParam string
|
41 |
+
RequestMethod string
|
42 |
+
RequestHeaders http.Header
|
43 |
+
}
|
44 |
+
type UnofficialApiProcess struct {
|
45 |
+
Context Context
|
46 |
+
WS *wsstostream.WssToStream
|
47 |
+
Response *http.Response
|
48 |
+
ID string
|
49 |
+
Model string
|
50 |
+
PromptTokens int
|
51 |
+
CompletionTokens int
|
52 |
+
OldString string
|
53 |
+
Mode string
|
54 |
+
ImagePointerList []ImagePointer
|
55 |
+
}
|
56 |
+
type ImagePointer struct {
|
57 |
+
Pointer string
|
58 |
+
Prompt string
|
59 |
+
}
|
60 |
+
type Result struct {
|
61 |
+
ApiRespStrStream ApiRespStrStream
|
62 |
+
ApiRespStrStreamEnd ApiRespStrStreamEnd
|
63 |
+
ApiImageGenerationRespStr ApiImageGenerationRespStr
|
64 |
+
Pass bool
|
65 |
+
}
|
66 |
+
|
67 |
+
func (p *UnofficialApiProcess) SetContext(conversation Context) {
|
68 |
+
p.Context = conversation
|
69 |
+
}
|
70 |
+
func (p *UnofficialApiProcess) GetContext() Context {
|
71 |
+
return p.Context
|
72 |
+
}
|
73 |
+
|
74 |
+
func (p *UnofficialApiProcess) ProcessMethod() {
|
75 |
+
context.Logger.Debug("UnofficialApiProcess")
|
76 |
+
var requestBody map[string]interface{}
|
77 |
+
err := p.decodeRequestBody(&requestBody)
|
78 |
+
if err != nil {
|
79 |
+
return
|
80 |
+
}
|
81 |
+
p.ID = IdGenerator()
|
82 |
+
_, exists := requestBody["model"]
|
83 |
+
if exists {
|
84 |
+
p.Model, _ = requestBody["model"].(string)
|
85 |
+
} else {
|
86 |
+
p.GetContext().GinContext.JSON(400, gin.H{"error": "Model not provided"})
|
87 |
+
return
|
88 |
+
}
|
89 |
+
if strings.Contains(p.GetContext().RequestParam, "chat/completions") {
|
90 |
+
p.Mode = "chat"
|
91 |
+
if err = p.chatApiProcess(requestBody); err != nil {
|
92 |
+
logger.Log.Error(err)
|
93 |
+
return
|
94 |
+
}
|
95 |
+
}
|
96 |
+
if strings.Contains(p.GetContext().RequestParam, "images/generations") {
|
97 |
+
p.Mode = "image"
|
98 |
+
if err = p.imageApiProcess(requestBody); err != nil {
|
99 |
+
logger.Log.Error(err)
|
100 |
+
return
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
104 |
+
|
105 |
+
func (p *UnofficialApiProcess) imageApiProcess(requestBody map[string]interface{}) error {
|
106 |
+
context.Logger.Debug("UnofficialApiProcess imageApiProcess")
|
107 |
+
response, err := p.MakeRequest(requestBody)
|
108 |
+
if err != nil {
|
109 |
+
return err
|
110 |
+
}
|
111 |
+
result := new(Result)
|
112 |
+
result.ApiImageGenerationRespStr = ApiImageGenerationRespStr{}
|
113 |
+
err = p.response(response, func(p *UnofficialApiProcess, a string) bool {
|
114 |
+
p.jsonImageProcess(a)
|
115 |
+
return false
|
116 |
+
})
|
117 |
+
if err = p.getImageUrlByPointer(&p.ImagePointerList, result); err != nil {
|
118 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "get image url failed"})
|
119 |
+
context.Logger.Warning(err)
|
120 |
+
}
|
121 |
+
if result.ApiImageGenerationRespStr.Created != 0 {
|
122 |
+
p.GetContext().GinContext.Header("Content-Type", "application/json")
|
123 |
+
p.GetContext().GinContext.JSON(response.StatusCode, result.ApiImageGenerationRespStr)
|
124 |
+
}
|
125 |
+
if err != nil {
|
126 |
+
return err
|
127 |
+
}
|
128 |
+
return nil
|
129 |
+
}
|
130 |
+
|
131 |
+
func (p *UnofficialApiProcess) chatApiProcess(requestBody map[string]interface{}) error {
|
132 |
+
context.Logger.Debug("UnofficialApiProcess chatApiProcess")
|
133 |
+
response, err := p.MakeRequest(requestBody)
|
134 |
+
if err != nil {
|
135 |
+
return err
|
136 |
+
}
|
137 |
+
value, exists := requestBody["stream"]
|
138 |
+
|
139 |
+
if exists && value.(bool) {
|
140 |
+
err = p.response(response, func(p *UnofficialApiProcess, a string) bool {
|
141 |
+
data := p.streamChatProcess(a)
|
142 |
+
if _, err = p.GetContext().GinContext.Writer.Write([]byte(data)); err != nil {
|
143 |
+
context.Logger.Warning(err)
|
144 |
+
return true
|
145 |
+
}
|
146 |
+
p.GetContext().GinContext.Writer.Flush()
|
147 |
+
return false
|
148 |
+
})
|
149 |
+
if err != nil {
|
150 |
+
return err
|
151 |
+
}
|
152 |
+
} else {
|
153 |
+
err = p.response(response, func(p *UnofficialApiProcess, a string) bool {
|
154 |
+
data := p.jsonChatProcess(a)
|
155 |
+
if data != nil {
|
156 |
+
context.Logger.Debug("Counting the number of tokens")
|
157 |
+
p.CompletionTokens = len(tke.Encode(data.Choices[0].Message.Content, nil, nil))
|
158 |
+
data.Usage.PromptTokens = p.PromptTokens
|
159 |
+
data.Usage.CompletionTokens = p.CompletionTokens
|
160 |
+
data.Usage.TotalTokens = p.PromptTokens + p.CompletionTokens
|
161 |
+
p.GetContext().GinContext.Header("Content-Type", "application/json")
|
162 |
+
p.GetContext().GinContext.JSON(response.StatusCode, data)
|
163 |
+
return true
|
164 |
+
}
|
165 |
+
return false
|
166 |
+
})
|
167 |
+
|
168 |
+
if err != nil {
|
169 |
+
return err
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
return nil
|
174 |
+
}
|
175 |
+
|
176 |
+
func (p *UnofficialApiProcess) MakeRequest(requestBody map[string]interface{}) (*http.Response, error) {
|
177 |
+
reqModel, err := p.checkModel(p.Model)
|
178 |
+
if err != nil {
|
179 |
+
p.GetContext().GinContext.JSON(400, gin.H{"error": err.Error()})
|
180 |
+
return nil, err
|
181 |
+
}
|
182 |
+
req := GetChatReqStr(reqModel)
|
183 |
+
if err = p.generateBody(req, requestBody); err != nil {
|
184 |
+
return nil, err
|
185 |
+
}
|
186 |
+
jsonData, _ := json.Marshal(req)
|
187 |
+
var requestData map[string]interface{}
|
188 |
+
err = json.Unmarshal(jsonData, &requestData)
|
189 |
+
if err != nil {
|
190 |
+
p.GetContext().GinContext.JSON(400, gin.H{"error": err.Error()})
|
191 |
+
return nil, err
|
192 |
+
}
|
193 |
+
request, err := p.createRequest(requestData) //创建请求
|
194 |
+
if err != nil {
|
195 |
+
return nil, err
|
196 |
+
}
|
197 |
+
ws := wsstostream.NewWssToStream(p.GetContext().RequestHeaders.Get("Authorization"))
|
198 |
+
err = ws.InitConnect()
|
199 |
+
p.WS = ws
|
200 |
+
if err != nil {
|
201 |
+
logger.Log.Error(err)
|
202 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": err.Error()})
|
203 |
+
return nil, err
|
204 |
+
}
|
205 |
+
response, err := p.GetContext().RequestClient.Do(request) //发送请求
|
206 |
+
common.CopyResponseHeaders(response, p.GetContext().GinContext) //设置响应头
|
207 |
+
if err != nil {
|
208 |
+
var responseBody interface{}
|
209 |
+
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
210 |
+
if err != nil {
|
211 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": err.Error()})
|
212 |
+
return nil, err
|
213 |
+
}
|
214 |
+
p.GetContext().GinContext.JSON(response.StatusCode, responseBody)
|
215 |
+
return nil, err
|
216 |
+
}
|
217 |
+
return response, nil
|
218 |
+
}
|
219 |
+
|
220 |
+
func (p *UnofficialApiProcess) createRequest(requestBody map[string]interface{}) (*http.Request, error) {
|
221 |
+
context.Logger.Debug("UnofficialApiProcess createRequest")
|
222 |
+
token, err := p.addArkoseTokenIfNeeded(&requestBody)
|
223 |
+
if err != nil {
|
224 |
+
return nil, err
|
225 |
+
}
|
226 |
+
bodyBytes, err := json.Marshal(requestBody)
|
227 |
+
if err != nil {
|
228 |
+
return nil, err
|
229 |
+
}
|
230 |
+
var request *http.Request
|
231 |
+
if p.Context.RequestBody == shttp.NoBody {
|
232 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, nil)
|
233 |
+
} else {
|
234 |
+
request, err = http.NewRequest(p.Context.RequestMethod, p.Context.RequestUrl, bytes.NewBuffer(bodyBytes))
|
235 |
+
}
|
236 |
+
if err != nil {
|
237 |
+
return nil, err
|
238 |
+
}
|
239 |
+
if token != "" {
|
240 |
+
p.addArkoseTokenInHeaderIfNeeded(request, token)
|
241 |
+
}
|
242 |
+
p.buildHeaders(request)
|
243 |
+
p.setCookies(request)
|
244 |
+
return request, nil
|
245 |
+
}
|
246 |
+
func (p *UnofficialApiProcess) setCookies(request *http.Request) {
|
247 |
+
context.Logger.Debug("UnofficialApiProcess setCookies")
|
248 |
+
for _, cookie := range p.GetContext().GinContext.Request.Cookies() {
|
249 |
+
request.AddCookie(&http.Cookie{
|
250 |
+
Name: cookie.Name,
|
251 |
+
Value: cookie.Value,
|
252 |
+
})
|
253 |
+
}
|
254 |
+
}
|
255 |
+
func (p *UnofficialApiProcess) buildHeaders(request *http.Request) {
|
256 |
+
context.Logger.Debug("UnofficialApiProcess buildHeaders")
|
257 |
+
headers := map[string]string{
|
258 |
+
"Host": context.Env.OpenaiHost,
|
259 |
+
"Origin": "https://" + context.Env.OpenaiHost + "/chat",
|
260 |
+
"Authorization": p.GetContext().GinContext.Request.Header.Get("Authorization"),
|
261 |
+
"Connection": "keep-alive",
|
262 |
+
"User-Agent": context.Env.UserAgent,
|
263 |
+
"Content-Type": p.GetContext().GinContext.Request.Header.Get("Content-Type"),
|
264 |
+
}
|
265 |
+
|
266 |
+
for key, value := range headers {
|
267 |
+
request.Header.Set(key, value)
|
268 |
+
}
|
269 |
+
|
270 |
+
if puid := p.GetContext().GinContext.Request.Header.Get("PUID"); puid != "" {
|
271 |
+
request.Header.Set("cookie", "_puid="+puid+";")
|
272 |
+
}
|
273 |
+
}
|
274 |
+
func (p *UnofficialApiProcess) addArkoseTokenInHeaderIfNeeded(request *http.Request, token string) {
|
275 |
+
context.Logger.Debug("UnofficialApiProcess addArkoseTokenInHeaderIfNeeded")
|
276 |
+
request.Header.Set("Openai-Sentinel-Arkose-Token", token)
|
277 |
+
}
|
278 |
+
func (p *UnofficialApiProcess) addArkoseTokenIfNeeded(requestBody *map[string]interface{}) (string, error) {
|
279 |
+
context.Logger.Debug("UnofficialApiProcess addArkoseTokenIfNeeded")
|
280 |
+
model, exists := (*requestBody)["model"]
|
281 |
+
if !exists {
|
282 |
+
return "", nil
|
283 |
+
}
|
284 |
+
if strings.HasPrefix(model.(string), "gpt-4") || context.Env.ArkoseMust {
|
285 |
+
token, err := funcaptcha.GetOpenAIArkoseToken(4, p.GetContext().RequestHeaders.Get("puid"))
|
286 |
+
if err != nil {
|
287 |
+
p.GetContext().GinContext.JSON(500, gin.H{"error": "Get ArkoseToken Failed"})
|
288 |
+
logger.Log.Error(err)
|
289 |
+
return "", err
|
290 |
+
}
|
291 |
+
(*requestBody)["arkose_token"] = token
|
292 |
+
return token, nil
|
293 |
+
}
|
294 |
+
return "", nil
|
295 |
+
}
|
296 |
+
func (p *UnofficialApiProcess) streamChatProcess(raw string) string {
|
297 |
+
result := p.getStreamResp(raw)
|
298 |
+
if strings.Contains(raw, "[DONE]") {
|
299 |
+
return "data: " + raw + "\n\n"
|
300 |
+
} else if result.Pass {
|
301 |
+
return ""
|
302 |
+
} else if result.ApiRespStrStreamEnd.Id != "" {
|
303 |
+
data, err := json.Marshal(result.ApiRespStrStreamEnd)
|
304 |
+
if err != nil {
|
305 |
+
context.Logger.Warning(err)
|
306 |
+
}
|
307 |
+
return "data: " + string(data) + "\n\n"
|
308 |
+
} else if result.ApiRespStrStream.Id != "" {
|
309 |
+
data, err := json.Marshal(result.ApiRespStrStream)
|
310 |
+
if err != nil {
|
311 |
+
context.Logger.Warning(err)
|
312 |
+
}
|
313 |
+
return "data: " + string(data) + "\n\n"
|
314 |
+
}
|
315 |
+
return ""
|
316 |
+
}
|
317 |
+
|
318 |
+
func (p *UnofficialApiProcess) response(response *http.Response, mid func(p *UnofficialApiProcess, a string) bool) error {
|
319 |
+
context.Logger.Debug("UnofficialApiProcess streamResponse")
|
320 |
+
var client *tools.SSEClient
|
321 |
+
if strings.Contains(p.Context.RequestParam, "/ws") {
|
322 |
+
var jsonData WsResponse
|
323 |
+
err := json.NewDecoder(response.Body).Decode(&jsonData)
|
324 |
+
if err != nil {
|
325 |
+
logger.Log.Error(err)
|
326 |
+
return err
|
327 |
+
}
|
328 |
+
p.WS.ResponseId = jsonData.ResponseId
|
329 |
+
p.WS.ConversationId = jsonData.ConversationId
|
330 |
+
p.GetContext().GinContext.Writer.Header().Set("Content-Type", "text/event-stream")
|
331 |
+
p.GetContext().GinContext.Writer.Header().Set("Cache-Control", "no-cache")
|
332 |
+
p.GetContext().GinContext.Writer.Header().Set("Connection", "keep-alive")
|
333 |
+
logger.Log.Debug("wss to stream")
|
334 |
+
client = tools.NewSSEClient(p.WS)
|
335 |
+
} else {
|
336 |
+
client = tools.NewSSEClient(response.Body)
|
337 |
+
}
|
338 |
+
events := client.Read()
|
339 |
+
for event := range events {
|
340 |
+
if event.Event == "message" {
|
341 |
+
if mid(p, event.Data) {
|
342 |
+
return nil
|
343 |
+
}
|
344 |
+
}
|
345 |
+
}
|
346 |
+
defer client.Close()
|
347 |
+
return nil
|
348 |
+
}
|
349 |
+
|
350 |
+
func (p *UnofficialApiProcess) jsonChatProcess(raw string) *ApiRespStr {
|
351 |
+
p.getStreamResp(raw)
|
352 |
+
if strings.Contains(raw, "[DONE]") {
|
353 |
+
resp := GetApiRespStr(p.ID)
|
354 |
+
choice := GetStrChoices()
|
355 |
+
choice.Message.Content = p.OldString
|
356 |
+
resp.Choices = append(resp.Choices, *choice)
|
357 |
+
resp.Model = p.Model
|
358 |
+
return resp
|
359 |
+
}
|
360 |
+
return nil
|
361 |
+
}
|
362 |
+
|
363 |
+
func (p *UnofficialApiProcess) jsonImageProcess(stream string) {
|
364 |
+
context.Logger.Debug("getImageResp")
|
365 |
+
var dalleRespStr DALLERespStr
|
366 |
+
json.Unmarshal([]byte(stream), &dalleRespStr)
|
367 |
+
if dalleRespStr.Message.Author.Name == "dalle.text2im" && dalleRespStr.Message.Content.ContentType == "multimodal_text" {
|
368 |
+
context.Logger.Debug("found image")
|
369 |
+
for _, v := range dalleRespStr.Message.Content.Parts {
|
370 |
+
item := new(ImagePointer)
|
371 |
+
item.Pointer = strings.ReplaceAll(v.AssetPointer, "file-service://", "")
|
372 |
+
item.Prompt = v.Metadata.Dalle.Prompt
|
373 |
+
p.ImagePointerList = append(p.ImagePointerList, *item)
|
374 |
+
}
|
375 |
+
}
|
376 |
+
}
|
377 |
+
func (p *UnofficialApiProcess) getImageUrlByPointer(imagePointerList *[]ImagePointer, result *Result) error {
|
378 |
+
context.Logger.Debug("getImageUrlByPointer")
|
379 |
+
for _, v := range *imagePointerList {
|
380 |
+
imageDownloadUrl, err := common.RequestOpenAI[ImageDownloadUrl]("/backend-api/files/"+v.Pointer+"/download", nil, "GET", p.GetContext().RequestHeaders.Get("Authorization"))
|
381 |
+
if err != nil {
|
382 |
+
return err
|
383 |
+
}
|
384 |
+
if imageDownloadUrl != nil && imageDownloadUrl.DownloadUrl != "" {
|
385 |
+
context.Logger.Debug("getDownloadUrl")
|
386 |
+
imageItem := new(ApiImageItem)
|
387 |
+
result.ApiImageGenerationRespStr.Created = time.Now().Unix()
|
388 |
+
imageItem.Url = imageDownloadUrl.DownloadUrl
|
389 |
+
imageItem.RevisedPrompt = v.Prompt
|
390 |
+
result.ApiImageGenerationRespStr.Data = append(result.ApiImageGenerationRespStr.Data, *imageItem)
|
391 |
+
}
|
392 |
+
}
|
393 |
+
return nil
|
394 |
+
}
|
395 |
+
|
396 |
+
func (p *UnofficialApiProcess) getStreamResp(stream string) *Result {
|
397 |
+
context.Logger.Debug("getStreamResp")
|
398 |
+
var chatRespStr ChatRespStr
|
399 |
+
var chatEndRespStr ChatEndRespStr
|
400 |
+
result := new(Result)
|
401 |
+
result.ApiRespStrStreamEnd = ApiRespStrStreamEnd{}
|
402 |
+
result.ApiRespStrStream = ApiRespStrStream{}
|
403 |
+
result.Pass = false
|
404 |
+
json.Unmarshal([]byte(stream), &chatRespStr)
|
405 |
+
if chatRespStr.Message.Id != "" {
|
406 |
+
if chatRespStr.Message.Metadata.ParentId == "" {
|
407 |
+
result.Pass = true
|
408 |
+
return result
|
409 |
+
}
|
410 |
+
context.Logger.Debug("chatRespStr")
|
411 |
+
resp := GetApiRespStrStream(p.ID)
|
412 |
+
choice := GetStreamChoice()
|
413 |
+
resp.Model = p.Model
|
414 |
+
choice.Delta.Content = strings.ReplaceAll(chatRespStr.Message.Content.Parts[0], p.OldString, "")
|
415 |
+
p.OldString = chatRespStr.Message.Content.Parts[0]
|
416 |
+
resp.Choices = resp.Choices[:0]
|
417 |
+
resp.Choices = append(resp.Choices, *choice)
|
418 |
+
result.ApiRespStrStream = *resp
|
419 |
+
}
|
420 |
+
json.Unmarshal([]byte(stream), &chatEndRespStr)
|
421 |
+
if chatEndRespStr.IsCompletion {
|
422 |
+
context.Logger.Debug("chatEndRespStr")
|
423 |
+
resp := GetApiRespStrStreamEnd(p.ID)
|
424 |
+
resp.Model = p.Model
|
425 |
+
result.ApiRespStrStreamEnd = *resp
|
426 |
+
}
|
427 |
+
if result.ApiRespStrStream.Id == "" && result.ApiRespStrStreamEnd.Id == "" {
|
428 |
+
result.Pass = true
|
429 |
+
}
|
430 |
+
return result
|
431 |
+
}
|
432 |
+
func (p *UnofficialApiProcess) checkModel(model string) (string, error) {
|
433 |
+
context.Logger.Debug("UnofficialApiProcess checkModel")
|
434 |
+
if strings.HasPrefix(model, "dall-e") || strings.HasPrefix(model, "gpt-4-vision") {
|
435 |
+
return "gpt-4", nil
|
436 |
+
} else if strings.HasPrefix(model, "gpt-3") {
|
437 |
+
return "text-davinci-002-render-sha", nil
|
438 |
+
} else if strings.HasPrefix(model, "gpt-4") {
|
439 |
+
return "gpt-4-gizmo", nil
|
440 |
+
} else {
|
441 |
+
return "", errors.New("unsupported model")
|
442 |
+
}
|
443 |
+
}
|
444 |
+
func (p *UnofficialApiProcess) generateBody(req *ChatReqStr, requestBody map[string]interface{}) error {
|
445 |
+
context.Logger.Debug("UnofficialApiProcess generateBody")
|
446 |
+
if p.Mode == "chat" {
|
447 |
+
logger.Log.Debug("Generate Chat Body")
|
448 |
+
messageList, exists := requestBody["messages"]
|
449 |
+
if !exists {
|
450 |
+
return errors.New("no message body")
|
451 |
+
}
|
452 |
+
messages, _ := messageList.([]interface{})
|
453 |
+
|
454 |
+
for _, message := range messages {
|
455 |
+
messageItem, _ := message.(map[string]interface{})
|
456 |
+
role, _ := messageItem["role"].(string)
|
457 |
+
if _, ok := messageItem["content"].(string); ok {
|
458 |
+
content, _ := messageItem["content"].(string)
|
459 |
+
p.PromptTokens += len(tke.Encode(content, nil, nil)) + 7
|
460 |
+
reqMessage := GetChatReqTemplate()
|
461 |
+
reqMessage.Content.Parts = reqMessage.Content.Parts[:0]
|
462 |
+
reqMessage.Author.Role = role
|
463 |
+
reqMessage.Content.Parts = append(reqMessage.Content.Parts, content)
|
464 |
+
req.Messages = append(req.Messages, *reqMessage)
|
465 |
+
}
|
466 |
+
if _, ok := messageItem["content"].([]map[string]interface{}); ok {
|
467 |
+
reqFileMessage := GetChatFileReqTemplate()
|
468 |
+
content, _ := messageItem["content"].([]map[string]interface{})
|
469 |
+
reqFileMessage.Content.Parts = reqFileMessage.Content.Parts[:0]
|
470 |
+
reqFileMessage.Author.Role = role
|
471 |
+
p.fileReqProcess(&content, &reqFileMessage.Content.Parts)
|
472 |
+
//reqMessage.Content.Parts = append(reqMessage.Content.Parts, content)
|
473 |
+
//req.Messages = append(req.Messages, *reqFileMessage)
|
474 |
+
}
|
475 |
+
}
|
476 |
+
}
|
477 |
+
if p.Mode == "image" {
|
478 |
+
logger.Log.Debug("Generate Image Body")
|
479 |
+
prompt, exists := requestBody["prompt"]
|
480 |
+
if !exists {
|
481 |
+
return errors.New("please provide prompt")
|
482 |
+
}
|
483 |
+
count, exists := requestBody["n"]
|
484 |
+
if !exists {
|
485 |
+
count = 1
|
486 |
+
}
|
487 |
+
size, exists := requestBody["size"]
|
488 |
+
if !exists {
|
489 |
+
size = "1024x1024"
|
490 |
+
}
|
491 |
+
reqMessage := GetChatReqTemplate()
|
492 |
+
reqMessage.Content.Parts = reqMessage.Content.Parts[:0]
|
493 |
+
reqMessage.Author.Role = "user"
|
494 |
+
reqMessage.Content.Parts = append(reqMessage.Content.Parts, fmt.Sprintf("Requirements for image generation:\n- ImageCount: %d\n- Size: %s\n- Prompt: [%s]\n- Requirements: Using the DALLE tool, each image is generated according to the number of ImageCount. It is not allowed to contain multiple elements in one image. You must call the tool multiple times to generate the number of ImageCount images, and the details of each image are different\n", int(count.(float64)), size.(string), prompt.(string)))
|
495 |
+
req.Messages = append(req.Messages, *reqMessage)
|
496 |
+
}
|
497 |
+
|
498 |
+
return nil
|
499 |
+
}
|
500 |
+
func (p *UnofficialApiProcess) fileReqProcess(content *[]map[string]interface{}, part *[]interface{}) {
|
501 |
+
|
502 |
+
}
|
503 |
+
|
504 |
+
func (p *UnofficialApiProcess) decodeRequestBody(requestBody *map[string]interface{}) error {
|
505 |
+
conversation := p.GetContext()
|
506 |
+
if conversation.RequestBody != shttp.NoBody {
|
507 |
+
if err := json.NewDecoder(conversation.RequestBody).Decode(requestBody); err != nil {
|
508 |
+
conversation.GinContext.JSON(400, gin.H{"error": "JSON invalid"})
|
509 |
+
return err
|
510 |
+
}
|
511 |
+
}
|
512 |
+
return nil
|
513 |
+
}
|
514 |
+
|
515 |
+
type UnOfficialApiRequestUrl struct {
|
516 |
+
}
|
517 |
+
|
518 |
+
func (u UnOfficialApiRequestUrl) Generate(path string, rawquery string) string {
|
519 |
+
if rawquery == "" {
|
520 |
+
return "https://" + context.Env.OpenaiHost + "/backend-api" + "/conversation"
|
521 |
+
}
|
522 |
+
return "https://" + context.Env.OpenaiHost + "/backend-api" + "/conversation" + "?" + rawquery
|
523 |
+
}
|
524 |
+
func (p *UnofficialApiProcess) Run(com *plugins.Component) {
|
525 |
+
context = com
|
526 |
+
context.Engine.Any("/r/*path", func(c *gin.Context) {
|
527 |
+
conversation := common.GetContextPack(c, UnOfficialApiRequestUrl{})
|
528 |
+
common.Do[Context](new(UnofficialApiProcess), Context(conversation))
|
529 |
+
})
|
530 |
+
}
|
pkg/plugins/plugins.go
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package plugins
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/db"
|
5 |
+
"WarpGPT/pkg/env"
|
6 |
+
"github.com/gin-gonic/gin"
|
7 |
+
"github.com/sirupsen/logrus"
|
8 |
+
)
|
9 |
+
|
10 |
+
type Component struct {
|
11 |
+
Engine *gin.Engine
|
12 |
+
Db db.DB
|
13 |
+
Logger *logrus.Logger
|
14 |
+
Env *env.ENV
|
15 |
+
Auth func(arkType int, puid string) (string, error)
|
16 |
+
}
|
17 |
+
|
18 |
+
type Plugin interface {
|
19 |
+
Run(com *Component)
|
20 |
+
}
|
pkg/plugins/service/proxypool/proxypool.go
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package proxypool
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/db"
|
5 |
+
"WarpGPT/pkg/plugins"
|
6 |
+
ctx "context"
|
7 |
+
"encoding/json"
|
8 |
+
"errors"
|
9 |
+
http "github.com/bogdanfinn/fhttp"
|
10 |
+
"io"
|
11 |
+
"strconv"
|
12 |
+
"strings"
|
13 |
+
"time"
|
14 |
+
)
|
15 |
+
|
16 |
+
type proxyUrl struct {
|
17 |
+
Code int `json:"code"`
|
18 |
+
Success bool `json:"success"`
|
19 |
+
Msg string `json:"msg"`
|
20 |
+
Data []struct {
|
21 |
+
Ip string `json:"ip"`
|
22 |
+
Port int `json:"port"`
|
23 |
+
} `json:"data"`
|
24 |
+
}
|
25 |
+
|
26 |
+
var context *plugins.Component
|
27 |
+
var redisdb db.DB
|
28 |
+
var ProxyPoolInstance ProxyPool
|
29 |
+
|
30 |
+
type ProxyPool struct {
|
31 |
+
}
|
32 |
+
|
33 |
+
// 检查代理池中的代理数量,如果数量不足,则从代理池中获取代理
|
34 |
+
func (p *ProxyPool) checkProxy() error {
|
35 |
+
context.Logger.Debug("检查redis代理ip")
|
36 |
+
client, err := redisdb.GetRedisClient()
|
37 |
+
if err != nil {
|
38 |
+
return err
|
39 |
+
}
|
40 |
+
keys, err := client.Keys(ctx.Background(), "ip:*").Result()
|
41 |
+
if err != nil {
|
42 |
+
return err
|
43 |
+
}
|
44 |
+
if len(keys) < 20 {
|
45 |
+
err = p.putIpsInRedis()
|
46 |
+
if err != nil {
|
47 |
+
return err
|
48 |
+
}
|
49 |
+
}
|
50 |
+
return nil
|
51 |
+
}
|
52 |
+
|
53 |
+
func (p *ProxyPool) getProxyUrlList() (*proxyUrl, error) {
|
54 |
+
context.Logger.Debug("请求代理ip池")
|
55 |
+
poolUrl := context.Env.ProxyPoolUrl
|
56 |
+
var proxy proxyUrl
|
57 |
+
get, err := http.Get(poolUrl)
|
58 |
+
if err != nil {
|
59 |
+
return nil, err
|
60 |
+
}
|
61 |
+
all, err := io.ReadAll(get.Body)
|
62 |
+
if err != nil {
|
63 |
+
return nil, err
|
64 |
+
}
|
65 |
+
err = json.Unmarshal(all, &proxy)
|
66 |
+
if err != nil {
|
67 |
+
return nil, err
|
68 |
+
}
|
69 |
+
if proxy.Success {
|
70 |
+
return &proxy, nil
|
71 |
+
} else {
|
72 |
+
return nil, errors.New("代理获取失败")
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
// 从代理url中获取url,放入redis中
|
77 |
+
func (p *ProxyPool) putIpsInRedis() error {
|
78 |
+
context.Logger.Debug("获取ip池并放入redis")
|
79 |
+
proxyList, err := p.getProxyUrlList()
|
80 |
+
client, err := redisdb.GetRedisClient()
|
81 |
+
if err != nil {
|
82 |
+
return err
|
83 |
+
}
|
84 |
+
if err != nil {
|
85 |
+
context.Logger.Warning(err)
|
86 |
+
return err
|
87 |
+
}
|
88 |
+
for _, ip := range proxyList.Data {
|
89 |
+
ipstr := "http://" + ip.Ip + ":" + strconv.Itoa(ip.Port)
|
90 |
+
_, err = client.Set(ctx.Background(), "ip:"+ipstr, "", time.Minute*3).Result()
|
91 |
+
if err != nil {
|
92 |
+
context.Logger.Error(err)
|
93 |
+
return err
|
94 |
+
}
|
95 |
+
}
|
96 |
+
return nil
|
97 |
+
}
|
98 |
+
|
99 |
+
func (p *ProxyPool) GetIpInRedis() (string, error) {
|
100 |
+
context.Logger.Debug("请求代理ip")
|
101 |
+
client, err := redisdb.GetRedisClient()
|
102 |
+
if err != nil {
|
103 |
+
return "", err
|
104 |
+
}
|
105 |
+
statusCmd := client.RandomKey(ctx.Background())
|
106 |
+
result, err := statusCmd.Result()
|
107 |
+
if err != nil {
|
108 |
+
return "", err
|
109 |
+
}
|
110 |
+
size, err := client.DBSize(ctx.Background()).Result()
|
111 |
+
if err != nil {
|
112 |
+
return "", err
|
113 |
+
}
|
114 |
+
if size == 0 {
|
115 |
+
context.Logger.Warning("数据库为空,无法获取代理ip,尝试获取")
|
116 |
+
err = p.putIpsInRedis()
|
117 |
+
if err != nil {
|
118 |
+
return "", err
|
119 |
+
}
|
120 |
+
}
|
121 |
+
if strings.HasPrefix(result, "ip:") {
|
122 |
+
client.Del(ctx.Background(), result)
|
123 |
+
ip := strings.ReplaceAll(result, "ip:", "")
|
124 |
+
context.Logger.Debug("获取的代理ip是: " + ip)
|
125 |
+
return ip, nil
|
126 |
+
} else {
|
127 |
+
context.Logger.Warning("非代理ip键,跳过")
|
128 |
+
ip, _ := p.GetIpInRedis()
|
129 |
+
return ip, nil
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
func (p *ProxyPool) ProxyThread() {
|
134 |
+
if context.Env.ProxyPoolUrl == "" {
|
135 |
+
context.Logger.Debug("未启动redis")
|
136 |
+
return
|
137 |
+
}
|
138 |
+
context.Logger.Debug("启动redis监视线程")
|
139 |
+
if err := p.checkProxy(); err != nil {
|
140 |
+
return
|
141 |
+
}
|
142 |
+
ticker := time.NewTicker(1 * time.Minute)
|
143 |
+
defer ticker.Stop()
|
144 |
+
for {
|
145 |
+
select {
|
146 |
+
case <-ticker.C:
|
147 |
+
err := p.checkProxy()
|
148 |
+
if err != nil {
|
149 |
+
context.Logger.Warning(err.Error())
|
150 |
+
return
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
func (p *ProxyPool) Run(com *plugins.Component) {
|
157 |
+
context = com
|
158 |
+
redisdb = context.Db
|
159 |
+
go p.ProxyThread()
|
160 |
+
}
|
pkg/plugins/service/wsstostream/wsstostream.go
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package wsstostream
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/common"
|
5 |
+
"WarpGPT/pkg/env"
|
6 |
+
"WarpGPT/pkg/logger"
|
7 |
+
"WarpGPT/pkg/tools"
|
8 |
+
"bytes"
|
9 |
+
"encoding/base64"
|
10 |
+
"encoding/json"
|
11 |
+
"errors"
|
12 |
+
http "github.com/bogdanfinn/fhttp"
|
13 |
+
"github.com/gorilla/websocket"
|
14 |
+
"golang.org/x/net/proxy"
|
15 |
+
"io"
|
16 |
+
shttp "net/http"
|
17 |
+
"net/url"
|
18 |
+
"time"
|
19 |
+
)
|
20 |
+
|
21 |
+
type RegisterWebsocket struct {
|
22 |
+
ExpiresAt time.Time `json:"expires_at"`
|
23 |
+
WssUrl string `json:"wss_url"`
|
24 |
+
}
|
25 |
+
type WsResponse struct {
|
26 |
+
SequenceId int `json:"sequenceId"`
|
27 |
+
Type string `json:"type"`
|
28 |
+
From string `json:"from"`
|
29 |
+
DataType string `json:"dataType"`
|
30 |
+
Data struct {
|
31 |
+
Type string `json:"type"`
|
32 |
+
Body string `json:"body"`
|
33 |
+
MoreBody bool `json:"more_body"`
|
34 |
+
ResponseId string `json:"response_id"`
|
35 |
+
ConversationId string `json:"conversation_id"`
|
36 |
+
MessageId string `json:"message_id"`
|
37 |
+
} `json:"data"`
|
38 |
+
}
|
39 |
+
type Reconnect struct {
|
40 |
+
Type string `json:"type"`
|
41 |
+
Event string `json:"event"`
|
42 |
+
UserId string `json:"userId"`
|
43 |
+
ConnectionId string `json:"connectionId"`
|
44 |
+
ReconnectionToken string `json:"reconnectionToken"`
|
45 |
+
}
|
46 |
+
type WssToStream struct {
|
47 |
+
ConversationId string
|
48 |
+
ResponseId string
|
49 |
+
AccessToken string
|
50 |
+
Server *websocket.Conn
|
51 |
+
WS *RegisterWebsocket
|
52 |
+
Reconnect
|
53 |
+
}
|
54 |
+
|
55 |
+
func NewWssToStream(accessToken string) *WssToStream {
|
56 |
+
return &WssToStream{AccessToken: accessToken}
|
57 |
+
}
|
58 |
+
|
59 |
+
func GetRegisterWebsocket(accessToken string) (*RegisterWebsocket, error) {
|
60 |
+
logger.Log.Debug("GetRegisterWebsocket")
|
61 |
+
WS, err := common.RequestOpenAI[RegisterWebsocket]("/backend-api/register-websocket", nil, accessToken, http.MethodPost)
|
62 |
+
|
63 |
+
if err != nil {
|
64 |
+
logger.Log.Error("Error decoding response:", err)
|
65 |
+
return nil, err
|
66 |
+
}
|
67 |
+
if WS != nil && WS.WssUrl != "" {
|
68 |
+
logger.Log.Debug("GetRegisterWebsocket Success WssUrl:", WS.WssUrl)
|
69 |
+
return WS, nil
|
70 |
+
} else {
|
71 |
+
logger.Log.Debug("accessToken:", accessToken)
|
72 |
+
return nil, errors.New("check your access_key")
|
73 |
+
}
|
74 |
+
}
|
75 |
+
func (s *WssToStream) InitConnect() error {
|
76 |
+
logger.Log.Debug("Try Connect To WS")
|
77 |
+
var dialer websocket.Dialer
|
78 |
+
|
79 |
+
// 当 env.E.Proxy 不为空字符串时,才配置代理
|
80 |
+
if env.E.Proxy != "" {
|
81 |
+
proxyAddr, err := url.Parse(env.E.Proxy)
|
82 |
+
if err != nil {
|
83 |
+
logger.Log.Error("Error parsing proxy URL:", err)
|
84 |
+
return err
|
85 |
+
}
|
86 |
+
|
87 |
+
switch proxyAddr.Scheme {
|
88 |
+
case "http", "https":
|
89 |
+
dialer.Proxy = shttp.ProxyURL(proxyAddr)
|
90 |
+
case "socks5":
|
91 |
+
socksDialer, err := proxy.FromURL(proxyAddr, proxy.Direct)
|
92 |
+
if err != nil {
|
93 |
+
logger.Log.Error("Error creating SOCKS proxy dialer:", err)
|
94 |
+
return err
|
95 |
+
}
|
96 |
+
dialer.NetDial = socksDialer.Dial
|
97 |
+
default:
|
98 |
+
logger.Log.Error("Unsupported proxy scheme:", proxyAddr.Scheme)
|
99 |
+
return errors.New("unsupported proxy scheme")
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
headers := http.Header{}
|
104 |
+
headers.Set("Origin", "https://"+env.E.OpenaiHost)
|
105 |
+
headers.Set("Sec-WebSocket-Protocol", "json.reliable.webpubsub.azure.v1")
|
106 |
+
headers.Set("User-Agent", env.E.UserAgent)
|
107 |
+
|
108 |
+
item, exists := tools.AllCache.CacheGet(s.AccessToken)
|
109 |
+
if !exists || item.ExpiresAt.Before(time.Now()) {
|
110 |
+
registerWebsocket, err := GetRegisterWebsocket(s.AccessToken)
|
111 |
+
if err != nil {
|
112 |
+
return err
|
113 |
+
}
|
114 |
+
tools.AllCache.CacheSet(s.AccessToken, tools.CacheItem{Data: registerWebsocket}, 55*time.Minute)
|
115 |
+
s.WS = registerWebsocket
|
116 |
+
} else {
|
117 |
+
s.WS = item.Data.(*RegisterWebsocket)
|
118 |
+
}
|
119 |
+
|
120 |
+
c, _, err := dialer.Dial(s.WS.WssUrl, shttp.Header(headers))
|
121 |
+
if err != nil {
|
122 |
+
logger.Log.Error("Dial error:", err)
|
123 |
+
return err
|
124 |
+
}
|
125 |
+
logger.Log.Debug("WS Connect Success")
|
126 |
+
s.Server = c
|
127 |
+
_, msg, err := s.Server.ReadMessage()
|
128 |
+
if err != nil {
|
129 |
+
return err
|
130 |
+
}
|
131 |
+
logger.Log.Debug("Init Read Message:", string(msg))
|
132 |
+
return nil
|
133 |
+
}
|
134 |
+
|
135 |
+
type NopCloser struct {
|
136 |
+
*bytes.Reader
|
137 |
+
}
|
138 |
+
|
139 |
+
func (NopCloser) Close() error {
|
140 |
+
return nil
|
141 |
+
}
|
142 |
+
func NewNopCloser(data []byte) io.ReadCloser {
|
143 |
+
return NopCloser{Reader: bytes.NewReader(data)}
|
144 |
+
}
|
145 |
+
|
146 |
+
func (s *WssToStream) ReadMessage() (io.ReadCloser, error) {
|
147 |
+
logger.Log.Debug("Read Messages")
|
148 |
+
_, msg, err := s.Server.ReadMessage()
|
149 |
+
if err != nil {
|
150 |
+
logger.Log.Error("read message error:", err)
|
151 |
+
}
|
152 |
+
var response WsResponse
|
153 |
+
if err = json.Unmarshal(msg, &response); err != nil {
|
154 |
+
logger.Log.Error("unmarshal message error:", err)
|
155 |
+
}
|
156 |
+
if response.Data.ResponseId == s.ResponseId && response.Data.ConversationId == s.ConversationId {
|
157 |
+
if response.Data.Body == "ZGF0YTogW0RPTkVdCgo=" {
|
158 |
+
s.Server.Close()
|
159 |
+
}
|
160 |
+
data, err := base64.StdEncoding.DecodeString(response.Data.Body)
|
161 |
+
if err != nil {
|
162 |
+
return nil, err
|
163 |
+
}
|
164 |
+
return NewNopCloser(data), nil
|
165 |
+
} else {
|
166 |
+
return nil, nil
|
167 |
+
}
|
168 |
+
}
|
169 |
+
func (s *WssToStream) Read(p []byte) (n int, err error) {
|
170 |
+
logger.Log.Debug("Read")
|
171 |
+
_, message, err := s.Server.ReadMessage()
|
172 |
+
if err != nil {
|
173 |
+
return 0, err
|
174 |
+
}
|
175 |
+
var response WsResponse
|
176 |
+
if err = json.Unmarshal(message, &response); err != nil {
|
177 |
+
logger.Log.Error("unmarshal message error:", err)
|
178 |
+
}
|
179 |
+
if response.Data.ResponseId == s.ResponseId && response.Data.ConversationId == s.ConversationId {
|
180 |
+
if response.Data.Body == "ZGF0YTogW0RPTkVdCgo=" {
|
181 |
+
s.Server.Close()
|
182 |
+
}
|
183 |
+
data, err := base64.StdEncoding.DecodeString(response.Data.Body)
|
184 |
+
if err != nil {
|
185 |
+
return 0, err
|
186 |
+
}
|
187 |
+
copyLen := copy(p, data)
|
188 |
+
if copyLen < len(data) {
|
189 |
+
return copyLen, errors.New("buffer too small to hold message")
|
190 |
+
}
|
191 |
+
return copyLen, nil
|
192 |
+
} else {
|
193 |
+
return 0, nil
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
func (s *WssToStream) Close() error {
|
198 |
+
return s.Server.Close()
|
199 |
+
}
|
pkg/tools/auth.go
ADDED
@@ -0,0 +1,559 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package tools
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/env"
|
5 |
+
"WarpGPT/pkg/funcaptcha"
|
6 |
+
"WarpGPT/pkg/logger"
|
7 |
+
"encoding/json"
|
8 |
+
"fmt"
|
9 |
+
http "github.com/bogdanfinn/fhttp"
|
10 |
+
tls_client "github.com/bogdanfinn/tls-client"
|
11 |
+
"github.com/bogdanfinn/tls-client/profiles"
|
12 |
+
"io"
|
13 |
+
"net/url"
|
14 |
+
"os"
|
15 |
+
"regexp"
|
16 |
+
"strings"
|
17 |
+
)
|
18 |
+
|
19 |
+
type Error struct {
|
20 |
+
Location string
|
21 |
+
StatusCode int
|
22 |
+
Details string
|
23 |
+
Error error
|
24 |
+
}
|
25 |
+
|
26 |
+
func NewError(location string, statusCode int, details string, err error) *Error {
|
27 |
+
return &Error{
|
28 |
+
Location: location,
|
29 |
+
StatusCode: statusCode,
|
30 |
+
Details: details,
|
31 |
+
Error: err,
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
type Authenticator struct {
|
36 |
+
EmailAddress string
|
37 |
+
Password string
|
38 |
+
Proxy string
|
39 |
+
Session tls_client.HttpClient
|
40 |
+
UserAgent string
|
41 |
+
State string
|
42 |
+
URL string
|
43 |
+
PUID string
|
44 |
+
Verifier_code string
|
45 |
+
Verifier_challenge string
|
46 |
+
AuthResult AuthResult
|
47 |
+
}
|
48 |
+
type ArkoseToken struct {
|
49 |
+
Token string `json:"token"`
|
50 |
+
ChallengeURL string `json:"challenge_url"`
|
51 |
+
ChallengeURLCDN string `json:"challenge_url_cdn"`
|
52 |
+
ChallengeURLCDNSRI *string `json:"challenge_url_cdn_sri"`
|
53 |
+
}
|
54 |
+
type AuthResult struct {
|
55 |
+
AccessToken map[string]interface{} `json:"access_token"`
|
56 |
+
PUID string `json:"puid"`
|
57 |
+
FreshToken string `json:"fresh_token"`
|
58 |
+
Model map[string]interface{} `json:"model"`
|
59 |
+
}
|
60 |
+
|
61 |
+
func NewAuthenticator(emailAddress, password string, puid string) *Authenticator {
|
62 |
+
auth := &Authenticator{
|
63 |
+
EmailAddress: emailAddress,
|
64 |
+
Password: password,
|
65 |
+
Proxy: os.Getenv("proxy"),
|
66 |
+
PUID: puid,
|
67 |
+
UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
68 |
+
}
|
69 |
+
jar := tls_client.NewCookieJar()
|
70 |
+
cookie := &http.Cookie{
|
71 |
+
Name: "_puid",
|
72 |
+
Value: puid,
|
73 |
+
Path: "/",
|
74 |
+
Domain: ".openai.com",
|
75 |
+
}
|
76 |
+
urls, _ := url.Parse("https://openai.com")
|
77 |
+
jar.SetCookies(urls, []*http.Cookie{cookie})
|
78 |
+
options := []tls_client.HttpClientOption{
|
79 |
+
tls_client.WithTimeoutSeconds(20),
|
80 |
+
tls_client.WithClientProfile(profiles.Chrome_109),
|
81 |
+
tls_client.WithNotFollowRedirects(),
|
82 |
+
tls_client.WithCookieJar(jar),
|
83 |
+
tls_client.WithProxyUrl(env.E.Proxy),
|
84 |
+
}
|
85 |
+
auth.Session, _ = tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
|
86 |
+
return auth
|
87 |
+
}
|
88 |
+
|
89 |
+
func (auth *Authenticator) URLEncode(str string) string {
|
90 |
+
return url.QueryEscape(str)
|
91 |
+
}
|
92 |
+
|
93 |
+
func (auth *Authenticator) Begin() *Error {
|
94 |
+
logger.Log.Debug("Auth Begin")
|
95 |
+
|
96 |
+
target := "https://" + env.E.OpenaiHost + "/api/auth/csrf"
|
97 |
+
req, err := http.NewRequest("GET", target, nil)
|
98 |
+
if err != nil {
|
99 |
+
return NewError("begin", 0, "", err)
|
100 |
+
}
|
101 |
+
|
102 |
+
req.Header.Set("Host", ""+env.E.OpenaiHost+"")
|
103 |
+
req.Header.Set("Accept", "*/*")
|
104 |
+
req.Header.Set("Connection", "keep-alive")
|
105 |
+
req.Header.Set("User-Agent", auth.UserAgent)
|
106 |
+
req.Header.Set("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8")
|
107 |
+
req.Header.Set("Referer", "https://"+env.E.OpenaiHost+"/auth/login")
|
108 |
+
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
109 |
+
|
110 |
+
resp, err := auth.Session.Do(req)
|
111 |
+
if err != nil {
|
112 |
+
return NewError("begin", 0, "", err)
|
113 |
+
}
|
114 |
+
defer resp.Body.Close()
|
115 |
+
|
116 |
+
body, err := io.ReadAll(resp.Body)
|
117 |
+
if err != nil {
|
118 |
+
return NewError("begin", 0, "", err)
|
119 |
+
}
|
120 |
+
|
121 |
+
if resp.StatusCode == 200 && strings.Contains(resp.Header.Get("Content-Type"), "json") {
|
122 |
+
|
123 |
+
var csrfTokenResponse struct {
|
124 |
+
CsrfToken string `json:"csrfToken"`
|
125 |
+
}
|
126 |
+
err = json.Unmarshal(body, &csrfTokenResponse)
|
127 |
+
if err != nil {
|
128 |
+
return NewError("begin", 0, "", err)
|
129 |
+
}
|
130 |
+
|
131 |
+
csrfToken := csrfTokenResponse.CsrfToken
|
132 |
+
return auth.partOne(csrfToken)
|
133 |
+
} else {
|
134 |
+
err := NewError("begin", resp.StatusCode, string(body), fmt.Errorf("error: Check details"))
|
135 |
+
return err
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
func (auth *Authenticator) partOne(csrfToken string) *Error {
|
140 |
+
logger.Log.Debug("Auth One")
|
141 |
+
|
142 |
+
auth_url := "https://" + env.E.OpenaiHost + "/api/auth/signin/auth0?prompt=login"
|
143 |
+
headers := map[string]string{
|
144 |
+
"Host": "" + env.E.OpenaiHost + "",
|
145 |
+
"User-Agent": auth.UserAgent,
|
146 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
147 |
+
"Accept": "*/*",
|
148 |
+
"Sec-Gpc": "1",
|
149 |
+
"Accept-Language": "en-US,en;q=0.8",
|
150 |
+
"Origin": "https://" + env.E.OpenaiHost + "",
|
151 |
+
"Sec-Fetch-Site": "same-origin",
|
152 |
+
"Sec-Fetch-Mode": "cors",
|
153 |
+
"Sec-Fetch-Dest": "empty",
|
154 |
+
"Referer": "https://" + env.E.OpenaiHost + "/auth/login",
|
155 |
+
"Accept-Encoding": "gzip, deflate",
|
156 |
+
}
|
157 |
+
|
158 |
+
// Construct payload
|
159 |
+
payload := fmt.Sprintf("callbackUrl=%%2F&csrfToken=%s&json=true", csrfToken)
|
160 |
+
req, _ := http.NewRequest("POST", auth_url, strings.NewReader(payload))
|
161 |
+
|
162 |
+
for k, v := range headers {
|
163 |
+
req.Header.Set(k, v)
|
164 |
+
}
|
165 |
+
|
166 |
+
resp, err := auth.Session.Do(req)
|
167 |
+
if err != nil {
|
168 |
+
return NewError("part_one", 0, "Failed to send request", err)
|
169 |
+
}
|
170 |
+
defer resp.Body.Close()
|
171 |
+
body, err := io.ReadAll(resp.Body)
|
172 |
+
if err != nil {
|
173 |
+
return NewError("part_one", 0, "Failed to read requestbody", err)
|
174 |
+
}
|
175 |
+
|
176 |
+
if resp.StatusCode == 200 && strings.Contains(resp.Header.Get("Content-Type"), "json") {
|
177 |
+
var urlResponse struct {
|
178 |
+
URL string `json:"url"`
|
179 |
+
}
|
180 |
+
err = json.Unmarshal(body, &urlResponse)
|
181 |
+
if err != nil {
|
182 |
+
return NewError("part_one", 0, "Failed to decode JSON", err)
|
183 |
+
}
|
184 |
+
if urlResponse.URL == "https://"+env.E.OpenaiHost+"/api/auth/error?error=OAuthSignin" || strings.Contains(urlResponse.URL, "error") {
|
185 |
+
err := NewError("part_one", resp.StatusCode, "You have been rate limited. Please try again later.", fmt.Errorf("error: Check details"))
|
186 |
+
return err
|
187 |
+
}
|
188 |
+
return auth.partTwo(urlResponse.URL)
|
189 |
+
} else {
|
190 |
+
return NewError("part_one", resp.StatusCode, string(body), fmt.Errorf("error: Check details"))
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
func (auth *Authenticator) partTwo(target string) *Error {
|
195 |
+
logger.Log.Debug("Auth Two")
|
196 |
+
|
197 |
+
headers := map[string]string{
|
198 |
+
"Host": "auth0.openai.com",
|
199 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
200 |
+
"Connection": "keep-alive",
|
201 |
+
"User-Agent": auth.UserAgent,
|
202 |
+
"Accept-Language": "en-US,en;q=0.9",
|
203 |
+
"Referer": "https://chat.openai.com/",
|
204 |
+
"Sec-Ch-Ua": "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\"",
|
205 |
+
"Sec-Ch-Ua-Arch": "\"x86\"",
|
206 |
+
"Sec-Ch-Ua-Bitness": "\"64\"",
|
207 |
+
"Sec-Ch-Ua-Full-Version": "\"121.0.6167.161\"",
|
208 |
+
"Sec-Ch-Ua-Full-Version-List": "\"Not A(Brand\";v=\"99.0.0.0\", \"Google Chrome\";v=\"121.0.6167.161\", \"Chromium\";v=\"121.0.6167.161\"",
|
209 |
+
}
|
210 |
+
|
211 |
+
req, _ := http.NewRequest("GET", target, nil)
|
212 |
+
for k, v := range headers {
|
213 |
+
req.Header.Set(k, v)
|
214 |
+
}
|
215 |
+
|
216 |
+
resp, err := auth.Session.Do(req)
|
217 |
+
if err != nil {
|
218 |
+
return NewError("part_two", 0, "Failed to make request", err)
|
219 |
+
}
|
220 |
+
defer resp.Body.Close()
|
221 |
+
body, _ := io.ReadAll(resp.Body)
|
222 |
+
|
223 |
+
if resp.StatusCode == 302 || resp.StatusCode == 200 {
|
224 |
+
stateRegex := regexp.MustCompile(`state=(.*)`)
|
225 |
+
stateMatch := stateRegex.FindStringSubmatch(string(body))
|
226 |
+
if len(stateMatch) < 2 {
|
227 |
+
return NewError("part_two", 0, "Could not find state in response", fmt.Errorf("error: Check details"))
|
228 |
+
}
|
229 |
+
|
230 |
+
state := strings.Split(stateMatch[1], `"`)[0]
|
231 |
+
return auth.partThree(state)
|
232 |
+
} else {
|
233 |
+
return NewError("part_two", resp.StatusCode, string(body), fmt.Errorf("error: Check details"))
|
234 |
+
|
235 |
+
}
|
236 |
+
}
|
237 |
+
func (auth *Authenticator) partThree(state string) *Error {
|
238 |
+
logger.Log.Debug("Auth Three")
|
239 |
+
|
240 |
+
target := fmt.Sprintf("https://auth0.openai.com/u/login/identifier?state=%s", state)
|
241 |
+
emailURLEncoded := auth.URLEncode(auth.EmailAddress)
|
242 |
+
|
243 |
+
payload := fmt.Sprintf(
|
244 |
+
"state=%s&username=%s&js-available=false&webauthn-available=true&is-brave=false&webauthn-platform-available=true&action=default",
|
245 |
+
state, emailURLEncoded,
|
246 |
+
)
|
247 |
+
|
248 |
+
headers := map[string]string{
|
249 |
+
"Host": "auth0.openai.com",
|
250 |
+
"Origin": "https://auth0.openai.com",
|
251 |
+
"Connection": "keep-alive",
|
252 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
253 |
+
"User-Agent": auth.UserAgent,
|
254 |
+
"Referer": fmt.Sprintf("https://auth0.openai.com/u/login/identifier?state=%s", state),
|
255 |
+
"Accept-Language": "en-US,en;q=0.9",
|
256 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
257 |
+
}
|
258 |
+
|
259 |
+
req, _ := http.NewRequest("POST", target, strings.NewReader(payload))
|
260 |
+
|
261 |
+
for k, v := range headers {
|
262 |
+
req.Header.Set(k, v)
|
263 |
+
}
|
264 |
+
|
265 |
+
resp, err := auth.Session.Do(req)
|
266 |
+
if err != nil {
|
267 |
+
return NewError("part_three", 0, "Failed to send request", err)
|
268 |
+
}
|
269 |
+
defer resp.Body.Close()
|
270 |
+
|
271 |
+
if resp.StatusCode == 302 || resp.StatusCode == 200 {
|
272 |
+
return auth.partFour(state)
|
273 |
+
} else {
|
274 |
+
return NewError("part_three", resp.StatusCode, "Your email address is invalid.", fmt.Errorf("error: Check details"))
|
275 |
+
|
276 |
+
}
|
277 |
+
|
278 |
+
}
|
279 |
+
func (auth *Authenticator) partFour(state string) *Error {
|
280 |
+
logger.Log.Debug("Auth Four")
|
281 |
+
|
282 |
+
target := fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", state)
|
283 |
+
emailURLEncoded := auth.URLEncode(auth.EmailAddress)
|
284 |
+
passwordURLEncoded := auth.URLEncode(auth.Password)
|
285 |
+
payload := fmt.Sprintf("state=%s&username=%s&password=%s", state, emailURLEncoded, passwordURLEncoded)
|
286 |
+
|
287 |
+
headers := map[string]string{
|
288 |
+
"Host": "auth0.openai.com",
|
289 |
+
"Origin": "https://auth0.openai.com",
|
290 |
+
"Connection": "keep-alive",
|
291 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
292 |
+
"User-Agent": auth.UserAgent,
|
293 |
+
"Referer": fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", state),
|
294 |
+
"Accept-Language": "en-US,en;q=0.9",
|
295 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
296 |
+
}
|
297 |
+
|
298 |
+
req, _ := http.NewRequest("POST", target, strings.NewReader(payload))
|
299 |
+
|
300 |
+
for k, v := range headers {
|
301 |
+
req.Header.Set(k, v)
|
302 |
+
}
|
303 |
+
token, err := funcaptcha.GetOpenAIArkoseToken(0, auth.PUID)
|
304 |
+
if err != nil {
|
305 |
+
return NewError("part_four", 0, "get arkose_token failed", err)
|
306 |
+
}
|
307 |
+
cookie := &http.Cookie{
|
308 |
+
Name: "arkoseToken",
|
309 |
+
Value: token,
|
310 |
+
Path: "/",
|
311 |
+
}
|
312 |
+
req.AddCookie(cookie)
|
313 |
+
resp, err := auth.Session.Do(req)
|
314 |
+
if err != nil {
|
315 |
+
return NewError("part_four", 0, "Failed to send request", err)
|
316 |
+
}
|
317 |
+
defer resp.Body.Close()
|
318 |
+
if resp.StatusCode == 302 {
|
319 |
+
redirectURL := resp.Header.Get("Location")
|
320 |
+
println(redirectURL)
|
321 |
+
return auth.partFive(state, redirectURL)
|
322 |
+
} else {
|
323 |
+
var body interface{}
|
324 |
+
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
325 |
+
return NewError("part_four", 0, "", err)
|
326 |
+
}
|
327 |
+
return NewError("part_four", resp.StatusCode, body.(string), fmt.Errorf("error: Check details"))
|
328 |
+
|
329 |
+
}
|
330 |
+
|
331 |
+
}
|
332 |
+
func (auth *Authenticator) partFive(oldState string, redirectURL string) *Error {
|
333 |
+
logger.Log.Debug("Auth Five")
|
334 |
+
|
335 |
+
target := "https://auth0.openai.com" + redirectURL
|
336 |
+
|
337 |
+
headers := map[string]string{
|
338 |
+
"Host": "auth0.openai.com",
|
339 |
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
340 |
+
"Connection": "keep-alive",
|
341 |
+
"User-Agent": auth.UserAgent,
|
342 |
+
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
|
343 |
+
"Referer": fmt.Sprintf("https://auth0.openai.com/u/login/password?state=%s", oldState),
|
344 |
+
}
|
345 |
+
|
346 |
+
req, _ := http.NewRequest("GET", target, nil)
|
347 |
+
|
348 |
+
for k, v := range headers {
|
349 |
+
req.Header.Set(k, v)
|
350 |
+
}
|
351 |
+
|
352 |
+
resp, err := auth.Session.Do(req)
|
353 |
+
if err != nil {
|
354 |
+
return NewError("part_five", 0, "Failed to send request", err)
|
355 |
+
}
|
356 |
+
defer resp.Body.Close()
|
357 |
+
|
358 |
+
if resp.StatusCode == 302 {
|
359 |
+
return auth.partSix(resp.Header.Get("Location"), target)
|
360 |
+
} else {
|
361 |
+
return NewError("part_five", resp.StatusCode, resp.Status, fmt.Errorf("error: Check details"))
|
362 |
+
|
363 |
+
}
|
364 |
+
|
365 |
+
}
|
366 |
+
func (auth *Authenticator) partSix(urls, redirect_url string) *Error {
|
367 |
+
logger.Log.Debug("Auth Six")
|
368 |
+
req, _ := http.NewRequest("GET", urls, nil)
|
369 |
+
for k, v := range map[string]string{
|
370 |
+
"Host": "" + env.E.OpenaiHost + "",
|
371 |
+
"Accept": "application/json",
|
372 |
+
"Connection": "keep-alive",
|
373 |
+
"User-Agent": auth.UserAgent,
|
374 |
+
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
|
375 |
+
"Referer": redirect_url,
|
376 |
+
} {
|
377 |
+
req.Header.Set(k, v)
|
378 |
+
}
|
379 |
+
resp, err := auth.Session.Do(req)
|
380 |
+
if err != nil {
|
381 |
+
return NewError("part_six", 0, "Failed to send request", err)
|
382 |
+
}
|
383 |
+
defer resp.Body.Close()
|
384 |
+
if err != nil {
|
385 |
+
return NewError("part_six", 0, "Response was not JSON", err)
|
386 |
+
}
|
387 |
+
if resp.StatusCode != 302 {
|
388 |
+
return NewError("part_six", resp.StatusCode, urls, fmt.Errorf("incorrect response code"))
|
389 |
+
}
|
390 |
+
// Check location header
|
391 |
+
if location := resp.Header.Get("Location"); location != "https://"+env.E.OpenaiHost+"/" {
|
392 |
+
return NewError("part_six", resp.StatusCode, location, fmt.Errorf("incorrect redirect"))
|
393 |
+
}
|
394 |
+
|
395 |
+
sessionUrl := "https://" + env.E.OpenaiHost + "/api/auth/session"
|
396 |
+
|
397 |
+
req, _ = http.NewRequest("GET", sessionUrl, nil)
|
398 |
+
|
399 |
+
// Set user agent
|
400 |
+
req.Header.Set("User-Agent", auth.UserAgent)
|
401 |
+
|
402 |
+
resp, err = auth.Session.Do(req)
|
403 |
+
if err != nil {
|
404 |
+
return NewError("get_access_token", 0, "Failed to send request", err)
|
405 |
+
}
|
406 |
+
|
407 |
+
if resp.StatusCode != 200 {
|
408 |
+
return NewError("get_access_token", resp.StatusCode, "Incorrect response code", fmt.Errorf("error: Check details"))
|
409 |
+
}
|
410 |
+
var result map[string]interface{}
|
411 |
+
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
412 |
+
return NewError("get_access_token", 0, "", err)
|
413 |
+
}
|
414 |
+
|
415 |
+
// Check if access token in data
|
416 |
+
if _, ok := result["accessToken"]; !ok {
|
417 |
+
resultString := fmt.Sprintf("%v", result)
|
418 |
+
return NewError("part_six", 0, resultString, fmt.Errorf("missing access token"))
|
419 |
+
}
|
420 |
+
cookieUrl, _ := url.Parse("https://" + env.E.OpenaiHost + "")
|
421 |
+
jar := auth.Session.GetCookies(cookieUrl)
|
422 |
+
auth.AuthResult.AccessToken = result
|
423 |
+
for _, cookie := range jar {
|
424 |
+
if cookie.Name == "__Secure-next-auth.session-token" {
|
425 |
+
auth.AuthResult.FreshToken = cookie.Value
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
return nil
|
430 |
+
}
|
431 |
+
|
432 |
+
func (auth *Authenticator) GetAccessTokenByRefreshToken(freshToken string) *Error {
|
433 |
+
logger.Log.Debug("GetAccessTokenByRefreshToken")
|
434 |
+
sessionUrl := "https://" + env.E.OpenaiHost + "/api/auth/session"
|
435 |
+
|
436 |
+
req, _ := http.NewRequest("GET", sessionUrl, nil)
|
437 |
+
cookies := &http.Cookie{
|
438 |
+
Name: "__Secure-next-auth.session-token",
|
439 |
+
Value: freshToken,
|
440 |
+
}
|
441 |
+
req.AddCookie(cookies)
|
442 |
+
|
443 |
+
// Set user agent
|
444 |
+
req.Header.Set("User-Agent", auth.UserAgent)
|
445 |
+
|
446 |
+
resp, err := auth.Session.Do(req)
|
447 |
+
if err != nil {
|
448 |
+
return NewError("GetAccessTokenByRefreshToken", 0, "Failed to send request", err)
|
449 |
+
}
|
450 |
+
|
451 |
+
if resp.StatusCode != 200 {
|
452 |
+
return NewError("GetAccessTokenByRefreshToken", resp.StatusCode, "Incorrect response code", fmt.Errorf("error: Check details"))
|
453 |
+
}
|
454 |
+
var result map[string]interface{}
|
455 |
+
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
456 |
+
return NewError("GetAccessTokenByRefreshToken", 0, "", err)
|
457 |
+
}
|
458 |
+
|
459 |
+
// Check if access token in data
|
460 |
+
if _, ok := result["accessToken"]; !ok {
|
461 |
+
resultString := fmt.Sprintf("%v", result)
|
462 |
+
return NewError("GetAccessTokenByRefreshToken", 0, resultString, fmt.Errorf("missing access token"))
|
463 |
+
}
|
464 |
+
cookieUrl, _ := url.Parse("https://" + env.E.OpenaiHost + "")
|
465 |
+
jar := auth.Session.GetCookies(cookieUrl)
|
466 |
+
auth.AuthResult.AccessToken = result
|
467 |
+
for _, cookie := range jar {
|
468 |
+
if cookie.Name == "__Secure-next-auth.session-token" {
|
469 |
+
auth.AuthResult.FreshToken = cookie.Value
|
470 |
+
}
|
471 |
+
}
|
472 |
+
return nil
|
473 |
+
}
|
474 |
+
|
475 |
+
func (auth *Authenticator) GetAccessToken() map[string]interface{} {
|
476 |
+
logger.Log.Debug("GetAccessToken")
|
477 |
+
return auth.AuthResult.AccessToken
|
478 |
+
}
|
479 |
+
|
480 |
+
func (auth *Authenticator) GetRefreshToken() string {
|
481 |
+
logger.Log.Debug("GetRefreshToken")
|
482 |
+
return auth.AuthResult.FreshToken
|
483 |
+
}
|
484 |
+
func (auth *Authenticator) GetModels() (map[string]interface{}, *Error) {
|
485 |
+
logger.Log.Debug("GetModels")
|
486 |
+
if len(auth.AuthResult.AccessToken) == 0 {
|
487 |
+
return nil, NewError("get_model", 0, "Missing access token", fmt.Errorf("error: Check details"))
|
488 |
+
}
|
489 |
+
// Make request to https://"+common.E.OpenAI_HOST+"/backend-api/models
|
490 |
+
req, _ := http.NewRequest("GET", "https://"+env.E.OpenaiHost+"/backend-api/models", nil)
|
491 |
+
// Add headers
|
492 |
+
req.Header.Add("Authorization", "Bearer "+auth.AuthResult.AccessToken["accessToken"].(string))
|
493 |
+
req.Header.Add("User-Agent", auth.UserAgent)
|
494 |
+
req.Header.Add("Accept", "application/json")
|
495 |
+
req.Header.Add("Accept-Language", "en-US,en;q=0.9")
|
496 |
+
req.Header.Add("Referer", "https://"+env.E.OpenaiHost+"/")
|
497 |
+
req.Header.Add("Origin", "https://"+env.E.OpenaiHost+"")
|
498 |
+
req.Header.Add("Connection", "keep-alive")
|
499 |
+
|
500 |
+
resp, err := auth.Session.Do(req)
|
501 |
+
if err != nil {
|
502 |
+
return nil, NewError("get_model", 0, "Failed to make request", err)
|
503 |
+
}
|
504 |
+
defer resp.Body.Close()
|
505 |
+
if resp.StatusCode != 200 {
|
506 |
+
return nil, NewError("get_model", resp.StatusCode, "Failed to make request", fmt.Errorf("error: Check details"))
|
507 |
+
}
|
508 |
+
var responseBody map[string]interface{}
|
509 |
+
r, err := io.ReadAll(resp.Body)
|
510 |
+
if err != nil {
|
511 |
+
return nil, NewError("get_model", resp.StatusCode, "Failed to get response", fmt.Errorf("error: Check details"))
|
512 |
+
}
|
513 |
+
if err := json.Unmarshal(r, &responseBody); err != nil {
|
514 |
+
return nil, NewError("get_model", resp.StatusCode, "Failed to get response", fmt.Errorf("error: Check details"))
|
515 |
+
}
|
516 |
+
auth.AuthResult.Model = responseBody
|
517 |
+
return responseBody, nil
|
518 |
+
}
|
519 |
+
|
520 |
+
func (auth *Authenticator) GetPUID() (string, *Error) {
|
521 |
+
logger.Log.Debug("GetPUID")
|
522 |
+
// Check if user has access token
|
523 |
+
if len(auth.AuthResult.AccessToken) == 0 {
|
524 |
+
return "", NewError("get_puid", 0, "Missing access token", fmt.Errorf("error: Check details"))
|
525 |
+
}
|
526 |
+
// Make request to https://"+common.E.OpenAI_HOST+"/backend-api/models
|
527 |
+
req, _ := http.NewRequest("GET", "https://"+env.E.OpenaiHost+"/backend-api/models", nil)
|
528 |
+
// Add headers
|
529 |
+
req.Header.Add("Authorization", "Bearer "+auth.AuthResult.AccessToken["accessToken"].(string))
|
530 |
+
req.Header.Add("User-Agent", auth.UserAgent)
|
531 |
+
req.Header.Add("Accept", "application/json")
|
532 |
+
req.Header.Add("Accept-Language", "en-US,en;q=0.9")
|
533 |
+
req.Header.Add("Referer", "https://"+env.E.OpenaiHost+"/")
|
534 |
+
req.Header.Add("Origin", "https://"+env.E.OpenaiHost+"")
|
535 |
+
req.Header.Add("Connection", "keep-alive")
|
536 |
+
|
537 |
+
resp, err := auth.Session.Do(req)
|
538 |
+
if err != nil {
|
539 |
+
return "", NewError("get_puid", 0, "Failed to make request", err)
|
540 |
+
}
|
541 |
+
defer resp.Body.Close()
|
542 |
+
if resp.StatusCode != 200 {
|
543 |
+
return "", NewError("get_puid", resp.StatusCode, "Failed to make request", fmt.Errorf("error: Check details"))
|
544 |
+
}
|
545 |
+
// Find `_puid` cookie in response
|
546 |
+
for _, cookie := range resp.Cookies() {
|
547 |
+
if cookie.Name == "_puid" {
|
548 |
+
auth.AuthResult.PUID = cookie.Value
|
549 |
+
return cookie.Value, nil
|
550 |
+
}
|
551 |
+
}
|
552 |
+
// If cookie not found, return error
|
553 |
+
return "", NewError("get_puid", 0, "PUID cookie not found", fmt.Errorf("error: Check details"))
|
554 |
+
}
|
555 |
+
|
556 |
+
func (auth *Authenticator) GetAuthResult() AuthResult {
|
557 |
+
logger.Log.Debug("GetAuthResult")
|
558 |
+
return auth.AuthResult
|
559 |
+
}
|
pkg/tools/cache.go
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package tools
|
2 |
+
|
3 |
+
import (
|
4 |
+
"WarpGPT/pkg/logger"
|
5 |
+
"sync"
|
6 |
+
"time"
|
7 |
+
)
|
8 |
+
|
9 |
+
type CacheItem struct {
|
10 |
+
Data interface{}
|
11 |
+
ExpiresAt time.Time
|
12 |
+
}
|
13 |
+
|
14 |
+
type Cache struct {
|
15 |
+
items map[string]CacheItem
|
16 |
+
lock sync.Mutex
|
17 |
+
}
|
18 |
+
|
19 |
+
var AllCache Cache
|
20 |
+
|
21 |
+
func init() {
|
22 |
+
AllCache = Cache{items: make(map[string]CacheItem)}
|
23 |
+
}
|
24 |
+
|
25 |
+
func (c *Cache) CacheSet(key string, value CacheItem, expiration time.Duration) {
|
26 |
+
c.lock.Lock()
|
27 |
+
defer c.lock.Unlock()
|
28 |
+
value.ExpiresAt = time.Now().Add(expiration)
|
29 |
+
c.items[key] = value
|
30 |
+
logger.Log.Debug("CacheSet: Key =", key, "Expiration =", expiration, "Data =", value.Data)
|
31 |
+
}
|
32 |
+
|
33 |
+
func (c *Cache) CacheGet(key string) (CacheItem, bool) {
|
34 |
+
c.lock.Lock()
|
35 |
+
defer c.lock.Unlock()
|
36 |
+
|
37 |
+
item, exists := c.items[key]
|
38 |
+
if exists && item.ExpiresAt.After(time.Now()) {
|
39 |
+
logger.Log.Debug("CacheGet (Hit): Key =", key, "Expiration =", item.ExpiresAt, "Data =", item.Data)
|
40 |
+
return item, true
|
41 |
+
}
|
42 |
+
logger.Log.Debug("CacheGet (Miss): Key =", key)
|
43 |
+
return CacheItem{}, false
|
44 |
+
}
|
pkg/tools/sseclient.go
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package tools
|
2 |
+
|
3 |
+
import (
|
4 |
+
"bufio"
|
5 |
+
"bytes"
|
6 |
+
"io"
|
7 |
+
"log"
|
8 |
+
"strings"
|
9 |
+
)
|
10 |
+
|
11 |
+
const fieldSeparator = ":"
|
12 |
+
|
13 |
+
type SSEClient struct {
|
14 |
+
EventSource io.ReadCloser
|
15 |
+
logger *log.Logger
|
16 |
+
}
|
17 |
+
|
18 |
+
type Event struct {
|
19 |
+
ID string
|
20 |
+
Event string
|
21 |
+
Data string
|
22 |
+
Retry string
|
23 |
+
}
|
24 |
+
|
25 |
+
func NewSSEClient(eventSource io.ReadCloser) *SSEClient {
|
26 |
+
return &SSEClient{
|
27 |
+
EventSource: eventSource,
|
28 |
+
logger: log.New(log.Writer(), "SSEClient: ", log.LstdFlags),
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
func (c *SSEClient) Read() <-chan Event {
|
33 |
+
events := make(chan Event)
|
34 |
+
go func() {
|
35 |
+
defer close(events)
|
36 |
+
reader := bufio.NewReaderSize(c.EventSource, 128*1024)
|
37 |
+
var data bytes.Buffer
|
38 |
+
|
39 |
+
for {
|
40 |
+
line, err := reader.ReadBytes('\n')
|
41 |
+
if err == io.EOF {
|
42 |
+
break
|
43 |
+
}
|
44 |
+
if err != nil {
|
45 |
+
c.logger.Printf("Error reading from event source: %v", err)
|
46 |
+
break
|
47 |
+
}
|
48 |
+
|
49 |
+
data.Write(line)
|
50 |
+
|
51 |
+
if bytes.HasSuffix(data.Bytes(), []byte("\n\n")) || bytes.HasSuffix(data.Bytes(), []byte("\r\n\r\n")) {
|
52 |
+
event := c.parseEvent(data.String())
|
53 |
+
if event.Data != "" {
|
54 |
+
events <- event
|
55 |
+
}
|
56 |
+
data.Reset()
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}()
|
60 |
+
|
61 |
+
return events
|
62 |
+
}
|
63 |
+
|
64 |
+
func (c *SSEClient) parseEvent(data string) Event {
|
65 |
+
event := Event{
|
66 |
+
ID: "",
|
67 |
+
Event: "message",
|
68 |
+
Data: "",
|
69 |
+
Retry: "",
|
70 |
+
}
|
71 |
+
lines := strings.Split(data, "\n")
|
72 |
+
for _, line := range lines {
|
73 |
+
if strings.TrimSpace(line) == "" || strings.HasPrefix(line, fieldSeparator) {
|
74 |
+
continue
|
75 |
+
}
|
76 |
+
|
77 |
+
parts := strings.SplitN(line, fieldSeparator, 2)
|
78 |
+
field := parts[0]
|
79 |
+
var value string
|
80 |
+
if len(parts) == 2 {
|
81 |
+
value = strings.TrimPrefix(parts[1], " ")
|
82 |
+
}
|
83 |
+
|
84 |
+
switch field {
|
85 |
+
case "id":
|
86 |
+
event.ID = value
|
87 |
+
case "event":
|
88 |
+
event.Event = value
|
89 |
+
case "data":
|
90 |
+
event.Data += value + "\n"
|
91 |
+
case "retry":
|
92 |
+
event.Retry = value
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
if strings.HasSuffix(event.Data, "\n") {
|
97 |
+
event.Data = strings.TrimSuffix(event.Data, "\n")
|
98 |
+
}
|
99 |
+
|
100 |
+
return event
|
101 |
+
}
|
102 |
+
|
103 |
+
func (c *SSEClient) Close() error {
|
104 |
+
err := c.EventSource.Close()
|
105 |
+
if err != nil {
|
106 |
+
return err
|
107 |
+
}
|
108 |
+
return nil
|
109 |
+
}
|
static/img1.png
ADDED
static/img2.png
ADDED
static/img3.png
ADDED