Add 39 shared skills from local agent inventory
Sources: ~/.openclaw/skills, ~/.agents/skills, workshop-skills, workspace/skills
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
"""
|
||||
行业热榜垂直(增强版) - Industry Hot Module
|
||||
|
||||
功能:
|
||||
- 十大行业分类:科技互联网、游戏、汽车、金融财经、数码消费、娱乐影视、房产家居、医疗健康、旅游出行、餐饮消费
|
||||
- 用户自主选择行业
|
||||
- 行业描述和平台标注
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
import asyncio
|
||||
|
||||
# 十大行业映射
|
||||
INDUSTRIES: Dict[str, Dict[str, Any]] = {
|
||||
"科技互联网": {
|
||||
"platforms": ["ithome", "36kr", "csdn", "juejin", "oschina", "infoq"],
|
||||
"description": "IT之家、36氪、CSDN、稀土掘金、开源中国、InfoQ"
|
||||
},
|
||||
"游戏行业": {
|
||||
"platforms": ["genshin", "miyoushe", "lol", "bilibili", "douyu", "huya", "netease-game"],
|
||||
"description": "原神、米游社、英雄联盟、B站、斗鱼、虎牙、网易游戏"
|
||||
},
|
||||
"汽车行业": {
|
||||
"platforms": ["autohome", "car", "懂车帝", "bitauto", "soufun-auto"],
|
||||
"description": "汽车之家、懂车帝、易车网、苏宁汽车"
|
||||
},
|
||||
"金融财经": {
|
||||
"platforms": ["sina-money", "eastmoney", "xueqiu", "jrj", "cnstock", "wallstreetcn", "money163"],
|
||||
"description": "新浪财经、东方财富、雪球、金融界、中国财经网、华尔街见闻、网易财经"
|
||||
},
|
||||
"数码消费": {
|
||||
"platforms": ["coolapk", "ithome", "sspai", "geekpark", "少数派", "smzdm"],
|
||||
"description": "酷安、IT之家、少数派、什么值得买"
|
||||
},
|
||||
"娱乐影视": {
|
||||
"platforms": ["weibo", "douban-group", "douban-movie", "mtime", "movie", "bilibili"],
|
||||
"description": "微博、豆瓣、豆瓣电影、时光网、B站"
|
||||
},
|
||||
"房产家居": {
|
||||
"platforms": ["lfang", "soufunianjia", "anjuke", "house", "lianjia"],
|
||||
"description": "链家、安居客、房天下、贝壳找房"
|
||||
},
|
||||
"医疗健康": {
|
||||
"platforms": ["zhihu", "知乎", "sina-health", "health", "baikemy", "丁香园"],
|
||||
"description": "知乎、新浪健康、丁香园、百度健康"
|
||||
},
|
||||
"旅游出行": {
|
||||
"platforms": ["mafengwo", "ctrip", "qunar", "飞猪", "马蜂窝", "携程"],
|
||||
"description": "马蜂窝、携程、去哪儿、飞猪"
|
||||
},
|
||||
"餐饮消费": {
|
||||
"platforms": ["dianping", "xiaohongshu", "大众点评", "ele.me", "meituan"],
|
||||
"description": "大众点评、美团、饿了么、小红书"
|
||||
}
|
||||
}
|
||||
|
||||
# 所有行业名称
|
||||
ALL_INDUSTRIES = list(INDUSTRIES.keys())
|
||||
|
||||
|
||||
class IndustryMode(Enum):
|
||||
"""行业模式"""
|
||||
SINGLE = "single" # 单行业
|
||||
MULTI = "multi" # 多行业
|
||||
|
||||
|
||||
@dataclass
|
||||
class IndustryConfig:
|
||||
"""行业配置"""
|
||||
industries: List[str] # 选择的行业列表
|
||||
mode: IndustryMode = IndustryMode.SINGLE
|
||||
items_per_platform: int = 10 # 每个平台显示的条目数
|
||||
total_items: int = 50 # 总条目数限制
|
||||
include_description: bool = True # 是否包含行业描述
|
||||
|
||||
|
||||
class IndustryHot:
|
||||
"""行业热榜类"""
|
||||
|
||||
def __init__(self, api_client=None, formatter=None):
|
||||
"""
|
||||
初始化行业热榜
|
||||
|
||||
Args:
|
||||
api_client: API客户端实例(可选)
|
||||
formatter: 格式化器实例(可选)
|
||||
"""
|
||||
self.api_client = api_client
|
||||
self.formatter = formatter
|
||||
self._all_platforms = self._get_all_platforms()
|
||||
|
||||
def _get_all_platforms(self) -> List[str]:
|
||||
"""获取所有行业相关的平台"""
|
||||
platforms = set()
|
||||
for industry_info in INDUSTRIES.values():
|
||||
platforms.update(industry_info.get("platforms", []))
|
||||
return list(platforms)
|
||||
|
||||
async def fetch_all_hot_data(self, limit_per_platform: int = 10) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
一次性获取全部平台的热榜数据
|
||||
|
||||
Args:
|
||||
limit_per_platform: 每个平台获取的条目数
|
||||
|
||||
Returns:
|
||||
全部平台的热榜数据列表
|
||||
"""
|
||||
all_items = []
|
||||
|
||||
if self.api_client:
|
||||
for platform in self._all_platforms:
|
||||
try:
|
||||
items = await self.api_client.get_hot榜单(platform, limit=limit_per_platform)
|
||||
if items:
|
||||
for item in items:
|
||||
item["source_platform"] = platform
|
||||
all_items.extend(items)
|
||||
except Exception as e:
|
||||
print(f"Error fetching {platform}: {e}")
|
||||
continue
|
||||
|
||||
return all_items
|
||||
|
||||
async def get_industry_options(self) -> str:
|
||||
"""
|
||||
获取行业选择选项(AI引导话术)
|
||||
|
||||
Returns:
|
||||
格式化的行业选择文本
|
||||
"""
|
||||
options_text = "🏭 **请选择您关注的行业**\n\n"
|
||||
|
||||
for i, (industry, info) in enumerate(INDUSTRIES.items(), 1):
|
||||
platforms_count = len(info["platforms"])
|
||||
options_text += f"{i}. **{industry}**\n"
|
||||
options_text += f" 📋 包含 {platforms_count} 个平台\n"
|
||||
options_text += f" 🔗 {info['description']}\n\n"
|
||||
|
||||
options_text += "-" * 50 + "\n"
|
||||
options_text += "💡 您可以输入行业名称或数字编号,支持多选(如:1,3或汽车+金融)"
|
||||
return options_text
|
||||
|
||||
def parse_industries_from_input(self, user_input: str) -> List[str]:
|
||||
"""
|
||||
解析用户输入的行业
|
||||
|
||||
Args:
|
||||
user_input: 用户输入的文本
|
||||
|
||||
Returns:
|
||||
匹配的行业列表
|
||||
"""
|
||||
user_input = user_input.strip().lower()
|
||||
matched_industries = []
|
||||
|
||||
# 处理数字选择
|
||||
import re
|
||||
number_pattern = re.findall(r'\d+', user_input)
|
||||
for num in number_pattern:
|
||||
idx = int(num) - 1
|
||||
if 0 <= idx < len(ALL_INDUSTRIES):
|
||||
matched_industries.append(ALL_INDUSTRIES[idx])
|
||||
|
||||
# 行业关键词映射(行业名称 -> 包含的关键词)
|
||||
industry_keywords = {
|
||||
"科技互联网": ["科技", "互联网", "IT", "技术"],
|
||||
"游戏行业": ["游戏", "手游", "网游"],
|
||||
"汽车行业": ["汽车", "车", "车企", "新能源车"],
|
||||
"金融财经": ["金融", "财经", "投资", "理财", "股票"],
|
||||
"数码消费": ["数码", "手机", "电脑", "电子"],
|
||||
"娱乐影视": ["娱乐", "影视", "电影", "综艺", "明星"],
|
||||
"房产家居": ["房产", "房", "家居", "装修", "买房"],
|
||||
"医疗健康": ["医疗", "健康", "医药", "养生"],
|
||||
"旅游出行": ["旅游", "出行", "旅行", "机票", "酒店"],
|
||||
"餐饮消费": ["餐饮", "美食", "外卖", "餐厅", "消费"]
|
||||
}
|
||||
|
||||
# 处理行业关键词
|
||||
for industry, keywords in industry_keywords.items():
|
||||
for keyword in keywords:
|
||||
if keyword in user_input:
|
||||
if industry not in matched_industries:
|
||||
matched_industries.append(industry)
|
||||
break
|
||||
|
||||
# 处理完整行业名称(向后兼容)
|
||||
for industry in ALL_INDUSTRIES:
|
||||
if industry.lower() in user_input or industry in user_input:
|
||||
if industry not in matched_industries:
|
||||
matched_industries.append(industry)
|
||||
|
||||
return matched_industries if matched_industries else []
|
||||
|
||||
async def get_industry_hot(self, industries: List[str], config: Optional[IndustryConfig] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
获取行业热榜(新版:先全部获取,再按行业筛选)
|
||||
|
||||
Args:
|
||||
industries: 行业列表
|
||||
config: 配置对象(可选)
|
||||
|
||||
Returns:
|
||||
行业热榜数据
|
||||
"""
|
||||
if config is None:
|
||||
config = IndustryConfig(industries=industries)
|
||||
|
||||
# 步骤1:全部获取
|
||||
all_items = await self.fetch_all_hot_data(limit_per_platform=config.items_per_platform)
|
||||
|
||||
# 步骤2:按行业筛选
|
||||
# 获取所有相关平台
|
||||
target_platforms = set()
|
||||
for industry in industries:
|
||||
if industry in INDUSTRIES:
|
||||
target_platforms.update(INDUSTRIES[industry]["platforms"])
|
||||
|
||||
filtered_items = []
|
||||
for item in all_items:
|
||||
platform = item.get("source_platform", "")
|
||||
if platform in target_platforms:
|
||||
item["source_industry"] = next((ind for ind in industries if ind in INDUSTRIES and platform in INDUSTRIES[ind]["platforms"]), industries[0])
|
||||
filtered_items.append(item)
|
||||
|
||||
# 去重和排序
|
||||
merged_items = self._merge_items(filtered_items)
|
||||
|
||||
# 限制总数
|
||||
merged_items = merged_items[:config.total_items]
|
||||
|
||||
return {
|
||||
"industries": industries,
|
||||
"industry_descriptions": {ind: INDUSTRIES[ind]["description"] for ind in industries},
|
||||
"total_items": len(merged_items),
|
||||
"items": merged_items
|
||||
}
|
||||
|
||||
def _merge_items(self, items: List[Dict]) -> List[Dict]:
|
||||
"""
|
||||
合并和去重条目
|
||||
|
||||
Args:
|
||||
items: 条目列表
|
||||
|
||||
Returns:
|
||||
合并后的条目列表
|
||||
"""
|
||||
if not items:
|
||||
return []
|
||||
|
||||
# 按标题去重
|
||||
seen_titles = set()
|
||||
unique_items = []
|
||||
|
||||
for item in items:
|
||||
title = item.get("title", "").strip().lower()
|
||||
if title and title not in seen_titles:
|
||||
seen_titles.add(title)
|
||||
unique_items.append(item)
|
||||
|
||||
# 按热度排序
|
||||
unique_items.sort(key=lambda x: x.get("hot", 0) or x.get("score", 0), reverse=True)
|
||||
|
||||
return unique_items
|
||||
|
||||
def format_industry_response(self, hot_data: Dict[str, Any]) -> str:
|
||||
"""
|
||||
格式化行业热榜响应
|
||||
|
||||
Args:
|
||||
hot_data: 热榜数据
|
||||
|
||||
Returns:
|
||||
格式化的响应文本
|
||||
"""
|
||||
if not hot_data.get("items"):
|
||||
return "❌ 暂无行业热榜数据"
|
||||
|
||||
industries = hot_data["industries"]
|
||||
items = hot_data["items"]
|
||||
|
||||
response = f"🏭 **行业热榜 - {', '.join(industries)}**\n\n"
|
||||
|
||||
# 显示行业描述
|
||||
for industry in industries:
|
||||
desc = hot_data["industry_descriptions"].get(industry, "")
|
||||
response += f"📌 **{industry}**: {desc}\n"
|
||||
|
||||
response += "-" * 50 + "\n"
|
||||
response += f"共 {hot_data['total_items']} 条热榜\n\n"
|
||||
|
||||
# 按行业分组显示
|
||||
items_by_industry = {}
|
||||
for item in items:
|
||||
industry = item.get("source_industry", "其他")
|
||||
if industry not in items_by_industry:
|
||||
items_by_industry[industry] = []
|
||||
items_by_industry[industry].append(item)
|
||||
|
||||
for industry, ind_items in items_by_industry.items():
|
||||
response += f"\n📊 **{industry}**\n"
|
||||
for i, item in enumerate(ind_items[:5], 1): # 每个行业显示5条
|
||||
title = item.get("title", "无标题")
|
||||
hot = item.get("hot", item.get("score", ""))
|
||||
platform = item.get("source_platform", "")
|
||||
|
||||
response += f"{i}. {title}\n"
|
||||
if hot:
|
||||
response += f" 🔥 {hot}"
|
||||
if platform:
|
||||
response += f" | 📱 {platform}"
|
||||
response += "\n"
|
||||
|
||||
return response
|
||||
|
||||
async def process_user_request(self, user_input: str) -> Dict[str, Any]:
|
||||
"""
|
||||
处理用户请求
|
||||
|
||||
Args:
|
||||
user_input: 用户输入
|
||||
|
||||
Returns:
|
||||
处理结果
|
||||
"""
|
||||
# 解析行业
|
||||
industries = self.parse_industries_from_input(user_input)
|
||||
|
||||
if not industries:
|
||||
# 返回引导信息
|
||||
return {
|
||||
"action": "ask_industry",
|
||||
"message": await self.get_industry_options()
|
||||
}
|
||||
|
||||
# 获取热榜
|
||||
hot_data = await self.get_industry_hot(industries)
|
||||
|
||||
# 格式化响应
|
||||
response_text = self.format_industry_response(hot_data)
|
||||
|
||||
return {
|
||||
"action": "show_industry_hot",
|
||||
"data": hot_data,
|
||||
"message": response_text
|
||||
}
|
||||
|
||||
|
||||
# 便捷函数
|
||||
async def create_industry_hot(api_client=None, formatter=None) -> IndustryHot:
|
||||
"""创建行业热榜实例"""
|
||||
return IndustryHot(api_client=api_client, formatter=formatter)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 测试代码
|
||||
async def test():
|
||||
industry_hot = await create_industry_hot()
|
||||
print(await industry_hot.get_industry_options())
|
||||
|
||||
print("\n" + "="*50 + "\n")
|
||||
|
||||
result = await industry_hot.process_user_request("汽车和金融")
|
||||
print(result["message"])
|
||||
|
||||
asyncio.run(test())
|
||||
Reference in New Issue
Block a user