From 10acb5c5f9a1f66757c79c968dc6a8abdc57869e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Mon, 10 Mar 2025 16:30:39 +0100 Subject: [PATCH 1/3] remove ChatManager --- livekit-rtc/livekit/rtc/__init__.py | 3 - livekit-rtc/livekit/rtc/chat.py | 131 ---------------------------- 2 files changed, 134 deletions(-) delete mode 100644 livekit-rtc/livekit/rtc/chat.py diff --git a/livekit-rtc/livekit/rtc/__init__.py b/livekit-rtc/livekit/rtc/__init__.py index f1309e1b..1482e846 100644 --- a/livekit-rtc/livekit/rtc/__init__.py +++ b/livekit-rtc/livekit/rtc/__init__.py @@ -38,7 +38,6 @@ from .audio_source import AudioSource from .audio_stream import AudioFrameEvent, AudioStream, NoiseCancellationOptions from .audio_filter import AudioFilter -from .chat import ChatManager, ChatMessage from .e2ee import ( E2EEManager, E2EEOptions, @@ -149,8 +148,6 @@ "VideoFrameEvent", "VideoSource", "VideoStream", - "ChatManager", - "ChatMessage", "AudioResampler", "AudioResamplerQuality", "RpcError", diff --git a/livekit-rtc/livekit/rtc/chat.py b/livekit-rtc/livekit/rtc/chat.py deleted file mode 100644 index 22e4b0c8..00000000 --- a/livekit-rtc/livekit/rtc/chat.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright 2023 LiveKit, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from dataclasses import dataclass, field -from datetime import datetime -import json -import logging -from typing import Any, Dict, Literal, Optional - -from .room import Room, Participant, DataPacket -from .event_emitter import EventEmitter -from ._utils import generate_random_base62 - -_CHAT_TOPIC = "lk-chat-topic" -_CHAT_UPDATE_TOPIC = "lk-chat-update-topic" - -EventTypes = Literal["message_received",] - - -class ChatManager(EventEmitter[EventTypes]): - """A utility class that sends and receives chat messages in the active session. - - It implements LiveKit Chat Protocol, and serializes data to/from JSON data packets. - """ - - def __init__(self, room: Room): - super().__init__() - self._lp = room.local_participant - self._room = room - - room.on("data_received", self._on_data_received) - - def close(self): - self._room.off("data_received", self._on_data_received) - - async def send_message(self, message: str) -> "ChatMessage": - """Send a chat message to the end user using LiveKit Chat Protocol. - - Args: - message (str): the message to send - - Returns: - ChatMessage: the message that was sent - """ - msg = ChatMessage( - message=message, - is_local=True, - participant=self._lp, - ) - await self._lp.publish_data( - payload=json.dumps(msg.asjsondict()), - topic=_CHAT_TOPIC, - ) - return msg - - async def update_message(self, message: "ChatMessage"): - """Update a chat message that was previously sent. - - If message.deleted is set to True, we'll signal to remote participants that the message - should be deleted. - """ - await self._lp.publish_data( - payload=json.dumps(message.asjsondict()), - topic=_CHAT_UPDATE_TOPIC, - ) - - def _on_data_received(self, dp: DataPacket): - # handle both new and updates the same way, as long as the ID is in there - # the user can decide how to replace the previous message - if dp.topic == _CHAT_TOPIC or dp.topic == _CHAT_UPDATE_TOPIC: - try: - parsed = json.loads(dp.data) - msg = ChatMessage.from_jsondict(parsed) - if dp.participant: - msg.participant = dp.participant - self.emit("message_received", msg) - except Exception as e: - logging.warning("failed to parse chat message: %s", e, exc_info=e) - - -@dataclass -class ChatMessage: - message: Optional[str] = None - id: str = field(default_factory=generate_random_base62) - timestamp: datetime = field(default_factory=datetime.now) - deleted: bool = field(default=False) - - # These fields are not part of the wire protocol. They are here to provide - # context for the application. - participant: Optional[Participant] = None - is_local: bool = field(default=False) - - @classmethod - def from_jsondict(cls, d: Dict[str, Any]) -> "ChatMessage": - # older version of the protocol didn't contain a message ID, so we'll create one - id = d.get("id") or generate_random_base62() - timestamp = datetime.now() - if d.get("timestamp"): - timestamp = datetime.fromtimestamp(d.get("timestamp", 0) / 1000.0) - msg = cls( - id=id, - timestamp=timestamp, - ) - msg.update_from_jsondict(d) - return msg - - def update_from_jsondict(self, d: Dict[str, Any]) -> None: - self.message = d.get("message") - self.deleted = d.get("deleted", False) - - def asjsondict(self): - """Returns a JSON serializable dictionary representation of the message.""" - d = { - "id": self.id, - "message": self.message, - "timestamp": int(self.timestamp.timestamp() * 1000), - } - if self.deleted: - d["deleted"] = True - return d From c61c566ed13291bddfe9a6068596c72b1a8c12db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Mon, 10 Mar 2025 16:32:09 +0100 Subject: [PATCH 2/3] Delete test_chat.py --- livekit-rtc/tests/test_chat.py | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 livekit-rtc/tests/test_chat.py diff --git a/livekit-rtc/tests/test_chat.py b/livekit-rtc/tests/test_chat.py deleted file mode 100644 index 0d2011d9..00000000 --- a/livekit-rtc/tests/test_chat.py +++ /dev/null @@ -1,32 +0,0 @@ -from datetime import datetime -import json - -from livekit.rtc import ChatMessage - - -def test_message_basics(): - msg = ChatMessage() - assert msg.id is not None, "message id should be set" - assert msg.timestamp is not None, "timestamp should be set" - assert msg.timestamp.day == datetime.now().day, "timestamp should be today" - assert len(msg.id) > 5, "message id should be long enough" - - -def test_message_serialization(): - msg = ChatMessage( - message="hello", - ) - data = msg.asjsondict() - msg2 = ChatMessage.from_jsondict(json.loads(json.dumps(data))) - assert msg2.message == msg.message, "message should be the same" - assert msg2.id == msg.id, "id should be the same" - assert int(msg2.timestamp.timestamp() / 1000) == int(msg.timestamp.timestamp() / 1000), ( - "timestamp should be the same" - ) - assert not msg2.deleted, "not deleted" - - # deletion is handled - msg.deleted = True - data = msg.asjsondict() - msg2 = ChatMessage.from_jsondict(json.loads(json.dumps(data))) - assert msg2.deleted, "should be deleted" From 5790a0353f1730ef1d88be44640526176c345050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=CC=81o=20Monnom?= Date: Thu, 13 Mar 2025 14:06:41 +0100 Subject: [PATCH 3/3] Update README.md --- README.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/README.md b/README.md index 1d45bcbf..adb3ace8 100644 --- a/README.md +++ b/README.md @@ -135,24 +135,6 @@ async def main(): print(f"\ttrack id: {publication}") ``` -### Sending and receiving chat - -```python - -room = rtc.Room() -... - -chat = rtc.ChatManager(room) - -# receiving chat -@chat.on("message_received") -def on_message_received(msg: rtc.ChatMessage): - print(f"message received: {msg.participant.identity}: {msg.message}") - -# sending chat -await chat.send_message("hello world") -``` - ### RPC Perform your own predefined method calls from one participant to another.