merge: resolve conflict with main (add mcp + homeassistant extras)

This commit is contained in:
0xbyt4 2026-03-03 14:52:22 +03:00
commit aefc330b8f
81 changed files with 8138 additions and 776 deletions

View file

@ -736,9 +736,13 @@ class BasePlatformAdapter(ABC):
chat_type: str = "dm",
user_id: Optional[str] = None,
user_name: Optional[str] = None,
thread_id: Optional[str] = None
thread_id: Optional[str] = None,
chat_topic: Optional[str] = None,
) -> SessionSource:
"""Helper to build a SessionSource for this platform."""
# Normalize empty topic to None
if chat_topic is not None and not chat_topic.strip():
chat_topic = None
return SessionSource(
platform=self.platform,
chat_id=str(chat_id),
@ -747,6 +751,7 @@ class BasePlatformAdapter(ABC):
user_id=str(user_id) if user_id else None,
user_name=user_name,
thread_id=str(thread_id) if thread_id else None,
chat_topic=chat_topic.strip() if chat_topic else None,
)
@abstractmethod

View file

@ -542,6 +542,9 @@ class DiscordAdapter(BasePlatformAdapter):
chat_name = interaction.channel.name
if hasattr(interaction.channel, "guild") and interaction.channel.guild:
chat_name = f"{interaction.channel.guild.name} / #{chat_name}"
# Get channel topic (if available)
chat_topic = getattr(interaction.channel, "topic", None)
source = self.build_source(
chat_id=str(interaction.channel_id),
@ -549,6 +552,7 @@ class DiscordAdapter(BasePlatformAdapter):
chat_type=chat_type,
user_id=str(interaction.user.id),
user_name=interaction.user.display_name,
chat_topic=chat_topic,
)
msg_type = MessageType.COMMAND if text.startswith("/") else MessageType.TEXT
@ -661,6 +665,9 @@ class DiscordAdapter(BasePlatformAdapter):
if isinstance(message.channel, discord.Thread):
thread_id = str(message.channel.id)
# Get channel topic (if available - TextChannels have topics, DMs/threads don't)
chat_topic = getattr(message.channel, "topic", None)
# Build source
source = self.build_source(
chat_id=str(message.channel.id),
@ -669,6 +676,7 @@ class DiscordAdapter(BasePlatformAdapter):
user_id=str(message.author.id),
user_name=message.author.display_name,
thread_id=thread_id,
chat_topic=chat_topic,
)
# Build media URLs -- download image attachments to local cache so the

View file

@ -29,7 +29,17 @@ except ImportError:
Bot = Any
Message = Any
Application = Any
ContextTypes = Any
CommandHandler = Any
TelegramMessageHandler = Any
filters = None
ParseMode = None
ChatType = None
# Mock ContextTypes so type annotations using ContextTypes.DEFAULT_TYPE
# don't crash during class definition when the library isn't installed.
class _MockContextTypes:
DEFAULT_TYPE = Any
ContextTypes = _MockContextTypes
import sys
from pathlib import Path as _Path

View file

@ -19,7 +19,10 @@ import asyncio
import json
import logging
import os
import platform
import subprocess
_IS_WINDOWS = platform.system() == "Windows"
from pathlib import Path
from typing import Dict, List, Optional, Any
@ -157,16 +160,18 @@ class WhatsAppAdapter(BasePlatformAdapter):
pass
# Start the bridge process in its own process group
whatsapp_mode = os.getenv("WHATSAPP_MODE", "self-chat")
self._bridge_process = subprocess.Popen(
[
"node",
str(bridge_path),
"--port", str(self._bridge_port),
"--session", str(self._session_path),
"--mode", whatsapp_mode,
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
preexec_fn=os.setsid,
preexec_fn=None if _IS_WINDOWS else os.setsid,
)
# Wait for bridge to be ready via HTTP health check
@ -211,13 +216,19 @@ class WhatsAppAdapter(BasePlatformAdapter):
# Kill the entire process group so child node processes die too
import signal
try:
os.killpg(os.getpgid(self._bridge_process.pid), signal.SIGTERM)
if _IS_WINDOWS:
self._bridge_process.terminate()
else:
os.killpg(os.getpgid(self._bridge_process.pid), signal.SIGTERM)
except (ProcessLookupError, PermissionError):
self._bridge_process.terminate()
await asyncio.sleep(1)
if self._bridge_process.poll() is None:
try:
os.killpg(os.getpgid(self._bridge_process.pid), signal.SIGKILL)
if _IS_WINDOWS:
self._bridge_process.kill()
else:
os.killpg(os.getpgid(self._bridge_process.pid), signal.SIGKILL)
except (ProcessLookupError, PermissionError):
self._bridge_process.kill()
except Exception as e: