class_name KeetFeed
extends Node
## Multi-room message aggregator.
## Polls multiple rooms and emits a unified timeline of messages.
signal feed_updated(messages: Array)
signal new_entry(room_id: String, message: Dictionary)
signal error_occurred(message: String)
var client: KeetUdsClient
var auto_poll: bool = false
var poll_interval: float = 5.0
var per_room_limit: int = 10
var _watched_rooms: Array[String] = []
var _poll_timer: Timer
var _known_ids: Dictionary = {}
var _timeline: Array = []
func _ready() -> void:
_poll_timer = Timer.new()
_poll_timer.wait_time = poll_interval
_poll_timer.timeout.connect(_poll_all)
add_child(_poll_timer)
if auto_poll and _watched_rooms.size() > 0:
start()
func add_room(room_id: String) -> void:
if room_id not in _watched_rooms:
_watched_rooms.append(room_id)
func remove_room(room_id: String) -> void:
_watched_rooms.erase(room_id)
func set_rooms(room_ids: Array[String]) -> void:
_watched_rooms = room_ids
func start() -> void:
if _poll_timer.is_stopped():
_poll_timer.start()
_poll_all()
func stop() -> void:
_poll_timer.stop()
func get_timeline() -> Array:
return _timeline
func refresh() -> void:
_poll_all()
func _poll_all() -> void:
if client == null or not client.is_connected:
return
for rid: String in _watched_rooms:
_poll_room(rid)
func _poll_room(room_id: String) -> void:
client.call_rpc("read", {"roomId": room_id, "limit": per_room_limit}, func(result, err):
if err != null:
return
if not result is Array:
return
var has_new := false
for msg: Variant in result:
if not msg is Dictionary:
continue
var mid: String = _msg_id(room_id, msg)
if _known_ids.has(mid):
continue
_known_ids[mid] = true
var entry: Dictionary = {
"room_id": room_id,
"message": msg,
"timestamp": msg.get("timestamp", 0)
}
_timeline.append(entry)
has_new = true
new_entry.emit(room_id, msg)
if has_new:
_timeline.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
return a.get("timestamp", 0) > b.get("timestamp", 0)
)
if _timeline.size() > 200:
_timeline.resize(200)
feed_updated.emit(_timeline)
)
func _msg_id(room_id: String, msg: Dictionary) -> String:
var id: Variant = msg.get("id", null)
if id is Dictionary:
return "%s/%s:%d" % [room_id, id.get("deviceId", ""), int(id.get("seq", 0))]
if id is String:
return "%s/%s" % [room_id, id]
return "%s/%s:%d" % [room_id, msg.get("memberId", ""), int(msg.get("seq", 0))]