diff --git a/src/event_loop.rs b/src/event_loop.rs index 32392ba..75a90d3 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -32,7 +32,10 @@ pub async fn run(cfg: AppConfig, passphrase: &str) -> AppResult<()> { let queue = QueueStore::new(); let facade = Arc::new(build_facade(&cfg)); - // 2. 启动 long-running sync(独立 task)—— 注意:sync future 非 Send,必须 select! + // 2. 注册 MSC3401 (Element Call) state event handler(v1 仅日志) + crate::rtc_handler::register(&client); + + // 3. 启动 long-running sync(独立 task)—— 注意:sync future 非 Send,必须 select! info!("开始 long-running sync(按 Ctrl-C 停止)"); tokio::select! { res = client.sync(SyncSettings::default()) => { diff --git a/src/main.rs b/src/main.rs index 34306aa..2b2cae3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ mod error; mod event_loop; mod music; mod queue; +mod rtc_handler; mod secret_store; mod transcoder; mod worker; diff --git a/src/rtc_handler.rs b/src/rtc_handler.rs new file mode 100644 index 0000000..daf34ad --- /dev/null +++ b/src/rtc_handler.rs @@ -0,0 +1,79 @@ +//! Element Call (MSC3401 MatrixRTC) 兼容性处理。 +//! +//! v1 阶段:bot 不参与 Element Call 的媒体通道(MatrixRTC 的 WebRTC 部分)。 +//! 本模块只负责: +//! - 观察 `org.matrix.msc3401.call.member` 状态事件(成员加入/离开通话) +//! - 在日志中记录,但不影响 bot 的正常聊天收发 +//! +//! 已知限制(plan 文档): +//! - Element Call 的媒体通道(WebRTC/LiveKit)是 DTLS-SRTP,**不**经过 Megolm 加密。 +//! - 真正让 bot "在通话里唱歌" 需要 v2 实现(matrix-sdk 的 rtc 模块 + livekit-rs)。 +//! +//! 设计: +//! - 通过 matrix-sdk 的 add_event_handler 注册全局 state event handler(闭包模式) +//! - 不需要任何额外依赖(MSC3401 类型已在 matrix-sdk::ruma 中暴露) + +use matrix_sdk::ruma::events::call::member::{ + CallMemberEventContent, PossiblyRedactedCallMemberEventContent, +}; +use matrix_sdk::ruma::events::SyncStateEvent; +use matrix_sdk::Room; +use tracing::{info, warn}; + +/// 在 Client 上注册 RTC handler(v1 仅日志) +pub fn register(client: &matrix_sdk::Client) { + client.add_event_handler(handle_call_member); + info!("已注册 MSC3401 call.member state event handler(v1 仅日志)"); +} + +/// 闭包 handler:MSC3401 call.member 状态事件 +async fn handle_call_member( + event: SyncStateEvent, + room: Room, +) { + let room_id = room.room_id().to_string(); + match event { + SyncStateEvent::Original(o) => { + // active_memberships 取"当前时间点仍在通话中"的成员 + let memberships = o.content.active_memberships(None); + let memberships_count = memberships.len(); + info!( + room_id = %room_id, + state_key = ?o.state_key, + memberships_count, + "MSC3401 call.member 状态变化(通话成员更新)" + ); + } + SyncStateEvent::Redacted(_) => { + info!(room_id = %room_id, "MSC3401 call.member 状态被 redact"); + } + } + // v1 阶段:仅记录,不参与通话媒体 + // v2 可在此扩展:检测 bot 是否被邀请加入通话(active_memberships 包含 bot user_id), + // 然后用 matrix_sdk::rtc + livekit-rs 加入通话并发布音频 track +} + +/// 编译期检查:确认 handler 闭包满足 trait bounds +#[allow(dead_code)] +fn _typecheck() { + fn assert_send_sync(_: T) {} + let f = handle_call_member; + assert_send_sync(f); +} + +// 抑制 unused 警告(CallMemberEventContent 暂未直接使用,但作为 import 占位) +#[allow(dead_code)] +fn _check_call_member_content(_c: &CallMemberEventContent) { + warn!("suppress unused"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn module_loads() { + // 简单 smoke test:确认模块可加载 + // 真实功能需集成测试(需要 homeserver) + } +} \ No newline at end of file