Эх сурвалжийг харах

平博API支持切换账号

flyzto 4 долоо хоног өмнө
parent
commit
c4962e0fbf

+ 114 - 0
pinnacle/libs/getAccount.js

@@ -0,0 +1,114 @@
+import { Logs } from "./logs.js";
+import { getData, setData } from "./cache.js";
+
+const accountOptionCacheFile = 'data/accountOptionCache.json';
+
+const ACCOUNT_OPTIONS = {
+  currentIndex: 0,
+  nextChangeTime: 0,
+};
+
+const IS_DEV = process.env.NODE_ENV == 'development';
+
+/**
+ * 随机一个毫秒数
+ * 范围:4小时 ~ 7小时
+ * 测试环境范围:4分钟 ~ 7分钟
+ * 加上当前时间戳
+ * 计算下次切换时间
+ */
+const getRandomTime = () => {
+  const nowTime = Date.now();
+  const min = 1000 * 60 * (IS_DEV ? 1 : 60) * 4;
+  const max = 1000 * 60 * (IS_DEV ? 1 : 60) * 7;
+  const randomTime = Math.floor(Math.random() * (max - min + 1)) + min;
+  return nowTime + randomTime;
+}
+
+/**
+ * 缓存GLOBAL_DATA数据到文件
+ * @returns {Promise<void>}
+ */
+const saveGlobalDataToCache = async () => {
+  return setData(accountOptionCacheFile, ACCOUNT_OPTIONS);
+}
+
+/**
+ * 从文件中加载GLOBAL_DATA数据
+ * @returns {Promise<void>}
+ */
+const loadGlobalDataFromCache = async () => {
+  return getData(accountOptionCacheFile)
+  .then(data => {
+    if (!data) {
+      return;
+    }
+    Object.keys(ACCOUNT_OPTIONS).forEach(key => {
+      if (key in data) {
+        ACCOUNT_OPTIONS[key] = data[key];
+      }
+    });
+  });
+}
+
+/**
+ * 获取当前账号信息
+ * @returns
+ */
+export const getAccountInfo = () => {
+  const nowTime = Date.now();
+
+  const accounts = process.env.PINNACLE_ACCOUNTS?.split(',').map(item => {
+    const [username, password, localAddress, platform] = item.split(':');
+    return { username, password, localAddress, platform };
+  }) ?? [];
+
+  if (accounts.length == 0) {
+    return null;
+  }
+
+  if (ACCOUNT_OPTIONS.nextChangeTime == 0) {
+    ACCOUNT_OPTIONS.currentIndex = 0;
+    ACCOUNT_OPTIONS.nextChangeTime = getRandomTime();
+  }
+  else if (nowTime > ACCOUNT_OPTIONS.nextChangeTime) {
+    ACCOUNT_OPTIONS.currentIndex++;
+    if (ACCOUNT_OPTIONS.currentIndex >= accounts.length) {
+      ACCOUNT_OPTIONS.currentIndex = 0;
+    }
+    ACCOUNT_OPTIONS.nextChangeTime = getRandomTime();
+    Logs.out('account changed', ACCOUNT_OPTIONS);
+    saveGlobalDataToCache();
+  }
+
+  const accountInfo = accounts[ACCOUNT_OPTIONS.currentIndex];
+  return accountInfo;
+}
+
+/**
+ * 监听进程退出事件
+ * 保存GLOBAL_DATA数据
+ */
+const saveExit = (code) => {
+  saveGlobalDataToCache()
+  .then(() => {
+    Logs.out('account options saved');
+  })
+  .catch(err => {
+    Logs.err('failed to save account options', err.message);
+  })
+  .finally(() => {
+    process.exit(code);
+  });
+}
+process.on('SIGINT', () => {
+  saveExit(0);
+});
+process.on('SIGTERM', () => {
+  saveExit(0);
+});
+process.on('SIGUSR2', () => {
+  saveExit(0);
+});
+
+loadGlobalDataFromCache();

+ 78 - 30
pinnacle/libs/pinnacleClient.js

@@ -1,53 +1,99 @@
+import dotenv from 'dotenv';
+import https from 'https';
 import axios from "axios";
 import { HttpsProxyAgent } from "https-proxy-agent";
 import { Logs } from "./logs.js";
+import { getAccountInfo } from "./getAccount.js";
+
+dotenv.config();
+
+const axiosDefaultOptions = {
+  baseURL: "",
+  url: "",
+  method: "GET",
+  headers: {},
+  params: {},
+  data: {},
+  timeout: 10000,
+};
 
 const BaseURL = {
   pinnacle: "https://api.pinnacle888.com",
+  ps3838: "https://api.ps3838.com",
   pstery: "http://127.0.0.1:9055",
 }
 
-export const pinnacleRequest = async (options) => {
-  const {
-    endpoint,
-    params = {},
-    username,
-    password,
-    method = "GET",
-    data,
-    proxy,
-    timeout = 10000,
-  } = options;
-
-  if (!endpoint || !username || !password) {
-    throw new Error("endpoint、username、password is required");
-  }
+export const pinnacleRequest = async (options, channel) => {
 
-  const authHeader = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
+  const { username, password, localAddress, platform } = getAccountInfo() ?? {};
+  const { url, ...optionsRest } = options;
+  if (!url || !channel && (!username || !password)) {
+    throw new Error("url、username、password、channel is required");
+  }
 
-  const axiosConfig = {
-    baseURL: BaseURL.pinnacle,
-    url: endpoint,
-    method,
-    headers: {
-      "Authorization": authHeader,
-      "Accept": "application/json",
-      "Content-Type": "application/json",
-    },
-    params,
-    data,
-    timeout,
-  };
+  Logs.outDev('pinnacle request', { url, channel, username, password, localAddress, platform });
 
+  const authHeader = channel ? `Basic ${channel}` : `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
+  const axiosConfig = { ...axiosDefaultOptions, ...optionsRest, url, baseURL: BaseURL[platform] ?? BaseURL.pinnacle };
+  Object.assign(axiosConfig.headers, {
+    "Authorization": authHeader,
+    "Accept": "application/json",
+  });
+  const proxy = process.env.NODE_HTTP_PROXY;
   if (proxy) {
     axiosConfig.proxy = false;
     axiosConfig.httpsAgent = new HttpsProxyAgent(proxy);
+    // Logs.outDev('pinnacle request using proxy', axiosConfig.httpsAgent);
   }
+  else if (localAddress) {
+    axiosConfig.httpsAgent = new https.Agent({ localAddress });
+  }
+  return axios(axiosConfig).then(res => {
+    // Logs.out('pinnacle request', url, axiosConfig, res.data);
+    return res.data;
+  });
+}
 
-  return axios(axiosConfig).then(res => res.data);
+/**
+ * Pinnacle API Get请求
+ * @param {*} url
+ * @param {*} params
+ * @returns
+ */
+export const pinnacleGet = async (url, params, channel) => {
+  return pinnacleRequest({
+    url,
+    params
+  }, channel)
+  .catch(err => {
+    const source = { url, params };
+    if (err?.response?.data) {
+      const data = err.response.data;
+      Object.assign(source, { data });
+    }
+    err.source = source;
+    return Promise.reject(err);
+  });
+}
 
+/**
+ * Pinnacle API Post请求
+ * @param {*} url
+ * @param {*} data
+ * @returns
+ */
+export const pinnaclePost = async (url, data, channel) => {
+  return pinnacleRequest({
+    url,
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    data
+  }, channel);
 }
 
+
 export const getPsteryRelations = async (mk=-1) => {
   const axiosConfig = {
     baseURL: BaseURL.pstery,
@@ -62,6 +108,7 @@ export const getPsteryRelations = async (mk=-1) => {
   return axios(axiosConfig).then(res => res.data);
 }
 
+
 export const updateBaseEvents = async (data) => {
   const axiosConfig = {
     baseURL: BaseURL.pstery,
@@ -83,6 +130,7 @@ export const updateBaseEvents = async (data) => {
   });
 }
 
+
 export const notifyException = async (message) => {
   const axiosConfig = {
     baseURL: BaseURL.pstery,

+ 1 - 39
pinnacle/main.js

@@ -1,6 +1,4 @@
-import 'dotenv/config';
-
-import { pinnacleRequest, getPsteryRelations, updateBaseEvents, notifyException } from "./libs/pinnacleClient.js";
+import { pinnacleGet, getPsteryRelations, updateBaseEvents, notifyException } from "./libs/pinnacleClient.js";
 import { Logs } from "./libs/logs.js";
 import { getData, setData } from "./libs/cache.js";
 
@@ -69,38 +67,6 @@ const getDateInTimezone = (offsetHours, withTime = false) => {
 }
 
 
-const pinnacleGet = async (endpoint, params) => {
-  return pinnacleRequest({
-    endpoint,
-    params,
-    username: process.env.PINNACLE_USERNAME,
-    password: process.env.PINNACLE_PASSWORD,
-    proxy: process.env.NODE_HTTP_PROXY,
-  })
-  .catch(err => {
-    const source = { endpoint, params };
-    if (err?.response?.data) {
-      const data = err.response.data;
-      Object.assign(source, { data });
-    }
-    err.source = source;
-    return Promise.reject(err);
-  });
-};
-
-
-// const pinnaclePost = async(endpoint, data) => {
-//   return pinnacleRequest({
-//     endpoint,
-//     data,
-//     method: 'POST',
-//     username: process.env.PINNACLE_USERNAME,
-//     password: process.env.PINNACLE_PASSWORD,
-//     proxy: process.env.NODE_HTTP_PROXY,
-//   });
-// };
-
-
 const isArrayEqualUnordered = (arr1, arr2) => {
   if (arr1.length !== arr2.length) return false;
   const s1 = [...arr1].sort();
@@ -858,10 +824,6 @@ process.on('SIGUSR2', () => {
 
 
 (() => {
-  if (!process.env.PINNACLE_USERNAME || !process.env.PINNACLE_PASSWORD) {
-    Logs.err('USERNAME or PASSWORD is not set');
-    return;
-  }
   loadGlobalDataFromCache()
   .then(() => {
     Logs.out('global data loaded');

+ 1 - 1
pinnacle/package.json

@@ -1,6 +1,6 @@
 {
   "name": "pinnacle",
-  "version": "1.1.4",
+  "version": "2.0.0",
   "description": "",
   "main": "main.js",
   "type": "module",