xhs_factory/main.py
zhoujie b635108b89 refactor: split monolithic main.py into services/ + ui/ modules (improve-maintainability)
- main.py: 4360 → 146 lines (96.6% reduction), entry layer only
- services/: rate_limiter, autostart, persona, connection, profile,
  hotspot, content, engagement, scheduler, queue_ops (10 business modules)
- ui/app.py: all Gradio UI code extracted into build_app(cfg, analytics)
- Fix: with gr.Blocks() indented inside build_app function
- Fix: cfg.all property (not get_all method)
- Fix: STATUS_LABELS, get_persona_keywords, fetch_proactive_notes imports
- Fix: queue_ops module-level set_publish_callback moved into configure()
- Fix: pub_queue.format_*() wrapped as queue_format_table/calendar helpers
- All 14 files syntax-verified, build_app() runtime-verified
- 58/58 tasks complete"
2026-02-24 22:50:56 +08:00

146 lines
4.9 KiB
Python

"""
小红书 AI 爆文生产工坊 V2.0
全自动工作台:灵感 -> 文案 -> 绘图 -> 发布 -> 运营
"""
import gradio as gr
import os
import re
import json
import time
import logging
import platform
import subprocess
import threading
import random
from datetime import datetime
from PIL import Image
import matplotlib
import matplotlib.pyplot as plt
from config_manager import ConfigManager, OUTPUT_DIR
from llm_service import LLMService
from sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, SD_PRESET_NAMES, get_sd_preset, get_model_profile, get_model_profile_info, detect_model_profile, SD_MODEL_PROFILES
from mcp_client import MCPClient, get_mcp_client
from analytics_service import AnalyticsService
from ui.tab_create import build_tab
# ================= matplotlib 中文字体配置 =================
_font_candidates = ["Microsoft YaHei", "SimHei", "PingFang SC", "WenQuanYi Micro Hei"]
for _fn in _font_candidates:
try:
matplotlib.font_manager.findfont(_fn, fallback_to_default=False)
plt.rcParams["font.sans-serif"] = [_fn]
break
except Exception:
continue
plt.rcParams["axes.unicode_minus"] = False
# ================= 日志配置 =================
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("autobot.log", encoding="utf-8"),
],
)
logger = logging.getLogger("autobot")
# 强制不走代理连接本地服务
os.environ["NO_PROXY"] = "127.0.0.1,localhost"
# ================= 全局服务初始化 =================
cfg = ConfigManager()
cfg.ensure_workspace()
mcp = get_mcp_client(cfg.get("mcp_url", "http://localhost:18060/mcp"))
analytics = AnalyticsService(OUTPUT_DIR)
# ================= 发布队列 =================
from publish_queue import (
PublishQueue, QueuePublisher,
STATUS_DRAFT, STATUS_APPROVED, STATUS_SCHEDULED, STATUS_PUBLISHING,
STATUS_PUBLISHED, STATUS_FAILED, STATUS_REJECTED, STATUS_LABELS,
)
pub_queue = PublishQueue(OUTPUT_DIR)
queue_publisher = QueuePublisher(pub_queue)
from services.connection import (
_get_llm_config, connect_llm, add_llm_provider, remove_llm_provider,
on_provider_selected, connect_sd, on_sd_model_change, check_mcp_status,
get_login_qrcode, logout_xhs, _auto_fetch_xsec_token, check_login,
save_my_user_id, upload_face_image, load_saved_face_image,
)
from services.content import (
generate_copy, generate_images, one_click_export, publish_to_xhs,
)
from services.hotspot import (
_cache_lock, _set_cache, _get_cache, _fetch_and_cache, _pick_from_cache,
search_hotspots, analyze_and_suggest, generate_from_hotspot,
fetch_proactive_notes, on_proactive_note_selected,
)
from services.engagement import (
load_note_for_comment, ai_generate_comment, send_comment,
fetch_my_notes, on_my_note_selected, fetch_my_note_comments,
ai_reply_comment, send_reply,
)
from services.profile import _parse_profile_json, _parse_count, fetch_my_profile
from services.scheduler import (
_auto_log_append, _auto_log, _auto_running, _auto_thread,
_auto_comment_with_log, auto_comment_once,
_auto_like_with_log, auto_like_once,
_auto_favorite_with_log, auto_favorite_once,
_auto_publish_with_log, auto_publish_once,
_auto_reply_with_log, auto_reply_once,
_scheduler_next_times, _scheduler_loop,
start_scheduler, stop_scheduler, get_auto_log, get_scheduler_status,
_learn_running, _learn_scheduler_loop, start_learn_scheduler, stop_learn_scheduler,
analytics_collect_data, analytics_calculate_weights,
analytics_llm_deep_analysis, analytics_get_report, analytics_get_weighted_topics,
configure_analytics,
)
from services.queue_ops import (
generate_to_queue, queue_refresh_table,
queue_refresh_calendar, queue_preview_item, queue_approve_item,
queue_reject_item, queue_delete_item, queue_retry_item, queue_publish_now,
queue_start_processor, queue_stop_processor, queue_get_status,
queue_batch_approve, queue_generate_and_refresh,
configure as configure_queue_ops,
)
# 注入依赖
configure_analytics(analytics)
configure_queue_ops(pub_queue, queue_publisher, analytics, _auto_log_append)
from ui.app import build_app, _GRADIO_CSS
app = build_app(cfg, analytics)
if __name__ == "__main__":
logger.info("🍒 小红书 AI 爆文工坊 V2.0 启动中...")
# ---- Gradio 登录认证 ----
# 从配置或环境变量读取,默认用户名/密码: admin / admin123
_auth_user = os.environ.get("GRADIO_AUTH_USER", cfg.get("auth_user", "admin"))
_auth_pass = os.environ.get("GRADIO_AUTH_PASS", cfg.get("auth_pass", "admin123"))
app.launch(
server_name=os.environ.get("GRADIO_SERVER_NAME", "127.0.0.1"),
server_port=int(os.environ.get("GRADIO_SERVER_PORT", "7860")),
inbrowser=True,
share=False,
auth=(_auth_user, _auth_pass),
theme=gr.themes.Soft(),
css=_GRADIO_CSS,
)