git » sdk » commit e520d69

Helper to check if we can send messages to a chat

author Stephen Paul Weber
2025-11-19 19:46:26 UTC
committer Stephen Paul Weber
2025-11-19 19:46:26 UTC
parent 78200c923ecb8d868321155b0156230de2c507cd

Helper to check if we can send messages to a chat

borogove/Chat.hx +25 -5
borogove/MucUser.hx +35 -0
borogove/Presence.hx +3 -2

diff --git a/borogove/Chat.hx b/borogove/Chat.hx
index 58f823f..3ef2f9c 100644
--- a/borogove/Chat.hx
+++ b/borogove/Chat.hx
@@ -694,6 +694,13 @@ abstract class Chat {
 		}
 	}
 
+	/**
+		Can the user send messages to this chat?
+	**/
+	public function canSend() {
+		return Caps.withFeature(getCaps(), "urn:xmpp:noreply:0").length < 1;
+	}
+
 	/**
 		Does this chat provide a menu of commands?
 	**/
@@ -1200,13 +1207,25 @@ class Channel extends Chat {
 		return disco?.data?.find(d -> d.field("FORM_TYPE")?.value?.at(0) == "http://jabber.org/protocol/muc#roominfo");
 	}
 
+	@HaxeCBridge.noemit // on superclass as abstract
+	override public function canSend() {
+		if (!super.canSend()) return false;
+		if (_nickInUse == null) return true;
+
+		final p = presence[_nickInUse];
+		if (p == null) return true;
+
+		return p.mucUser.role != "visitor";
+	}
+
 	@:allow(borogove)
 	override private function getCaps():KeyValueIterator<String, Caps> {
 		return ["" => disco].keyValueIterator();
 	}
 
-	override public function setPresence(resource:String, presence:Presence) {
-		final oneTen = presence?.mucUser?.allTags("status").find((status) -> status.attr.get("code") == "110");
+	@:allow(borogove)
+	override private function setPresence(resource:String, presence:Presence) {
+		final oneTen = presence?.mucUser?.statusCodes?.find((status) -> status == "110");
 		if (oneTen != null) {
 			_nickInUse = resource;
 			outbox.start();
@@ -1216,14 +1235,15 @@ class Channel extends Chat {
 		}
 		if (presence != null && presence.mucUser != null && oneTen == null) {
 			final existing = this.presence.get(resource);
-			if (existing != null && existing?.mucUser?.allTags("status").find((status) -> status.attr.get("code") == "110") != null) {
-				presence.mucUser.tag("status", { code: "110" });
+			if (existing != null && existing?.mucUser?.statusCodes?.find((status) -> status == "110") != null) {
+				final mucUser: Stanza = presence.mucUser;
+				mucUser.tag("status", { code: "110" }).up();
 				setPresence(resource, presence);
 				return;
 			}
 		}
 		super.setPresence(resource, presence);
-		final tripleThree = presence?.mucUser?.allTags("status").find((status) -> status.attr.get("code") == "333");
+		final tripleThree = presence?.mucUser?.statusCodes?.find((status) -> status == "333");
 		if (oneTen != null && tripleThree != null) {
 			selfPing(true);
 		}
diff --git a/borogove/MucUser.hx b/borogove/MucUser.hx
new file mode 100644
index 0000000..1c8ced7
--- /dev/null
+++ b/borogove/MucUser.hx
@@ -0,0 +1,35 @@
+package borogove;
+
+import borogove.Stanza;
+import borogove.JID;
+
+@:forward(toString)
+abstract MucUser(Stanza) from Stanza to Stanza {
+	public var statusCodes(get, never): Array<String>;
+	public var role(get, never): String;
+	public var affiliation(get, never): String;
+	public var jid(get, never): Null<JID>;
+
+	inline private function get_statusCodes() {
+		return this.allTags("status").map(el -> el.attr.get("code"));
+	}
+
+	inline private function get_role() {
+		return item()?.attr?.get("role") ?? "none";
+	}
+
+	inline private function get_affiliation() {
+		return item()?.attr?.get("affiliation") ?? "none";
+	}
+
+	inline private function get_jid() {
+		final jid = item()?.attr?.get("jid");
+		if (jid == null) return null;
+
+		return JID.parse(jid);
+	}
+
+	inline private function item() {
+		return this.getChild("item");
+	}
+}
diff --git a/borogove/Presence.hx b/borogove/Presence.hx
index 64b9261..987e916 100644
--- a/borogove/Presence.hx
+++ b/borogove/Presence.hx
@@ -1,14 +1,15 @@
 package borogove;
 
+import borogove.MucUser;
 import borogove.Hash;
 
 @:expose
 class Presence {
 	public var caps:Null<Caps>;
-	public final mucUser:Null<Stanza>;
+	public final mucUser:Null<MucUser>;
 	public final avatarHash:Null<Hash>;
 
-	public function new(caps: Null<Caps>, mucUser: Null<Stanza>, avatarHash: Null<Hash>) {
+	public function new(caps: Null<Caps>, mucUser: Null<MucUser>, avatarHash: Null<Hash>) {
 		this.caps = caps;
 		this.mucUser = mucUser;
 		this.avatarHash = avatarHash;