mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-24 08:03:53 +08:00
Merge branch 'main' into mcp-client
# Conflicts: # main/xiaozhi-server/core/connection.py # main/xiaozhi-server/requirements.txt
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
import random
|
||||
import requests
|
||||
import xml.etree.ElementTree as ET
|
||||
from bs4 import BeautifulSoup
|
||||
from config.logger import setup_logging
|
||||
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
GET_NEWS_FUNCTION_DESC = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_news",
|
||||
"description": (
|
||||
"获取最新新闻,随机选择一条新闻进行播报。"
|
||||
"用户可以指定新闻类型,如社会新闻、科技新闻、国际新闻等。"
|
||||
"如果没有指定,默认播报社会新闻。"
|
||||
"用户可以要求获取详细内容,此时会获取新闻的详细内容。"
|
||||
),
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "新闻类别,例如社会、科技、国际。可选参数,如果不提供则使用默认类别"
|
||||
},
|
||||
"detail": {
|
||||
"type": "boolean",
|
||||
"description": "是否获取详细内容,默认为false。如果为true,则获取上一条新闻的详细内容"
|
||||
},
|
||||
"lang": {
|
||||
"type": "string",
|
||||
"description": "返回用户使用的语言code,例如zh_CN/zh_HK/en_US/ja_JP等,默认zh_CN"
|
||||
}
|
||||
},
|
||||
"required": ["lang"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def fetch_news_from_rss(rss_url):
|
||||
"""从RSS源获取新闻列表"""
|
||||
try:
|
||||
response = requests.get(rss_url)
|
||||
response.raise_for_status()
|
||||
|
||||
# 解析XML
|
||||
root = ET.fromstring(response.content)
|
||||
|
||||
# 查找所有item元素(新闻条目)
|
||||
news_items = []
|
||||
for item in root.findall('.//item'):
|
||||
title = item.find('title').text if item.find('title') is not None else "无标题"
|
||||
link = item.find('link').text if item.find('link') is not None else "#"
|
||||
description = item.find('description').text if item.find('description') is not None else "无描述"
|
||||
pubDate = item.find('pubDate').text if item.find('pubDate') is not None else "未知时间"
|
||||
|
||||
news_items.append({
|
||||
'title': title,
|
||||
'link': link,
|
||||
'description': description,
|
||||
'pubDate': pubDate
|
||||
})
|
||||
|
||||
return news_items
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"获取RSS新闻失败: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def fetch_news_detail(url):
|
||||
"""获取新闻详情页内容并总结"""
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
|
||||
soup = BeautifulSoup(response.content, 'html.parser')
|
||||
|
||||
# 尝试提取正文内容 (这里的选择器需要根据实际网站结构调整)
|
||||
content_div = soup.select_one('.content_desc, .content, article, .article-content')
|
||||
if content_div:
|
||||
paragraphs = content_div.find_all('p')
|
||||
content = '\n'.join([p.get_text().strip() for p in paragraphs if p.get_text().strip()])
|
||||
return content
|
||||
else:
|
||||
# 如果找不到特定的内容区域,尝试获取所有段落
|
||||
paragraphs = soup.find_all('p')
|
||||
content = '\n'.join([p.get_text().strip() for p in paragraphs if p.get_text().strip()])
|
||||
return content[:2000] # 限制长度
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"获取新闻详情失败: {e}")
|
||||
return "无法获取详细内容"
|
||||
|
||||
|
||||
def map_category(category_text):
|
||||
"""将用户输入的中文类别映射到配置文件中的类别键"""
|
||||
if not category_text:
|
||||
return None
|
||||
|
||||
# 类别映射字典,目前支持社会、国际、财经新闻,如需更多类型,参见配置文件
|
||||
category_map = {
|
||||
# 社会新闻
|
||||
"社会": "society",
|
||||
"社会新闻": "society",
|
||||
# 国际新闻
|
||||
"国际": "world",
|
||||
"国际新闻": "world",
|
||||
# 财经新闻
|
||||
"财经": "finance",
|
||||
"财经新闻": "finance",
|
||||
"金融": "finance",
|
||||
"经济": "finance"
|
||||
}
|
||||
|
||||
# 转换为小写并去除空格
|
||||
normalized_category = category_text.lower().strip()
|
||||
|
||||
# 返回映射结果,如果没有匹配项则返回原始输入
|
||||
return category_map.get(normalized_category, category_text)
|
||||
|
||||
|
||||
@register_function('get_news', GET_NEWS_FUNCTION_DESC, ToolType.SYSTEM_CTL)
|
||||
def get_news(conn, category: str = None, detail: bool = False, lang: str = "zh_CN"):
|
||||
"""获取新闻并随机选择一条进行播报,或获取上一条新闻的详细内容"""
|
||||
try:
|
||||
# 如果detail为True,获取上一条新闻的详细内容
|
||||
if detail:
|
||||
if not hasattr(conn, 'last_news_link') or not conn.last_news_link or 'link' not in conn.last_news_link:
|
||||
return ActionResponse(Action.REQLLM, "抱歉,没有找到最近查询的新闻,请先获取一条新闻。", None)
|
||||
|
||||
link = conn.last_news_link.get('link')
|
||||
title = conn.last_news_link.get('title', '未知标题')
|
||||
|
||||
if link == '#':
|
||||
return ActionResponse(Action.REQLLM, "抱歉,该新闻没有可用的链接获取详细内容。", None)
|
||||
|
||||
logger.bind(tag=TAG).debug(f"获取新闻详情: {title}, URL={link}")
|
||||
|
||||
# 获取新闻详情
|
||||
detail_content = fetch_news_detail(link)
|
||||
|
||||
if not detail_content or detail_content == "无法获取详细内容":
|
||||
return ActionResponse(Action.REQLLM, f"抱歉,无法获取《{title}》的详细内容,可能是链接已失效或网站结构发生变化。", None)
|
||||
|
||||
# 构建详情报告
|
||||
detail_report = (
|
||||
f"根据下列数据,用{lang}回应用户的新闻详情查询请求:\n\n"
|
||||
f"新闻标题: {title}\n"
|
||||
f"详细内容: {detail_content}\n\n"
|
||||
f"(请对上述新闻内容进行总结,提取关键信息,以自然、流畅的方式向用户播报,"
|
||||
f"不要提及这是总结,就像是在讲述一个完整的新闻故事)"
|
||||
)
|
||||
|
||||
return ActionResponse(Action.REQLLM, detail_report, None)
|
||||
|
||||
# 否则,获取新闻列表并随机选择一条
|
||||
# 从配置中获取RSS URL
|
||||
rss_config = conn.config["plugins"]["get_news"]
|
||||
default_rss_url = rss_config.get("default_rss_url", "https://www.chinanews.com.cn/rss/society.xml")
|
||||
|
||||
# 将用户输入的类别映射到配置中的类别键
|
||||
mapped_category = map_category(category)
|
||||
|
||||
# 如果提供了类别,尝试从配置中获取对应的URL
|
||||
rss_url = default_rss_url
|
||||
if mapped_category and mapped_category in rss_config.get("category_urls", {}):
|
||||
rss_url = rss_config["category_urls"][mapped_category]
|
||||
|
||||
logger.bind(tag=TAG).info(f"获取新闻: 原始类别={category}, 映射类别={mapped_category}, URL={rss_url}")
|
||||
|
||||
# 获取新闻列表
|
||||
news_items = fetch_news_from_rss(rss_url)
|
||||
|
||||
if not news_items:
|
||||
return ActionResponse(Action.REQLLM, "抱歉,未能获取到新闻信息,请稍后再试。", None)
|
||||
|
||||
# 随机选择一条新闻
|
||||
selected_news = random.choice(news_items)
|
||||
|
||||
# 保存当前新闻链接到连接对象,以便后续查询详情
|
||||
if not hasattr(conn, 'last_news_link'):
|
||||
conn.last_news_link = {}
|
||||
conn.last_news_link = {
|
||||
'link': selected_news.get('link', '#'),
|
||||
'title': selected_news.get('title', '未知标题')
|
||||
}
|
||||
|
||||
# 构建新闻报告
|
||||
news_report = (
|
||||
f"根据下列数据,用{lang}回应用户的新闻查询请求:\n\n"
|
||||
f"新闻标题: {selected_news['title']}\n"
|
||||
f"发布时间: {selected_news['pubDate']}\n"
|
||||
f"新闻内容: {selected_news['description']}\n"
|
||||
f"(请以自然、流畅的方式向用户播报这条新闻,可以适当总结内容,"
|
||||
f"直接读出新闻即可,不需要额外多余的内容。"
|
||||
f"如果用户询问更多详情,告知用户可以说'请详细介绍这条新闻'获取更多内容)"
|
||||
)
|
||||
|
||||
return ActionResponse(Action.REQLLM, news_report, None)
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"获取新闻出错: {e}")
|
||||
return ActionResponse(Action.REQLLM, "抱歉,获取新闻时发生错误,请稍后再试。", None)
|
||||
@@ -0,0 +1,56 @@
|
||||
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
||||
from plugins_func.functions.hass_init import initialize_hass_handler
|
||||
from config.logger import setup_logging
|
||||
import asyncio
|
||||
import requests
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
hass_get_state_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "hass_get_state",
|
||||
"description": "获取homeassistant里设备的状态,包括灯光亮度,媒体播放器的音量,设备的暂停、继续操作",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity_id": {
|
||||
"type": "string",
|
||||
"description": "需要操作的设备id,homeassistant里的entity_id"
|
||||
}
|
||||
},
|
||||
"required": ["entity_id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL)
|
||||
def hass_get_state(conn, entity_id=''):
|
||||
try:
|
||||
|
||||
future = asyncio.run_coroutine_threadsafe(
|
||||
handle_hass_get_state(conn, entity_id),
|
||||
conn.loop
|
||||
)
|
||||
ha_response = future.result()
|
||||
return ActionResponse(action=Action.REQLLM, result="执行成功", response=ha_response)
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
|
||||
|
||||
|
||||
async def handle_hass_get_state(conn, entity_id):
|
||||
HASS_CACHE = initialize_hass_handler(conn)
|
||||
api_key = HASS_CACHE['api_key']
|
||||
base_url = HASS_CACHE['base_url']
|
||||
url = f"{base_url}/api/states/{entity_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
response = requests.get(url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
return response.json()['state']
|
||||
else:
|
||||
return f"切换失败,错误码: {response.status_code}"
|
||||
@@ -0,0 +1,35 @@
|
||||
from config.logger import setup_logging
|
||||
from core.utils.util import check_model_key
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
HASS_CACHE = {}
|
||||
|
||||
|
||||
def append_devices_to_prompt(conn):
|
||||
if conn.use_function_call_mode:
|
||||
funcs = conn.config["Intent"]["function_call"].get("functions", [])
|
||||
if "hass_get_state" in funcs or "hass_get_state" in funcs:
|
||||
prompt = "下面是我家智能设备,可以通过homeassistant控制\n"
|
||||
devices = conn.config["plugins"]["home_assistant"].get("devices", [])
|
||||
if len(devices) == 0:
|
||||
return
|
||||
for device in devices:
|
||||
prompt += device + "\n"
|
||||
conn.prompt += prompt
|
||||
# 更新提示词
|
||||
conn.dialogue.update_system_message(conn.prompt)
|
||||
|
||||
|
||||
def initialize_hass_handler(conn):
|
||||
global HASS_CACHE
|
||||
if HASS_CACHE == {}:
|
||||
if conn.use_function_call_mode:
|
||||
funcs = conn.config["Intent"]["function_call"].get("functions", [])
|
||||
if "hass_get_state" in funcs or "hass_get_state" in funcs:
|
||||
HASS_CACHE['base_url'] = conn.config["plugins"]["home_assistant"].get("base_url")
|
||||
HASS_CACHE['api_key'] = conn.config["plugins"]["home_assistant"].get("api_key")
|
||||
|
||||
check_model_key("home_assistant", HASS_CACHE['api_key'])
|
||||
return HASS_CACHE
|
||||
@@ -0,0 +1,64 @@
|
||||
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
||||
from plugins_func.functions.hass_init import initialize_hass_handler
|
||||
from config.logger import setup_logging
|
||||
import asyncio
|
||||
import requests
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
hass_play_music_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "hass_play_music",
|
||||
"description": "用户想听音乐、有声书的时候使用,在房间的媒体播放器(media_player)里播放对应音频",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"media_content_id": {
|
||||
"type": "string",
|
||||
"description": "可以是音乐或有声书的专辑名称、歌曲名、演唱者,如果未指定就填random"
|
||||
},
|
||||
"entity_id": {
|
||||
"type": "string",
|
||||
"description": "需要操作的音箱的设备id,homeassistant里的entity_id,media_player开头"
|
||||
}
|
||||
},
|
||||
"required": ["media_content_id", "entity_id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@register_function('hass_play_music', hass_play_music_function_desc, ToolType.SYSTEM_CTL)
|
||||
def hass_play_music(conn, entity_id='', media_content_id='random'):
|
||||
try:
|
||||
# 执行音乐播放命令
|
||||
future = asyncio.run_coroutine_threadsafe(
|
||||
handle_hass_play_music(conn, entity_id, media_content_id),
|
||||
conn.loop
|
||||
)
|
||||
ha_response = future.result()
|
||||
return ActionResponse(action=Action.RESPONSE, result="退出意图已处理", response=ha_response)
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}")
|
||||
|
||||
|
||||
async def handle_hass_play_music(conn, entity_id, media_content_id):
|
||||
HASS_CACHE = initialize_hass_handler(conn)
|
||||
api_key = HASS_CACHE['api_key']
|
||||
base_url = HASS_CACHE['base_url']
|
||||
url = f"{base_url}/api/services/music_assistant/play_media"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
data = {
|
||||
"entity_id": entity_id,
|
||||
"media_id": media_content_id
|
||||
}
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if response.status_code == 200:
|
||||
return f"正在播放{media_content_id}的音乐"
|
||||
else:
|
||||
return f"音乐播放失败,错误码: {response.status_code}"
|
||||
@@ -0,0 +1,159 @@
|
||||
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
||||
from plugins_func.functions.hass_init import initialize_hass_handler
|
||||
from config.logger import setup_logging
|
||||
import asyncio
|
||||
import requests
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
hass_set_state_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "hass_set_state",
|
||||
"description": "设置homeassistant里设备的状态,包括开、关,调整灯光亮度,调整播放器的音量,设备的暂停、继续、静音操作",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"state": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "需要操作的动作,打开设备:turn_on,关闭设备:turn_off,增加亮度:brightness_up,降低亮度:brightness_down,设置亮度:brightness_value,增加>音量:,volume_up降低音量:volume_down,设置音量:volume_set,设备暂停:pause,设备继续:continue,静音/取消静音:volume_mute"
|
||||
},
|
||||
"input": {
|
||||
"type": "int",
|
||||
"description": "只有在设置音量,设置亮度时候才需要,有效值为1-100,对应音量和亮度的1%-100%"
|
||||
},
|
||||
"is_muted": {
|
||||
"type": "string",
|
||||
"description": "只有在设置静音操作时才需要,设置静音的时候该值为true,取消静音时该值为false"
|
||||
}
|
||||
},
|
||||
"required": ["type"]
|
||||
},
|
||||
"entity_id": {
|
||||
"type": "string",
|
||||
"description": "需要操作的设备id,homeassistant里的entity_id"
|
||||
}
|
||||
},
|
||||
"required": ["state", "entity_id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@register_function('hass_set_state', hass_set_state_function_desc, ToolType.SYSTEM_CTL)
|
||||
def hass_set_state(conn, entity_id='', state={}):
|
||||
try:
|
||||
future = asyncio.run_coroutine_threadsafe(
|
||||
handle_hass_set_state(conn, entity_id, state),
|
||||
conn.loop
|
||||
)
|
||||
ha_response = future.result()
|
||||
return ActionResponse(action=Action.REQLLM, result="执行成功", response=ha_response)
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
|
||||
|
||||
|
||||
async def handle_hass_set_state(conn, entity_id, state):
|
||||
HASS_CACHE = initialize_hass_handler(conn)
|
||||
api_key = HASS_CACHE['api_key']
|
||||
base_url = HASS_CACHE['base_url']
|
||||
'''
|
||||
state = { "type":"brightness_up","input":"80","is_muted":"true"}
|
||||
'''
|
||||
domains = entity_id.split(".")
|
||||
if len(domains) > 1:
|
||||
domain = domains[0]
|
||||
else:
|
||||
return "执行失败,错误的设备id"
|
||||
action = ''
|
||||
arg = ''
|
||||
value = ''
|
||||
if state['type'] == 'turn_on':
|
||||
description = "设备已打开"
|
||||
if domain == "cover":
|
||||
action = "open_cover"
|
||||
elif domain == "vacuum":
|
||||
action = "start"
|
||||
else:
|
||||
action = "turn_on"
|
||||
elif state['type'] == 'turn_off':
|
||||
description = "设备已关闭"
|
||||
if domain == 'cover':
|
||||
action = "close_cover"
|
||||
elif domain == 'vacuum':
|
||||
action = "stop"
|
||||
else:
|
||||
action = "turn_off"
|
||||
elif state['type'] == 'brightness_up':
|
||||
description = "灯光已调亮"
|
||||
action = 'turn_on'
|
||||
arg = 'brightness_step_pct'
|
||||
value = 10
|
||||
elif state['type'] == 'brightness_down':
|
||||
description = "灯光已调暗"
|
||||
action = 'turn_on'
|
||||
arg = 'brightness_step_pct'
|
||||
value = -10
|
||||
elif state['type'] == 'brightness_value':
|
||||
description = f"亮度已调整到{state['input']}"
|
||||
action = 'turn_on'
|
||||
arg = 'brightness_pct'
|
||||
value = state['input']
|
||||
elif state['type'] == 'volume_up':
|
||||
description = "音量已调大"
|
||||
action = state['type']
|
||||
elif state['type'] == 'volume_down':
|
||||
description = "音量已调小"
|
||||
action = state['type']
|
||||
elif state['type'] == 'volume_set':
|
||||
description = f"音量已调整到{state['input']}"
|
||||
action = state['type']
|
||||
arg = 'volume_level'
|
||||
value = state['input']
|
||||
elif state['type'] == 'volume_mute':
|
||||
description = f"设备已静音"
|
||||
action = state['type']
|
||||
arg = 'is_volume_muted'
|
||||
value = state['is_muted']
|
||||
elif state['type'] == 'pause':
|
||||
description = f"设备已暂停"
|
||||
action = state['type']
|
||||
if domain == 'media_player':
|
||||
action = 'media_pause'
|
||||
if domain == 'cover':
|
||||
action = 'stop_cover'
|
||||
if domain == 'vacuum':
|
||||
action = 'pause'
|
||||
elif state['type'] == 'continue':
|
||||
description = f"设备已继续"
|
||||
if domain == 'media_player':
|
||||
action = 'media_play'
|
||||
if domain == 'vacuum':
|
||||
action = 'start'
|
||||
else:
|
||||
return f"{domain} {state.type}功能尚未支持"
|
||||
|
||||
if arg == '':
|
||||
data = {
|
||||
"entity_id": entity_id,
|
||||
}
|
||||
else:
|
||||
data = {
|
||||
"entity_id": entity_id,
|
||||
arg: value
|
||||
}
|
||||
url = f"{base_url}/api/services/{domain}/{action}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
logger.bind(tag=TAG).info(f"设置状态:url:{url},return_code:{response.status_code}")
|
||||
if response.status_code == 200:
|
||||
return description
|
||||
else:
|
||||
return f"设置失败,错误码: {response.status_code}"
|
||||
@@ -21,13 +21,13 @@ play_music_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "play_music",
|
||||
"description": "唱歌、听歌、播放音乐方法。比如用户说播放音乐,参数为:random,比如用户说播放两只老虎,参数为:两只老虎",
|
||||
"description": "唱歌、听歌、播放音乐的方法。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"song_name": {
|
||||
"type": "string",
|
||||
"description": "歌曲名称,如果没有指定具体歌名则为'random'"
|
||||
"description": "歌曲名称,如果用户没有指定具体歌名则为'random', 明确指定的时返回音乐的名字 示例: ```用户:播放两只老虎\n参数:两只老虎``` ```用户:播放音乐 \n参数:random ```"
|
||||
}
|
||||
},
|
||||
"required": ["song_name"]
|
||||
@@ -112,8 +112,8 @@ def get_music_files(music_dir, music_ext):
|
||||
def initialize_music_handler(conn):
|
||||
global MUSIC_CACHE
|
||||
if MUSIC_CACHE == {}:
|
||||
if "music" in conn.config:
|
||||
MUSIC_CACHE["music_config"] = conn.config["music"]
|
||||
if "play_music" in conn.config["plugins"]:
|
||||
MUSIC_CACHE["music_config"] = conn.config["plugins"]["play_music"]
|
||||
MUSIC_CACHE["music_dir"] = os.path.abspath(
|
||||
MUSIC_CACHE["music_config"].get("music_dir", "./music") # 默认路径修改
|
||||
)
|
||||
|
||||
@@ -22,6 +22,4 @@ def auto_import_modules(package_name):
|
||||
# 导入模块
|
||||
full_module_name = f"{package_name}.{module_name}"
|
||||
importlib.import_module(full_module_name)
|
||||
#logger.bind(tag=TAG).info(f"模块 '{full_module_name}' 已加载")
|
||||
|
||||
auto_import_modules('plugins_func.functions')
|
||||
#logger.bind(tag=TAG).info(f"模块 '{full_module_name}' 已加载")
|
||||
Reference in New Issue
Block a user