sophhub 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/package.json +1 -1
  2. package/skills/consensus/skill.json +20 -0
  3. package/skills/consensus/src/SKILL.md +93 -0
  4. package/skills/deepwiki/skill.json +20 -0
  5. package/skills/deepwiki/src/SKILL.md +45 -0
  6. package/skills/deepwiki/src/_meta.json +6 -0
  7. package/skills/deepwiki/src/scripts/deepwiki.js +135 -0
  8. package/skills/feishu-bitable/skill.json +20 -0
  9. package/skills/feishu-bitable/src/CHECKLIST.md +150 -0
  10. package/skills/feishu-bitable/src/README.md +178 -0
  11. package/skills/feishu-bitable/src/SKILL.md +113 -0
  12. package/skills/feishu-bitable/src/_meta.json +6 -0
  13. package/skills/feishu-bitable/src/api.js +381 -0
  14. package/skills/feishu-bitable/src/bin/cli.js +284 -0
  15. package/skills/feishu-bitable/src/description.md +143 -0
  16. package/skills/feishu-bitable/src/examples/create-records.json +52 -0
  17. package/skills/feishu-bitable/src/examples/create-table.json +64 -0
  18. package/skills/feishu-bitable/src/package-lock.json +324 -0
  19. package/skills/feishu-bitable/src/package.json +33 -0
  20. package/skills/feishu-bitable/src/publish-config.json +14 -0
  21. package/skills/feishu-bitable/src/test-simple.js +61 -0
  22. package/skills/feishu-bitable/src/utils.js +261 -0
  23. package/skills/google-maps/skill.json +20 -0
  24. package/skills/google-maps/src/SKILL.md +237 -0
  25. package/skills/google-maps/src/_meta.json +6 -0
  26. package/skills/google-maps/src/lib/map_helper.py +912 -0
  27. package/skills/large-task-router/skill.json +20 -0
  28. package/skills/large-task-router/src/SKILL.md +79 -0
  29. package/skills/large-task-router/src/templates/plan.md +74 -0
  30. package/skills/notes-hub-assistant/skill.json +20 -0
  31. package/skills/notes-hub-assistant/src/SKILL.md +233 -0
  32. package/skills/notes-hub-assistant/src/scripts/_resolve_lark_cli.py +48 -0
  33. package/skills/notes-hub-assistant/src/scripts/openclaw_meeting_minutes.py +473 -0
  34. package/skills/notes-hub-assistant/src/scripts/openclaw_notes_crud.py +596 -0
  35. package/skills/notes-hub-assistant/src/scripts/openclaw_wolai_notes_crud.py +364 -0
  36. package/skills/notes-hub-assistant/src/scripts/run_meeting_minutes.py +79 -0
  37. package/skills/notes-hub-assistant/src/scripts/run_note_crud.py +37 -0
  38. package/skills/notes-hub-assistant/src/scripts/run_notionbot.py +36 -0
  39. package/skills/notes-hub-assistant/src/scripts/run_wolai_note_crud.py +27 -0
  40. package/skills/skillhub/skill.json +11 -4
  41. package/skills/skillhub/src/SKILL.md +11 -1
  42. package/skills/sophnet-dailynews/skill.json +20 -0
  43. package/skills/sophnet-dailynews/src/SKILL.md +179 -0
  44. package/skills/sophnet-dailynews/src/cache.json +151 -0
  45. package/skills/sophnet-dailynews/src/sources.json +230 -0
  46. package/skills/sophnet-schedule/skill.json +20 -0
  47. package/skills/sophnet-schedule/src/ARCHITECTURE.md +321 -0
  48. package/skills/sophnet-schedule/src/IMPROVEMENTS.md +145 -0
  49. package/skills/sophnet-schedule/src/SKILL.md +1050 -0
  50. package/skills/sophnet-schedule/src/_meta.json +6 -0
  51. package/skills/sophnet-schedule/src/api/__init__.py +0 -0
  52. package/skills/sophnet-schedule/src/api/models.py +245 -0
  53. package/skills/sophnet-schedule/src/apps/add_event.py +237 -0
  54. package/skills/sophnet-schedule/src/apps/check_reminders.py +112 -0
  55. package/skills/sophnet-schedule/src/apps/check_roc.py +246 -0
  56. package/skills/sophnet-schedule/src/apps/generate_daily_plan.py +342 -0
  57. package/skills/sophnet-schedule/src/apps/import_events.py +216 -0
  58. package/skills/sophnet-schedule/src/apps/monitor_calendar_changes.py +140 -0
  59. package/skills/sophnet-schedule/src/apps/register_tasks.py +169 -0
  60. package/skills/sophnet-schedule/src/apps/sync_roc_to_gcal.py +174 -0
  61. package/skills/sophnet-schedule/src/compat.py +66 -0
  62. package/skills/sophnet-schedule/src/config/__init__.py +0 -0
  63. package/skills/sophnet-schedule/src/config/reminder_rules.yaml +96 -0
  64. package/skills/sophnet-schedule/src/config/roc_events.yaml +44 -0
  65. package/skills/sophnet-schedule/src/config/settings.py +133 -0
  66. package/skills/sophnet-schedule/src/config/task_registry.yaml +92 -0
  67. package/skills/sophnet-schedule/src/docs/FRONTEND_INTEGRATION_GUIDE.md +437 -0
  68. package/skills/sophnet-schedule/src/gcal/__init__.py +0 -0
  69. package/skills/sophnet-schedule/src/gcal/client.py +374 -0
  70. package/skills/sophnet-schedule/src/gcal/models.py +91 -0
  71. package/skills/sophnet-schedule/src/requirements.txt +6 -0
  72. package/skills/sophnet-schedule/src/scripts/setup_gcal_token.py +85 -0
  73. package/skills/sophnet-schedule/src/server.py +669 -0
  74. package/skills/sophnet-schedule/src/services/__init__.py +0 -0
  75. package/skills/sophnet-schedule/src/services/calendar_backend.py +139 -0
  76. package/skills/sophnet-schedule/src/services/conflict_detector.py +96 -0
  77. package/skills/sophnet-schedule/src/services/datetime_utils.py +117 -0
  78. package/skills/sophnet-schedule/src/services/event_classifier.py +100 -0
  79. package/skills/sophnet-schedule/src/services/event_diff.py +160 -0
  80. package/skills/sophnet-schedule/src/services/google_integration.py +500 -0
  81. package/skills/sophnet-schedule/src/services/job_store.py +100 -0
  82. package/skills/sophnet-schedule/src/services/local_event_store.py +266 -0
  83. package/skills/sophnet-schedule/src/services/reminder_planner.py +116 -0
  84. package/skills/sophnet-schedule/src/services/runtime_utils.py +31 -0
  85. package/skills/sophnet-schedule/src/services/table_parser.py +286 -0
  86. package/skills/sophnet-schedule/src/services/task_builder.py +167 -0
  87. package/skills/sophnet-schedule/src/services/time_window.py +72 -0
  88. package/skills/sophnet-stock/skill.json +20 -0
  89. package/skills/sophnet-stock/src/App-Plan.md +442 -0
  90. package/skills/sophnet-stock/src/README.md +214 -0
  91. package/skills/sophnet-stock/src/SKILL.md +236 -0
  92. package/skills/sophnet-stock/src/TODO.md +394 -0
  93. package/skills/sophnet-stock/src/_meta.json +6 -0
  94. package/skills/sophnet-stock/src/docs/ARCHITECTURE.md +408 -0
  95. package/skills/sophnet-stock/src/docs/CONCEPT.md +233 -0
  96. package/skills/sophnet-stock/src/docs/HOT_SCANNER.md +288 -0
  97. package/skills/sophnet-stock/src/docs/README.md +95 -0
  98. package/skills/sophnet-stock/src/docs/USAGE.md +465 -0
  99. package/skills/sophnet-stock/src/scripts/analyze_stock.py +2565 -0
  100. package/skills/sophnet-stock/src/scripts/dividends.py +365 -0
  101. package/skills/sophnet-stock/src/scripts/hot_scanner.py +582 -0
  102. package/skills/sophnet-stock/src/scripts/portfolio.py +548 -0
  103. package/skills/sophnet-stock/src/scripts/rumor_scanner.py +342 -0
  104. package/skills/sophnet-stock/src/scripts/test_stock_analysis.py +409 -0
  105. package/skills/sophnet-stock/src/scripts/watchlist.py +336 -0
  106. package/skills/xiaohongshu/skill.json +20 -0
  107. package/skills/xiaohongshu/src/SKILL.md +91 -0
  108. package/skills/xiaohongshu/src/_meta.json +6 -0
  109. package/skills/xiaohongshu/src/assets/card.html +216 -0
  110. package/skills/xiaohongshu/src/assets/cover.html +82 -0
  111. package/skills/xiaohongshu/src/assets/example.md +84 -0
  112. package/skills/xiaohongshu/src/assets/styles.css +318 -0
  113. package/skills/xiaohongshu/src/scripts/render_xhs_v2.py +737 -0
  114. package/skills/xiaohongshu/src/scripts/sign_server.py +158 -0
  115. package/skills/xiaohongshu/src/scripts/stealth.min.js +7 -0
  116. package/skills/xiaohongshu/src/scripts/xhs_tool.py +186 -0
  117. package/skills/xiaohongshu/src/workflow.py +185 -0
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 小红书工具 - 使用内置签名
4
+ """
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+ from dotenv import load_dotenv
12
+
13
+ # 加载环境变量
14
+ env_path = Path(__file__).parent.parent / '.env'
15
+ load_dotenv(env_path)
16
+
17
+ def get_client():
18
+ """获取 XHS 客户端(使用内置签名)"""
19
+ from xhs import XhsClient
20
+ from xhs.help import sign as local_sign
21
+
22
+ cookie = os.getenv('XHS_COOKIE')
23
+ if not cookie:
24
+ print("❌ 错误: 未配置 XHS_COOKIE")
25
+ sys.exit(1)
26
+
27
+ def sign_func(uri, data=None, a1="", web_session=""):
28
+ return local_sign(uri, data, a1=a1)
29
+
30
+ client = XhsClient(cookie=cookie, sign=sign_func)
31
+ return client
32
+
33
+ def cmd_search(args):
34
+ """搜索笔记"""
35
+ client = get_client()
36
+ print(f"🔍 搜索: {args.keyword}...")
37
+ result = client.get_note_by_keyword(args.keyword)
38
+ notes = result.get('items', [])
39
+
40
+ print(f"找到 {len(notes)} 条笔记\n")
41
+ for i, item in enumerate(notes[:args.limit], 1):
42
+ note = item.get('note_card', {})
43
+ print(f"{i}. {note.get('display_title', note.get('title', '无标题'))}")
44
+ print(f" 👤 {note.get('user', {}).get('nickname', '未知')}")
45
+ print(f" ❤️ {note.get('liked_count', 0)}")
46
+ print(f" 🔗 ID: {note.get('note_id', 'N/A')}")
47
+ print()
48
+
49
+ def cmd_note(args):
50
+ """查看笔记详情"""
51
+ client = get_client()
52
+ print(f"📖 获取笔记...")
53
+ note = client.get_note_by_id(args.note_id, xsec_token=args.token or "")
54
+
55
+ info = note.get('note_card', note)
56
+ print(f"\n📝 {info.get('title', info.get('display_title', '无标题'))}")
57
+ print(f"{'='*50}")
58
+ print(f"👤 作者: {info.get('user', {}).get('nickname', '未知')}")
59
+ print(f"❤️ 点赞: {info.get('interact_info', {}).get('liked_count', 0)}")
60
+ print(f"⭐ 收藏: {info.get('interact_info', {}).get('collected_count', 0)}")
61
+ print(f"💬 评论: {info.get('interact_info', {}).get('comment_count', 0)}")
62
+ print(f"\n📄 内容:\n{info.get('desc', '无内容')}")
63
+
64
+ def cmd_user(args):
65
+ """查看用户信息"""
66
+ client = get_client()
67
+ print(f"👤 获取用户信息...")
68
+ user = client.get_user_info(args.user_id)
69
+
70
+ info = user.get('basic_info', user)
71
+ print(f"\n👤 {info.get('nickname', '未知')}")
72
+ print(f"🔴 小红书号: {info.get('red_id', 'N/A')}")
73
+ print(f"📝 简介: {info.get('desc', '无')}")
74
+ print(f"👥 粉丝: {info.get('fans', 0)}")
75
+
76
+ def cmd_me(args):
77
+ """查看自己的账号"""
78
+ client = get_client()
79
+ print(f"👤 获取账号信息...")
80
+ info = client.get_self_info()
81
+
82
+ basic = info.get('basic_info', info)
83
+ print(f"\n👤 我的账号")
84
+ print(f"{'='*50}")
85
+ print(f"昵称: {basic.get('nickname', 'N/A')}")
86
+ print(f"小红书号: {basic.get('red_id', 'N/A')}")
87
+ print(f"粉丝: {basic.get('fans', 0)}")
88
+
89
+ def cmd_publish(args):
90
+ """发布图文笔记"""
91
+ client = get_client()
92
+
93
+ # 读取内容
94
+ if args.content:
95
+ desc = args.content
96
+ elif args.file:
97
+ with open(args.file, 'r', encoding='utf-8') as f:
98
+ desc = f.read()
99
+ else:
100
+ print("❌ 需要 --content 或 --file")
101
+ sys.exit(1)
102
+
103
+ # 验证图片
104
+ images = []
105
+ for img in args.images:
106
+ if os.path.exists(img):
107
+ images.append(os.path.abspath(img))
108
+ else:
109
+ print(f"⚠️ 图片不存在: {img}")
110
+
111
+ if not images:
112
+ print("❌ 没有有效图片")
113
+ sys.exit(1)
114
+
115
+ print(f"📤 准备发布...")
116
+ print(f" 标题: {args.title}")
117
+ print(f" 图片: {len(images)} 张")
118
+
119
+ if args.dry_run:
120
+ print("\n⚠️ [试运行] 不会实际发布")
121
+ return
122
+
123
+ result = client.create_image_note(
124
+ title=args.title[:20],
125
+ desc=desc,
126
+ files=images,
127
+ is_private=args.private
128
+ )
129
+
130
+ print(f"\n✅ 发布成功!")
131
+ if isinstance(result, dict):
132
+ note_id = result.get('note_id') or result.get('id')
133
+ if note_id:
134
+ print(f"🔗 https://www.xiaohongshu.com/explore/{note_id}")
135
+
136
+ def main():
137
+ parser = argparse.ArgumentParser(description='小红书工具')
138
+ subparsers = parser.add_subparsers(dest='command')
139
+
140
+ # search
141
+ p = subparsers.add_parser('search', help='搜索笔记')
142
+ p.add_argument('keyword')
143
+ p.add_argument('-n', '--limit', type=int, default=10)
144
+ p.set_defaults(func=cmd_search)
145
+
146
+ # note
147
+ p = subparsers.add_parser('note', help='查看笔记')
148
+ p.add_argument('note_id')
149
+ p.add_argument('--token', default='')
150
+ p.set_defaults(func=cmd_note)
151
+
152
+ # user
153
+ p = subparsers.add_parser('user', help='查看用户')
154
+ p.add_argument('user_id')
155
+ p.set_defaults(func=cmd_user)
156
+
157
+ # me
158
+ p = subparsers.add_parser('me', help='查看自己')
159
+ p.set_defaults(func=cmd_me)
160
+
161
+ # publish
162
+ p = subparsers.add_parser('publish', help='发布笔记')
163
+ p.add_argument('-t', '--title', required=True)
164
+ p.add_argument('-c', '--content')
165
+ p.add_argument('-f', '--file')
166
+ p.add_argument('-i', '--images', nargs='+', required=True)
167
+ p.add_argument('--private', action='store_true')
168
+ p.add_argument('--dry-run', action='store_true')
169
+ p.set_defaults(func=cmd_publish)
170
+
171
+ args = parser.parse_args()
172
+
173
+ if not args.command:
174
+ parser.print_help()
175
+ sys.exit(1)
176
+
177
+ try:
178
+ args.func(args)
179
+ except Exception as e:
180
+ print(f"❌ 错误: {e}")
181
+ import traceback
182
+ traceback.print_exc()
183
+ sys.exit(1)
184
+
185
+ if __name__ == '__main__':
186
+ main()
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 小红书工作流 - 从内容到发布的完整流程
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ import os
9
+ import sys
10
+ from datetime import datetime, timedelta
11
+ from pathlib import Path
12
+
13
+ # 添加路径
14
+ SKILL_DIR = Path(__file__).parent
15
+ sys.path.insert(0, str(SKILL_DIR / "social-auto-upload"))
16
+
17
+ from dotenv import load_dotenv
18
+ load_dotenv(SKILL_DIR / ".env")
19
+
20
+
21
+ async def setup_cookie():
22
+ """首次登录获取 Cookie(需要有显示器)"""
23
+ from uploader.xiaohongshu_uploader.main import xiaohongshu_cookie_gen
24
+ cookie_file = SKILL_DIR / "social-auto-upload" / "cookies" / "xhs_account.json"
25
+ cookie_file.parent.mkdir(parents=True, exist_ok=True)
26
+ await xiaohongshu_cookie_gen(str(cookie_file))
27
+ print(f"✅ Cookie 已保存到: {cookie_file}")
28
+
29
+
30
+ async def check_cookie():
31
+ """检查 Cookie 是否有效"""
32
+ from uploader.xiaohongshu_uploader.main import cookie_auth
33
+ cookie_file = SKILL_DIR / "social-auto-upload" / "cookies" / "xhs_account.json"
34
+
35
+ if not cookie_file.exists():
36
+ print("❌ Cookie 文件不存在,请先运行 setup 命令")
37
+ return False
38
+
39
+ valid = await cookie_auth(str(cookie_file))
40
+ if valid:
41
+ print("✅ Cookie 有效")
42
+ else:
43
+ print("❌ Cookie 已失效,请重新登录")
44
+ return valid
45
+
46
+
47
+ async def publish_images(title: str, desc: str, images: list, schedule_time=None, is_private=False):
48
+ """
49
+ 发布图文笔记
50
+
51
+ Args:
52
+ title: 笔记标题
53
+ desc: 笔记描述/正文
54
+ images: 图片路径列表
55
+ schedule_time: 定时发布时间 (datetime 对象)
56
+ is_private: 是否私密
57
+ """
58
+ from playwright.async_api import async_playwright
59
+ from conf import LOCAL_CHROME_PATH
60
+
61
+ cookie_file = SKILL_DIR / "social-auto-upload" / "cookies" / "xhs_account.json"
62
+
63
+ if not cookie_file.exists():
64
+ print("❌ Cookie 文件不存在")
65
+ return False
66
+
67
+ async with async_playwright() as p:
68
+ browser = await p.chromium.launch(headless=True)
69
+ context = await browser.new_context(storage_state=str(cookie_file))
70
+ page = await context.new_page()
71
+
72
+ print("🌐 打开小红书创作者中心...")
73
+ await page.goto("https://creator.xiaohongshu.com/publish/publish")
74
+ await asyncio.sleep(3)
75
+
76
+ # 选择"上传图文"
77
+ print("📝 选择图文发布...")
78
+ try:
79
+ await page.click('text=发布图文')
80
+ await asyncio.sleep(2)
81
+ except:
82
+ pass # 可能已经在图文页面
83
+
84
+ # 上传图片
85
+ print(f"📷 上传 {len(images)} 张图片...")
86
+ upload_input = await page.query_selector('input[type="file"]')
87
+ if upload_input:
88
+ await upload_input.set_input_files(images)
89
+ await asyncio.sleep(5) # 等待上传
90
+
91
+ # 填写标题
92
+ print("✏️ 填写标题...")
93
+ title_input = await page.query_selector('[placeholder*="标题"]')
94
+ if title_input:
95
+ await title_input.fill(title[:20]) # 小红书标题限制
96
+
97
+ # 填写正文
98
+ print("📄 填写正文...")
99
+ desc_input = await page.query_selector('[placeholder*="正文"]')
100
+ if not desc_input:
101
+ desc_input = await page.query_selector('.ql-editor')
102
+ if desc_input:
103
+ await desc_input.fill(desc)
104
+
105
+ await asyncio.sleep(2)
106
+
107
+ # 发布
108
+ print("🚀 发布中...")
109
+ publish_btn = await page.query_selector('button:has-text("发布")')
110
+ if publish_btn:
111
+ await publish_btn.click()
112
+ await asyncio.sleep(5)
113
+ print("✅ 发布成功!")
114
+
115
+ await browser.close()
116
+ return True
117
+
118
+
119
+ def render_content(markdown_file: str, style: str = "xiaohongshu", output_dir: str = None):
120
+ """渲染 Markdown 为图片"""
121
+ import subprocess
122
+
123
+ if output_dir is None:
124
+ output_dir = str(SKILL_DIR / "output")
125
+
126
+ cmd = [
127
+ str(SKILL_DIR / "venv" / "bin" / "python"),
128
+ str(SKILL_DIR / "scripts" / "render_xhs_v2.py"),
129
+ markdown_file,
130
+ "--style", style,
131
+ "-o", output_dir
132
+ ]
133
+
134
+ result = subprocess.run(cmd, capture_output=True, text=True)
135
+ print(result.stdout)
136
+ if result.returncode != 0:
137
+ print(result.stderr)
138
+ return []
139
+
140
+ # 返回生成的图片列表
141
+ output_path = Path(output_dir)
142
+ images = sorted(output_path.glob("*.png"))
143
+ return [str(img) for img in images]
144
+
145
+
146
+ def main():
147
+ import argparse
148
+ parser = argparse.ArgumentParser(description="小红书工作流")
149
+ subparsers = parser.add_subparsers(dest="command")
150
+
151
+ # setup 命令
152
+ subparsers.add_parser("setup", help="首次登录获取 Cookie")
153
+
154
+ # check 命令
155
+ subparsers.add_parser("check", help="检查 Cookie 状态")
156
+
157
+ # render 命令
158
+ render_parser = subparsers.add_parser("render", help="渲染内容为图片")
159
+ render_parser.add_argument("markdown_file", help="Markdown 文件路径")
160
+ render_parser.add_argument("--style", default="xiaohongshu", help="样式")
161
+ render_parser.add_argument("-o", "--output", help="输出目录")
162
+
163
+ # publish 命令
164
+ pub_parser = subparsers.add_parser("publish", help="发布图文笔记")
165
+ pub_parser.add_argument("-t", "--title", required=True, help="标题")
166
+ pub_parser.add_argument("-d", "--desc", required=True, help="描述")
167
+ pub_parser.add_argument("-i", "--images", nargs="+", required=True, help="图片路径")
168
+
169
+ args = parser.parse_args()
170
+
171
+ if args.command == "setup":
172
+ asyncio.run(setup_cookie())
173
+ elif args.command == "check":
174
+ asyncio.run(check_cookie())
175
+ elif args.command == "render":
176
+ images = render_content(args.markdown_file, args.style, args.output)
177
+ print(f"生成了 {len(images)} 张图片")
178
+ elif args.command == "publish":
179
+ asyncio.run(publish_images(args.title, args.desc, args.images))
180
+ else:
181
+ parser.print_help()
182
+
183
+
184
+ if __name__ == "__main__":
185
+ main()