mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 16:19:18 +00:00
Compare commits
No commits in common. "3e01d94268535720833b619d791a96c93ab4eaad" and "64d2022d0e22818e21a23b55c227f48cb24fae4b" have entirely different histories.
3e01d94268
...
64d2022d0e
414
apps/RCtools.js
414
apps/RCtools.js
@ -6,7 +6,6 @@ import fetch from 'node-fetch';
|
||||
import fs from 'node:fs';
|
||||
import PQueue from 'p-queue';
|
||||
import querystring from 'querystring';
|
||||
import { genVerifyFp } from '../utils/tiktok.js';
|
||||
import {
|
||||
BILI_CDN_SELECT_LIST,
|
||||
BILI_DEFAULT_INTRO_LEN_LIMIT,
|
||||
@ -26,8 +25,6 @@ import {
|
||||
BILI_SSID_INFO,
|
||||
BILI_STREAM_FLV,
|
||||
BILI_STREAM_INFO,
|
||||
DY_LIVE_INFO,
|
||||
DY_LIVE_INFO_2,
|
||||
BILI_SUMMARY,
|
||||
MIYOUSHE_ARTICLE,
|
||||
} from '../constants/tools.js';
|
||||
@ -48,7 +45,6 @@ import { getWbi } from '../utils/biliWbi.js';
|
||||
import {
|
||||
checkToolInCurEnv,
|
||||
formatBiliInfo,
|
||||
retryAxiosReq,
|
||||
secondsToTime,
|
||||
truncateString,
|
||||
urlTransformShortLink,
|
||||
@ -59,9 +55,6 @@ import { getDS } from '../utils/mihoyo.js';
|
||||
import { redisExistKey, redisGetKey, redisSetKey } from '../utils/redis-util.js';
|
||||
import { textArrayToMakeForward } from '../utils/yunzai-util.js';
|
||||
import GeneralLinkAdapter from '../utils/general-link-adapter.js';
|
||||
import aBogus from '../utils/a-bogus.cjs';
|
||||
import * as DY_INFO from 'es-toolkit/compat';
|
||||
import * as DY_TOUTIAO_INFO from 'es-toolkit/compat';
|
||||
|
||||
export class RCtools extends plugin {
|
||||
constructor() {
|
||||
@ -83,10 +76,6 @@ export class RCtools extends plugin {
|
||||
reg: '(miyoushe.com)',
|
||||
fnc: 'miyoushe',
|
||||
},
|
||||
{
|
||||
reg: '(v.douyin.com|live.douyin.com)',
|
||||
fnc: 'douyin',
|
||||
},
|
||||
],
|
||||
});
|
||||
// 配置文件
|
||||
@ -172,409 +161,6 @@ export class RCtools extends plugin {
|
||||
this.nickName = '真寻';
|
||||
}
|
||||
|
||||
async douyin(e) {
|
||||
// 切面判断是否需要解析
|
||||
if (!(await this.isEnableResolve(RESOLVE_CONTROLLER_NAME_ENUM.douyin))) {
|
||||
logger.info(`[R插件][全局解析控制] ${RESOLVE_CONTROLLER_NAME_ENUM.douyin} 已拦截`);
|
||||
return true;
|
||||
}
|
||||
const urlRex = /(http:\/\/|https:\/\/)(v|live).douyin.com\/[A-Za-z\d._?%&+\-=\/#]*/;
|
||||
// 检测无效链接,例如:v.douyin.com
|
||||
if (!urlRex.test(e.msg)) {
|
||||
e.reply(`看上去这不像一个正经的抖音链接呢,${this.nickName}就不帮你解析咯..`);
|
||||
return;
|
||||
}
|
||||
// 获取链接
|
||||
let douUrl = urlRex.exec(e.msg.trim())[0];
|
||||
let ttwid = '';
|
||||
if (douUrl.includes('v.douyin.com')) {
|
||||
const { location, ttwidValue } = await this.douyinRequest(douUrl);
|
||||
ttwid = ttwidValue;
|
||||
douUrl = location;
|
||||
}
|
||||
// TODO 如果有新的好解决方案可以删除,如果遇到https://www.iesdouyin.com/share/slides,这类动图暂时交付给其他API解析,感谢群u:"Error: Cannot find id"提供的服务器
|
||||
if (douUrl.includes('share/slides')) {
|
||||
const detailIdMatch = douUrl.match(/\/slides\/(\d+)/);
|
||||
const detailId = detailIdMatch[1];
|
||||
const apiUrl = 'http://tk.xigua.wiki:5555/douyin/detail';
|
||||
const postData = {
|
||||
cookie: '',
|
||||
proxy: '',
|
||||
source: false,
|
||||
detail_id: detailId,
|
||||
};
|
||||
// 用于存储下载的文件路径
|
||||
const downloadedFilePaths = [];
|
||||
try {
|
||||
const apiResponse = await axios.post(apiUrl, postData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
accept: 'application/json',
|
||||
},
|
||||
timeout: 15000,
|
||||
});
|
||||
if (apiResponse.status !== 200 || !apiResponse.data || !apiResponse.data.data) {
|
||||
logger.error(
|
||||
`[R插件][抖音解析] API返回异常状态码或数据结构错误: ${apiResponse.status}, ${JSON.stringify(apiResponse.data)}`
|
||||
);
|
||||
e.reply(
|
||||
'解析抖音动图失败了,可能是小猫踹翻了路由器,或者是小老鼠咬断了网线,总之等等再试试看..'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
const apiData = apiResponse.data.data;
|
||||
const downloads = apiData.downloads;
|
||||
const desc = apiData.desc || '无简介';
|
||||
const authorNickname = apiData.nickname || '未知作者';
|
||||
|
||||
const replyMessages = [];
|
||||
replyMessages.push(
|
||||
`${this.nickName}识别:抖音动图 \n 作者是:${authorNickname}\n 简介:${desc}`
|
||||
);
|
||||
|
||||
const messageSegments = [];
|
||||
const downloadPath = this.getCurDownloadPath(e);
|
||||
await mkdirIfNotExists(downloadPath);
|
||||
await e.reply(replyMessages.join('\n'));
|
||||
for (const [index, downloadUrl] of downloads.entries()) {
|
||||
let filePath;
|
||||
let fileName;
|
||||
|
||||
try {
|
||||
if (downloadUrl.includes('.mp4') || downloadUrl.includes('video_id')) {
|
||||
fileName = `temp${index > 0 ? index : ''}.mp4`;
|
||||
filePath = `${downloadPath}/${fileName}`;
|
||||
logger.info(`[R插件][抖音动图] 下载视频: ${downloadUrl}`);
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url: downloadUrl,
|
||||
responseType: 'stream',
|
||||
});
|
||||
const writer = fs.createWriteStream(filePath);
|
||||
response.data.pipe(writer);
|
||||
await new Promise((resolve, reject) => {
|
||||
writer.on('finish', resolve);
|
||||
writer.on('error', reject);
|
||||
});
|
||||
logger.info(`[R插件][抖音动图] 视频下载完成: ${filePath}`);
|
||||
messageSegments.push({
|
||||
message: segment.video(filePath),
|
||||
nickname: e.sender.card || e.user_id,
|
||||
user_id: e.user_id,
|
||||
});
|
||||
downloadedFilePaths.push(filePath);
|
||||
} else {
|
||||
fileName = `temp${index > 0 ? index : ''}.png`;
|
||||
filePath = `${downloadPath}/${fileName}`;
|
||||
logger.info(`[R插件][抖音动图] 下载图片: ${downloadUrl}`);
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url: downloadUrl,
|
||||
responseType: 'stream',
|
||||
});
|
||||
const writer = fs.createWriteStream(filePath);
|
||||
response.data.pipe(writer);
|
||||
await new Promise((resolve, reject) => {
|
||||
writer.on('finish', resolve);
|
||||
writer.on('error', reject);
|
||||
});
|
||||
logger.info(`[R插件][抖音动图] 图片下载完成: ${filePath}`);
|
||||
messageSegments.push({
|
||||
message: segment.image(filePath),
|
||||
nickname: e.sender.card || e.user_id,
|
||||
user_id: e.user_id,
|
||||
});
|
||||
downloadedFilePaths.push(filePath);
|
||||
}
|
||||
} catch (downloadError) {
|
||||
logger.error(
|
||||
`[R插件][抖音动图] 下载文件失败: ${downloadUrl}, 错误: ${downloadError.message}`
|
||||
);
|
||||
messageSegments.push({
|
||||
message: { type: 'text', text: `下载文件失败: ${downloadUrl}` },
|
||||
nickname: e.sender.card || e.user_id,
|
||||
user_id: e.user_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (messageSegments.length > 0) {
|
||||
const forwardMsg = await Bot.makeForwardMsg(messageSegments);
|
||||
await e.reply(forwardMsg);
|
||||
|
||||
// 删除文件
|
||||
for (const filePath of downloadedFilePaths) {
|
||||
await checkAndRemoveFile(filePath);
|
||||
}
|
||||
}
|
||||
const headers = {
|
||||
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
|
||||
'User-Agent': COMMON_USER_AGENT,
|
||||
Referer: 'https://www.douyin.com/',
|
||||
cookie: this.douyinCookie,
|
||||
};
|
||||
await this.douyinComment(e, detailId, headers);
|
||||
} catch (error) {
|
||||
logger.error(`[R插件][抖音动图] 调用API或处理下载时发生错误: ${error.message}`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 获取 ID
|
||||
const douId =
|
||||
/note\/(\d+)/g.exec(douUrl)?.[1] ||
|
||||
/video\/(\d+)/g.exec(douUrl)?.[1] ||
|
||||
/live.douyin.com\/(\d+)/.exec(douUrl)?.[1] ||
|
||||
/live\/(\d+)/.exec(douUrl)?.[1] ||
|
||||
/webcast.amemv.com\/douyin\/webcast\/reflow\/(\d+)/.exec(douUrl)?.[1];
|
||||
// 当前版本需要填入cookie
|
||||
if (_.isEmpty(this.douyinCookie) || _.isEmpty(douId)) {
|
||||
e.reply(`${this.nickName}的主人还没有给我配置cookie,没办法帮你解析抖音啦`);
|
||||
return;
|
||||
}
|
||||
// 以下是更新了很多次的抖音API历史,且用且珍惜
|
||||
// const url = `https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=${ douId }`;
|
||||
// const url = `https://www.iesdouyin.com/aweme/v1/web/aweme/detail/?aweme_id=${ douId }&aid=1128&version_name=23.5.0&device_platform=android&os_version=2333`;
|
||||
// 感谢 Evil0ctal(https://github.com/Evil0ctal)提供的header 和 B1gM8c(https://github.com/B1gM8c)的逆向算法X-Bogus
|
||||
const headers = {
|
||||
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
|
||||
'User-Agent': COMMON_USER_AGENT,
|
||||
Referer: 'https://www.douyin.com/',
|
||||
cookie: this.douyinCookie,
|
||||
};
|
||||
let dyApi;
|
||||
if (douUrl.includes('live.douyin.com')) {
|
||||
// 第一类直播类型
|
||||
dyApi = DY_LIVE_INFO.replaceAll('{}', douId);
|
||||
} else if (douUrl.includes('webcast.amemv.com')) {
|
||||
// 第二类直播类型,这里必须使用客户端的 fetch 请求
|
||||
dyApi =
|
||||
DY_LIVE_INFO_2.replace('{}', douId) + `&verifyFp=${genVerifyFp()}` + `&msToken=${ttwid}`;
|
||||
const webcastResp = await fetch(dyApi);
|
||||
const webcastData = await webcastResp.json();
|
||||
const item = webcastData.data.room;
|
||||
const { title, cover, user_count, stream_url } = item;
|
||||
const dySendContent = `${this.nickName}发现了一个抖音直播,${title}`;
|
||||
e.reply([
|
||||
segment.image(cover?.url_list?.[0]),
|
||||
dySendContent,
|
||||
`\n🏄共有${user_count}人正在观看`,
|
||||
]);
|
||||
// 下载10s的直播流
|
||||
await this.sendStreamSegment(
|
||||
e,
|
||||
stream_url?.flv_pull_url?.HD1 ||
|
||||
stream_url?.flv_pull_url?.FULL_HD1 ||
|
||||
stream_url?.flv_pull_url?.SD1 ||
|
||||
stream_url?.flv_pull_url?.SD2
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
// 普通类型
|
||||
dyApi = DY_INFO.replace('{}', douId);
|
||||
}
|
||||
// a-bogus参数
|
||||
const abParam = aBogus.generate_a_bogus(
|
||||
new URLSearchParams(new URL(dyApi).search).toString(),
|
||||
headers['User-Agent']
|
||||
);
|
||||
// const param = resp.data.result[0].paramsencode;
|
||||
const resDyApi = `${dyApi}&a_bogus=${abParam}`;
|
||||
headers['Referer'] = `https://www.douyin.com/`;
|
||||
// 定义一个dy请求
|
||||
const dyResponse = () =>
|
||||
axios.get(resDyApi, {
|
||||
headers,
|
||||
});
|
||||
// 如果失败进行3次重试
|
||||
try {
|
||||
const data = await retryAxiosReq(dyResponse);
|
||||
// saveJsonToFile(data);
|
||||
// 直播数据逻辑
|
||||
if (douUrl.includes('live')) {
|
||||
const item = await data.data.data?.[0];
|
||||
const { title, cover, user_count_str, stream_url } = item;
|
||||
const dySendContent = `${this.nickName}发现了一个抖音直播,${title}`;
|
||||
e.reply([
|
||||
segment.image(cover?.url_list?.[0]),
|
||||
dySendContent,
|
||||
`\n共有${user_count_str}人正在观看`,
|
||||
]);
|
||||
// 下载10s的直播流
|
||||
await this.sendStreamSegment(
|
||||
e,
|
||||
stream_url?.flv_pull_url?.HD1 ||
|
||||
stream_url?.flv_pull_url?.FULL_HD1 ||
|
||||
stream_url?.flv_pull_url?.SD1 ||
|
||||
stream_url?.flv_pull_url?.SD2
|
||||
);
|
||||
return;
|
||||
}
|
||||
const item = await data.aweme_detail;
|
||||
// await saveJsonToFile(item);
|
||||
// 如果为null则退出
|
||||
if (item == null) {
|
||||
e.reply(`${this.nickName}无法识别当前抖音内容了..换一个试试`);
|
||||
return;
|
||||
}
|
||||
const urlTypeCode = item.aweme_type;
|
||||
const urlType = douyinTypeMap[urlTypeCode];
|
||||
// 核心内容
|
||||
if (urlType === 'video') {
|
||||
// logger.info(item.video);
|
||||
// 多位面选择:play_addr、play_addr_265、play_addr_h264
|
||||
const {
|
||||
play_addr: { uri: videoAddrURI },
|
||||
duration,
|
||||
cover,
|
||||
} = item.video;
|
||||
// 进行时间判断,如果超过时间阈值就不发送
|
||||
const dyDuration = Math.trunc(duration / 1000);
|
||||
const durationThreshold = this.biliDuration;
|
||||
// 一些共同发送内容
|
||||
let dySendContent = `${this.nickName}猜这是一个抖音视频\n${item.author.nickname}\n简介:${item.desc}`;
|
||||
if (dyDuration >= durationThreshold) {
|
||||
// 超过阈值,不发送的情况
|
||||
// 封面
|
||||
const dyCover = cover.url_list?.pop();
|
||||
// logger.info(cover.url_list);
|
||||
dySendContent += `\n
|
||||
${DIVIDING_LINE.replace('{}', '这视频真带派')}\n当前视频时长约:${(dyDuration / 60).toFixed(2).replace(/\.00$/, '')} 分钟,\n大于${this.nickName}管理员设置的最大时长 ${(durationThreshold / 60).toFixed(2).replace(/\.00$/, '')} 分钟,还是到抖音里面看吧..`;
|
||||
e.reply([segment.image(dyCover), dySendContent]);
|
||||
// 如果开启评论的就调用
|
||||
await this.douyinComment(e, douId, headers);
|
||||
return;
|
||||
}
|
||||
e.reply(`${dySendContent}`);
|
||||
// 分辨率判断是否压缩
|
||||
const resolution = this.douyinCompression ? '720p' : '1080p';
|
||||
// 使用今日头条 CDN 进一步加快解析速度
|
||||
const resUrl = DY_TOUTIAO_INFO.replace('1080p', resolution).replace('{}', videoAddrURI);
|
||||
|
||||
// ⚠️ 暂时废弃代码
|
||||
/*if (this.douyinCompression) {
|
||||
// H.265压缩率更高、流量省一半. 相对于H.264
|
||||
// 265 和 264 随机均衡负载
|
||||
const videoAddrList = Math.random() > 0.5 ? play_addr_265.url_list : play_addr_h264.url_list;
|
||||
resUrl = videoAddrList[videoAddrList.length - 1] || videoAddrList[0];
|
||||
} else {
|
||||
// 原始格式,ps. videoAddrList这里[0]、[1]是 http,[最后一个]是 https
|
||||
const videoAddrList = play_addr.url_list;
|
||||
resUrl = videoAddrList[videoAddrList.length - 1] || videoAddrList[0];
|
||||
}*/
|
||||
|
||||
// logger.info(resUrl);
|
||||
const path = `${this.getCurDownloadPath(e)}/temp.mp4`;
|
||||
// 加入队列
|
||||
await this.downloadVideo(resUrl).then(() => {
|
||||
this.sendVideoToUpload(e, path);
|
||||
});
|
||||
} else if (urlType === 'image') {
|
||||
// 发送描述
|
||||
e.reply(`${this.nickName}识别: 抖音, ${item.desc}`);
|
||||
// 无水印图片列表
|
||||
let no_watermark_image_list = [];
|
||||
// 有水印图片列表
|
||||
// let watermark_image_list = [];
|
||||
for (let i of item.images) {
|
||||
// 无水印图片列表
|
||||
no_watermark_image_list.push({
|
||||
message: segment.image(i.url_list[0]),
|
||||
nickname: this.e.sender.card || this.e.user_id,
|
||||
user_id: this.e.user_id,
|
||||
});
|
||||
// 有水印图片列表
|
||||
// watermark_image_list.push(i.download_url_list[0]);
|
||||
// e.reply(segment.image(i.url_list[0]));
|
||||
}
|
||||
// console.log(no_watermark_image_list)
|
||||
await e.reply(await Bot.makeForwardMsg(no_watermark_image_list));
|
||||
}
|
||||
// 如果开启评论的就调用
|
||||
await this.douyinComment(e, douId, headers);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
logger.mark(
|
||||
`Cookie 过期或者 Cookie 没有填写,请参考\n${HELP_DOC}\n尝试无效后可以到官方QQ群[575663150]提出 bug 等待解决`
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* douyin 请求参数
|
||||
* @param url
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async douyinRequest(url) {
|
||||
const params = {
|
||||
headers: {
|
||||
'User-Agent': COMMON_USER_AGENT,
|
||||
},
|
||||
timeout: 10000,
|
||||
};
|
||||
try {
|
||||
const resp = await axios.get(url, params);
|
||||
|
||||
const location = resp.request.res.responseUrl;
|
||||
|
||||
const setCookieHeaders = resp.headers['set-cookie'];
|
||||
let ttwidValue;
|
||||
if (setCookieHeaders) {
|
||||
setCookieHeaders.forEach((cookie) => {
|
||||
// 使用正则表达式提取 ttwid 的值
|
||||
const ttwidMatch = cookie.match(/ttwid=([^;]+)/);
|
||||
if (ttwidMatch) {
|
||||
ttwidValue = ttwidMatch[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (location != null) {
|
||||
return resolve({
|
||||
location: location,
|
||||
ttwidValue: ttwidValue,
|
||||
});
|
||||
} else {
|
||||
return reject('获取失败');
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 DY 评论
|
||||
* @param e
|
||||
* @param douId
|
||||
* @param headers
|
||||
*/
|
||||
async douyinComment(e, douId, headers) {
|
||||
if (!this.douyinComments) {
|
||||
return;
|
||||
}
|
||||
const dyCommentUrl = DY_COMMENT.replace('{}', douId);
|
||||
const abParam = aBogus.generate_a_bogus(
|
||||
new URLSearchParams(new URL(dyCommentUrl).search).toString(),
|
||||
headers['User-Agent']
|
||||
);
|
||||
const commentsResp = await axios.get(`${dyCommentUrl}&a_bogus=${abParam}`, {
|
||||
headers,
|
||||
});
|
||||
// logger.info(headers)
|
||||
// saveJsonToFile(commentsResp.data, "data.json", _);
|
||||
const comments = commentsResp.data.comments;
|
||||
const replyComments = comments.map((item) => {
|
||||
return {
|
||||
message: item.text,
|
||||
nickname: this.e.sender.card || this.e.user_id,
|
||||
user_id: this.e.user_id,
|
||||
};
|
||||
});
|
||||
e.reply(await Bot.makeForwardMsg(replyComments));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载直播片段
|
||||
* @param e
|
||||
|
@ -13,20 +13,6 @@ export const BILI_SUMMARY = 'https://api.bilibili.com/x/web-interface/view/concl
|
||||
export const BILI_PLAY_STREAM =
|
||||
'https://api.bilibili.com/x/player/wbi/playurl?cid={cid}&bvid={bvid}&qn={qn}&fnval=16';
|
||||
|
||||
/**
|
||||
* DY 直播信息
|
||||
* @type {string}
|
||||
*/
|
||||
export const DY_LIVE_INFO =
|
||||
'https://live.douyin.com/webcast/room/web/enter/?device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_version=124.0&browser_online=true&engine_name=Gecko&engine_version=122.0.0.0&os_name=Windows&os_version=10&cpu_core_num=12&device_memory=8&platform=PC&web_rid={}&room_id_str={}';
|
||||
|
||||
/**
|
||||
* DY 直播信息 二类型
|
||||
* @type {string}
|
||||
*/
|
||||
export const DY_LIVE_INFO_2 =
|
||||
'https://webcast.amemv.com/webcast/room/reflow/info/?type_id=0&live_id=1&sec_user_id=&version_code=99.99.99&app_id=1128&room_id={}';
|
||||
|
||||
/**
|
||||
* 动态信息
|
||||
* https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/dynamic/get_dynamic_detail.md
|
||||
|
@ -23,31 +23,6 @@ export function formatBiliInfo(data) {
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试 axios 请求
|
||||
* @param requestFunction
|
||||
* @param retries
|
||||
* @param delay
|
||||
* @returns {*}
|
||||
*/
|
||||
export async function retryAxiosReq(requestFunction, retries = 3, delay = 1000) {
|
||||
try {
|
||||
const response = await requestFunction();
|
||||
if (!response.data) {
|
||||
throw new Error('请求空数据');
|
||||
}
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (retries > 0) {
|
||||
logger.mark(`[R插件][重试模块]重试中... (${3 - retries + 1}/3) 次`);
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
return retryAxiosReq(requestFunction, retries - 1, delay);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字转换成具体时间
|
||||
* @param seconds
|
||||
|
@ -1,36 +0,0 @@
|
||||
export function genVerifyFp() {
|
||||
const baseStr = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
const t = baseStr.length;
|
||||
let milliseconds = Date.now(); // 获取当前的时间戳(毫秒)
|
||||
let base36 = "";
|
||||
|
||||
// 将时间戳转换为base36
|
||||
while (milliseconds > 0) {
|
||||
let remainder = milliseconds % 36;
|
||||
if (remainder < 10) {
|
||||
base36 = remainder.toString() + base36;
|
||||
} else {
|
||||
base36 = String.fromCharCode('a'.charCodeAt(0) + remainder - 10) + base36;
|
||||
}
|
||||
milliseconds = Math.floor(milliseconds / 36);
|
||||
}
|
||||
|
||||
const r = base36;
|
||||
let o = new Array(36).fill("");
|
||||
o[8] = o[13] = o[18] = o[23] = "_";
|
||||
o[14] = "4";
|
||||
|
||||
// 生成随机字符
|
||||
for (let i = 0; i < 36; i++) {
|
||||
if (!o[i]) {
|
||||
let n = Math.floor(Math.random() * t);
|
||||
if (i === 19) {
|
||||
n = (3 & n) | 8;
|
||||
}
|
||||
o[i] = baseStr[n];
|
||||
}
|
||||
}
|
||||
|
||||
return "verify_" + r + "_" + o.join("");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user