企业微信智能机器人API插件
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { maskContactPhone } from './phone';
|
||||
import { shanghaiYmd, startOfShanghaiDay } from './shanghai-date';
|
||||
import { wecomReportPeriod, type WecomReportKind, type WecomReportPeriod } from './wecom-report';
|
||||
|
||||
export const WECOM_PLUGIN_PAGE_SIZE_DEFAULT = 5;
|
||||
export const WECOM_PLUGIN_PAGE_SIZE_MAX = 10;
|
||||
|
||||
export const WECOM_PLUGIN_METRICS_KINDS = ['today', 'daily', 'weekly', 'monthly'] as const;
|
||||
export type WecomPluginMetricsKind = (typeof WECOM_PLUGIN_METRICS_KINDS)[number];
|
||||
|
||||
export type WecomPluginMetricsPeriod = Omit<WecomReportPeriod, 'kind'> & {
|
||||
kind: WecomPluginMetricsKind;
|
||||
};
|
||||
|
||||
export function isWecomPluginMetricsKind(v: string): v is WecomPluginMetricsKind {
|
||||
return (WECOM_PLUGIN_METRICS_KINDS as readonly string[]).includes(v);
|
||||
}
|
||||
|
||||
export function parseWecomPluginMetricsKind(raw?: string | null): WecomPluginMetricsKind | null {
|
||||
const v = String(raw ?? '').trim().toLowerCase();
|
||||
if (!v) return 'today';
|
||||
return isWecomPluginMetricsKind(v) ? v : null;
|
||||
}
|
||||
|
||||
export function clampWecomPluginPageSize(raw?: string | number | null): number {
|
||||
const n = typeof raw === 'number' ? raw : Number(raw);
|
||||
if (!Number.isFinite(n) || n <= 0) return WECOM_PLUGIN_PAGE_SIZE_DEFAULT;
|
||||
return Math.min(WECOM_PLUGIN_PAGE_SIZE_MAX, Math.max(1, Math.floor(n)));
|
||||
}
|
||||
|
||||
export function clampWecomPluginPage(raw?: string | number | null): number {
|
||||
const n = typeof raw === 'number' ? raw : Number(raw);
|
||||
if (!Number.isFinite(n) || n <= 0) return 1;
|
||||
return Math.min(100, Math.floor(n));
|
||||
}
|
||||
|
||||
/** 长度必须相同后再逐字符 XOR,避免短密码走快速失败路径时的明显差异(仍非密码学级) */
|
||||
export function verifyWecomPluginApiKey(provided: string, expected?: string | null): boolean {
|
||||
const exp = String(expected ?? '');
|
||||
const got = String(provided ?? '');
|
||||
if (!exp || !got || exp.length !== got.length) return false;
|
||||
let diff = 0;
|
||||
for (let i = 0; i < exp.length; i++) {
|
||||
diff |= exp.charCodeAt(i) ^ got.charCodeAt(i);
|
||||
}
|
||||
return diff === 0;
|
||||
}
|
||||
|
||||
export function isWecomPluginEnabled(env: {
|
||||
WECOM_PLUGIN_ENABLED?: string;
|
||||
WECOM_PLUGIN_API_KEY?: string;
|
||||
}): boolean {
|
||||
return env.WECOM_PLUGIN_ENABLED === 'true' && Boolean(String(env.WECOM_PLUGIN_API_KEY ?? '').trim());
|
||||
}
|
||||
|
||||
export function wecomPluginMetricsPeriod(
|
||||
kind: WecomPluginMetricsKind,
|
||||
now = new Date(),
|
||||
): WecomPluginMetricsPeriod {
|
||||
if (kind === 'today') {
|
||||
const start = startOfShanghaiDay(now);
|
||||
const ymd = shanghaiYmd(start);
|
||||
return {
|
||||
kind,
|
||||
start,
|
||||
endExclusive: now,
|
||||
periodKey: ymd,
|
||||
title: `今日(${ymd})`,
|
||||
rangeLabel: ymd,
|
||||
incrementLabel: '今日新增',
|
||||
};
|
||||
}
|
||||
const period = wecomReportPeriod(kind as WecomReportKind, now);
|
||||
return { ...period, kind };
|
||||
}
|
||||
|
||||
export function toWecomPluginUserView(user: {
|
||||
userNo: string;
|
||||
nickname?: string | null;
|
||||
phone?: string | null;
|
||||
}): { userNo: string; nickname: string; phone: string } {
|
||||
return {
|
||||
userNo: user.userNo,
|
||||
nickname: user.nickname?.trim() || '—',
|
||||
phone: maskContactPhone(user.phone),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user