quick fix
This commit is contained in:
30
.env.example
30
.env.example
@@ -29,16 +29,16 @@ AI_TEMPERATURE=0.7
|
||||
# Bot Identity & Personality
|
||||
# ===========================================
|
||||
# The bot's name, used in the system prompt to tell the AI who it is
|
||||
BOT_NAME=My Bot
|
||||
BOT_NAME="My Bot"
|
||||
|
||||
# Personality traits that define how the bot responds (used in system prompt)
|
||||
BOT_PERSONALITY=helpful and friendly
|
||||
BOT_PERSONALITY="helpful and friendly"
|
||||
|
||||
# Message shown when someone mentions the bot without saying anything
|
||||
BOT_DESCRIPTION=I'm an AI assistant here to help you.
|
||||
BOT_DESCRIPTION="I'm an AI assistant here to help you."
|
||||
|
||||
# Status message shown in Discord (displays as "Watching <BOT_STATUS>")
|
||||
BOT_STATUS=for mentions
|
||||
BOT_STATUS="for mentions"
|
||||
|
||||
# Optional: Override the entire system prompt (leave commented to use auto-generated)
|
||||
# SYSTEM_PROMPT=You are a custom assistant...
|
||||
@@ -104,7 +104,7 @@ FACT_EXTRACTION_RATE=0.3
|
||||
PROACTIVE_ENABLED=true
|
||||
|
||||
# Enable cross-user associations (privacy-sensitive - shows shared interests)
|
||||
CROSS_USER_ENABLED=false
|
||||
CROSS_USER_ENABLED=true
|
||||
|
||||
# Enable bot opinion formation (bot develops topic preferences)
|
||||
OPINION_FORMATION_ENABLED=true
|
||||
@@ -119,18 +119,18 @@ MOOD_DECAY_RATE=0.1
|
||||
# Command Toggles
|
||||
# ===========================================
|
||||
# Master switch for all commands (when false, bot handles via conversation)
|
||||
COMMANDS_ENABLED=true
|
||||
COMMANDS_ENABLED=false
|
||||
|
||||
# Individual command toggles
|
||||
CMD_RELATIONSHIP_ENABLED=true
|
||||
CMD_MOOD_ENABLED=true
|
||||
CMD_BOTSTATS_ENABLED=true
|
||||
CMD_OURHISTORY_ENABLED=true
|
||||
CMD_BIRTHDAY_ENABLED=true
|
||||
CMD_REMEMBER_ENABLED=true
|
||||
CMD_SETNAME_ENABLED=true
|
||||
CMD_WHATDOYOUKNOW_ENABLED=true
|
||||
CMD_FORGETME_ENABLED=true
|
||||
# CMD_RELATIONSHIP_ENABLED=true
|
||||
# CMD_MOOD_ENABLED=true
|
||||
# CMD_BOTSTATS_ENABLED=true
|
||||
# CMD_OURHISTORY_ENABLED=true
|
||||
# CMD_BIRTHDAY_ENABLED=true
|
||||
# CMD_REMEMBER_ENABLED=true
|
||||
# CMD_SETNAME_ENABLED=true
|
||||
# CMD_WHATDOYOUKNOW_ENABLED=true
|
||||
# CMD_FORGETME_ENABLED=true
|
||||
|
||||
# ===========================================
|
||||
# Logging & Monitoring
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
services:
|
||||
daemon-boyfriend:
|
||||
build: .
|
||||
container_name: daemon-boyfriend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- DATABASE_URL=postgresql+asyncpg://daemon:${POSTGRES_PASSWORD:-daemon}@postgres:5432/daemon_boyfriend
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
daemon-boyfriend:
|
||||
build: .
|
||||
container_name: daemon-boyfriend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- DATABASE_URL=postgresql+asyncpg://daemon:${POSTGRES_PASSWORD:-daemon}@postgres:5432/daemon_boyfriend
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: daemon-boyfriend-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: daemon
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-daemon}
|
||||
POSTGRES_DB: daemon_boyfriend
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U daemon -d daemon_boyfriend"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: daemon-boyfriend-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: daemon
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-daemon}
|
||||
POSTGRES_DB: daemon_boyfriend
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U daemon -d daemon_boyfriend"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
postgres_data:
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
"""SQLAlchemy base model and metadata configuration."""
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import MetaData
|
||||
from sqlalchemy import DateTime, MetaData
|
||||
from sqlalchemy.ext.asyncio import AsyncAttrs
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
|
||||
def utc_now() -> datetime:
|
||||
"""Return current UTC time as timezone-aware datetime."""
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
# Naming convention for constraints (helps with migrations)
|
||||
convention = {
|
||||
"ix": "ix_%(column_0_label)s",
|
||||
@@ -23,6 +29,8 @@ class Base(AsyncAttrs, DeclarativeBase):
|
||||
|
||||
metadata = metadata
|
||||
|
||||
# Common timestamp columns
|
||||
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
# Common timestamp columns - use timezone-aware datetimes
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utc_now, onupdate=utc_now
|
||||
)
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import ARRAY, BigInteger, Boolean, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy import ARRAY, BigInteger, Boolean, DateTime, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from .base import Base
|
||||
from .base import Base, utc_now
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User
|
||||
@@ -21,8 +21,10 @@ class Conversation(Base):
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
||||
guild_id: Mapped[int | None] = mapped_column(BigInteger)
|
||||
channel_id: Mapped[int | None] = mapped_column(BigInteger, index=True)
|
||||
started_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
last_message_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, index=True)
|
||||
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
last_message_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utc_now, index=True
|
||||
)
|
||||
message_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, index=True)
|
||||
|
||||
|
||||
@@ -3,11 +3,20 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import ARRAY, BigInteger, Boolean, ForeignKey, String, Text, UniqueConstraint
|
||||
from sqlalchemy import (
|
||||
ARRAY,
|
||||
BigInteger,
|
||||
Boolean,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from .base import Base
|
||||
from .base import Base, utc_now
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User
|
||||
@@ -21,7 +30,7 @@ class Guild(Base):
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
discord_id: Mapped[int] = mapped_column(BigInteger, unique=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(255))
|
||||
joined_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
joined_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
settings: Mapped[dict] = mapped_column(JSONB, default=dict)
|
||||
|
||||
@@ -41,7 +50,7 @@ class GuildMember(Base):
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
||||
guild_nickname: Mapped[str | None] = mapped_column(String(255))
|
||||
roles: Mapped[list[str] | None] = mapped_column(ARRAY(Text), default=None)
|
||||
joined_guild_at: Mapped[datetime | None] = mapped_column(default=None)
|
||||
joined_guild_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), default=None)
|
||||
|
||||
# Relationships
|
||||
guild: Mapped["Guild"] = relationship(back_populates="members")
|
||||
|
||||
@@ -3,11 +3,20 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import BigInteger, Boolean, Float, ForeignKey, String, Text, UniqueConstraint
|
||||
from sqlalchemy import (
|
||||
BigInteger,
|
||||
Boolean,
|
||||
DateTime,
|
||||
Float,
|
||||
ForeignKey,
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from .base import Base
|
||||
from .base import Base, utc_now
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User, UserFact
|
||||
@@ -24,13 +33,13 @@ class BotState(Base):
|
||||
# Current mood state (valence-arousal model)
|
||||
mood_valence: Mapped[float] = mapped_column(Float, default=0.0) # -1.0 (sad) to 1.0 (happy)
|
||||
mood_arousal: Mapped[float] = mapped_column(Float, default=0.0) # -1.0 (calm) to 1.0 (excited)
|
||||
mood_updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
mood_updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
|
||||
# Bot statistics
|
||||
total_messages_sent: Mapped[int] = mapped_column(default=0)
|
||||
total_facts_learned: Mapped[int] = mapped_column(default=0)
|
||||
total_users_known: Mapped[int] = mapped_column(default=0)
|
||||
first_activated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
first_activated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
|
||||
# Bot preferences (evolved over time)
|
||||
preferences: Mapped[dict] = mapped_column(JSONB, default=dict)
|
||||
@@ -50,8 +59,8 @@ class BotOpinion(Base):
|
||||
discussion_count: Mapped[int] = mapped_column(default=0)
|
||||
|
||||
reasoning: Mapped[str | None] = mapped_column(Text)
|
||||
formed_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
last_reinforced_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
formed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
last_reinforced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
|
||||
__table_args__ = (UniqueConstraint("guild_id", "topic"),)
|
||||
|
||||
@@ -81,8 +90,8 @@ class UserRelationship(Base):
|
||||
# Inside jokes / shared references
|
||||
shared_references: Mapped[dict] = mapped_column(JSONB, default=dict)
|
||||
|
||||
first_interaction_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
last_interaction_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
first_interaction_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
last_interaction_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
|
||||
# Relationships
|
||||
user: Mapped["User"] = relationship(back_populates="relationships")
|
||||
@@ -128,7 +137,7 @@ class ScheduledEvent(Base):
|
||||
channel_id: Mapped[int | None] = mapped_column(BigInteger)
|
||||
|
||||
event_type: Mapped[str] = mapped_column(String(50), index=True)
|
||||
trigger_at: Mapped[datetime] = mapped_column(index=True)
|
||||
trigger_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
|
||||
|
||||
title: Mapped[str] = mapped_column(String(255))
|
||||
context: Mapped[dict] = mapped_column(JSONB, default=dict)
|
||||
@@ -137,7 +146,7 @@ class ScheduledEvent(Base):
|
||||
recurrence_rule: Mapped[str | None] = mapped_column(String(100))
|
||||
|
||||
status: Mapped[str] = mapped_column(String(20), default="pending", index=True)
|
||||
triggered_at: Mapped[datetime | None] = mapped_column(default=None)
|
||||
triggered_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), default=None)
|
||||
|
||||
# Relationships
|
||||
user: Mapped["User"] = relationship(back_populates="scheduled_events")
|
||||
@@ -159,7 +168,7 @@ class FactAssociation(Base):
|
||||
|
||||
association_type: Mapped[str] = mapped_column(String(50))
|
||||
strength: Mapped[float] = mapped_column(Float, default=0.5)
|
||||
discovered_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
discovered_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
|
||||
# Relationships
|
||||
fact_1: Mapped["UserFact"] = relationship(foreign_keys=[fact_id_1])
|
||||
@@ -183,4 +192,6 @@ class MoodHistory(Base):
|
||||
trigger_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
||||
trigger_description: Mapped[str | None] = mapped_column(Text)
|
||||
|
||||
recorded_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, index=True)
|
||||
recorded_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utc_now, index=True
|
||||
)
|
||||
|
||||
@@ -3,10 +3,19 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import BigInteger, Boolean, Float, ForeignKey, String, Text, UniqueConstraint
|
||||
from sqlalchemy import (
|
||||
BigInteger,
|
||||
Boolean,
|
||||
DateTime,
|
||||
Float,
|
||||
ForeignKey,
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from .base import Base
|
||||
from .base import Base, utc_now
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .conversation import Conversation, Message
|
||||
@@ -24,8 +33,8 @@ class User(Base):
|
||||
discord_username: Mapped[str] = mapped_column(String(255))
|
||||
discord_display_name: Mapped[str | None] = mapped_column(String(255))
|
||||
custom_name: Mapped[str | None] = mapped_column(String(255))
|
||||
first_seen_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
last_seen_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
first_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
|
||||
# Relationships
|
||||
@@ -88,8 +97,10 @@ class UserFact(Base):
|
||||
confidence: Mapped[float] = mapped_column(Float, default=1.0)
|
||||
source: Mapped[str] = mapped_column(String(50), default="conversation")
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, index=True)
|
||||
learned_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
||||
last_referenced_at: Mapped[datetime | None] = mapped_column(default=None)
|
||||
learned_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
|
||||
last_referenced_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True), default=None
|
||||
)
|
||||
|
||||
# Relationships
|
||||
user: Mapped["User"] = relationship(back_populates="facts")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Association Service - discovers and manages cross-user fact associations."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import and_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -97,7 +97,7 @@ class AssociationService:
|
||||
fact_id_2=fact_2.id,
|
||||
association_type=association_type,
|
||||
strength=strength,
|
||||
discovered_at=datetime.utcnow(),
|
||||
discovered_at=datetime.now(timezone.utc),
|
||||
)
|
||||
self._session.add(assoc)
|
||||
await self._session.flush()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -311,7 +311,7 @@ Return ONLY the JSON array, no other text."""
|
||||
confidence=fact_data.get("confidence", 0.8),
|
||||
source="auto_extraction",
|
||||
is_active=True,
|
||||
learned_at=datetime.utcnow(),
|
||||
learned_at=datetime.now(timezone.utc),
|
||||
# New fields from Living AI
|
||||
category=fact_data["type"],
|
||||
importance=fact_data.get("importance", 0.5),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import select
|
||||
@@ -60,7 +60,9 @@ class MoodService:
|
||||
bot_state = await self.get_or_create_bot_state(guild_id)
|
||||
|
||||
# Apply time decay toward neutral
|
||||
hours_since_update = (datetime.utcnow() - bot_state.mood_updated_at).total_seconds() / 3600
|
||||
hours_since_update = (
|
||||
datetime.now(timezone.utc) - bot_state.mood_updated_at
|
||||
).total_seconds() / 3600
|
||||
decay_factor = max(0, 1 - (settings.mood_decay_rate * hours_since_update))
|
||||
|
||||
valence = bot_state.mood_valence * decay_factor
|
||||
@@ -105,7 +107,7 @@ class MoodService:
|
||||
bot_state = await self.get_or_create_bot_state(guild_id)
|
||||
bot_state.mood_valence = new_valence
|
||||
bot_state.mood_arousal = new_arousal
|
||||
bot_state.mood_updated_at = datetime.utcnow()
|
||||
bot_state.mood_updated_at = datetime.now(timezone.utc)
|
||||
|
||||
# Record history
|
||||
await self._record_mood_history(
|
||||
@@ -140,7 +142,7 @@ class MoodService:
|
||||
async def get_stats(self, guild_id: int | None = None) -> dict:
|
||||
"""Get bot statistics."""
|
||||
bot_state = await self.get_or_create_bot_state(guild_id)
|
||||
age_delta = datetime.utcnow() - bot_state.first_activated_at
|
||||
age_delta = datetime.now(timezone.utc) - bot_state.first_activated_at
|
||||
|
||||
return {
|
||||
"age_days": age_delta.days,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Opinion Service - manages bot opinion formation on topics."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -77,7 +77,7 @@ class OpinionService:
|
||||
)
|
||||
opinion.interest_level = max(0.0, min(1.0, opinion.interest_level))
|
||||
|
||||
opinion.last_reinforced_at = datetime.utcnow()
|
||||
opinion.last_reinforced_at = datetime.now(timezone.utc)
|
||||
|
||||
logger.debug(
|
||||
f"Updated opinion on '{topic}': sentiment={opinion.sentiment:.2f}, "
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Persistent conversation management using PostgreSQL."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -38,7 +38,7 @@ class PersistentConversationManager:
|
||||
Conversation model instance
|
||||
"""
|
||||
# Look for recent active conversation in this channel
|
||||
cutoff = datetime.utcnow() - self._timeout
|
||||
cutoff = datetime.now(timezone.utc) - self._timeout
|
||||
|
||||
stmt = select(Conversation).where(
|
||||
Conversation.user_id == user.id,
|
||||
@@ -133,7 +133,7 @@ class PersistentConversationManager:
|
||||
self._session.add(message)
|
||||
|
||||
# Update conversation stats
|
||||
conversation.last_message_at = datetime.utcnow()
|
||||
conversation.last_message_at = datetime.now(timezone.utc)
|
||||
conversation.message_count += 1
|
||||
|
||||
await self._session.flush()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -71,7 +71,7 @@ Examples:
|
||||
if result and result.get("has_event"):
|
||||
days_until = result.get("days_until", 1) or 1
|
||||
# Schedule follow-up for 1 day after the event
|
||||
trigger_at = datetime.utcnow() + timedelta(days=days_until + 1)
|
||||
trigger_at = datetime.now(timezone.utc) + timedelta(days=days_until + 1)
|
||||
|
||||
event = ScheduledEvent(
|
||||
user_id=user.id,
|
||||
@@ -159,7 +159,7 @@ Examples:
|
||||
break
|
||||
|
||||
# Create the event
|
||||
trigger_at = datetime.utcnow() + timedelta(days=days_until + 1)
|
||||
trigger_at = datetime.now(timezone.utc) + timedelta(days=days_until + 1)
|
||||
|
||||
event = ScheduledEvent(
|
||||
user_id=user.id,
|
||||
@@ -300,7 +300,7 @@ Examples:
|
||||
|
||||
def _next_birthday(self, birthday: datetime) -> datetime:
|
||||
"""Calculate the next occurrence of a birthday."""
|
||||
today = datetime.utcnow().date()
|
||||
today = datetime.now(timezone.utc).date()
|
||||
this_year = birthday.replace(year=today.year)
|
||||
|
||||
if this_year.date() < today:
|
||||
@@ -322,7 +322,7 @@ Examples:
|
||||
|
||||
async def get_pending_events(self, before: datetime | None = None) -> list[ScheduledEvent]:
|
||||
"""Get events that should be triggered."""
|
||||
cutoff = before or datetime.utcnow()
|
||||
cutoff = before or datetime.now(timezone.utc)
|
||||
stmt = (
|
||||
select(ScheduledEvent)
|
||||
.where(
|
||||
@@ -390,7 +390,7 @@ Examples:
|
||||
async def mark_event_triggered(self, event: ScheduledEvent) -> None:
|
||||
"""Mark an event as triggered and handle recurrence."""
|
||||
event.status = "triggered"
|
||||
event.triggered_at = datetime.utcnow()
|
||||
event.triggered_at = datetime.now(timezone.utc)
|
||||
|
||||
# Handle recurring events
|
||||
if event.is_recurring and event.recurrence_rule:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Relationship Service - manages relationship tracking with users."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
|
||||
from sqlalchemy import select
|
||||
@@ -69,7 +69,7 @@ class RelationshipService:
|
||||
rel = await self.get_or_create_relationship(user, guild_id)
|
||||
|
||||
rel.total_interactions += 1
|
||||
rel.last_interaction_at = datetime.utcnow()
|
||||
rel.last_interaction_at = datetime.now(timezone.utc)
|
||||
|
||||
# Track sentiment
|
||||
if sentiment > 0.2:
|
||||
@@ -211,7 +211,7 @@ class RelationshipService:
|
||||
level = self.get_level(rel.relationship_score)
|
||||
|
||||
# Calculate time since first interaction
|
||||
time_known = datetime.utcnow() - rel.first_interaction_at
|
||||
time_known = datetime.now(timezone.utc) - rel.first_interaction_at
|
||||
days_known = time_known.days
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Self Awareness Service - provides bot self-reflection and statistics."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -36,7 +36,7 @@ class SelfAwarenessService:
|
||||
await self._session.flush()
|
||||
|
||||
# Calculate age
|
||||
age_delta = datetime.utcnow() - bot_state.first_activated_at
|
||||
age_delta = datetime.now(timezone.utc) - bot_state.first_activated_at
|
||||
|
||||
# Count users (from database)
|
||||
user_count = await self._count_users()
|
||||
@@ -76,7 +76,7 @@ class SelfAwarenessService:
|
||||
facts_count = facts_result.scalar() or 0
|
||||
|
||||
if rel:
|
||||
days_known = (datetime.utcnow() - rel.first_interaction_at).days
|
||||
days_known = (datetime.now(timezone.utc) - rel.first_interaction_at).days
|
||||
return {
|
||||
"first_met": rel.first_interaction_at,
|
||||
"days_known": days_known,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""User management service."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -40,7 +40,7 @@ class UserService:
|
||||
|
||||
if user:
|
||||
# Update last seen and current name
|
||||
user.last_seen_at = datetime.utcnow()
|
||||
user.last_seen_at = datetime.now(timezone.utc)
|
||||
user.discord_username = username
|
||||
if display_name:
|
||||
user.discord_display_name = display_name
|
||||
@@ -232,7 +232,7 @@ class UserService:
|
||||
for fact in facts[:20]: # Limit to 20 most recent facts
|
||||
lines.append(f"- [{fact.fact_type}] {fact.fact_content}")
|
||||
# Mark as referenced
|
||||
fact.last_referenced_at = datetime.utcnow()
|
||||
fact.last_referenced_at = datetime.now(timezone.utc)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user