chatgpt-plugin/client/CopilotAIClient.js
2024-12-02 21:27:56 +08:00

156 lines
4.3 KiB
JavaScript

import WebSocket from 'ws';
export class BingAIClient {
constructor(baseUrl = 'wss://copilot.microsoft.com/c/api/chat') {
this.baseUrl = baseUrl;
this.ws = null;
this.conversationId = null;
this.currentMessageId = null;
this.partialMessages = new Map();
}
async sendMessage(text, options = {}) {
// If conversationId is provided, use it, otherwise create a new one
if (options.conversationId) {
this.conversationId = options.conversationId;
} else {
this.conversationId = this._generateConversationId();
}
// Connect WebSocket
await this.connectWebSocket();
// Send the initial message or challenge response
await this.sendInitialMessage();
// Send the text message
const messagePayload = {
event: 'send',
conversationId: this.conversationId,
content: [{ type: 'text', text }],
mode: 'chat',
context: { edge: 'NonContextual' },
};
this.ws.send(JSON.stringify(messagePayload));
// Wait for the response and collect the full message
const responseText = await this.collectResponse();
return responseText;
}
async connectWebSocket() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.baseUrl);
this.ws.on('open', () => {
console.log('WebSocket connection established.');
resolve();
});
this.ws.on('message', (data) => this.handleServerMessage(data));
this.ws.on('close', () => {
console.log('WebSocket connection closed.');
});
this.ws.on('error', (err) => {
reject(err);
});
});
}
async sendInitialMessage() {
return new Promise((resolve, reject) => {
this.ws.once('message', (data) => {
const message = JSON.parse(data);
if (message.event === 'challenge') {
// Handle challenge by sending the challenge response
this.handleChallenge(message)
.then(resolve)
.catch(reject);
} else {
resolve(); // Proceed if no challenge event
}
});
});
}
async handleChallenge(challenge) {
// Get the token by calling getTurnstile function (you need to define this function)
const token = await this.getTurnstile(challenge.conversationId);
const challengeResponse = {
event: 'challengeResponse',
token: token,
method: 'cloudflare',
};
this.ws.send(JSON.stringify(challengeResponse));
}
async collectResponse() {
return new Promise((resolve, reject) => {
let fullResponse = '';
const checkMessageComplete = (messageId) => {
// Wait for the complete message
if (this.partialMessages.has(messageId) && this.partialMessages.get(messageId).done) {
const completeMessage = this.partialMessages.get(messageId).text;
resolve(completeMessage);
}
};
this.ws.on('message', (data) => {
const message = JSON.parse(data);
switch (message.event) {
case 'received':
break;
case 'startMessage':
this.currentMessageId = message.messageId;
break;
case 'appendText':
if (!this.partialMessages.has(message.messageId)) {
this.partialMessages.set(message.messageId, { text: '', done: false });
}
this.partialMessages.get(message.messageId).text += message.text;
// Check if this part is the last one
if (message.partId === '0') {
this.partialMessages.get(message.messageId).done = true;
}
checkMessageComplete(message.messageId);
break;
case 'partCompleted':
break;
case 'done':
checkMessageComplete(message.messageId);
break;
default:
console.warn('Unexpected event:', message.event);
break;
}
});
});
}
async getTurnstile(conversationId) {
// This is a mock implementation, you should replace this with the actual logic
// to interact with the turnstile system to get the token.
// In a real-world scenario, this would involve making a request to some endpoint.
return '0.EHn0JUxxxx';
}
_generateConversationId() {
return 'conversation-' + Math.random().toString(36).substring(2, 15);
}
}