#!/usr/bin/env python3 """ nanobot集成接口 将小红书集成功能暴露给nanobot使用 """ import os import sys import json import logging from pathlib import Path from typing import Dict, List, Optional, Any # 添加当前目录到Python路径 sys.path.append(os.path.dirname(os.path.abspath(__file__))) from xiaohongshu_publisher import XiaohongshuPublisher, PublishResult from content_converter import ContentConverter, Article # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) class XiaohongshuIntegration: """小红书集成主类""" def __init__(self, config_path: Optional[str] = None): """ 初始化集成 Args: config_path: 配置文件路径 """ self.config_path = config_path or self._get_default_config_path() self.config = self._load_config() # 初始化组件 self.publisher = XiaohongshuPublisher(self.config_path) self.converter = ContentConverter(self.config.get('content', {})) logger.info("小红书集成初始化完成") logger.info(f"配置文件: {self.config_path}") def _get_default_config_path(self) -> str: """获取默认配置文件路径""" skill_dir = Path(__file__).parent config_path = skill_dir / "config.json" if not config_path.exists(): # 创建默认配置 default_config = { "xiaohongshu": { "enabled": True, "cli_path": "xhs-cli", "username": None, "password": None, "max_images": 9, "add_watermark": False, "watermark_text": "技术资讯", "default_tags": ["#技术资讯", "#科技新闻", "#AI"], "publish_interval": 3600, "timeout": 30 }, "content": { "max_title_length": 30, "max_content_length": 1000, "auto_generate_tags": True, "add_source_info": True, "add_emoji": True, "max_tags": 10 }, "images": { "max_width": 1080, "max_height": 1920, "quality": 85, "format": "JPEG" } } with open(config_path, 'w', encoding='utf-8') as f: json.dump(default_config, f, indent=2, ensure_ascii=False) logger.info(f"创建默认配置文件: {config_path}") return str(config_path) def _load_config(self) -> Dict: """加载配置""" try: with open(self.config_path, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: logger.error(f"加载配置文件失败: {e}") return {} def publish_article(self, article_data: Dict) -> Dict[str, Any]: """ 发布文章到小红书 Args: article_data: 文章数据字典,包含: - title: 标题 - content: 内容 - source: 来源 - url: 原文链接 - images: 图片路径列表 - publish_date: 发布日期(可选) - author: 作者(可选) - category: 分类(可选) Returns: 发布结果字典 """ if not self.config.get('xiaohongshu', {}).get('enabled', True): return { 'success': False, 'error': '小红书集成未启用' } try: # 创建文章对象 article = Article( title=article_data.get('title', ''), content=article_data.get('content', ''), source=article_data.get('source', '未知来源'), url=article_data.get('url', ''), images=article_data.get('images', []), publish_date=article_data.get('publish_date'), author=article_data.get('author'), category=article_data.get('category') ) # 转换为小红书格式 xhs_content = self.converter.to_xiaohongshu(article) # 生成标签 tags = [] if self.config.get('content', {}).get('auto_generate_tags', True): tags = self.converter._generate_tags(article).replace('#', '').split() # 合并默认标签 default_tags = self.config.get('xiaohongshu', {}).get('default_tags', []) tags = list(set(tags + [tag.replace('#', '') for tag in default_tags])) # 发布到小红书 result = self.publisher.publish( title=article.title, content=xhs_content, images=article.images, tags=tags ) # 转换为字典格式 return { 'success': result.success, 'post_id': result.post_id, 'url': result.url, 'message': result.message, 'error': result.error, 'timestamp': result.timestamp, 'content_preview': xhs_content[:200] + '...' if len(xhs_content) > 200 else xhs_content, 'tags_used': tags } except Exception as e: logger.error(f"发布文章失败: {e}") return { 'success': False, 'error': str(e) } def test_connection(self) -> Dict[str, Any]: """测试连接""" try: status = self.publisher.get_status() # 尝试登录 login_success = False if status.get('username') and status.get('cli_available', False): login_success = self.publisher.login() return { 'success': True, 'status': status, 'login_success': login_success, 'config_loaded': bool(self.config), 'converter_ready': True } except Exception as e: return { 'success': False, 'error': str(e) } def get_status(self) -> Dict[str, Any]: """获取集成状态""" publisher_status = self.publisher.get_status() return { 'publisher': publisher_status, 'converter': { 'config': self.converter.config, 'emoji_count': len(self.converter.EMOJI_MAPPING), 'tag_mapping_count': len(self.converter.TAG_MAPPING) }, 'config': { 'path': self.config_path, 'enabled': self.config.get('xiaohongshu', {}).get('enabled', True) } } def update_config(self, new_config: Dict) -> bool: """ 更新配置 Args: new_config: 新的配置字典 Returns: 是否成功 """ try: # 合并配置 current_config = self._load_config() # 深度合并 def deep_update(target, source): for key, value in source.items(): if key in target and isinstance(target[key], dict) and isinstance(value, dict): deep_update(target[key], value) else: target[key] = value deep_update(current_config, new_config) # 保存配置 with open(self.config_path, 'w', encoding='utf-8') as f: json.dump(current_config, f, indent=2, ensure_ascii=False) # 重新加载配置 self.config = current_config self.converter = ContentConverter(self.config.get('content', {})) logger.info("配置更新成功") return True except Exception as e: logger.error(f"更新配置失败: {e}") return False def convert_content_only(self, article_data: Dict) -> Dict[str, Any]: """ 仅转换内容(不发布) Args: article_data: 文章数据 Returns: 转换结果 """ try: article = Article( title=article_data.get('title', ''), content=article_data.get('content', ''), source=article_data.get('source', '未知来源'), url=article_data.get('url', ''), images=article_data.get('images', []), publish_date=article_data.get('publish_date'), author=article_data.get('author'), category=article_data.get('category') ) # 转换内容 xhs_content = self.converter.to_xiaohongshu(article) # 分析文章 analysis = self.converter.analyze_article(article) return { 'success': True, 'xhs_content': xhs_content, 'analysis': analysis, 'title': article.title, 'source': article.source, 'content_length': len(xhs_content), 'recommended_tags': analysis.get('recommended_tags', []) } except Exception as e: return { 'success': False, 'error': str(e) } # nanobot技能接口 class XiaohongshuSkill: """nanobot技能接口类""" def __init__(self): self.integration = None self.skill_dir = Path(__file__).parent def initialize(self): """初始化技能""" try: config_path = self.skill_dir / "config.json" self.integration = XiaohongshuIntegration(str(config_path)) # 测试连接 test_result = self.integration.test_connection() return { 'success': True, 'message': '小红书集成技能初始化成功', 'test_result': test_result, 'config_path': str(config_path) } except Exception as e: return { 'success': False, 'error': str(e), 'message': '小红书集成技能初始化失败' } def handle_command(self, command: str, args: Dict) -> Dict[str, Any]: """ 处理命令 Args: command: 命令名称 args: 命令参数 Returns: 命令执行结果 """ if not self.integration: init_result = self.initialize() if not init_result['success']: return init_result try: if command == 'publish': # 发布文章 return self.integration.publish_article(args) elif command == 'convert': # 转换内容 return self.integration.convert_content_only(args) elif command == 'status': # 获取状态 status = self.integration.get_status() return { 'success': True, 'status': status } elif command == 'test': # 测试连接 return self.integration.test_connection() elif command == 'update_config': # 更新配置 success = self.integration.update_config(args.get('config', {})) return { 'success': success, 'message': '配置更新成功' if success else '配置更新失败' } else: return { 'success': False, 'error': f'未知命令: {command}', 'available_commands': ['publish', 'convert', 'status', 'test', 'update_config'] } except Exception as e: return { 'success': False, 'error': str(e) } def get_skill_info(self) -> Dict[str, Any]: """获取技能信息""" return { 'name': 'xiaohongshu_integration', 'version': '1.0.0', 'description': '小红书平台集成技能', 'author': 'nanobot', 'commands': { 'publish': '发布文章到小红书', 'convert': '转换文章为小红书格式', 'status': '查看集成状态', 'test': '测试连接', 'update_config': '更新配置' }, 'config_file': str(self.skill_dir / "config.json"), 'requirements': ['Pillow'] } # 创建全局实例 xiaohongshu_skill = XiaohongshuSkill() # nanobot直接调用接口 def publish_to_xiaohongshu(article_data: Dict) -> Dict: """发布到小红书(直接调用)""" return xiaohongshu_skill.handle_command('publish', article_data) def convert_to_xiaohongshu_format(article_data: Dict) -> Dict: """转换为小红书格式(直接调用)""" return xiaohongshu_skill.handle_command('convert', article_data) def get_xiaohongshu_status() -> Dict: """获取小红书集成状态(直接调用)""" return xiaohongshu_skill.handle_command('status', {}) # 使用示例 if __name__ == "__main__": print("=" * 60) print("小红书集成技能测试") print("=" * 60) # 初始化技能 result = xiaohongshu_skill.initialize() print("初始化结果:", json.dumps(result, indent=2, ensure_ascii=False)) if result['success']: # 测试文章 test_article = { 'title': '测试:Python 3.12性能大幅提升', 'content': 'Python 3.12正式发布,带来了显著的性能改进。\n\n主要改进:\n1. 解释器性能提升25%\n2. 更好的错误信息\n3. 新的类型提示语法\n4. 改进的异步支持\n\n对于开发者来说,这是值得升级的版本。', 'source': 'Python官方博客', 'url': 'https://python.org/news', 'images': [], 'publish_date': '2026-03-15', 'author': 'Python核心团队', 'category': '编程语言' } # 测试转换 print("\n" + "=" * 60) print("测试内容转换:") print("=" * 60) convert_result = xiaohongshu_skill.handle_command('convert', test_article) if convert_result['success']: print("转换成功!") print("\n小红书格式内容预览:") print("-" * 40) print(convert_result.get('xhs_content', '')[:300] + '...') print("-" * 40) print("\n文章分析:") print(json.dumps(convert_result.get('analysis', {}), indent=2, ensure_ascii=False)) else: print(f"转换失败: {convert_result.get('error')}") # 测试状态 print("\n" + "=" * 60) print("测试状态查询:") print("=" * 60) status_result = xiaohongshu_skill.handle_command('status', {}) if status_result['success']: print("状态查询成功") # 简化显示 status = status_result.get('status', {}) publisher_status = status.get('publisher', {}) print(f"CLI可用: {publisher_status.get('cli_available', False)}") print(f"已登录: {publisher_status.get('is_logged_in', False)}") print(f"用户名: {publisher_status.get('username', '未设置')}") else: print(f"状态查询失败: {status_result.get('error')}") # 显示技能信息 print("\n" + "=" * 60) print("技能信息:") print("=" * 60) skill_info = xiaohongshu_skill.get_skill_info() print(json.dumps(skill_info, indent=2, ensure_ascii=False)) print("\n" + "=" * 60) print("测试完成") print("=" * 60)