File size: 14,440 Bytes
084ae67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
const express = require("express");
const { io } = require("socket.io-client");
const { v4: uuidv4 } = require("uuid");
const { ProxyAgent } = require("proxy-agent");

// 创建代理实例
const 代理 = new ProxyAgent();

// 获取环境变量值并赋给中文变量名
const 曲奇池 = process.env.COOKIE_POOL || '';
const 用户代理 = process.env.USER_AGENT || '';
const 全局代理 = process.env.ALL_PROXY || '';

const 应用 = express();
const 端口 = process.env.PORT || 7860;

// 解析曲奇池,支持单个或多个曲奇(用逗号分隔)
const cookiePool = 曲奇池
    ? 曲奇池.split(',').map(cookie => cookie.trim()).filter(cookie => cookie.length > 0)
    : [];

if (cookiePool.length === 0) {
    console.error("错误:环境变量 曲奇池 未设置或为空。");
    process.exit(1);
}

// 创建事件流的工具函数
function 创建事件(事件, 数据) {
    if (typeof 数据 === "object") {
        数据 = JSON.stringify(数据);
    }
    return `event: ${事件}\ndata: ${数据}\n\n`;
}

// 处理 POST 请求的路由 /v1/messages
应用.post("/v1/messages", (请求, 响应) => {
    请求.rawBody = "";
    请求.setEncoding("utf8");

    // 收集请求的原始数据
    请求.on("data", function (片段) {
        请求.rawBody += 片段;
    });

    请求.on("end", async () => {
        响应.setHeader("Content-Type", "text/event-stream;charset=utf-8");
        try {
            let 请求数据 = JSON.parse(请求.rawBody);
            if (请求数据.stream == false) {
                // 当 stream 为 false 时,返回固定的响应
                响应.send(
                    JSON.stringify({
                        id: uuidv4(),
                        content: [
                            {
                                text: "请开启流式传输。",
                            },
                            {
                                id: "string",
                                name: "string",
                                input: {},
                            },
                        ],
                        model: "string",
                        stop_reason: "end_turn",
                        stop_sequence: "string",
                        usage: {
                            input_tokens: 0,
                            output_tokens: 0,
                        },
                    })
                );
            } else if (请求数据.stream == true) {
                // 处理 stream 为 true 的情况
                let 用户消息 = [{ 问题: "", 回答: "" }];
                let 最后更新 = true;

                if (请求数据.system) {
                    // 将系统消息添加到消息列表的首位
                    请求数据.messages.unshift({ role: "system", content: 请求数据.system });
                }

                console.log(请求数据.messages);

                // 遍历消息列表,构建用户和助手的问答对
                请求数据.messages.forEach((消息) => {
                    if (消息.role == "system" || 消息.role == "user") {
                        if (最后更新) {
                            用户消息[用户消息.length - 1].问题 += 消息.content + "\n";
                        } else if (用户消息[用户消息.length - 1].问题 == "") {
                            用户消息[用户消息.length - 1].问题 += 消息.content + "\n";
                        } else {
                            用户消息.push({ 问题: 消息.content + "\n", 回答: "" });
                        }
                        最后更新 = true;
                    } else if (消息.role == "assistant") {
                        if (!最后更新) {
                            用户消息[用户消息.length - 1].回答 += 消息.content + "\n";
                        } else if (用户消息[用户消息.length - 1].回答 == "") {
                            用户消息[用户消息.length - 1].回答 += 消息.content + "\n";
                        } else {
                            用户消息.push({ 问题: "", 回答: 消息.content + "\n" });
                        }
                        最后更新 = false;
                    }
                });

                // 查找最新的用户消息
                const 最新用户消息 = 请求数据.messages
                    .slice().reverse()
                    .find(消息 => 消息.role === "user");

                if (!最新用户消息) {
                    throw new Error("请求中未找到用户消息。");
                }

                // 获取最新用户消息的索引
                const 最新用户索引 = 请求数据.messages.lastIndexOf(最新用户消息);

                // 构建除最新用户消息外的所有消息
                const 先前消息列表 = 请求数据.messages.slice(0, 最新用户索引);

                // 改进上下文拼接格式,增加提示并避免重复
                let 先前消息 = 先前消息列表
                    .map((消息) => {
                        if (消息.role === "user") {
                            return `用户: ${消息.content}`;
                        } else if (消息.role === "assistant") {
                            return `助手: ${消息.content}`;
                        } else if (消息.role === "system") {
                            return `系统: ${消息.content}`;
                        }
                        return 消息.content;
                    })
                    .join("\n\n");

                // 在上下文末尾添加提示,不再重复添加最新的用户消息
                先前消息 += `\n\n# ↓请关注用户最新的消息↓\n\n`;

                // 追加最新的用户消息
                先前消息 += `用户: ${最新用户消息.content}`;

                let 消息ID = uuidv4();

                // 发送消息开始的事件
                响应.write(
                    创建事件("message_start", {
                        type: "message_start",
                        message: {
                            id: 消息ID,
                            type: "message",
                            role: "assistant",
                            content: [],
                            model: "claude-3-opus-20240229",
                            stop_reason: null,
                            stop_sequence: null,
                            usage: { input_tokens: 8, output_tokens: 1 },
                        },
                    })
                );
                响应.write(创建事件("content_block_start", { type: "content_block_start", index: 0, content_block: { type: "text", text: "" } }));
                响应.write(创建事件("ping", { type: "ping" }));

                // 随机选择一个曲奇
                const 随机曲奇 = cookiePool[Math.floor(Math.random() * cookiePool.length)];

                // 配置 Socket.IO 客户端选项
                var 选项 = {
                    agent: 代理,
                    auth: {
                        jwt: "anonymous-ask-user",
                    },
                    reconnection: false,
                    transports: ["websocket"],
                    path: "/socket.io",
                    hostname: "www.perplexity.ai",
                    secure: true,
                    port: "443",
                    extraHeaders: {
                        Cookie: 随机曲奇, // 使用随机选择的曲奇
                        "User-Agent": 用户代理,
                        Accept: "*/*",
                        priority: "u=1, i",
                        Referer: "https://www.perplexity.ai/",
                    },
                };

                // 通过 Socket.IO 连接 Perplexity.ai 的 WebSocket
                var socket = io("wss://www.perplexity.ai/", 选项);

                socket.on("connect", function () {
                    console.log(" > [已连接]");
                    socket
                        .emitWithAck("perplexity_ask", 先前消息, {
                            "version": "2.9",
                            "source": "default",
                            "attachments": [],
                            "language": "en-GB",
                            "timezone": "Europe/London",
                            "search_focus": "writing",
                            "frontend_uuid": uuidv4(),
                            "mode": "concise",
                            "is_related_query": false,
                            "is_default_related_query": false,
                            "visitor_id": uuidv4(),
                            "frontend_context_uuid": uuidv4(),
                            "prompt_source": "user",
                            "query_source": "home"
                        })
                        .then((响应数据) => {
                            console.log(响应数据);
                            响应.write(创建事件("content_block_stop", { type: "content_block_stop", index: 0 }));
                            响应.write(
                                创建事件("message_delta", {
                                    type: "message_delta",
                                    delta: { stop_reason: "end_turn", stop_sequence: null },
                                    usage: { output_tokens: 12 },
                                })
                            );
                            响应.write(创建事件("message_stop", { type: "message_stop" }));

                            响应.end();
                        }).catch((错误) => {
                            if(错误.message != "socket has been disconnected"){
                                console.log(错误);
                            }
                        });
                });

                // 监听所有事件,记录日志
                socket.onAny((事件, ...参数) => {
                    console.log(`> [收到事件: ${事件}]`);
                });

                // 监听查询进度事件,处理返回的文本块
                socket.on("query_progress", (数据) => {
                    if(数据.text){
                        try {
                            var 文本 = JSON.parse(数据.text)
                            var 块 = 文本.chunks[文本.chunks.length - 1];
                            if (块) {
                                letJSON = JSON.stringify({
                                    type: "content_block_delta",
                                    index: 0,
                                    delta: { type: "text_delta", text: 块 },
                                });
                                响应.write(创建事件("content_block_delta", 块JSON));
                            }
                        } catch (错误) {
                            console.log("解析 query_progress 数据时出错:", 错误);
                        }
                    }
                });

                // 监听断开连接事件
                socket.on("disconnect", function () {
                    console.log(" > [已断开连接]");
                });

                // 监听错误事件,返回错误信息给客户端
                socket.on("error", (错误) => {
                    letJSON = JSON.stringify({
                        type: "content_block_delta",
                        index: 0,
                        delta: { type: "text_delta", text: "获取输出时出现错误\n请查看日志以获取更多信息" },
                    });
                    响应.write(创建事件("content_block_delta", 块JSON));
                    响应.write(创建事件("content_block_stop", { type: "content_block_stop", index: 0 }));
                    响应.write(
                        创建事件("message_delta", {
                            type: "message_delta",
                            delta: { stop_reason: "end_turn", stop_sequence: null },
                            usage: { output_tokens: 12 },
                        })
                    );
                    响应.write(创建事件("message_stop", { type: "message_stop" }));

                    响应.end();
                    console.log(错误);
                });

                // 监听连接错误事件,返回错误信息给客户端
                socket.on("connect_error", function (错误) {
                    letJSON = JSON.stringify({
                        type: "content_block_delta",
                        index: 0,
                        delta: { type: "text_delta", text: "连接到 Perplexity.ai 失败\n请查看日志以获取更多信息" },
                    });
                    响应.write(创建事件("content_block_delta", 块JSON));
                    响应.write(创建事件("content_block_stop", { type: "content_block_stop", index: 0 }));
                    响应.write(
                        创建事件("message_delta", {
                            type: "message_delta",
                            delta: { stop_reason: "end_turn", stop_sequence: null },
                            usage: { output_tokens: 12 },
                        })
                    );
                    响应.write(创建事件("message_stop", { type: "message_stop" }));

                    响应.end();
                    console.log(错误);
                });

                // 监听客户端关闭事件,断开 Socket 连接
                响应.on("close", function () {
                    console.log(" > [客户端已关闭连接]");
                    socket.disconnect();
                });
            } else {
                throw new Error("无效的请求");
            }
        } catch (错误) {
            console.log(错误);
            响应.write(JSON.stringify({ error: 错误.message }));
            响应.end();
            return;
        }
    });
});

// 处理所有未定义的路由,返回 418
应用.use((请求, 响应, 下一个) => {
    响应.status(418).send("418 I'm a teapot");
});

// 启动服务器,监听指定端口
应用.listen(端口, () => {
    console.log(`Perplexity 代理服务器正在监听端口 ${端口}`);
});