git » sdk » commit 99a0315

Enable fetching profile for any participant

author Stephen Paul Weber
2025-12-03 15:12:02 UTC
committer Stephen Paul Weber
2025-12-03 15:12:02 UTC
parent dd360805111b8404bfb713e14bbd17030538b005

Enable fetching profile for any participant

borogove/Chat.hx +5 -4
borogove/Participant.hx +23 -1
borogove/Profile.hx +107 -0
npm/index.ts +2 -0

diff --git a/borogove/Chat.hx b/borogove/Chat.hx
index cb6511f..3c8f2ab 100644
--- a/borogove/Chat.hx
+++ b/borogove/Chat.hx
@@ -991,7 +991,7 @@ class DirectChat extends Chat {
 	@HaxeCBridge.noemit // on superclass as abstract
 	public function getParticipantDetails(participantId:String): Participant {
 		final chat = client.getDirectChat(participantId);
-		return new Participant(chat.getDisplayName(), chat.getPhoto(), chat.getPlaceholder(), chat.chatId == client.accountId());
+		return new Participant(chat.getDisplayName(), chat.getPhoto(), chat.getPlaceholder(), chat.chatId == client.accountId(), JID.parse(participantId));
 	}
 
 	@HaxeCBridge.noemit // on superclass as abstract
@@ -1627,11 +1627,12 @@ class Channel extends Chat {
 	public function getParticipantDetails(participantId:String): Participant {
 		if (participantId == getFullJid().asString()) {
 			final chat = client.getDirectChat(client.accountId(), false);
-			return new Participant(client.displayName(), chat.getPhoto(), chat.getPlaceholder(), true);
+			return new Participant(client.displayName(), chat.getPhoto(), chat.getPlaceholder(), true, JID.parse(chat.chatId));
 		} else {
-			final nick = JID.parse(participantId).resource;
+			final jid = JID.parse(participantId);
+			final nick = jid.resource;
 			final placeholderUri = Color.defaultPhoto(participantId, nick == null ? " " : nick.charAt(0));
-			return new Participant(nick ?? "", presence[nick]?.avatarHash?.toUri(), placeholderUri, false);
+			return new Participant(nick ?? "", presence[nick]?.avatarHash?.toUri(), placeholderUri, false, jid);
 		}
 	}
 
diff --git a/borogove/Participant.hx b/borogove/Participant.hx
index a68ff86..a700638 100644
--- a/borogove/Participant.hx
+++ b/borogove/Participant.hx
@@ -1,5 +1,9 @@
 package borogove;
 
+import thenshim.Promise;
+
+import borogove.queries.PubsubGet;
+
 #if cpp
 import HaxeCBridge;
 #end
@@ -15,12 +19,30 @@ class Participant {
 	public final photoUri: Null<String>;
 	public final placeholderUri: String;
 	public final isSelf: Bool;
+	private final jid: JID;
 
 	@:allow(borogove)
-	private function new(displayName: String, photoUri: Null<String>, placeholderUri: String, isSelf: Bool) {
+	private function new(displayName: String, photoUri: Null<String>, placeholderUri: String, isSelf: Bool, jid: JID) {
 		this.displayName = displayName;
 		this.photoUri = photoUri;
 		this.placeholderUri = placeholderUri;
 		this.isSelf = isSelf;
+		this.jid = jid;
+	}
+
+	public function profile(client: Client): Promise<Profile> {
+		return new Promise((resolve, reject) -> {
+			final get = new PubsubGet(jid.asString(), "urn:xmpp:vcard4");
+			get.onFinished(() -> {
+				final item = get.getResult()[0];
+				final vcard = item?.getChild("vcard", "urn:ietf:params:xml:ns:vcard-4.0");
+				if (vcard == null) {
+					resolve(new Profile(new Stanza("vcard")));
+				} else {
+					resolve(new Profile(vcard));
+				}
+			});
+			client.sendQuery(get);
+		});
 	}
 }
diff --git a/borogove/Profile.hx b/borogove/Profile.hx
new file mode 100644
index 0000000..6948e59
--- /dev/null
+++ b/borogove/Profile.hx
@@ -0,0 +1,107 @@
+package borogove;
+
+import borogove.Stanza;
+
+#if cpp
+import HaxeCBridge;
+#end
+
+@:expose
+@:nullSafety(Strict)
+#if cpp
+@:build(HaxeCBridge.expose())
+@:build(HaxeSwiftBridge.expose())
+#end
+class Profile {
+	private final vcard: Stanza;
+
+	@:allow(borogove)
+	private function new(vcard: Stanza) {
+		this.vcard = vcard;
+	}
+
+	/**
+		Get a property from the profile
+
+		@param key what property to get
+		@returns the property value
+	**/
+	public function get(key: String): Array<ProfileItem> {
+		return vcard.allTags(key).map(child -> new ProfileItem(child));
+	}
+
+	/**
+		List the regular properties which can be represented by a ProfileItem
+	**/
+	public function properties(): Array<String> {
+		final names = vcard.allTags().map(el -> el.name);
+		final result = [];
+		final seen: Map<String, Bool> = [];
+
+		for (name in names) {
+			// Compound properties that don't follow the normal rules
+			if (seen[name] != true && !["n", "adr", "gender"].contains(name)) {
+				seen[name] = true;
+				result.push(name);
+			}
+		}
+
+		return result;
+	}
+}
+
+@:expose
+@:nullSafety(Strict)
+#if cpp
+@:build(HaxeCBridge.expose())
+@:build(HaxeSwiftBridge.expose())
+#end
+class ProfileItem {
+	private final item: Stanza;
+
+	@:allow(borogove.Profile)
+	private function new(item: Stanza) {
+		this.item = item;
+	}
+
+	public function key() {
+		return item.name;
+	}
+
+	public function parameters() {
+		final params = item.getChild("parameters")?.allTags() ?? [];
+		return params.map(param -> new ProfileItem(param));
+	}
+
+	public function text() {
+		return item.allTags("text").map(s -> s.getText());
+	}
+
+	public function uri() {
+		return item.allTags("uri").map(s -> s.getText());
+	}
+
+	public function date() {
+		return item.allTags("date").map(s -> s.getText());
+	}
+
+	public function time() {
+		return item.allTags("time").map(s -> s.getText());
+	}
+
+	public function datetime() {
+		return item.allTags("datetime").map(s -> s.getText());
+	}
+
+	public function boolean() {
+		return item.allTags("boolean").map(s -> s.getText() == "true");
+	}
+
+	public function integer() {
+		return item.allTags("integer").map(s -> Std.parseInt(s.getText()));
+	}
+
+	public function languageTag() {
+		return item.allTags("language-tag").map(s -> s.getText());
+	}
+}
diff --git a/npm/index.ts b/npm/index.ts
index 286bc83..b8db24a 100644
--- a/npm/index.ts
+++ b/npm/index.ts
@@ -31,6 +31,8 @@ export {
     borogove_Notification as Notification,
     borogove_Participant as Participant,
     borogove_Persistence as Persistence,
+    borogove_Profile as Profile,
+    borogove_ProfileItem as ProfileItem,
     borogove_Push as Push,
     borogove_Reaction as Reaction,
     borogove_Register as Register,