class_name KeetDiscovery
extends Node
## Room discovery using registry rooms (replaces Autobase registries).
## A "registry room" is a normal keet room with _hs:room-meta type "registry".
## Rooms are listed/unlisted via _hs:room-listing messages in registry rooms.
signal rooms_listed(rooms: Array)
signal room_registered(room_id: String)
signal room_unregistered(room_id: String)
signal discovery_error(message: String)
signal registries_listed(registries: Array)
signal registry_added(room_id: String, title: String)
signal registry_removed(room_id: String)
signal topic_results(rooms: Array)
signal topic_announced(room_id: String, topics: Array)
signal topic_unannounced(room_id: String)
var client: KeetUdsClient
## List all registry-type rooms the user has joined.
## Returns: [{ roomId, title }]
func list_registries(callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("registry.rooms", {}, func(result, err):
if err != null:
discovery_error.emit("list_registries: %s" % str(err))
var empty: Array = []
registries_listed.emit(empty)
if callback.is_valid():
callback.call(empty)
return
var regs: Array = result if result is Array else []
registries_listed.emit(regs)
if callback.is_valid():
callback.call(regs)
)
## List rooms registered in a specific registry room.
## Optional query filters by title/description/topics.
func list_rooms(registry_room_id: String, query: String = "", callback: Callable = Callable()) -> void:
_require_client()
var params := {"registryRoomId": registry_room_id}
if query != "":
params["query"] = query
client.call_rpc("registry.list", params, func(result, err):
if err != null:
discovery_error.emit("list_rooms: %s" % str(err))
return
var rooms: Array = result if result is Array else []
rooms_listed.emit(rooms)
if callback.is_valid():
callback.call(rooms)
)
## Register a room in a registry room.
func register_room(registry_room_id: String, room_id: String, title: String, description: String = "", topics: Array = [], invite_token: String = "", callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("registry.add", {
"registryRoomId": registry_room_id,
"roomId": room_id,
"title": title,
"description": description,
"topics": topics,
"inviteToken": invite_token
}, func(result, err):
if err != null:
discovery_error.emit("register_room: %s" % str(err))
if callback.is_valid():
callback.call(null)
return
room_registered.emit(room_id)
if callback.is_valid():
callback.call(result)
)
## Unregister a room from a registry room.
func unregister_room(registry_room_id: String, room_id: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("registry.remove", {
"registryRoomId": registry_room_id,
"roomId": room_id
}, func(result, err):
if err != null:
discovery_error.emit("unregister_room: %s" % str(err))
if callback.is_valid():
callback.call(null)
return
room_unregistered.emit(room_id)
if callback.is_valid():
callback.call(result)
)
## Create a new registry room. Creates a keet room and tags it as type "registry".
func create_registry(title: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("room.create", {"title": title}, func(result, err):
if err != null:
discovery_error.emit("create_registry: %s" % str(err))
return
var room_id: String = ""
if result is Dictionary:
room_id = result.get("roomId", result.get("id", ""))
elif result is String:
room_id = result
if room_id == "":
discovery_error.emit("create_registry: no roomId returned")
return
# Tag it as a registry room
client.call_rpc("meta.set", {
"roomId": room_id,
"metadata": {"type": "registry"}
}, func(_meta_result, meta_err):
if meta_err != null:
discovery_error.emit("create_registry meta: %s" % str(meta_err))
registry_added.emit(room_id, title)
if callback.is_valid():
callback.call({"roomId": room_id, "title": title})
)
)
## Join an existing registry room via invite token.
func join_registry(invite: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("room.join", {"invite": invite}, func(result, err):
if err != null:
discovery_error.emit("join_registry: %s" % str(err))
return
var room_id: String = ""
if result is Dictionary:
room_id = result.get("roomId", result.get("id", ""))
registry_added.emit(room_id, "")
if callback.is_valid():
callback.call(result)
)
## Leave a registry room.
func remove_registry(room_id: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("room.leave", {"roomId": room_id}, func(result, err):
if err != null:
discovery_error.emit("remove_registry: %s" % str(err))
return
registry_removed.emit(room_id)
if callback.is_valid():
callback.call(result)
)
## Get room config (title, description, avatar) for a registry room.
func get_room_config(room_id: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("room.config", {"roomId": room_id}, func(result, err):
if err != null:
discovery_error.emit("get_room_config: %s" % str(err))
return
if callback.is_valid():
callback.call(result if result is Dictionary else {})
)
func _require_client() -> void:
if client == null:
push_error("KeetDiscovery: client not set")
# --- DHT Topic Discovery ---
## Announce a discoverable room on the DHT by its topics.
## Room must have discoverable: true and topics set in _hs:room-meta.
func announce_room(room_id: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("topic.announce", {"roomId": room_id}, func(result, err):
if err != null:
discovery_error.emit("announce_room: %s" % str(err))
if callback.is_valid():
callback.call(null)
return
var topics: Array = result.get("topics", []) if result is Dictionary else []
topic_announced.emit(room_id, topics)
if callback.is_valid():
callback.call(result)
)
## Stop announcing a room on the DHT.
func unannounce_room(room_id: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("topic.unannounce", {"roomId": room_id}, func(result, err):
if err != null:
discovery_error.emit("unannounce_room: %s" % str(err))
return
topic_unannounced.emit(room_id)
if callback.is_valid():
callback.call(result)
)
## Search the DHT for rooms matching a topic query.
## Returns: [{ roomId, title, description, topics, inviteToken, ts }]
func search_topic(query: String, callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("topic.search", {"query": query}, func(result, err):
if err != null:
discovery_error.emit("search_topic: %s" % str(err))
var empty: Array = []
topic_results.emit(empty)
if callback.is_valid():
callback.call(empty)
return
var rooms: Array = result if result is Array else []
topic_results.emit(rooms)
if callback.is_valid():
callback.call(rooms)
)
## Announce all discoverable rooms on the DHT.
func announce_discoverable_rooms(callback: Callable = Callable()) -> void:
_require_client()
client.call_rpc("topic.announceAll", {}, func(result, err):
if err != null:
discovery_error.emit("announce_all: %s" % str(err))
return
if callback.is_valid():
callback.call(result)
)