Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions astrbot/core/conversation_mgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from astrbot.core.agent.message import AssistantMessageSegment, UserMessageSegment
from astrbot.core.db import BaseDatabase
from astrbot.core.db.po import Conversation, ConversationV2
from astrbot.core.utils.datetime_utils import to_utc_timestamp


class ConversationManager:
Expand Down Expand Up @@ -58,8 +59,10 @@ async def _trigger_session_deleted(self, unified_msg_origin: str) -> None:

def _convert_conv_from_v2_to_v1(self, conv_v2: ConversationV2) -> Conversation:
"""将 ConversationV2 对象转换为 Conversation 对象"""
created_at = int(conv_v2.created_at.timestamp())
updated_at = int(conv_v2.updated_at.timestamp())
created_ts = to_utc_timestamp(conv_v2.created_at)
updated_ts = to_utc_timestamp(conv_v2.updated_at)
created_at = int(created_ts) if created_ts is not None else 0
updated_at = int(updated_ts) if updated_ts is not None else 0
return Conversation(
platform_id=conv_v2.platform_id,
user_id=conv_v2.user_id,
Expand Down
27 changes: 27 additions & 0 deletions astrbot/core/utils/datetime_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from datetime import datetime, timezone


def normalize_datetime_utc(dt: datetime | None) -> datetime | None:
"""Normalize datetime values to UTC.

Naive datetimes are interpreted as UTC to match SQLite storage behavior.
"""
if dt is None:
return None
if dt.tzinfo is None or dt.tzinfo.utcoffset(dt) is None:
return dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)


def to_utc_isoformat(dt: datetime | None) -> str | None:
normalized = normalize_datetime_utc(dt)
if normalized is None:
return None
return normalized.isoformat()


def to_utc_timestamp(dt: datetime | None) -> float | None:
normalized = normalize_datetime_utc(dt)
if normalized is None:
return None
return normalized.timestamp()
7 changes: 2 additions & 5 deletions astrbot/dashboard/routes/api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from quart import g, request

from astrbot.core.db import BaseDatabase
from astrbot.core.utils.datetime_utils import normalize_datetime_utc

from .route import Response, Route, RouteContext

Expand All @@ -25,11 +26,7 @@ def __init__(self, context: RouteContext, db: BaseDatabase) -> None:

@staticmethod
def _normalize_utc(dt: datetime | None) -> datetime | None:
if dt is None:
return None
if dt.tzinfo is None or dt.tzinfo.utcoffset(dt) is None:
return dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)
return normalize_datetime_utc(dt)

@classmethod
def _serialize_datetime(cls, dt: datetime | None) -> str | None:
Expand Down
9 changes: 6 additions & 3 deletions astrbot/dashboard/routes/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from astrbot.core.platform.sources.webchat.webchat_queue_mgr import webchat_queue_mgr
from astrbot.core.utils.active_event_registry import active_event_registry
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
from astrbot.core.utils.datetime_utils import to_utc_isoformat

from .route import Response, Route, RouteContext

Expand Down Expand Up @@ -486,7 +487,9 @@ async def stream():
"type": "message_saved",
"data": {
"id": saved_record.id,
"created_at": saved_record.created_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(
saved_record.created_at
),
},
}
try:
Expand Down Expand Up @@ -718,8 +721,8 @@ async def get_sessions(self):
"creator": session.creator,
"display_name": session.display_name,
"is_group": session.is_group,
"created_at": session.created_at.astimezone().isoformat(),
"updated_at": session.updated_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(session.created_at),
"updated_at": to_utc_isoformat(session.updated_at),
}
)

Expand Down
17 changes: 9 additions & 8 deletions astrbot/dashboard/routes/chatui_project.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from quart import g, request

from astrbot.core.db import BaseDatabase
from astrbot.core.utils.datetime_utils import to_utc_isoformat

from .route import Response, Route, RouteContext

Expand Down Expand Up @@ -51,8 +52,8 @@ async def create_project(self):
"title": project.title,
"emoji": project.emoji,
"description": project.description,
"created_at": project.created_at.astimezone().isoformat(),
"updated_at": project.updated_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(project.created_at),
"updated_at": to_utc_isoformat(project.updated_at),
}
)
.__dict__
Expand All @@ -70,8 +71,8 @@ async def list_projects(self):
"title": project.title,
"emoji": project.emoji,
"description": project.description,
"created_at": project.created_at.astimezone().isoformat(),
"updated_at": project.updated_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(project.created_at),
"updated_at": to_utc_isoformat(project.updated_at),
}
for project in projects
]
Expand Down Expand Up @@ -102,8 +103,8 @@ async def get_project(self):
"title": project.title,
"emoji": project.emoji,
"description": project.description,
"created_at": project.created_at.astimezone().isoformat(),
"updated_at": project.updated_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(project.created_at),
"updated_at": to_utc_isoformat(project.updated_at),
}
)
.__dict__
Expand Down Expand Up @@ -236,8 +237,8 @@ async def get_project_sessions(self):
"creator": session.creator,
"display_name": session.display_name,
"is_group": session.is_group,
"created_at": session.created_at.astimezone().isoformat(),
"updated_at": session.updated_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(session.created_at),
"updated_at": to_utc_isoformat(session.updated_at),
}
for session in sessions
]
Expand Down
5 changes: 4 additions & 1 deletion astrbot/dashboard/routes/live_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from astrbot.core.platform.sources.webchat.webchat_queue_mgr import webchat_queue_mgr
from astrbot.core.utils.astrbot_path import get_astrbot_data_path, get_astrbot_temp_path
from astrbot.core.utils.datetime_utils import to_utc_isoformat

from .route import Route, RouteContext

Expand Down Expand Up @@ -621,7 +622,9 @@ async def _handle_chat_message(
"type": "message_saved",
"data": {
"id": saved_record.id,
"created_at": saved_record.created_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(
saved_record.created_at
),
},
},
)
Expand Down
9 changes: 6 additions & 3 deletions astrbot/dashboard/routes/open_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
webchat_message_parts_have_content,
)
from astrbot.core.platform.sources.webchat.webchat_queue_mgr import webchat_queue_mgr
from astrbot.core.utils.datetime_utils import to_utc_isoformat

from .api_key import ALL_OPEN_API_SCOPES
from .chat import ChatRoute
Expand Down Expand Up @@ -481,7 +482,9 @@ async def _handle_chat_ws_send(self, post_data: dict) -> None:
"type": "message_saved",
"data": {
"id": saved_record.id,
"created_at": saved_record.created_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(
saved_record.created_at
),
},
"session_id": session_id,
}
Expand Down Expand Up @@ -579,8 +582,8 @@ async def get_chat_sessions(self):
"creator": session.creator,
"display_name": session.display_name,
"is_group": session.is_group,
"created_at": session.created_at.astimezone().isoformat(),
"updated_at": session.updated_at.astimezone().isoformat(),
"created_at": to_utc_isoformat(session.created_at),
"updated_at": to_utc_isoformat(session.updated_at),
}
)

Expand Down
12 changes: 11 additions & 1 deletion astrbot/dashboard/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import os
import socket
from datetime import datetime
from pathlib import Path
from typing import Protocol, cast

Expand All @@ -19,6 +20,7 @@
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
from astrbot.core.db import BaseDatabase
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
from astrbot.core.utils.datetime_utils import to_utc_isoformat
from astrbot.core.utils.io import get_local_ip_addresses

from .routes import *
Expand All @@ -45,6 +47,13 @@ def _parse_env_bool(value: str | None, default: bool) -> bool:
return value.strip().lower() in {"1", "true", "yes", "on"}


class AstrBotJSONProvider(DefaultJSONProvider):
def default(self, obj):
if isinstance(obj, datetime):
return to_utc_isoformat(obj)
return super().default(obj)


class AstrBotDashboard:
def __init__(
self,
Expand All @@ -70,7 +79,8 @@ def __init__(
self.app.config["MAX_CONTENT_LENGTH"] = (
128 * 1024 * 1024
) # 将 Flask 允许的最大上传文件体大小设置为 128 MB
cast(DefaultJSONProvider, self.app.json).sort_keys = False
self.app.json = AstrBotJSONProvider(self.app)
self.app.json.sort_keys = False
self.app.before_request(self.auth_middleware)
# token 用于验证请求
logging.getLogger(self.app.name).removeHandler(default_handler)
Expand Down