| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-12-03 15:12:02 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-12-03 15:12:02 UTC |
| parent | dd360805111b8404bfb713e14bbd17030538b005 |
| 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,