/*
Adapted and rewritten to Node based on ading2210/poe-api
ading2210/poe-api: a reverse engineered Python API wrapper for Quora's Poe
Copyright (C) 2023 ading2210
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
const WebSocket = require('ws');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const parent_path = path.resolve(__dirname);
const queries_path = path.join(parent_path, "poe_graphql");
let queries = {};
const logger = console;
const user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0";
function load_queries() {
const files = fs.readdirSync(queries_path);
for (const filename of files) {
const ext = path.extname(filename);
if (ext !== '.graphql') {
continue;
}
const queryName = path.basename(filename, ext);
const query = fs.readFileSync(path.join(queries_path, filename), 'utf-8');
queries[queryName] = query;
}
}
function generate_payload(query_name, variables) {
return {
query: queries[query_name],
variables: variables,
}
}
async function request_with_retries(method, attempts = 10) {
const url = '';
for (let i = 0; i < attempts; i++) {
try {
const response = await method();
if (response.status === 200) {
return response;
}
logger.warn(`Server returned a status code of ${response.status} while downloading ${url}. Retrying (${i + 1}/${attempts})...`);
}
catch (err) {
console.log(err);
}
}
throw new Error(`Failed to download ${url} too many times.`);
}
class Client {
gql_url = "https://poe.com/api/gql_POST";
gql_recv_url = "https://poe.com/api/receive_POST";
home_url = "https://poe.com";
settings_url = "https://poe.com/api/settings";
formkey = "";
next_data = {};
bots = {};
active_messages = {};
message_queues = {};
bot_names = [];
ws = null;
ws_connected = false;
auto_reconnect = false;
constructor(auto_reconnect = false) {
this.auto_reconnect = auto_reconnect;
}
async init(token, proxy = null) {
this.proxy = proxy;
this.session = axios.default.create({
timeout: 60000,
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
});
if (proxy) {
this.session.defaults.proxy = {
"http": proxy,
"https": proxy,
};
logger.info(`Proxy enabled: ${proxy}`);
}
const cookies = `p-b=${token}; Domain=poe.com`;
this.headers = {
"User-Agent": user_agent,
"Referrer": "https://poe.com/",
"Origin": "https://poe.com",
"Cookie": cookies,
};
this.ws_domain = `tch${Math.floor(Math.random() * 1e6)}`;
this.session.defaults.headers.common = this.headers;
this.next_data = await this.get_next_data();
this.channel = await this.get_channel_data();
await this.connect_ws();
this.bots = await this.get_bots();
this.bot_names = this.get_bot_names();
this.gql_headers = {
"poe-formkey": this.formkey,
"poe-tchannel": this.channel["channel"],
...this.headers,
};
await this.subscribe();
}
async get_next_data() {
logger.info('Downloading next_data...');
const r = await request_with_retries(() => this.session.get(this.home_url));
const jsonRegex = /