/** 拟人配置默认值(对齐 LIKE AI human_behavior.py / human_guard.py) */ export type MsRange = { min_ms: number; max_ms: number }; export type BrowsePlan = Record; export interface GuardConfig { enabled: boolean; publish: { max_per_day: number; min_interval_hours: number; pre_delay_min_sec: number; pre_delay_max_sec: number; warmup_enabled: boolean; warmup_pause_min_sec: number; warmup_pause_max_sec: number; post_stay_min_sec: number; post_stay_max_sec: number; }; browse: { min_interval_sec: number; max_interval_sec: number; max_per_hour: number; }; interact: { min_interval_sec: number; max_per_day: number; }; quiet_hours: { enabled: boolean; start: string; end: string; }; content: { max_title_len: number; warn_extreme_words: boolean; }; } export interface BehaviorConfig { default_max_attempts: number; stagnant_limit: number; min_scroll_delta: number; max_click_per_round: number; stagnant_check_threshold: number; large_scroll_trigger: number; button_click_interval: number; final_sprint_push_count: number; human_delay: MsRange; reaction_time: MsRange; hover_time: MsRange; read_time: MsRange; short_read: MsRange; scroll_wait: MsRange; post_scroll: MsRange; navigation_delay: MsRange; scroll_slow: { interval_base_ms: number; interval_jitter_ms: number; scroll_ratio: number }; scroll_normal: { interval_base_ms: number; interval_jitter_ms: number; scroll_ratio: number }; scroll_fast: { interval_base_ms: number; interval_jitter_ms: number; scroll_ratio: number }; home_browse_plan: BrowsePlan; search_browse_plan: BrowsePlan; inaccessible_keywords: string[]; } export function defaultBrowsePlan(): BrowsePlan { return { stable_sec_lo: 2, stable_sec_hi: 3, scroll_lo: 8, scroll_hi: 15, click_lo: 1, click_hi: 3, sprint_sec_lo: 4, sprint_sec_hi: 6, parse_sec_lo: 1.5, parse_sec_hi: 2.5, detail_stay_sec_lo: 10, detail_stay_sec_hi: 20, detail_scroll_lo: 3, detail_scroll_hi: 6, }; } export function defaultBehavior(): BehaviorConfig { return { default_max_attempts: 500, stagnant_limit: 20, min_scroll_delta: 10, max_click_per_round: 3, stagnant_check_threshold: 2, large_scroll_trigger: 5, button_click_interval: 3, final_sprint_push_count: 15, human_delay: { min_ms: 300, max_ms: 700 }, reaction_time: { min_ms: 300, max_ms: 800 }, hover_time: { min_ms: 100, max_ms: 300 }, read_time: { min_ms: 500, max_ms: 1200 }, short_read: { min_ms: 600, max_ms: 1200 }, scroll_wait: { min_ms: 100, max_ms: 200 }, post_scroll: { min_ms: 300, max_ms: 500 }, navigation_delay: { min_ms: 1000, max_ms: 2500 }, scroll_slow: { interval_base_ms: 1200, interval_jitter_ms: 300, scroll_ratio: 0.5 }, scroll_normal: { interval_base_ms: 600, interval_jitter_ms: 200, scroll_ratio: 0.7 }, scroll_fast: { interval_base_ms: 300, interval_jitter_ms: 100, scroll_ratio: 0.9 }, home_browse_plan: { ...defaultBrowsePlan(), scroll_lo: 16, scroll_hi: 51, click_lo: 2, click_hi: 8 }, search_browse_plan: { ...defaultBrowsePlan(), scroll_lo: 13, scroll_hi: 39, click_lo: 1, click_hi: 5 }, inaccessible_keywords: [ '当前笔记暂时无法浏览', '该内容因违规已被删除', '该笔记已被删除', '内容不存在', '笔记不存在', '已失效', '私密笔记', '仅作者可见', '因用户设置,你无法查看', '因违规无法查看', ], }; } export function defaultGuard(): GuardConfig { return { enabled: true, publish: { max_per_day: 2, min_interval_hours: 6, pre_delay_min_sec: 60, pre_delay_max_sec: 180, warmup_enabled: true, warmup_pause_min_sec: 30, warmup_pause_max_sec: 90, post_stay_min_sec: 0, post_stay_max_sec: 0, }, browse: { min_interval_sec: 25, max_interval_sec: 45, max_per_hour: 20, }, interact: { min_interval_sec: 45, max_per_day: 10, }, quiet_hours: { enabled: true, start: '02:00', end: '07:00', }, content: { max_title_len: 20, warn_extreme_words: true, }, }; } export function mergeBehavior(raw: unknown): BehaviorConfig { const base = defaultBehavior(); if (!raw || typeof raw !== 'object') return base; const r = raw as Record; return { ...base, ...r, human_delay: { ...base.human_delay, ...(r.human_delay as object) }, reaction_time: { ...base.reaction_time, ...(r.reaction_time as object) }, hover_time: { ...base.hover_time, ...(r.hover_time as object) }, read_time: { ...base.read_time, ...(r.read_time as object) }, short_read: { ...base.short_read, ...(r.short_read as object) }, scroll_wait: { ...base.scroll_wait, ...(r.scroll_wait as object) }, post_scroll: { ...base.post_scroll, ...(r.post_scroll as object) }, navigation_delay: { ...base.navigation_delay, ...(r.navigation_delay as object) }, scroll_slow: { ...base.scroll_slow, ...(r.scroll_slow as object) }, scroll_normal: { ...base.scroll_normal, ...(r.scroll_normal as object) }, scroll_fast: { ...base.scroll_fast, ...(r.scroll_fast as object) }, home_browse_plan: { ...base.home_browse_plan, ...(r.home_browse_plan as object) }, search_browse_plan: { ...base.search_browse_plan, ...(r.search_browse_plan as object) }, inaccessible_keywords: Array.isArray(r.inaccessible_keywords) ? r.inaccessible_keywords.map(String) : base.inaccessible_keywords, } as BehaviorConfig; } export function mergeGuard(raw: unknown): GuardConfig { const base = defaultGuard(); if (!raw || typeof raw !== 'object') return base; const r = raw as Record; return { ...base, enabled: r.enabled !== undefined ? Boolean(r.enabled) : base.enabled, publish: { ...base.publish, ...(r.publish as object) }, browse: { ...base.browse, ...(r.browse as object) }, interact: { ...base.interact, ...(r.interact as object) }, quiet_hours: { ...base.quiet_hours, ...(r.quiet_hours as object) }, content: { ...base.content, ...(r.content as object) }, }; } export function applyClickFromScroll(plan: BrowsePlan) { const lo = Math.max(0, Math.floor((plan.scroll_lo ?? 0) / 3)); const hi = Math.max(lo, Math.ceil((plan.scroll_hi ?? 0) / 3)); plan.click_lo = lo; plan.click_hi = hi; }