AstrBot 3.5.6__py3-none-any.whl → 4.7.0__py3-none-any.whl
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.
- astrbot/api/__init__.py +16 -4
- astrbot/api/all.py +2 -1
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -34
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +8 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__init__.py +1 -0
- astrbot/cli/__main__.py +18 -197
- astrbot/cli/commands/__init__.py +6 -0
- astrbot/cli/commands/cmd_conf.py +209 -0
- astrbot/cli/commands/cmd_init.py +56 -0
- astrbot/cli/commands/cmd_plug.py +245 -0
- astrbot/cli/commands/cmd_run.py +62 -0
- astrbot/cli/utils/__init__.py +18 -0
- astrbot/cli/utils/basic.py +76 -0
- astrbot/cli/utils/plugin.py +246 -0
- astrbot/cli/utils/version_comparator.py +90 -0
- astrbot/core/__init__.py +17 -19
- astrbot/core/agent/agent.py +14 -0
- astrbot/core/agent/handoff.py +38 -0
- astrbot/core/agent/hooks.py +30 -0
- astrbot/core/agent/mcp_client.py +385 -0
- astrbot/core/agent/message.py +175 -0
- astrbot/core/agent/response.py +14 -0
- astrbot/core/agent/run_context.py +22 -0
- astrbot/core/agent/runners/__init__.py +3 -0
- astrbot/core/agent/runners/base.py +65 -0
- astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
- astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
- astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
- astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
- astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
- astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
- astrbot/core/agent/tool.py +285 -0
- astrbot/core/agent/tool_executor.py +17 -0
- astrbot/core/astr_agent_context.py +19 -0
- astrbot/core/astr_agent_hooks.py +36 -0
- astrbot/core/astr_agent_run_util.py +80 -0
- astrbot/core/astr_agent_tool_exec.py +246 -0
- astrbot/core/astrbot_config_mgr.py +275 -0
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +60 -20
- astrbot/core/config/default.py +1972 -453
- astrbot/core/config/i18n_utils.py +110 -0
- astrbot/core/conversation_mgr.py +285 -75
- astrbot/core/core_lifecycle.py +167 -62
- astrbot/core/db/__init__.py +305 -102
- astrbot/core/db/migration/helper.py +69 -0
- astrbot/core/db/migration/migra_3_to_4.py +357 -0
- astrbot/core/db/migration/migra_45_to_46.py +44 -0
- astrbot/core/db/migration/migra_webchat_session.py +131 -0
- astrbot/core/db/migration/shared_preferences_v3.py +48 -0
- astrbot/core/db/migration/sqlite_v3.py +497 -0
- astrbot/core/db/po.py +259 -55
- astrbot/core/db/sqlite.py +773 -528
- astrbot/core/db/vec_db/base.py +73 -0
- astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
- astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
- astrbot/core/event_bus.py +26 -22
- astrbot/core/exceptions.py +9 -0
- astrbot/core/file_token_service.py +98 -0
- astrbot/core/initial_loader.py +19 -10
- astrbot/core/knowledge_base/chunking/__init__.py +9 -0
- astrbot/core/knowledge_base/chunking/base.py +25 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
- astrbot/core/knowledge_base/chunking/recursive.py +161 -0
- astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
- astrbot/core/knowledge_base/kb_helper.py +642 -0
- astrbot/core/knowledge_base/kb_mgr.py +330 -0
- astrbot/core/knowledge_base/models.py +120 -0
- astrbot/core/knowledge_base/parsers/__init__.py +13 -0
- astrbot/core/knowledge_base/parsers/base.py +51 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
- astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
- astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
- astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
- astrbot/core/knowledge_base/parsers/util.py +13 -0
- astrbot/core/knowledge_base/prompts.py +65 -0
- astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
- astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
- astrbot/core/knowledge_base/retrieval/manager.py +276 -0
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
- astrbot/core/log.py +21 -15
- astrbot/core/message/components.py +413 -287
- astrbot/core/message/message_event_result.py +35 -24
- astrbot/core/persona_mgr.py +192 -0
- astrbot/core/pipeline/__init__.py +14 -14
- astrbot/core/pipeline/content_safety_check/stage.py +13 -9
- astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +13 -14
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +7 -1
- astrbot/core/pipeline/context_utils.py +107 -0
- astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
- astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
- astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
- astrbot/core/pipeline/process_stage/stage.py +21 -15
- astrbot/core/pipeline/process_stage/utils.py +125 -0
- astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
- astrbot/core/pipeline/respond/stage.py +142 -101
- astrbot/core/pipeline/result_decorate/stage.py +124 -57
- astrbot/core/pipeline/scheduler.py +21 -16
- astrbot/core/pipeline/session_status_check/stage.py +37 -0
- astrbot/core/pipeline/stage.py +11 -76
- astrbot/core/pipeline/waking_check/stage.py +69 -33
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +107 -129
- astrbot/core/platform/astrbot_message.py +32 -12
- astrbot/core/platform/manager.py +62 -18
- astrbot/core/platform/message_session.py +30 -0
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +9 -4
- astrbot/core/platform/register.py +12 -7
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
- astrbot/core/platform/sources/discord/client.py +129 -0
- astrbot/core/platform/sources/discord/components.py +139 -0
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
- astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
- astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
- astrbot/core/platform/sources/lark/lark_event.py +39 -13
- astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
- astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
- astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
- astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +14 -8
- astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
- astrbot/core/platform/sources/satori/satori_event.py +432 -0
- astrbot/core/platform/sources/slack/client.py +164 -0
- astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
- astrbot/core/platform/sources/slack/slack_event.py +253 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
- astrbot/core/platform/sources/telegram/tg_event.py +136 -36
- astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
- astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
- astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
- astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
- astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
- astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
- astrbot/core/platform_message_history_mgr.py +49 -0
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +154 -98
- astrbot/core/provider/func_tool_manager.py +446 -458
- astrbot/core/provider/manager.py +345 -207
- astrbot/core/provider/provider.py +188 -73
- astrbot/core/provider/register.py +9 -7
- astrbot/core/provider/sources/anthropic_source.py +295 -115
- astrbot/core/provider/sources/azure_tts_source.py +224 -0
- astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
- astrbot/core/provider/sources/dashscope_tts.py +138 -14
- astrbot/core/provider/sources/edge_tts_source.py +24 -19
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
- astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
- astrbot/core/provider/sources/gemini_source.py +310 -132
- astrbot/core/provider/sources/gemini_tts_source.py +81 -0
- astrbot/core/provider/sources/groq_source.py +15 -0
- astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
- astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
- astrbot/core/provider/sources/openai_embedding_source.py +40 -0
- astrbot/core/provider/sources/openai_source.py +241 -145
- astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
- astrbot/core/provider/sources/volcengine_tts.py +115 -0
- astrbot/core/provider/sources/whisper_api_source.py +18 -13
- astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
- astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
- astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
- astrbot/core/provider/sources/zhipu_source.py +6 -73
- astrbot/core/star/__init__.py +43 -11
- astrbot/core/star/config.py +17 -18
- astrbot/core/star/context.py +362 -138
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +111 -35
- astrbot/core/star/filter/command_group.py +46 -34
- astrbot/core/star/filter/custom_filter.py +6 -5
- astrbot/core/star/filter/event_message_type.py +4 -2
- astrbot/core/star/filter/permission.py +4 -2
- astrbot/core/star/filter/platform_adapter_type.py +45 -12
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -15
- astrbot/core/star/register/star.py +41 -13
- astrbot/core/star/register/star_handler.py +236 -86
- astrbot/core/star/session_llm_manager.py +280 -0
- astrbot/core/star/session_plugin_manager.py +170 -0
- astrbot/core/star/star.py +36 -43
- astrbot/core/star/star_handler.py +47 -85
- astrbot/core/star/star_manager.py +442 -260
- astrbot/core/star/star_tools.py +167 -45
- astrbot/core/star/updator.py +17 -20
- astrbot/core/umop_config_router.py +106 -0
- astrbot/core/updator.py +38 -13
- astrbot/core/utils/astrbot_path.py +39 -0
- astrbot/core/utils/command_parser.py +1 -1
- astrbot/core/utils/io.py +119 -60
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +11 -10
- astrbot/core/utils/migra_helper.py +73 -0
- astrbot/core/utils/path_util.py +63 -62
- astrbot/core/utils/pip_installer.py +37 -15
- astrbot/core/utils/session_lock.py +29 -0
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +174 -34
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/local_strategy.py +386 -238
- astrbot/core/utils/t2i/network_strategy.py +109 -49
- astrbot/core/utils/t2i/renderer.py +29 -14
- astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
- astrbot/core/utils/t2i/template_manager.py +111 -0
- astrbot/core/utils/tencent_record_helper.py +115 -1
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +112 -65
- astrbot/dashboard/routes/__init__.py +20 -13
- astrbot/dashboard/routes/auth.py +20 -9
- astrbot/dashboard/routes/chat.py +297 -141
- astrbot/dashboard/routes/config.py +652 -55
- astrbot/dashboard/routes/conversation.py +107 -37
- astrbot/dashboard/routes/file.py +26 -0
- astrbot/dashboard/routes/knowledge_base.py +1244 -0
- astrbot/dashboard/routes/log.py +27 -2
- astrbot/dashboard/routes/persona.py +202 -0
- astrbot/dashboard/routes/plugin.py +197 -139
- astrbot/dashboard/routes/route.py +27 -7
- astrbot/dashboard/routes/session_management.py +354 -0
- astrbot/dashboard/routes/stat.py +85 -18
- astrbot/dashboard/routes/static_file.py +5 -2
- astrbot/dashboard/routes/t2i.py +233 -0
- astrbot/dashboard/routes/tools.py +184 -120
- astrbot/dashboard/routes/update.py +59 -36
- astrbot/dashboard/server.py +96 -36
- astrbot/dashboard/utils.py +165 -0
- astrbot-4.7.0.dist-info/METADATA +294 -0
- astrbot-4.7.0.dist-info/RECORD +274 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
- astrbot/core/db/plugin/sqlite_impl.py +0 -112
- astrbot/core/db/sqlite_init.sql +0 -50
- astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
- astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
- astrbot/core/platform/sources/gewechat/client.py +0 -806
- astrbot/core/platform/sources/gewechat/downloader.py +0 -55
- astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
- astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
- astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
- astrbot/core/provider/sources/dashscope_source.py +0 -203
- astrbot/core/provider/sources/dify_source.py +0 -281
- astrbot/core/provider/sources/llmtuner_source.py +0 -132
- astrbot/core/rag/embedding/openai_source.py +0 -20
- astrbot/core/rag/knowledge_db_mgr.py +0 -94
- astrbot/core/rag/store/__init__.py +0 -9
- astrbot/core/rag/store/chroma_db.py +0 -42
- astrbot/core/utils/dify_api_client.py +0 -152
- astrbot-3.5.6.dist-info/METADATA +0 -249
- astrbot-3.5.6.dist-info/RECORD +0 -158
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import text
|
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
|
+
|
|
7
|
+
from astrbot.api import logger, sp
|
|
8
|
+
from astrbot.core.config import AstrBotConfig
|
|
9
|
+
from astrbot.core.config.default import DB_PATH
|
|
10
|
+
from astrbot.core.db.po import ConversationV2, PlatformMessageHistory
|
|
11
|
+
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
12
|
+
|
|
13
|
+
from .. import BaseDatabase
|
|
14
|
+
from .shared_preferences_v3 import sp as sp_v3
|
|
15
|
+
from .sqlite_v3 import SQLiteDatabase as SQLiteV3DatabaseV3
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
1. 迁移旧的 webchat_conversation 表到新的 conversation 表。
|
|
19
|
+
2. 迁移旧的 platform 到新的 platform_stats 表。
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_platform_id(
|
|
24
|
+
platform_id_map: dict[str, dict[str, str]],
|
|
25
|
+
old_platform_name: str,
|
|
26
|
+
) -> str:
|
|
27
|
+
return platform_id_map.get(
|
|
28
|
+
old_platform_name,
|
|
29
|
+
{"platform_id": old_platform_name, "platform_type": old_platform_name},
|
|
30
|
+
).get("platform_id", old_platform_name)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_platform_type(
|
|
34
|
+
platform_id_map: dict[str, dict[str, str]],
|
|
35
|
+
old_platform_name: str,
|
|
36
|
+
) -> str:
|
|
37
|
+
return platform_id_map.get(
|
|
38
|
+
old_platform_name,
|
|
39
|
+
{"platform_id": old_platform_name, "platform_type": old_platform_name},
|
|
40
|
+
).get("platform_type", old_platform_name)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def migration_conversation_table(
|
|
44
|
+
db_helper: BaseDatabase,
|
|
45
|
+
platform_id_map: dict[str, dict[str, str]],
|
|
46
|
+
):
|
|
47
|
+
db_helper_v3 = SQLiteV3DatabaseV3(
|
|
48
|
+
db_path=DB_PATH.replace("data_v4.db", "data_v3.db"),
|
|
49
|
+
)
|
|
50
|
+
conversations, total_cnt = db_helper_v3.get_all_conversations(
|
|
51
|
+
page=1,
|
|
52
|
+
page_size=10000000,
|
|
53
|
+
)
|
|
54
|
+
logger.info(f"迁移 {total_cnt} 条旧的会话数据到新的表中...")
|
|
55
|
+
|
|
56
|
+
async with db_helper.get_db() as dbsession:
|
|
57
|
+
dbsession: AsyncSession
|
|
58
|
+
async with dbsession.begin():
|
|
59
|
+
for idx, conversation in enumerate(conversations):
|
|
60
|
+
if total_cnt > 0 and (idx + 1) % max(1, total_cnt // 10) == 0:
|
|
61
|
+
progress = int((idx + 1) / total_cnt * 100)
|
|
62
|
+
if progress % 10 == 0:
|
|
63
|
+
logger.info(f"进度: {progress}% ({idx + 1}/{total_cnt})")
|
|
64
|
+
try:
|
|
65
|
+
conv = db_helper_v3.get_conversation_by_user_id(
|
|
66
|
+
user_id=conversation.get("user_id", "unknown"),
|
|
67
|
+
cid=conversation.get("cid", "unknown"),
|
|
68
|
+
)
|
|
69
|
+
if not conv:
|
|
70
|
+
logger.info(
|
|
71
|
+
f"未找到该条旧会话对应的具体数据: {conversation}, 跳过。",
|
|
72
|
+
)
|
|
73
|
+
if ":" not in conv.user_id:
|
|
74
|
+
continue
|
|
75
|
+
session = MessageSesion.from_str(session_str=conv.user_id)
|
|
76
|
+
platform_id = get_platform_id(
|
|
77
|
+
platform_id_map,
|
|
78
|
+
session.platform_name,
|
|
79
|
+
)
|
|
80
|
+
session.platform_id = platform_id # 更新平台名称为新的 ID
|
|
81
|
+
conv_v2 = ConversationV2(
|
|
82
|
+
user_id=str(session),
|
|
83
|
+
content=json.loads(conv.history) if conv.history else [],
|
|
84
|
+
platform_id=platform_id,
|
|
85
|
+
title=conv.title,
|
|
86
|
+
persona_id=conv.persona_id,
|
|
87
|
+
conversation_id=conv.cid,
|
|
88
|
+
created_at=datetime.datetime.fromtimestamp(conv.created_at),
|
|
89
|
+
updated_at=datetime.datetime.fromtimestamp(conv.updated_at),
|
|
90
|
+
)
|
|
91
|
+
dbsession.add(conv_v2)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
logger.error(
|
|
94
|
+
f"迁移旧会话 {conversation.get('cid', 'unknown')} 失败: {e}",
|
|
95
|
+
exc_info=True,
|
|
96
|
+
)
|
|
97
|
+
logger.info(f"成功迁移 {total_cnt} 条旧的会话数据到新表。")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def migration_platform_table(
|
|
101
|
+
db_helper: BaseDatabase,
|
|
102
|
+
platform_id_map: dict[str, dict[str, str]],
|
|
103
|
+
):
|
|
104
|
+
db_helper_v3 = SQLiteV3DatabaseV3(
|
|
105
|
+
db_path=DB_PATH.replace("data_v4.db", "data_v3.db"),
|
|
106
|
+
)
|
|
107
|
+
secs_from_2023_4_10_to_now = (
|
|
108
|
+
datetime.datetime.now(datetime.timezone.utc)
|
|
109
|
+
- datetime.datetime(2023, 4, 10, tzinfo=datetime.timezone.utc)
|
|
110
|
+
).total_seconds()
|
|
111
|
+
offset_sec = int(secs_from_2023_4_10_to_now)
|
|
112
|
+
logger.info(f"迁移旧平台数据,offset_sec: {offset_sec} 秒。")
|
|
113
|
+
stats = db_helper_v3.get_base_stats(offset_sec=offset_sec)
|
|
114
|
+
logger.info(f"迁移 {len(stats.platform)} 条旧的平台数据到新的表中...")
|
|
115
|
+
platform_stats_v3 = stats.platform
|
|
116
|
+
|
|
117
|
+
if not platform_stats_v3:
|
|
118
|
+
logger.info("没有找到旧平台数据,跳过迁移。")
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
first_time_stamp = platform_stats_v3[0].timestamp
|
|
122
|
+
end_time_stamp = platform_stats_v3[-1].timestamp
|
|
123
|
+
start_time = first_time_stamp - (first_time_stamp % 3600) # 向下取整到小时
|
|
124
|
+
end_time = end_time_stamp + (3600 - (end_time_stamp % 3600)) # 向上取整到小时
|
|
125
|
+
|
|
126
|
+
idx = 0
|
|
127
|
+
|
|
128
|
+
async with db_helper.get_db() as dbsession:
|
|
129
|
+
dbsession: AsyncSession
|
|
130
|
+
async with dbsession.begin():
|
|
131
|
+
total_buckets = (end_time - start_time) // 3600
|
|
132
|
+
for bucket_idx, bucket_end in enumerate(range(start_time, end_time, 3600)):
|
|
133
|
+
if bucket_idx % 500 == 0:
|
|
134
|
+
progress = int((bucket_idx + 1) / total_buckets * 100)
|
|
135
|
+
logger.info(f"进度: {progress}% ({bucket_idx + 1}/{total_buckets})")
|
|
136
|
+
cnt = 0
|
|
137
|
+
while (
|
|
138
|
+
idx < len(platform_stats_v3)
|
|
139
|
+
and platform_stats_v3[idx].timestamp < bucket_end
|
|
140
|
+
):
|
|
141
|
+
cnt += platform_stats_v3[idx].count
|
|
142
|
+
idx += 1
|
|
143
|
+
if cnt == 0:
|
|
144
|
+
continue
|
|
145
|
+
platform_id = get_platform_id(
|
|
146
|
+
platform_id_map,
|
|
147
|
+
platform_stats_v3[idx].name,
|
|
148
|
+
)
|
|
149
|
+
platform_type = get_platform_type(
|
|
150
|
+
platform_id_map,
|
|
151
|
+
platform_stats_v3[idx].name,
|
|
152
|
+
)
|
|
153
|
+
try:
|
|
154
|
+
await dbsession.execute(
|
|
155
|
+
text("""
|
|
156
|
+
INSERT INTO platform_stats (timestamp, platform_id, platform_type, count)
|
|
157
|
+
VALUES (:timestamp, :platform_id, :platform_type, :count)
|
|
158
|
+
ON CONFLICT(timestamp, platform_id, platform_type) DO UPDATE SET
|
|
159
|
+
count = platform_stats.count + EXCLUDED.count
|
|
160
|
+
"""),
|
|
161
|
+
{
|
|
162
|
+
"timestamp": datetime.datetime.fromtimestamp(
|
|
163
|
+
bucket_end,
|
|
164
|
+
tz=datetime.timezone.utc,
|
|
165
|
+
),
|
|
166
|
+
"platform_id": platform_id,
|
|
167
|
+
"platform_type": platform_type,
|
|
168
|
+
"count": cnt,
|
|
169
|
+
},
|
|
170
|
+
)
|
|
171
|
+
except Exception:
|
|
172
|
+
logger.error(
|
|
173
|
+
f"迁移平台统计数据失败: {platform_id}, {platform_type}, 时间戳: {bucket_end}",
|
|
174
|
+
exc_info=True,
|
|
175
|
+
)
|
|
176
|
+
logger.info(f"成功迁移 {len(platform_stats_v3)} 条旧的平台数据到新表。")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def migration_webchat_data(
|
|
180
|
+
db_helper: BaseDatabase,
|
|
181
|
+
platform_id_map: dict[str, dict[str, str]],
|
|
182
|
+
):
|
|
183
|
+
"""迁移 WebChat 的历史记录到新的 PlatformMessageHistory 表中"""
|
|
184
|
+
db_helper_v3 = SQLiteV3DatabaseV3(
|
|
185
|
+
db_path=DB_PATH.replace("data_v4.db", "data_v3.db"),
|
|
186
|
+
)
|
|
187
|
+
conversations, total_cnt = db_helper_v3.get_all_conversations(
|
|
188
|
+
page=1,
|
|
189
|
+
page_size=10000000,
|
|
190
|
+
)
|
|
191
|
+
logger.info(f"迁移 {total_cnt} 条旧的 WebChat 会话数据到新的表中...")
|
|
192
|
+
|
|
193
|
+
async with db_helper.get_db() as dbsession:
|
|
194
|
+
dbsession: AsyncSession
|
|
195
|
+
async with dbsession.begin():
|
|
196
|
+
for idx, conversation in enumerate(conversations):
|
|
197
|
+
if total_cnt > 0 and (idx + 1) % max(1, total_cnt // 10) == 0:
|
|
198
|
+
progress = int((idx + 1) / total_cnt * 100)
|
|
199
|
+
if progress % 10 == 0:
|
|
200
|
+
logger.info(f"进度: {progress}% ({idx + 1}/{total_cnt})")
|
|
201
|
+
try:
|
|
202
|
+
conv = db_helper_v3.get_conversation_by_user_id(
|
|
203
|
+
user_id=conversation.get("user_id", "unknown"),
|
|
204
|
+
cid=conversation.get("cid", "unknown"),
|
|
205
|
+
)
|
|
206
|
+
if not conv:
|
|
207
|
+
logger.info(
|
|
208
|
+
f"未找到该条旧会话对应的具体数据: {conversation}, 跳过。",
|
|
209
|
+
)
|
|
210
|
+
if ":" in conv.user_id:
|
|
211
|
+
continue
|
|
212
|
+
platform_id = "webchat"
|
|
213
|
+
history = json.loads(conv.history) if conv.history else []
|
|
214
|
+
for msg in history:
|
|
215
|
+
type_ = msg.get("type") # user type, "bot" or "user"
|
|
216
|
+
new_history = PlatformMessageHistory(
|
|
217
|
+
platform_id=platform_id,
|
|
218
|
+
user_id=conv.cid, # we use conv.cid as user_id for webchat
|
|
219
|
+
content=msg,
|
|
220
|
+
sender_id=type_,
|
|
221
|
+
sender_name=type_,
|
|
222
|
+
)
|
|
223
|
+
dbsession.add(new_history)
|
|
224
|
+
|
|
225
|
+
except Exception:
|
|
226
|
+
logger.error(
|
|
227
|
+
f"迁移旧 WebChat 会话 {conversation.get('cid', 'unknown')} 失败",
|
|
228
|
+
exc_info=True,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
logger.info(f"成功迁移 {total_cnt} 条旧的 WebChat 会话数据到新表。")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
async def migration_persona_data(
|
|
235
|
+
db_helper: BaseDatabase,
|
|
236
|
+
astrbot_config: AstrBotConfig,
|
|
237
|
+
):
|
|
238
|
+
"""迁移 Persona 数据到新的表中。
|
|
239
|
+
旧的 Persona 数据存储在 preference 中,新的 Persona 数据存储在 persona 表中。
|
|
240
|
+
"""
|
|
241
|
+
v3_persona_config: list[dict] = astrbot_config.get("persona", [])
|
|
242
|
+
total_personas = len(v3_persona_config)
|
|
243
|
+
logger.info(f"迁移 {total_personas} 个 Persona 配置到新表中...")
|
|
244
|
+
|
|
245
|
+
for idx, persona in enumerate(v3_persona_config):
|
|
246
|
+
if total_personas > 0 and (idx + 1) % max(1, total_personas // 10) == 0:
|
|
247
|
+
progress = int((idx + 1) / total_personas * 100)
|
|
248
|
+
if progress % 10 == 0:
|
|
249
|
+
logger.info(f"进度: {progress}% ({idx + 1}/{total_personas})")
|
|
250
|
+
try:
|
|
251
|
+
begin_dialogs = persona.get("begin_dialogs", [])
|
|
252
|
+
mood_imitation_dialogs = persona.get("mood_imitation_dialogs", [])
|
|
253
|
+
parts = []
|
|
254
|
+
user_turn = True
|
|
255
|
+
for mood_dialog in mood_imitation_dialogs:
|
|
256
|
+
if user_turn:
|
|
257
|
+
parts.append(f"A: {mood_dialog}\n")
|
|
258
|
+
else:
|
|
259
|
+
parts.append(f"B: {mood_dialog}\n")
|
|
260
|
+
user_turn = not user_turn
|
|
261
|
+
mood_prompt = "".join(parts)
|
|
262
|
+
system_prompt = persona.get("prompt", "")
|
|
263
|
+
if mood_prompt:
|
|
264
|
+
system_prompt += f"Here are few shots of dialogs, you need to imitate the tone of 'B' in the following dialogs to respond:\n {mood_prompt}"
|
|
265
|
+
persona_new = await db_helper.insert_persona(
|
|
266
|
+
persona_id=persona["name"],
|
|
267
|
+
system_prompt=system_prompt,
|
|
268
|
+
begin_dialogs=begin_dialogs,
|
|
269
|
+
)
|
|
270
|
+
logger.info(
|
|
271
|
+
f"迁移 Persona {persona['name']}({persona_new.system_prompt[:30]}...) 到新表成功。",
|
|
272
|
+
)
|
|
273
|
+
except Exception as e:
|
|
274
|
+
logger.error(f"解析 Persona 配置失败:{e}")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
async def migration_preferences(
|
|
278
|
+
db_helper: BaseDatabase,
|
|
279
|
+
platform_id_map: dict[str, dict[str, str]],
|
|
280
|
+
):
|
|
281
|
+
# 1. global scope migration
|
|
282
|
+
keys = [
|
|
283
|
+
"inactivated_llm_tools",
|
|
284
|
+
"inactivated_plugins",
|
|
285
|
+
"curr_provider",
|
|
286
|
+
"curr_provider_tts",
|
|
287
|
+
"curr_provider_stt",
|
|
288
|
+
"alter_cmd",
|
|
289
|
+
]
|
|
290
|
+
for key in keys:
|
|
291
|
+
value = sp_v3.get(key)
|
|
292
|
+
if value is not None:
|
|
293
|
+
await sp.put_async("global", "global", key, value)
|
|
294
|
+
logger.info(f"迁移全局偏好设置 {key} 成功,值: {value}")
|
|
295
|
+
|
|
296
|
+
# 2. umo scope migration
|
|
297
|
+
session_conversation = sp_v3.get("session_conversation", default={})
|
|
298
|
+
for umo, conversation_id in session_conversation.items():
|
|
299
|
+
if not umo or not conversation_id:
|
|
300
|
+
continue
|
|
301
|
+
try:
|
|
302
|
+
session = MessageSesion.from_str(session_str=umo)
|
|
303
|
+
platform_id = get_platform_id(platform_id_map, session.platform_name)
|
|
304
|
+
session.platform_id = platform_id
|
|
305
|
+
await sp.put_async("umo", str(session), "sel_conv_id", conversation_id)
|
|
306
|
+
logger.info(f"迁移会话 {umo} 的对话数据到新表成功,平台 ID: {platform_id}")
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.error(f"迁移会话 {umo} 的对话数据失败: {e}", exc_info=True)
|
|
309
|
+
|
|
310
|
+
session_service_config = sp_v3.get("session_service_config", default={})
|
|
311
|
+
for umo, config in session_service_config.items():
|
|
312
|
+
if not umo or not config:
|
|
313
|
+
continue
|
|
314
|
+
try:
|
|
315
|
+
session = MessageSesion.from_str(session_str=umo)
|
|
316
|
+
platform_id = get_platform_id(platform_id_map, session.platform_name)
|
|
317
|
+
session.platform_id = platform_id
|
|
318
|
+
|
|
319
|
+
await sp.put_async("umo", str(session), "session_service_config", config)
|
|
320
|
+
|
|
321
|
+
logger.info(f"迁移会话 {umo} 的服务配置到新表成功,平台 ID: {platform_id}")
|
|
322
|
+
except Exception as e:
|
|
323
|
+
logger.error(f"迁移会话 {umo} 的服务配置失败: {e}", exc_info=True)
|
|
324
|
+
|
|
325
|
+
session_variables = sp_v3.get("session_variables", default={})
|
|
326
|
+
for umo, variables in session_variables.items():
|
|
327
|
+
if not umo or not variables:
|
|
328
|
+
continue
|
|
329
|
+
try:
|
|
330
|
+
session = MessageSesion.from_str(session_str=umo)
|
|
331
|
+
platform_id = get_platform_id(platform_id_map, session.platform_name)
|
|
332
|
+
session.platform_id = platform_id
|
|
333
|
+
await sp.put_async("umo", str(session), "session_variables", variables)
|
|
334
|
+
except Exception as e:
|
|
335
|
+
logger.error(f"迁移会话 {umo} 的变量失败: {e}", exc_info=True)
|
|
336
|
+
|
|
337
|
+
session_provider_perf = sp_v3.get("session_provider_perf", default={})
|
|
338
|
+
for umo, perf in session_provider_perf.items():
|
|
339
|
+
if not umo or not perf:
|
|
340
|
+
continue
|
|
341
|
+
try:
|
|
342
|
+
session = MessageSesion.from_str(session_str=umo)
|
|
343
|
+
platform_id = get_platform_id(platform_id_map, session.platform_name)
|
|
344
|
+
session.platform_id = platform_id
|
|
345
|
+
|
|
346
|
+
for provider_type, provider_id in perf.items():
|
|
347
|
+
await sp.put_async(
|
|
348
|
+
"umo",
|
|
349
|
+
str(session),
|
|
350
|
+
f"provider_perf_{provider_type}",
|
|
351
|
+
provider_id,
|
|
352
|
+
)
|
|
353
|
+
logger.info(
|
|
354
|
+
f"迁移会话 {umo} 的提供商偏好到新表成功,平台 ID: {platform_id}",
|
|
355
|
+
)
|
|
356
|
+
except Exception as e:
|
|
357
|
+
logger.error(f"迁移会话 {umo} 的提供商偏好失败: {e}", exc_info=True)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from astrbot.api import logger, sp
|
|
2
|
+
from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
|
|
3
|
+
from astrbot.core.umop_config_router import UmopConfigRouter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def migrate_45_to_46(acm: AstrBotConfigManager, ucr: UmopConfigRouter):
|
|
7
|
+
abconf_data = acm.abconf_data
|
|
8
|
+
|
|
9
|
+
if not isinstance(abconf_data, dict):
|
|
10
|
+
# should be unreachable
|
|
11
|
+
logger.warning(
|
|
12
|
+
f"migrate_45_to_46: abconf_data is not a dict (type={type(abconf_data)}). Value: {abconf_data!r}",
|
|
13
|
+
)
|
|
14
|
+
return
|
|
15
|
+
|
|
16
|
+
# 如果任何一项带有 umop,则说明需要迁移
|
|
17
|
+
need_migration = False
|
|
18
|
+
for conf_id, conf_info in abconf_data.items():
|
|
19
|
+
if isinstance(conf_info, dict) and "umop" in conf_info:
|
|
20
|
+
need_migration = True
|
|
21
|
+
break
|
|
22
|
+
|
|
23
|
+
if not need_migration:
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
logger.info("Starting migration from version 4.5 to 4.6")
|
|
27
|
+
|
|
28
|
+
# extract umo->conf_id mapping
|
|
29
|
+
umo_to_conf_id = {}
|
|
30
|
+
for conf_id, conf_info in abconf_data.items():
|
|
31
|
+
if isinstance(conf_info, dict) and "umop" in conf_info:
|
|
32
|
+
umop_ls = conf_info.pop("umop")
|
|
33
|
+
if not isinstance(umop_ls, list):
|
|
34
|
+
continue
|
|
35
|
+
for umo in umop_ls:
|
|
36
|
+
if isinstance(umo, str) and umo not in umo_to_conf_id:
|
|
37
|
+
umo_to_conf_id[umo] = conf_id
|
|
38
|
+
|
|
39
|
+
# update the abconf data
|
|
40
|
+
await sp.global_put("abconf_mapping", abconf_data)
|
|
41
|
+
# update the umop config router
|
|
42
|
+
await ucr.update_routing_data(umo_to_conf_id)
|
|
43
|
+
|
|
44
|
+
logger.info("Migration from version 45 to 46 completed successfully")
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Migration script for WebChat sessions.
|
|
2
|
+
|
|
3
|
+
This migration creates PlatformSession from existing platform_message_history records.
|
|
4
|
+
|
|
5
|
+
Changes:
|
|
6
|
+
- Creates platform_sessions table
|
|
7
|
+
- Adds platform_id field (default: 'webchat')
|
|
8
|
+
- Adds display_name field
|
|
9
|
+
- Session_id format: {platform_id}_{uuid}
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from sqlalchemy import func, select
|
|
13
|
+
from sqlmodel import col
|
|
14
|
+
|
|
15
|
+
from astrbot.api import logger, sp
|
|
16
|
+
from astrbot.core.db import BaseDatabase
|
|
17
|
+
from astrbot.core.db.po import ConversationV2, PlatformMessageHistory, PlatformSession
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def migrate_webchat_session(db_helper: BaseDatabase):
|
|
21
|
+
"""Create PlatformSession records from platform_message_history.
|
|
22
|
+
|
|
23
|
+
This migration extracts all unique user_ids from platform_message_history
|
|
24
|
+
where platform_id='webchat' and creates corresponding PlatformSession records.
|
|
25
|
+
"""
|
|
26
|
+
# 检查是否已经完成迁移
|
|
27
|
+
migration_done = await db_helper.get_preference(
|
|
28
|
+
"global", "global", "migration_done_webchat_session_1"
|
|
29
|
+
)
|
|
30
|
+
if migration_done:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
logger.info("开始执行数据库迁移(WebChat 会话迁移)...")
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
async with db_helper.get_db() as session:
|
|
37
|
+
# 从 platform_message_history 创建 PlatformSession
|
|
38
|
+
query = (
|
|
39
|
+
select(
|
|
40
|
+
col(PlatformMessageHistory.user_id),
|
|
41
|
+
col(PlatformMessageHistory.sender_name),
|
|
42
|
+
func.min(PlatformMessageHistory.created_at).label("earliest"),
|
|
43
|
+
func.max(PlatformMessageHistory.updated_at).label("latest"),
|
|
44
|
+
)
|
|
45
|
+
.where(col(PlatformMessageHistory.platform_id) == "webchat")
|
|
46
|
+
.where(col(PlatformMessageHistory.sender_id) != "bot")
|
|
47
|
+
.group_by(col(PlatformMessageHistory.user_id))
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
result = await session.execute(query)
|
|
51
|
+
webchat_users = result.all()
|
|
52
|
+
|
|
53
|
+
if not webchat_users:
|
|
54
|
+
logger.info("没有找到需要迁移的 WebChat 数据")
|
|
55
|
+
await sp.put_async(
|
|
56
|
+
"global", "global", "migration_done_webchat_session_1", True
|
|
57
|
+
)
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
logger.info(f"找到 {len(webchat_users)} 个 WebChat 会话需要迁移")
|
|
61
|
+
|
|
62
|
+
# 检查已存在的会话
|
|
63
|
+
existing_query = select(col(PlatformSession.session_id))
|
|
64
|
+
existing_result = await session.execute(existing_query)
|
|
65
|
+
existing_session_ids = {row[0] for row in existing_result.fetchall()}
|
|
66
|
+
|
|
67
|
+
# 查询 Conversations 表中的 title,用于设置 display_name
|
|
68
|
+
# 对于每个 user_id,对应的 conversation user_id 格式为: webchat:FriendMessage:webchat!astrbot!{user_id}
|
|
69
|
+
user_ids_to_query = [
|
|
70
|
+
f"webchat:FriendMessage:webchat!astrbot!{user_id}"
|
|
71
|
+
for user_id, _, _, _ in webchat_users
|
|
72
|
+
]
|
|
73
|
+
conv_query = select(
|
|
74
|
+
col(ConversationV2.user_id), col(ConversationV2.title)
|
|
75
|
+
).where(col(ConversationV2.user_id).in_(user_ids_to_query))
|
|
76
|
+
conv_result = await session.execute(conv_query)
|
|
77
|
+
# 创建 user_id -> title 的映射字典
|
|
78
|
+
title_map = {
|
|
79
|
+
user_id.replace("webchat:FriendMessage:webchat!astrbot!", ""): title
|
|
80
|
+
for user_id, title in conv_result.fetchall()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# 批量创建 PlatformSession 记录
|
|
84
|
+
sessions_to_add = []
|
|
85
|
+
skipped_count = 0
|
|
86
|
+
|
|
87
|
+
for user_id, sender_name, created_at, updated_at in webchat_users:
|
|
88
|
+
# user_id 就是 webchat_conv_id (session_id)
|
|
89
|
+
session_id = user_id
|
|
90
|
+
|
|
91
|
+
# sender_name 通常是 username,但可能为 None
|
|
92
|
+
creator = sender_name if sender_name else "guest"
|
|
93
|
+
|
|
94
|
+
# 检查是否已经存在该会话
|
|
95
|
+
if session_id in existing_session_ids:
|
|
96
|
+
logger.debug(f"会话 {session_id} 已存在,跳过")
|
|
97
|
+
skipped_count += 1
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
# 从 Conversations 表中获取 display_name
|
|
101
|
+
display_name = title_map.get(user_id)
|
|
102
|
+
|
|
103
|
+
# 创建新的 PlatformSession(保留原有的时间戳)
|
|
104
|
+
new_session = PlatformSession(
|
|
105
|
+
session_id=session_id,
|
|
106
|
+
platform_id="webchat",
|
|
107
|
+
creator=creator,
|
|
108
|
+
is_group=0,
|
|
109
|
+
created_at=created_at,
|
|
110
|
+
updated_at=updated_at,
|
|
111
|
+
display_name=display_name,
|
|
112
|
+
)
|
|
113
|
+
sessions_to_add.append(new_session)
|
|
114
|
+
|
|
115
|
+
# 批量插入
|
|
116
|
+
if sessions_to_add:
|
|
117
|
+
session.add_all(sessions_to_add)
|
|
118
|
+
await session.commit()
|
|
119
|
+
|
|
120
|
+
logger.info(
|
|
121
|
+
f"WebChat 会话迁移完成!成功迁移: {len(sessions_to_add)}, 跳过: {skipped_count}",
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
logger.info("没有新会话需要迁移")
|
|
125
|
+
|
|
126
|
+
# 标记迁移完成
|
|
127
|
+
await sp.put_async("global", "global", "migration_done_webchat_session_1", True)
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.error(f"迁移过程中发生错误: {e}", exc_info=True)
|
|
131
|
+
raise
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
6
|
+
|
|
7
|
+
_VT = TypeVar("_VT")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SharedPreferences:
|
|
11
|
+
def __init__(self, path=None):
|
|
12
|
+
if path is None:
|
|
13
|
+
path = os.path.join(get_astrbot_data_path(), "shared_preferences.json")
|
|
14
|
+
self.path = path
|
|
15
|
+
self._data = self._load_preferences()
|
|
16
|
+
|
|
17
|
+
def _load_preferences(self):
|
|
18
|
+
if os.path.exists(self.path):
|
|
19
|
+
try:
|
|
20
|
+
with open(self.path) as f:
|
|
21
|
+
return json.load(f)
|
|
22
|
+
except json.JSONDecodeError:
|
|
23
|
+
os.remove(self.path)
|
|
24
|
+
return {}
|
|
25
|
+
|
|
26
|
+
def _save_preferences(self):
|
|
27
|
+
with open(self.path, "w") as f:
|
|
28
|
+
json.dump(self._data, f, indent=4, ensure_ascii=False)
|
|
29
|
+
f.flush()
|
|
30
|
+
|
|
31
|
+
def get(self, key, default: _VT = None) -> _VT:
|
|
32
|
+
return self._data.get(key, default)
|
|
33
|
+
|
|
34
|
+
def put(self, key, value):
|
|
35
|
+
self._data[key] = value
|
|
36
|
+
self._save_preferences()
|
|
37
|
+
|
|
38
|
+
def remove(self, key):
|
|
39
|
+
if key in self._data:
|
|
40
|
+
del self._data[key]
|
|
41
|
+
self._save_preferences()
|
|
42
|
+
|
|
43
|
+
def clear(self):
|
|
44
|
+
self._data.clear()
|
|
45
|
+
self._save_preferences()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
sp = SharedPreferences()
|