feat: Matrix Gateway integration for Rooms Layer
Matrix Gateway:
- Add POST /internal/matrix/room/join endpoint
- Add POST /internal/matrix/message/send endpoint
- Add GET /internal/matrix/rooms/{room_id}/messages endpoint
City Service:
- Add POST /rooms/sync/matrix endpoint for bulk sync
- Update get_agent_chat_room to auto-create Matrix rooms
- Update get_node_chat_room to auto-create Matrix rooms
- Update get_microdao_chat_room to auto-create Matrix rooms
- Add join_user_to_room, send_message_to_room, get_room_messages to matrix_client
- Add ensure_room_has_matrix helper function
This enables:
- Automatic Matrix room creation for all entity types
- chat_available = true when Matrix room exists
- Real-time messaging via Matrix
This commit is contained in:
@@ -4,14 +4,14 @@ Matrix Gateway Client for City Service
|
||||
import os
|
||||
import httpx
|
||||
import logging
|
||||
from typing import Optional, Tuple
|
||||
from typing import Optional, Tuple, List, Dict, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MATRIX_GATEWAY_URL = os.getenv("MATRIX_GATEWAY_URL", "http://daarion-matrix-gateway:7025")
|
||||
|
||||
|
||||
async def create_matrix_room(slug: str, name: str, visibility: str = "public") -> Tuple[Optional[str], Optional[str]]:
|
||||
async def create_matrix_room(slug: str, name: str, visibility: str = "public", topic: str = None) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Create a Matrix room via Matrix Gateway.
|
||||
|
||||
@@ -20,13 +20,17 @@ async def create_matrix_room(slug: str, name: str, visibility: str = "public") -
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
try:
|
||||
payload = {
|
||||
"slug": slug,
|
||||
"name": name,
|
||||
"visibility": visibility
|
||||
}
|
||||
if topic:
|
||||
payload["topic"] = topic
|
||||
|
||||
response = await client.post(
|
||||
f"{MATRIX_GATEWAY_URL}/internal/matrix/rooms/create",
|
||||
json={
|
||||
"slug": slug,
|
||||
"name": name,
|
||||
"visibility": visibility
|
||||
}
|
||||
json=payload
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
@@ -73,6 +77,108 @@ async def find_matrix_room_by_alias(alias: str) -> Tuple[Optional[str], Optional
|
||||
return None, None
|
||||
|
||||
|
||||
async def join_user_to_room(room_id: str, user_id: str) -> bool:
|
||||
"""
|
||||
Join a Matrix user to a room.
|
||||
|
||||
Args:
|
||||
room_id: Matrix room ID (!abc:daarion.space)
|
||||
user_id: Matrix user ID (@user:daarion.space)
|
||||
|
||||
Returns:
|
||||
True if joined successfully, False otherwise
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
try:
|
||||
response = await client.post(
|
||||
f"{MATRIX_GATEWAY_URL}/internal/matrix/room/join",
|
||||
json={
|
||||
"room_id": room_id,
|
||||
"user_id": user_id
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"User {user_id} joined room {room_id}")
|
||||
return data.get("ok", False)
|
||||
else:
|
||||
logger.error(f"Failed to join room: {response.text}")
|
||||
return False
|
||||
|
||||
except httpx.RequestError as e:
|
||||
logger.error(f"Matrix Gateway request error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
async def send_message_to_room(room_id: str, body: str, sender: str = None) -> Optional[str]:
|
||||
"""
|
||||
Send a message to a Matrix room.
|
||||
|
||||
Args:
|
||||
room_id: Matrix room ID
|
||||
body: Message text
|
||||
sender: Optional sender Matrix user ID
|
||||
|
||||
Returns:
|
||||
Event ID if sent successfully, None otherwise
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
try:
|
||||
payload = {
|
||||
"room_id": room_id,
|
||||
"body": body,
|
||||
"sender": sender or "@daarion-bot:daarion.space"
|
||||
}
|
||||
|
||||
response = await client.post(
|
||||
f"{MATRIX_GATEWAY_URL}/internal/matrix/message/send",
|
||||
json=payload
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
logger.info(f"Message sent to {room_id}: {data.get('event_id')}")
|
||||
return data.get("event_id")
|
||||
else:
|
||||
logger.error(f"Failed to send message: {response.text}")
|
||||
return None
|
||||
|
||||
except httpx.RequestError as e:
|
||||
logger.error(f"Matrix Gateway request error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def get_room_messages(room_id: str, limit: int = 50) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get recent messages from a Matrix room.
|
||||
|
||||
Args:
|
||||
room_id: Matrix room ID
|
||||
limit: Max number of messages to fetch
|
||||
|
||||
Returns:
|
||||
List of message dicts with event_id, sender, body, timestamp
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
try:
|
||||
response = await client.get(
|
||||
f"{MATRIX_GATEWAY_URL}/internal/matrix/rooms/{room_id}/messages",
|
||||
params={"limit": limit}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get("messages", [])
|
||||
else:
|
||||
logger.error(f"Failed to get messages: {response.text}")
|
||||
return []
|
||||
|
||||
except httpx.RequestError as e:
|
||||
logger.error(f"Matrix Gateway request error: {e}")
|
||||
return []
|
||||
|
||||
|
||||
async def check_matrix_gateway_health() -> bool:
|
||||
"""Check if Matrix Gateway is available."""
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
@@ -82,3 +188,21 @@ async def check_matrix_gateway_health() -> bool:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
async def ensure_room_has_matrix(room_slug: str, room_name: str, visibility: str = "public") -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Ensure a room has a Matrix room ID. Creates one if it doesn't exist.
|
||||
|
||||
Returns:
|
||||
Tuple of (matrix_room_id, matrix_room_alias) or (None, None) on failure
|
||||
"""
|
||||
# First try to find existing room
|
||||
alias = f"#city_{room_slug}:daarion.space"
|
||||
matrix_room_id, matrix_room_alias = await find_matrix_room_by_alias(alias)
|
||||
|
||||
if matrix_room_id:
|
||||
return matrix_room_id, matrix_room_alias
|
||||
|
||||
# Create new room
|
||||
return await create_matrix_room(room_slug, room_name, visibility)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user