OverviewFilesCommitsIssuesDocsReleasesPeers

godot-keet / addons / keet_social / keet_discovery.gd

236 lines · gd ref:
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)
	)