ipc-framework 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ipc_framework/core.py CHANGED
@@ -1,353 +1,365 @@
1
- """
2
- Core classes for the IPC Framework
3
- """
4
-
5
- import json
6
- import uuid
7
- import time
8
- import threading
9
- from typing import Dict, List, Optional, Callable, Any
10
- from dataclasses import dataclass, asdict
11
- from enum import Enum
12
-
13
-
14
- class MessageType(Enum):
15
- """Types of messages in the IPC system"""
16
- REQUEST = "request"
17
- RESPONSE = "response"
18
- NOTIFICATION = "notification"
19
- SUBSCRIBE = "subscribe"
20
- UNSUBSCRIBE = "unsubscribe"
21
- PUBLISH = "publish"
22
-
23
-
24
- @dataclass
25
- class Message:
26
- """Represents a message in the IPC system"""
27
- message_id: str
28
- app_id: str
29
- channel_id: str
30
- message_type: MessageType
31
- payload: Any
32
- timestamp: float
33
- reply_to: Optional[str] = None
34
-
35
- def __post_init__(self):
36
- if not self.message_id:
37
- self.message_id = str(uuid.uuid4())
38
- if not self.timestamp:
39
- self.timestamp = time.time()
40
-
41
- def to_dict(self) -> Dict:
42
- """Convert message to dictionary for serialization"""
43
- data = asdict(self)
44
- data['message_type'] = self.message_type.value
45
- return data
46
-
47
- @classmethod
48
- def from_dict(cls, data: Dict) -> 'Message':
49
- """Create message from dictionary"""
50
- data['message_type'] = MessageType(data['message_type'])
51
- return cls(**data)
52
-
53
- def to_json(self) -> str:
54
- """Serialize message to JSON"""
55
- return json.dumps(self.to_dict())
56
-
57
- @classmethod
58
- def from_json(cls, json_str: str) -> 'Message':
59
- """Deserialize message from JSON"""
60
- return cls.from_dict(json.loads(json_str))
61
-
62
-
63
- class Channel:
64
- """Represents a communication channel within an application"""
65
-
66
- def __init__(self, channel_id: str, app_id: str):
67
- self.channel_id = channel_id
68
- self.app_id = app_id
69
- self.subscribers: List[str] = [] # Connection IDs
70
- self.handlers: Dict[MessageType, Callable] = {}
71
- self.created_at = time.time()
72
- self._lock = threading.RLock()
73
-
74
- def add_subscriber(self, connection_id: str):
75
- """Add a subscriber to this channel"""
76
- with self._lock:
77
- if connection_id not in self.subscribers:
78
- self.subscribers.append(connection_id)
79
-
80
- def remove_subscriber(self, connection_id: str):
81
- """Remove a subscriber from this channel"""
82
- with self._lock:
83
- if connection_id in self.subscribers:
84
- self.subscribers.remove(connection_id)
85
-
86
- def set_handler(self, message_type: MessageType, handler: Callable):
87
- """Set a message handler for this channel"""
88
- with self._lock:
89
- self.handlers[message_type] = handler
90
-
91
- def get_handler(self, message_type: MessageType) -> Optional[Callable]:
92
- """Get the handler for a message type"""
93
- with self._lock:
94
- return self.handlers.get(message_type)
95
-
96
- def get_subscribers(self) -> List[str]:
97
- """Get list of current subscribers"""
98
- with self._lock:
99
- return self.subscribers.copy()
100
-
101
-
102
- class Application:
103
- """Represents an application with multiple channels"""
104
-
105
- def __init__(self, app_id: str, name: str = None):
106
- self.app_id = app_id
107
- self.name = name or app_id
108
- self.channels: Dict[str, Channel] = {}
109
- self.created_at = time.time()
110
- self.last_activity = time.time()
111
- self._lock = threading.RLock()
112
-
113
- def create_channel(self, channel_id: str) -> Channel:
114
- """Create a new channel in this application"""
115
- with self._lock:
116
- if channel_id in self.channels:
117
- return self.channels[channel_id]
118
-
119
- channel = Channel(channel_id, self.app_id)
120
- self.channels[channel_id] = channel
121
- self.last_activity = time.time()
122
- return channel
123
-
124
- def get_channel(self, channel_id: str) -> Optional[Channel]:
125
- """Get a channel by ID"""
126
- with self._lock:
127
- return self.channels.get(channel_id)
128
-
129
- def remove_channel(self, channel_id: str) -> bool:
130
- """Remove a channel"""
131
- with self._lock:
132
- if channel_id in self.channels:
133
- del self.channels[channel_id]
134
- self.last_activity = time.time()
135
- return True
136
- return False
137
-
138
- def list_channels(self) -> List[str]:
139
- """List all channel IDs in this application"""
140
- with self._lock:
141
- return list(self.channels.keys())
142
-
143
- def update_activity(self):
144
- """Update the last activity timestamp"""
145
- self.last_activity = time.time()
146
-
147
-
148
- class ConnectionManager:
149
- """Manages client connections"""
150
-
151
- def __init__(self):
152
- self.connections: Dict[str, Any] = {} # connection_id -> connection object
153
- self.connection_apps: Dict[str, str] = {} # connection_id -> app_id
154
- self._lock = threading.RLock()
155
-
156
- def add_connection(self, connection_id: str, connection_obj: Any, app_id: str):
157
- """Add a new connection"""
158
- with self._lock:
159
- self.connections[connection_id] = connection_obj
160
- self.connection_apps[connection_id] = app_id
161
-
162
- def remove_connection(self, connection_id: str):
163
- """Remove a connection"""
164
- with self._lock:
165
- self.connections.pop(connection_id, None)
166
- self.connection_apps.pop(connection_id, None)
167
-
168
- def get_connection(self, connection_id: str) -> Optional[Any]:
169
- """Get a connection object"""
170
- with self._lock:
171
- return self.connections.get(connection_id)
172
-
173
- def get_connections_for_app(self, app_id: str) -> List[str]:
174
- """Get all connection IDs for an application"""
175
- with self._lock:
176
- return [conn_id for conn_id, app in self.connection_apps.items() if app == app_id]
177
-
178
- def list_connections(self) -> List[str]:
179
- """List all connection IDs"""
180
- with self._lock:
181
- return list(self.connections.keys())
182
-
183
-
184
- class IPCServer:
185
- """Base IPC Server class"""
186
-
187
- def __init__(self, host: str = "localhost", port: int = 8888):
188
- self.host = host
189
- self.port = port
190
- self.applications: Dict[str, Application] = {}
191
- self.connection_manager = ConnectionManager()
192
- self.running = False
193
- self._lock = threading.RLock()
194
-
195
- def create_application(self, app_id: str, name: str = None) -> Application:
196
- """Create a new application"""
197
- with self._lock:
198
- if app_id in self.applications:
199
- return self.applications[app_id]
200
-
201
- app = Application(app_id, name)
202
- self.applications[app_id] = app
203
- return app
204
-
205
- def get_application(self, app_id: str) -> Optional[Application]:
206
- """Get an application by ID"""
207
- with self._lock:
208
- return self.applications.get(app_id)
209
-
210
- def route_message(self, message: Message) -> bool:
211
- """Route a message to the appropriate handlers"""
212
- with self._lock:
213
- app = self.get_application(message.app_id)
214
- if not app:
215
- return False
216
-
217
- channel = app.get_channel(message.channel_id)
218
- if not channel:
219
- return False
220
-
221
- app.update_activity()
222
-
223
- # Handle different message types
224
- if message.message_type == MessageType.PUBLISH:
225
- return self._handle_publish(message, channel)
226
- elif message.message_type == MessageType.SUBSCRIBE:
227
- return self._handle_subscribe(message, channel)
228
- elif message.message_type == MessageType.UNSUBSCRIBE:
229
- return self._handle_unsubscribe(message, channel)
230
- else:
231
- handler = channel.get_handler(message.message_type)
232
- if handler:
233
- try:
234
- handler(message)
235
- return True
236
- except Exception as e:
237
- print(f"Handler error: {e}")
238
- return False
239
-
240
- return False
241
-
242
- def _handle_publish(self, message: Message, channel: Channel) -> bool:
243
- """Handle publish message by broadcasting to subscribers"""
244
- subscribers = channel.get_subscribers()
245
- for subscriber_id in subscribers:
246
- connection = self.connection_manager.get_connection(subscriber_id)
247
- if connection:
248
- self.send_to_connection(connection, message)
249
- return True
250
-
251
- def _handle_subscribe(self, message: Message, channel: Channel) -> bool:
252
- """Handle subscribe message"""
253
- # Extract connection ID from message payload
254
- connection_id = message.payload.get('connection_id')
255
- if connection_id:
256
- channel.add_subscriber(connection_id)
257
- return True
258
- return False
259
-
260
- def _handle_unsubscribe(self, message: Message, channel: Channel) -> bool:
261
- """Handle unsubscribe message"""
262
- connection_id = message.payload.get('connection_id')
263
- if connection_id:
264
- channel.remove_subscriber(connection_id)
265
- return True
266
- return False
267
-
268
- def send_to_connection(self, connection, message: Message):
269
- """Send message to a specific connection - to be implemented by subclasses"""
270
- raise NotImplementedError("Subclasses must implement send_to_connection")
271
-
272
- def start(self):
273
- """Start the server - to be implemented by subclasses"""
274
- raise NotImplementedError("Subclasses must implement start")
275
-
276
- def stop(self):
277
- """Stop the server - to be implemented by subclasses"""
278
- raise NotImplementedError("Subclasses must implement stop")
279
-
280
-
281
- class IPCClient:
282
- """Base IPC Client class"""
283
-
284
- def __init__(self, app_id: str, host: str = "localhost", port: int = 8888):
285
- self.app_id = app_id
286
- self.host = host
287
- self.port = port
288
- self.connection_id = str(uuid.uuid4())
289
- self.connected = False
290
- self.message_handlers: Dict[str, Callable] = {} # channel_id -> handler
291
- self._lock = threading.RLock()
292
-
293
- def connect(self) -> bool:
294
- """Connect to the server - to be implemented by subclasses"""
295
- raise NotImplementedError("Subclasses must implement connect")
296
-
297
- def disconnect(self):
298
- """Disconnect from the server - to be implemented by subclasses"""
299
- raise NotImplementedError("Subclasses must implement disconnect")
300
-
301
- def send_message(self, channel_id: str, message_type: MessageType, payload: Any, reply_to: str = None) -> str:
302
- """Send a message to a specific channel"""
303
- message = Message(
304
- message_id=str(uuid.uuid4()),
305
- app_id=self.app_id,
306
- channel_id=channel_id,
307
- message_type=message_type,
308
- payload=payload,
309
- timestamp=time.time(),
310
- reply_to=reply_to
311
- )
312
- return self._send_message(message)
313
-
314
- def _send_message(self, message: Message) -> str:
315
- """Internal method to send message - to be implemented by subclasses"""
316
- raise NotImplementedError("Subclasses must implement _send_message")
317
-
318
- def subscribe(self, channel_id: str, handler: Callable = None) -> bool:
319
- """Subscribe to a channel"""
320
- if handler:
321
- with self._lock:
322
- self.message_handlers[channel_id] = handler
323
-
324
- payload = {'connection_id': self.connection_id}
325
- self.send_message(channel_id, MessageType.SUBSCRIBE, payload)
326
- return True
327
-
328
- def unsubscribe(self, channel_id: str) -> bool:
329
- """Unsubscribe from a channel"""
330
- with self._lock:
331
- self.message_handlers.pop(channel_id, None)
332
-
333
- payload = {'connection_id': self.connection_id}
334
- self.send_message(channel_id, MessageType.UNSUBSCRIBE, payload)
335
- return True
336
-
337
- def publish(self, channel_id: str, data: Any) -> str:
338
- """Publish data to a channel"""
339
- return self.send_message(channel_id, MessageType.PUBLISH, data)
340
-
341
- def request(self, channel_id: str, data: Any) -> str:
342
- """Send a request message"""
343
- return self.send_message(channel_id, MessageType.REQUEST, data)
344
-
345
- def handle_message(self, message: Message):
346
- """Handle incoming message"""
347
- with self._lock:
348
- handler = self.message_handlers.get(message.channel_id)
349
- if handler:
350
- try:
351
- handler(message)
352
- except Exception as e:
1
+ """
2
+ Core classes for the IPC Framework
3
+ """
4
+
5
+ import json
6
+ import uuid
7
+ import time
8
+ import threading
9
+ from typing import Dict, List, Optional, Callable, Any
10
+ from dataclasses import dataclass, asdict
11
+ from enum import Enum
12
+
13
+
14
+ class MessageType(Enum):
15
+ """Types of messages in the IPC system"""
16
+ REQUEST = "request"
17
+ RESPONSE = "response"
18
+ NOTIFICATION = "notification"
19
+ SUBSCRIBE = "subscribe"
20
+ UNSUBSCRIBE = "unsubscribe"
21
+ PUBLISH = "publish"
22
+
23
+
24
+ @dataclass
25
+ class Message:
26
+ """Represents a message in the IPC system"""
27
+ message_id: str
28
+ app_id: str
29
+ channel_id: str
30
+ message_type: MessageType
31
+ payload: Any
32
+ timestamp: float
33
+ reply_to: Optional[str] = None
34
+
35
+ def __post_init__(self):
36
+ if not self.message_id:
37
+ self.message_id = str(uuid.uuid4())
38
+ if not self.timestamp:
39
+ self.timestamp = time.time()
40
+
41
+ def create_response(self, payload: Any) -> 'Message':
42
+ """Create a response message to this message"""
43
+ return Message(
44
+ message_id=str(uuid.uuid4()),
45
+ app_id=self.app_id,
46
+ channel_id=self.channel_id,
47
+ message_type=MessageType.RESPONSE,
48
+ payload=payload,
49
+ timestamp=time.time(),
50
+ reply_to=self.message_id
51
+ )
52
+
53
+ def to_dict(self) -> Dict:
54
+ """Convert message to dictionary for serialization"""
55
+ data = asdict(self)
56
+ data['message_type'] = self.message_type.value
57
+ return data
58
+
59
+ @classmethod
60
+ def from_dict(cls, data: Dict) -> 'Message':
61
+ """Create message from dictionary"""
62
+ data['message_type'] = MessageType(data['message_type'])
63
+ return cls(**data)
64
+
65
+ def to_json(self) -> str:
66
+ """Serialize message to JSON"""
67
+ return json.dumps(self.to_dict())
68
+
69
+ @classmethod
70
+ def from_json(cls, json_str: str) -> 'Message':
71
+ """Deserialize message from JSON"""
72
+ return cls.from_dict(json.loads(json_str))
73
+
74
+
75
+ class Channel:
76
+ """Represents a communication channel within an application"""
77
+
78
+ def __init__(self, channel_id: str, app_id: str):
79
+ self.channel_id = channel_id
80
+ self.app_id = app_id
81
+ self.subscribers: List[str] = [] # Connection IDs
82
+ self.handlers: Dict[MessageType, Callable] = {}
83
+ self.created_at = time.time()
84
+ self._lock = threading.RLock()
85
+
86
+ def add_subscriber(self, connection_id: str):
87
+ """Add a subscriber to this channel"""
88
+ with self._lock:
89
+ if connection_id not in self.subscribers:
90
+ self.subscribers.append(connection_id)
91
+
92
+ def remove_subscriber(self, connection_id: str):
93
+ """Remove a subscriber from this channel"""
94
+ with self._lock:
95
+ if connection_id in self.subscribers:
96
+ self.subscribers.remove(connection_id)
97
+
98
+ def set_handler(self, message_type: MessageType, handler: Callable):
99
+ """Set a message handler for this channel"""
100
+ with self._lock:
101
+ self.handlers[message_type] = handler
102
+
103
+ def get_handler(self, message_type: MessageType) -> Optional[Callable]:
104
+ """Get the handler for a message type"""
105
+ with self._lock:
106
+ return self.handlers.get(message_type)
107
+
108
+ def get_subscribers(self) -> List[str]:
109
+ """Get list of current subscribers"""
110
+ with self._lock:
111
+ return self.subscribers.copy()
112
+
113
+
114
+ class Application:
115
+ """Represents an application with multiple channels"""
116
+
117
+ def __init__(self, app_id: str, name: str = None):
118
+ self.app_id = app_id
119
+ self.name = name or app_id
120
+ self.channels: Dict[str, Channel] = {}
121
+ self.created_at = time.time()
122
+ self.last_activity = time.time()
123
+ self._lock = threading.RLock()
124
+
125
+ def create_channel(self, channel_id: str) -> Channel:
126
+ """Create a new channel in this application"""
127
+ with self._lock:
128
+ if channel_id in self.channels:
129
+ return self.channels[channel_id]
130
+
131
+ channel = Channel(channel_id, self.app_id)
132
+ self.channels[channel_id] = channel
133
+ self.last_activity = time.time()
134
+ return channel
135
+
136
+ def get_channel(self, channel_id: str) -> Optional[Channel]:
137
+ """Get a channel by ID"""
138
+ with self._lock:
139
+ return self.channels.get(channel_id)
140
+
141
+ def remove_channel(self, channel_id: str) -> bool:
142
+ """Remove a channel"""
143
+ with self._lock:
144
+ if channel_id in self.channels:
145
+ del self.channels[channel_id]
146
+ self.last_activity = time.time()
147
+ return True
148
+ return False
149
+
150
+ def list_channels(self) -> List[str]:
151
+ """List all channel IDs in this application"""
152
+ with self._lock:
153
+ return list(self.channels.keys())
154
+
155
+ def update_activity(self):
156
+ """Update the last activity timestamp"""
157
+ self.last_activity = time.time()
158
+
159
+
160
+ class ConnectionManager:
161
+ """Manages client connections"""
162
+
163
+ def __init__(self):
164
+ self.connections: Dict[str, Any] = {} # connection_id -> connection object
165
+ self.connection_apps: Dict[str, str] = {} # connection_id -> app_id
166
+ self._lock = threading.RLock()
167
+
168
+ def add_connection(self, connection_id: str, connection_obj: Any, app_id: str):
169
+ """Add a new connection"""
170
+ with self._lock:
171
+ self.connections[connection_id] = connection_obj
172
+ self.connection_apps[connection_id] = app_id
173
+
174
+ def remove_connection(self, connection_id: str):
175
+ """Remove a connection"""
176
+ with self._lock:
177
+ self.connections.pop(connection_id, None)
178
+ self.connection_apps.pop(connection_id, None)
179
+
180
+ def get_connection(self, connection_id: str) -> Optional[Any]:
181
+ """Get a connection object"""
182
+ with self._lock:
183
+ return self.connections.get(connection_id)
184
+
185
+ def get_connections_for_app(self, app_id: str) -> List[str]:
186
+ """Get all connection IDs for an application"""
187
+ with self._lock:
188
+ return [conn_id for conn_id, app in self.connection_apps.items() if app == app_id]
189
+
190
+ def list_connections(self) -> List[str]:
191
+ """List all connection IDs"""
192
+ with self._lock:
193
+ return list(self.connections.keys())
194
+
195
+
196
+ class IPCServer:
197
+ """Base IPC Server class"""
198
+
199
+ def __init__(self, host: str = "localhost", port: int = 8888):
200
+ self.host = host
201
+ self.port = port
202
+ self.applications: Dict[str, Application] = {}
203
+ self.connection_manager = ConnectionManager()
204
+ self.running = False
205
+ self._lock = threading.RLock()
206
+
207
+ def create_application(self, app_id: str, name: str = None) -> Application:
208
+ """Create a new application"""
209
+ with self._lock:
210
+ if app_id in self.applications:
211
+ return self.applications[app_id]
212
+
213
+ app = Application(app_id, name)
214
+ self.applications[app_id] = app
215
+ return app
216
+
217
+ def get_application(self, app_id: str) -> Optional[Application]:
218
+ """Get an application by ID"""
219
+ with self._lock:
220
+ return self.applications.get(app_id)
221
+
222
+ def route_message(self, message: Message) -> bool:
223
+ """Route a message to the appropriate handlers"""
224
+ with self._lock:
225
+ app = self.get_application(message.app_id)
226
+ if not app:
227
+ return False
228
+
229
+ channel = app.get_channel(message.channel_id)
230
+ if not channel:
231
+ return False
232
+
233
+ app.update_activity()
234
+
235
+ # Handle different message types
236
+ if message.message_type == MessageType.PUBLISH:
237
+ return self._handle_publish(message, channel)
238
+ elif message.message_type == MessageType.SUBSCRIBE:
239
+ return self._handle_subscribe(message, channel)
240
+ elif message.message_type == MessageType.UNSUBSCRIBE:
241
+ return self._handle_unsubscribe(message, channel)
242
+ else:
243
+ handler = channel.get_handler(message.message_type)
244
+ if handler:
245
+ try:
246
+ handler(message)
247
+ return True
248
+ except Exception as e:
249
+ print(f"Handler error: {e}")
250
+ return False
251
+
252
+ return False
253
+
254
+ def _handle_publish(self, message: Message, channel: Channel) -> bool:
255
+ """Handle publish message by broadcasting to subscribers"""
256
+ subscribers = channel.get_subscribers()
257
+ for subscriber_id in subscribers:
258
+ connection = self.connection_manager.get_connection(subscriber_id)
259
+ if connection:
260
+ self.send_to_connection(connection, message)
261
+ return True
262
+
263
+ def _handle_subscribe(self, message: Message, channel: Channel) -> bool:
264
+ """Handle subscribe message"""
265
+ # Extract connection ID from message payload
266
+ connection_id = message.payload.get('connection_id')
267
+ if connection_id:
268
+ channel.add_subscriber(connection_id)
269
+ return True
270
+ return False
271
+
272
+ def _handle_unsubscribe(self, message: Message, channel: Channel) -> bool:
273
+ """Handle unsubscribe message"""
274
+ connection_id = message.payload.get('connection_id')
275
+ if connection_id:
276
+ channel.remove_subscriber(connection_id)
277
+ return True
278
+ return False
279
+
280
+ def send_to_connection(self, connection, message: Message):
281
+ """Send message to a specific connection - to be implemented by subclasses"""
282
+ raise NotImplementedError("Subclasses must implement send_to_connection")
283
+
284
+ def start(self):
285
+ """Start the server - to be implemented by subclasses"""
286
+ raise NotImplementedError("Subclasses must implement start")
287
+
288
+ def stop(self):
289
+ """Stop the server - to be implemented by subclasses"""
290
+ raise NotImplementedError("Subclasses must implement stop")
291
+
292
+
293
+ class IPCClient:
294
+ """Base IPC Client class"""
295
+
296
+ def __init__(self, app_id: str, host: str = "localhost", port: int = 8888):
297
+ self.app_id = app_id
298
+ self.host = host
299
+ self.port = port
300
+ self.connection_id = str(uuid.uuid4())
301
+ self.connected = False
302
+ self.message_handlers: Dict[str, Callable] = {} # channel_id -> handler
303
+ self._lock = threading.RLock()
304
+
305
+ def connect(self) -> bool:
306
+ """Connect to the server - to be implemented by subclasses"""
307
+ raise NotImplementedError("Subclasses must implement connect")
308
+
309
+ def disconnect(self):
310
+ """Disconnect from the server - to be implemented by subclasses"""
311
+ raise NotImplementedError("Subclasses must implement disconnect")
312
+
313
+ def send_message(self, channel_id: str, message_type: MessageType, payload: Any, reply_to: str = None) -> str:
314
+ """Send a message to a specific channel"""
315
+ message = Message(
316
+ message_id=str(uuid.uuid4()),
317
+ app_id=self.app_id,
318
+ channel_id=channel_id,
319
+ message_type=message_type,
320
+ payload=payload,
321
+ timestamp=time.time(),
322
+ reply_to=reply_to
323
+ )
324
+ return self._send_message(message)
325
+
326
+ def _send_message(self, message: Message) -> str:
327
+ """Internal method to send message - to be implemented by subclasses"""
328
+ raise NotImplementedError("Subclasses must implement _send_message")
329
+
330
+ def subscribe(self, channel_id: str, handler: Callable = None) -> bool:
331
+ """Subscribe to a channel"""
332
+ if handler:
333
+ with self._lock:
334
+ self.message_handlers[channel_id] = handler
335
+
336
+ payload = {'connection_id': self.connection_id}
337
+ self.send_message(channel_id, MessageType.SUBSCRIBE, payload)
338
+ return True
339
+
340
+ def unsubscribe(self, channel_id: str) -> bool:
341
+ """Unsubscribe from a channel"""
342
+ with self._lock:
343
+ self.message_handlers.pop(channel_id, None)
344
+
345
+ payload = {'connection_id': self.connection_id}
346
+ self.send_message(channel_id, MessageType.UNSUBSCRIBE, payload)
347
+ return True
348
+
349
+ def publish(self, channel_id: str, data: Any) -> str:
350
+ """Publish data to a channel"""
351
+ return self.send_message(channel_id, MessageType.PUBLISH, data)
352
+
353
+ def request(self, channel_id: str, data: Any) -> str:
354
+ """Send a request message"""
355
+ return self.send_message(channel_id, MessageType.REQUEST, data)
356
+
357
+ def handle_message(self, message: Message):
358
+ """Handle incoming message"""
359
+ with self._lock:
360
+ handler = self.message_handlers.get(message.channel_id)
361
+ if handler:
362
+ try:
363
+ handler(message)
364
+ except Exception as e:
353
365
  print(f"Message handler error: {e}")