wabot
Version:
Whatsapp Bot Module for automate response and interact whit users
278 lines (256 loc) • 7.95 kB
JavaScript
const { randomUUID } = require("node:crypto");
const { io } = require("socket.io-client");
const fs = require("fs");
function getCurrentTime() {
const date = new Date();
const year = date.getFullYear();
const month = `0${date.getMonth() + 1}`.slice(-2);
const day = `0${date.getDate()}`.slice(-2);
const hours = `0${date.getHours()}`.slice(-2);
const minutes = `0${date.getMinutes()}`.slice(-2);
const seconds = `0${date.getSeconds()}`.slice(-2);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const ErrorType = {
Timeout: 1,
AccountRateLimitExceeded: 2,
AnotherMessageInProgress: 3,
SessionTokenExpired: 4,
MessageTooLong: 5,
UnknownError: 6,
}
class ChatGPT {
constructor(
sessionToken = '',
options = {
name: "default",
reconnection: true,
forceNew: false,
bypassNode: "https://gpt.pawan.krd",
proAccount: false
}
) {
var { reconnection, forceNew, proAccount, name } = options;
this.name = name;
this.path = `./${this.name}-chatgpt-io.json`;
this.proAccount = proAccount;
this.ready = false;
this.socket = io(options.bypassNode ?? "https://gpt.pawan.krd", {
query: {
client: "nodejs",
version: "1.1.0",
versionCode: "110",
},
transportOptions: {
websocket: {
pingInterval: 10000,
pingTimeout: 5000,
},
},
reconnection: reconnection ?? true,
reconnectionAttempts: 100,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
timeout: 10000,
transports: ["websocket", "polling"],
upgrade: false,
forceNew: forceNew ?? false,
});
this.sessionToken = sessionToken;
this.conversations = [];
this.auth = null;
this.expires = Date.now();
this.pauseTokenChecks = true;
this.load();
this.socket.on("connect", () => {
if (this.onConnected) this.onConnected();
console.log("Connected to server");
});
this.socket.on("disconnect", () => {
if (this.onDisconnected) this.onDisconnected();
console.log("Disconnected from server");
});
this.socket.on("serverMessage", (data) => {
console.log(`[SERVER MESSAGE] ${getCurrentTime()} ${data}`);
});
setInterval(async () => {
if (this.pauseTokenChecks) return;
this.pauseTokenChecks = true;
if(!this.auth){
await this.getTokens();
this.pauseTokenChecks = false;
return;
}
const now = Date.now();
const offset = 2 * 60 * 1000;
if (this.expires < now - offset || !this.auth) {
await this.getTokens();
}
this.pauseTokenChecks = false;
}, 500);
setInterval(() => {
const now = Date.now();
this.conversations = this.conversations.filter((conversation) => {
return now - conversation.lastActive < 1800000; // 2 minutes in milliseconds
});
}, 60000);
this.intervalId = setInterval(() => {
this.save();
}, this.saveInterval);
process.on('beforeExit', async () => {
clearInterval(this.intervalId);
if(this.ready) await this.save();
});
}
async load() {
this.pauseTokenChecks = true;
if (!fs.existsSync(this.path)) {
await this.wait(1000);
this.pauseTokenChecks = false;
return;
}
let data = await fs.promises.readFile(this.path, "utf8");
let json = JSON.parse(data);
for (let key in json) {
this[key] = json[key];
}
await this.wait(1000);
if(this.auth) this.ready = true;
this.pauseTokenChecks = false;
}
async save() {
let result = {};
for (let key in this) {
if (key === "pauseTokenChecks") continue;
if (key === "ready") continue;
if (key === "name") continue;
if (key === "path") continue;
if (key === "saveInterval") continue;
if (this[key] instanceof Array || typeof this[key] === "string" || typeof this[key] === "number" || typeof this[key] === "boolean") {
result[key] = this[key];
}
}
await fs.promises.writeFile(this.path, JSON.stringify(result, null, 4));
}
addConversation(id) {
let conversation = {
id: id,
conversationId: null,
parentId: randomUUID(),
lastActive: Date.now(),
};
this.conversations.push(conversation);
this.save();
return conversation;
}
getConversationById(id) {
let conversation = this.conversations.find(
(conversation) => conversation.id === id
);
if (!conversation) {
conversation = this.addConversation(id);
} else {
conversation.lastActive = Date.now();
}
return conversation;
}
resetConversation(id = "default") {
let conversation = this.conversations.find(
(conversation) => conversation.id === id
);
if (!conversation) return;
conversation.conversationId = null;
}
wait(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
async waitForReady() {
while (!this.ready) await this.wait(25);
if (this.onReady) this.onReady();
console.log("Ready!");
}
async ask(prompt, id = "default") {
if (!this.auth || !this.validateToken(this.auth)) await this.getTokens();
let conversation = this.getConversationById(id);
let data = await new Promise((resolve) => {
this.socket.emit(
this.proAccount ? "askQuestionPro" : "askQuestion",
{
prompt: prompt,
parentId: conversation.parentId,
conversationId: conversation.conversationId,
auth: this.auth,
},
(data) => {
resolve(data);
}
);
});
if (data.error) {
console.error(data.error);
this.processError(data.error, prompt, id);
throw new Error(data.error);
}
conversation.parentId = data.messageId;
conversation.conversationId = data.conversationId;
return data.answer;
}
processError(error, prompt = null, conversationId = null) {
let errorType = ErrorType.UnknownError;
if (!error) {
errorType = ErrorType.UnknownError;
}
if (typeof error !== "string") {
errorType = ErrorType.UnknownError;
}
if (error.toLowerCase().includes("too many requests")) {
errorType = ErrorType.AccountRateLimitExceeded;
}
if (error.toLowerCase().includes("try refreshing your browser")) {
errorType = ErrorType.UnknownError;
}
if (error.toLowerCase().includes("too long")) {
errorType = ErrorType.MessageTooLong;
}
if (error.toLowerCase().includes("one message at a time")) {
errorType = ErrorType.AnotherMessageInProgress;
}
if (error.toLowerCase().includes("expired")) {
errorType = ErrorType.SessionTokenExpired;
}
if (this.onError) this.onError(errorType, prompt, conversationId);
}
validateToken(token) {
if (!token) return false;
const parsed = JSON.parse(
Buffer.from(token.split(".")[1], "base64").toString()
);
return Date.now() <= parsed.exp * 1000;
}
async getTokens() {
await this.wait(1000);
let data = await new Promise((resolve) => {
this.socket.emit("getSession", this.sessionToken, (data) => {
resolve(data);
});
});
if (data.error) {
console.error(data.error);
this.processError(data.error);
throw new Error(data.error);
}
this.sessionToken = data.sessionToken;
this.auth = data.auth;
this.expires = data.expires;
this.ready = true;
await this.save();
}
async disconnect() {
clearInterval(this.intervalId);
await this.save();
return await this.socket.disconnect(true);
}
}
module.exports = ChatGPT;