You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
5.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { randomBytes } from "node:crypto";
import { qqDnaEmailPool } from "@/lib/flock/qqdna-email-pool";
const PLUS_ALIAS_DOMAINS = new Set([
"gmail.com",
"googlemail.com",
"outlook.com",
"hotmail.com",
"live.com",
"yahoo.com",
"icloud.com",
"me.com",
]);
/** 录制验证可用的 QQ随机虚构 QQ 会被官网拒绝) */
const VERIFIED_QQ_BASE = "3457189488@qq.com";
/** 轮询取池,避免同次批量撞同一邮箱 */
let poolCursor = 0;
function takeFromPool(pool: string[]): string {
if (pool.length === 0) {
throw new Error("empty email pool");
}
const item = pool[poolCursor % pool.length]!;
poolCursor += 1;
return item;
}
/** 解析逗号/分号/换行分隔的池 */
export function parseFlockEnvPool(raw: string | undefined): string[] {
if (!raw?.trim()) {
return [];
}
return raw
.split(/[,;\n]+/)
.map((s) => s.trim())
.filter(Boolean);
}
function pickPoolItem(pool: string[]): string {
return takeFromPool(pool);
}
function randomUsPhone10(): string {
const areaCodes = [
212, 213, 310, 312, 404, 408, 415, 469, 512, 617, 626, 702, 713, 718, 753,
786, 818, 832, 917, 972,
];
const area = areaCodes[randomBytes(1)[0]! % areaCodes.length]!;
// 避开 N11 保留号段411 等),否则官网可能禁用提交
let exchange = 200 + (randomBytes(1)[0]! % 800);
if (exchange % 100 === 11) {
exchange += 1;
}
const line = String(1000 + (randomBytes(2).readUInt16BE(0) % 9000));
return `${area}${exchange}${line}`.slice(0, 10);
}
/**
* 短 QQ 邮箱1/2/3 开头 + 9 位随机数字
* 注意:虚构 QQ 号常被 Flock 以「We had an issue creating your account」拒绝
* 仅 FLOCK_EMAIL_MODE=random_qq 时使用
*/
export function createRandomQqEmail(): string {
const prefix = 1 + (randomBytes(1)[0]! % 3);
const nineDigits = String(randomBytes(4).readUInt32BE(0) % 1_000_000_000).padStart(
9,
"0",
);
return `${prefix}${nineDigits}@qq.com`;
}
/**
* 已验证 QQ + 短别名3457189488+a1b2c3@qq.com总计较短
* 官网曾对该格式成功出过价
*/
export function createVerifiedQqAliasedEmail(): string {
const tag = randomBytes(3).toString("hex"); // 6 字符
return `3457189488+${tag}@qq.com`;
}
/** Gmail/Outlook 等 + 子地址 */
export function buildFlockAliasedEmail(baseEmail: string, unique: string): string {
const trimmed = baseEmail.trim();
const at = trimmed.lastIndexOf("@");
if (at < 1) {
return trimmed;
}
const local = trimmed.slice(0, at).split("+")[0]!;
const domain = trimmed.slice(at + 1).toLowerCase();
const tag =
unique.replace(/[^a-z0-9]/gi, "").slice(0, 8) ||
randomBytes(3).toString("hex");
if (PLUS_ALIAS_DOMAINS.has(domain)) {
return `${local}+${tag}@${domain}`;
}
if (domain === "qq.com") {
return `${local}+${tag.slice(0, 6)}@qq.com`;
}
return createVerifiedQqAliasedEmail();
}
function resolveFreshEmail(id: string, ts: string): string {
const emailPool = parseFlockEnvPool(process.env.FLOCK_EMAIL_POOL);
if (emailPool.length > 0) {
return pickPoolItem(emailPool);
}
const mode = process.env.FLOCK_EMAIL_MODE?.trim().toLowerCase();
// qq.qqdna.com 号码库拼 @qq.com用户指定探针模式
if (mode === "qqdna" || mode === "qq_dna") {
return takeFromPool(qqDnaEmailPool());
}
if (mode === "random_qq") {
return createRandomQqEmail();
}
const aliasBase =
process.env.FLOCK_EMAIL_ALIAS_BASE?.trim() ||
process.env.FLOCK_TEST_EMAIL?.trim() ||
VERIFIED_QQ_BASE;
const domain = aliasBase.split("@")[1]?.toLowerCase() ?? "";
if (PLUS_ALIAS_DOMAINS.has(domain) || domain === "qq.com") {
return buildFlockAliasedEmail(aliasBase, `${ts}${id}`);
}
return createVerifiedQqAliasedEmail();
}
const FIRST_NAMES = [
"James",
"Michael",
"Robert",
"David",
"Daniel",
"Emily",
"Sarah",
"Jessica",
"Amanda",
"Lauren",
] as const;
const LAST_NAMES = [
"Johnson",
"Williams",
"Brown",
"Miller",
"Davis",
"Wilson",
"Anderson",
"Taylor",
"Thomas",
"Moore",
] as const;
const COMPANIES = [
"Pacific Freight Solutions",
"Horizon Logistics LLC",
"Summit Cargo Partners",
"Northstar Shipping Co",
"Blue Ridge Transport",
"Atlas Trucking Group",
"Crescent Supply Chain",
"Midwest Load Line",
] as const;
function pickName(list: readonly string[]): string {
return list[randomBytes(1)[0]! % list.length]!;
}
/**
* 每次查价生成新注册账号:
* - 优先 FLOCK_EMAIL_POOL
* - FLOCK_EMAIL_MODE=qqdnaqq.qqdna.com 号码 + @qq.com
* - FLOCK_EMAIL_MODE=random_qq虚构 {1|2|3}{9位}@qq.com
* - 默认:已验证 QQ + 短 +别名
* 姓名/公司用真人风格(避免 Guest3493 触发风控)
*/
export function createFreshFlockTestAccount(): {
firstName: string;
lastName: string;
company: string;
phone: string;
email: string;
shipmentsPerMonth: string;
} {
const id = randomBytes(5).toString("hex");
const ts = Date.now().toString(36);
const phonePool = parseFlockEnvPool(process.env.FLOCK_PHONE_POOL).map((p) =>
p.replace(/\D/g, "").slice(-10),
);
const email = resolveFreshEmail(id, ts);
const phone =
phonePool.length > 0 ? pickPoolItem(phonePool) : randomUsPhone10();
return {
firstName: process.env.FLOCK_TEST_FIRST_NAME?.trim() || pickName(FIRST_NAMES),
lastName: process.env.FLOCK_TEST_LAST_NAME?.trim() || pickName(LAST_NAMES),
company: process.env.FLOCK_TEST_COMPANY?.trim() || pickName(COMPANIES),
phone,
email,
shipmentsPerMonth:
process.env.FLOCK_TEST_SHIPMENTS_MONTH?.trim() || "1-25",
};
}