blfm commited on
Commit
7fba48e
Β·
verified Β·
1 Parent(s): eb7135a

Upload app.js

Browse files
Files changed (1) hide show
  1. app.js +401 -0
app.js ADDED
@@ -0,0 +1,401 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const bodyParser = require("body-parser");
3
+ const axios = require("axios");
4
+ const https = require("https");
5
+ const { encode } = require("gpt-3-encoder");
6
+ const { randomUUID, randomInt, createHash } = require("crypto");
7
+ const { config } = require("dotenv");
8
+
9
+ config();
10
+
11
+ // Constants for the server and API configuration
12
+ const port = process.env.SERVER_PORT || 3040;
13
+ const baseUrl = "https://chat.openai.com";
14
+ const apiUrl = `${baseUrl}/backend-api/conversation`;
15
+ const refreshInterval = 60000; // Interval to refresh token in ms
16
+ const errorWait = 120000; // Wait time in ms after an error
17
+ const newSessionRetries = parseInt(process.env.NEW_SESSION_RETRIES) || 5;
18
+ const userAgent = process.env.USER_AGENT || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
19
+
20
+ // Initialize global variables to store the session token and device ID
21
+ let token;
22
+ let oaiDeviceId;
23
+
24
+ // Function to wait for a specified duration
25
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
26
+
27
+ function GenerateCompletionId(prefix = "cmpl-") {
28
+ const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
29
+ const length = 28;
30
+
31
+ for (let i = 0; i < length; i++) {
32
+ prefix += characters.charAt(Math.floor(Math.random() * characters.length));
33
+ }
34
+
35
+ return prefix;
36
+ }
37
+
38
+ async function* chunksToLines(chunksAsync) {
39
+ let previous = "";
40
+ for await (const chunk of chunksAsync) {
41
+ const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
42
+ previous += bufferChunk;
43
+ let eolIndex;
44
+ while ((eolIndex = previous.indexOf("\n")) >= 0) {
45
+ // line includes the EOL
46
+ const line = previous.slice(0, eolIndex + 1).trimEnd();
47
+ if (line === "data: [DONE]") break;
48
+ if (line.startsWith("data: ")) yield line;
49
+ previous = previous.slice(eolIndex + 1);
50
+ }
51
+ }
52
+ }
53
+
54
+ async function* linesToMessages(linesAsync) {
55
+ for await (const line of linesAsync) {
56
+ const message = line.substring("data :".length);
57
+
58
+ yield message;
59
+ }
60
+ }
61
+
62
+ async function* StreamCompletion(data) {
63
+ yield* linesToMessages(chunksToLines(data));
64
+ }
65
+
66
+ // Setup axios instance for API requests with predefined configurations
67
+ const axiosInstance = axios.create({
68
+ httpsAgent: new https.Agent({ rejectUnauthorized: false }),
69
+ headers: {
70
+ accept: "*/*",
71
+ "accept-language": "en-US,en;q=0.9",
72
+ "cache-control": "no-cache",
73
+ "content-type": "application/json",
74
+ "oai-language": "en-US",
75
+ origin: baseUrl,
76
+ pragma: "no-cache",
77
+ referer: baseUrl,
78
+ "sec-ch-ua": '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"',
79
+ "sec-ch-ua-mobile": "?0",
80
+ "sec-ch-ua-platform": '"Windows"',
81
+ "sec-fetch-dest": "empty",
82
+ "sec-fetch-mode": "cors",
83
+ "sec-fetch-site": "same-origin",
84
+ "user-agent": userAgent,
85
+ },
86
+ });
87
+
88
+ function generateProofToken(seed, diff, userAgent) {
89
+ const cores = [8, 12, 16, 24];
90
+ const screens = [3000, 4000, 6000];
91
+ const core = cores[randomInt(0, cores.length)];
92
+ const screen = screens[randomInt(0, screens.length)];
93
+ const now = new Date(Date.now() - 8 * 3600 * 1000);
94
+ const parseTime = now.toUTCString().replace("GMT", "GMT-0500 (Eastern Time)");
95
+ const config = [core + screen, parseTime, 4294705152, 0, userAgent];
96
+ const diffLen = diff.length / 2;
97
+ for (let i = 0; i < 100000; i++) {
98
+ config[3] = i;
99
+ const jsonData = JSON.stringify(config);
100
+ const base = Buffer.from(jsonData).toString("base64");
101
+ const hashValue = createHash("sha3-512")
102
+ .update(seed + base)
103
+ .digest();
104
+ if (hashValue.toString("hex").substring(0, diffLen) <= diff) {
105
+ const result = "gAAAAAB" + base;
106
+ return result;
107
+ }
108
+ }
109
+ const fallbackBase = Buffer.from(`"${seed}"`).toString("base64");
110
+ return "gAAAAABwQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" + fallbackBase;
111
+ }
112
+
113
+ // Function to get a new session ID and token from the OpenAI API
114
+ async function getNewSession(retries = 0) {
115
+ let newDeviceId = randomUUID();
116
+ try {
117
+ const response = await axiosInstance.post(
118
+ `${baseUrl}/backend-anon/sentinel/chat-requirements`,
119
+ {},
120
+ {
121
+ headers: { "oai-device-id": newDeviceId },
122
+ }
123
+ );
124
+
125
+ let session = response.data;
126
+ session.deviceId = newDeviceId;
127
+
128
+ console.log(
129
+ `System: Successfully refreshed session ID and token. ${
130
+ !token ? "(Now it's ready to process requests)" : ""
131
+ }`
132
+ );
133
+ oaiDeviceId = newDeviceId;
134
+ token = session.token;
135
+
136
+ return session;
137
+ } catch (error) {
138
+ await wait(500);
139
+ return retries < newSessionRetries ? getNewSession(retries + 1) : null;
140
+ }
141
+ }
142
+
143
+ // Middleware to enable CORS and handle pre-flight requests
144
+ function enableCORS(req, res, next) {
145
+ res.header("Access-Control-Allow-Origin", "*");
146
+ res.header("Access-Control-Allow-Headers", "*");
147
+ res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
148
+ if (req.method === "OPTIONS") {
149
+ return res.status(200).end();
150
+ }
151
+ next();
152
+ }
153
+
154
+ // Middleware to handle chat completions
155
+ async function handleChatCompletion(req, res) {
156
+ console.log(
157
+ "Request:",
158
+ `${req.method} ${req.originalUrl}`,
159
+ `${req.body?.messages?.length ?? 0} messages`,
160
+ req.body.stream ? "(stream-enabled)" : "(stream-disabled)"
161
+ );
162
+ try {
163
+ let session = await getNewSession();
164
+ if (!session) {
165
+ res.write(
166
+ JSON.stringify({
167
+ status: false,
168
+ error: {
169
+ message: `Error getting a new session, please try again later, if the issue persists, please open an issue on the GitHub repository.`,
170
+ type: "invalid_request_error",
171
+ },
172
+ })
173
+ );
174
+ return res.end();
175
+ }
176
+ let proofToken = generateProofToken(
177
+ session.proofofwork.seed,
178
+ session.proofofwork.difficulty,
179
+ userAgent
180
+ );
181
+ const body = {
182
+ action: "next",
183
+ messages: req.body.messages.map((message) => ({
184
+ author: { role: message.role },
185
+ content: { content_type: "text", parts: [message.content] },
186
+ })),
187
+ parent_message_id: randomUUID(),
188
+ model: "text-davinci-002-render-sha",
189
+ timezone_offset_min: -180,
190
+ suggestions: [],
191
+ history_and_training_disabled: true,
192
+ conversation_mode: { kind: "primary_assistant" },
193
+ websocket_request_id: randomUUID(),
194
+ };
195
+ let promptTokens = 0;
196
+ let completionTokens = 0;
197
+ for (let message of req.body.messages) {
198
+ promptTokens += encode(message.content).length;
199
+ }
200
+
201
+ const response = await axiosInstance.post(apiUrl, body, {
202
+ responseType: "stream",
203
+ headers: {
204
+ "oai-device-id": session.deviceId,
205
+ "openai-sentinel-chat-requirements-token": session.token,
206
+ "openai-sentinel-proof-token": proofToken,
207
+ },
208
+ });
209
+
210
+ // Set the response headers based on the request type
211
+ if (req.body.stream) {
212
+ res.setHeader("Content-Type", "text/event-stream");
213
+ res.setHeader("Cache-Control", "no-cache");
214
+ res.setHeader("Connection", "keep-alive");
215
+ } else {
216
+ res.setHeader("Content-Type", "application/json");
217
+ }
218
+
219
+ let fullContent = "";
220
+ let requestId = GenerateCompletionId("chatcmpl-");
221
+ let created = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
222
+ let finish_reason = null;
223
+ let error;
224
+
225
+ for await (const message of StreamCompletion(response.data)) {
226
+ // Skip heartbeat detection
227
+ if (message.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{6}$/))
228
+ continue;
229
+
230
+ const parsed = JSON.parse(message);
231
+
232
+ if (parsed.error) {
233
+ error = `Error message from OpenAI: ${parsed.error}`;
234
+ finish_reason = "stop";
235
+ break;
236
+ }
237
+
238
+ let content = parsed?.message?.content?.parts[0] ?? "";
239
+ let status = parsed?.message?.status ?? "";
240
+
241
+ for (let message of req.body.messages) {
242
+ if (message.content === content) {
243
+ content = "";
244
+ break;
245
+ }
246
+ }
247
+
248
+ switch (status) {
249
+ case "in_progress":
250
+ finish_reason = null;
251
+ break;
252
+ case "finished_successfully":
253
+ let finish_reason_data =
254
+ parsed?.message?.metadata?.finish_details?.type ?? null;
255
+ switch (finish_reason_data) {
256
+ case "max_tokens":
257
+ finish_reason = "length";
258
+ break;
259
+ case "stop":
260
+ default:
261
+ finish_reason = "stop";
262
+ }
263
+ break;
264
+ default:
265
+ finish_reason = null;
266
+ }
267
+
268
+ if (content === "") continue;
269
+
270
+ let completionChunk = content.replace(fullContent, "");
271
+
272
+ completionTokens += encode(completionChunk).length;
273
+
274
+ if (req.body.stream) {
275
+ let response = {
276
+ id: requestId,
277
+ created: created,
278
+ object: "chat.completion.chunk",
279
+ model: "gpt-3.5-turbo",
280
+ choices: [
281
+ {
282
+ delta: {
283
+ content: completionChunk,
284
+ },
285
+ index: 0,
286
+ finish_reason: finish_reason,
287
+ },
288
+ ],
289
+ };
290
+
291
+ res.write(`data: ${JSON.stringify(response)}\n\n`);
292
+ }
293
+
294
+ fullContent = content.length > fullContent.length ? content : fullContent;
295
+ }
296
+
297
+ if (req.body.stream) {
298
+ res.write(
299
+ `data: ${JSON.stringify({
300
+ id: requestId,
301
+ created: created,
302
+ object: "chat.completion.chunk",
303
+ model: "gpt-3.5-turbo",
304
+ choices: [
305
+ {
306
+ delta: {
307
+ content: error ?? "",
308
+ },
309
+ index: 0,
310
+ finish_reason: finish_reason,
311
+ },
312
+ ],
313
+ })}\n\n`
314
+ );
315
+ } else {
316
+ res.write(
317
+ JSON.stringify({
318
+ id: requestId,
319
+ created: created,
320
+ model: "gpt-3.5-turbo",
321
+ object: "chat.completion",
322
+ choices: [
323
+ {
324
+ finish_reason: finish_reason,
325
+ index: 0,
326
+ message: {
327
+ content: error ?? fullContent,
328
+ role: "assistant",
329
+ },
330
+ },
331
+ ],
332
+ usage: {
333
+ prompt_tokens: promptTokens,
334
+ completion_tokens: completionTokens,
335
+ total_tokens: promptTokens + completionTokens,
336
+ },
337
+ })
338
+ );
339
+ }
340
+
341
+ res.end();
342
+ } catch (error) {
343
+ if (!res.headersSent) res.setHeader("Content-Type", "application/json");
344
+ res.write(
345
+ JSON.stringify({
346
+ status: false,
347
+ error: {
348
+ message:
349
+ "An error occurred. Please try again. Additionally, ensure that your request complies with OpenAI's policy.",
350
+ type: "invalid_request_error",
351
+ },
352
+ })
353
+ );
354
+ res.end();
355
+ }
356
+ }
357
+
358
+ // Initialize Express app and use middlewares
359
+ const app = express();
360
+ app.use(bodyParser.json());
361
+ app.use(enableCORS);
362
+
363
+ // Route to handle POST requests for chat completions
364
+ app.post("/proxies/v1/chat/completions", handleChatCompletion);
365
+
366
+ // 404 handler for unmatched routes
367
+ app.use((req, res) =>
368
+ res.status(404).send({
369
+ status: false,
370
+ error: {
371
+ message: `The requested endpoint (${req.method.toLocaleUpperCase()} ${req.path}) was not found. https://github.com/missuo/FreeGPT35`,
372
+ type: "invalid_request_error",
373
+ },
374
+ })
375
+ );
376
+
377
+ // Start the server and the session ID refresh loop
378
+ app.listen(port, async () => {
379
+ console.log(`πŸ’‘ Server is running at http://localhost:${port}`);
380
+ console.log();
381
+ console.log(`πŸ”— Local Base URL: http://localhost:${port}/v1`);
382
+ console.log(`πŸ”— Local Endpoint: http://localhost:${port}/v1/chat/completions`);
383
+ console.log();
384
+ console.log("πŸ“ Original TS Source By: Pawan.Krd");
385
+ console.log("πŸ“ Modified By: Vincent");
386
+ console.log();
387
+
388
+ setTimeout(async () => {
389
+ while (true) {
390
+ try {
391
+ await getNewSession();
392
+ await wait(refreshInterval);
393
+ } catch (error) {
394
+ console.error("Error refreshing session ID, retrying in 2 minute...");
395
+ console.error("If this error persists, your country may not be supported yet.");
396
+ console.error("If your country was the issue, please consider using a U.S. VPN.");
397
+ await wait(errorWait);
398
+ }
399
+ }
400
+ }, 0);
401
+ });