| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2024-09-24 18:51:45 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2024-09-24 18:51:45 UTC |
| parent | e8a4f64875b582af2f43cfb7773cd3f1ea1769b8 |
| Makefile | +1 | -0 |
| npm/index.ts | +1 | -0 |
| snikket/Chat.hx | +10 | -2 |
| snikket/ChatMessage.hx | +1 | -1 |
| snikket/Client.hx | +29 | -1 |
| snikket/Message.hx | +23 | -10 |
| snikket/MessageSync.hx | +1 | -1 |
diff --git a/Makefile b/Makefile index 5036ce2..6946754 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ npm/snikket.js: sed -i 's/snikket\.UiState/enums.UiState/g' npm/snikket.d.ts sed -i 's/snikket\.MessageStatus/enums.MessageStatus/g' npm/snikket.d.ts sed -i 's/snikket\.MessageDirection/enums.MessageDirection/g' npm/snikket.d.ts + sed -i 's/snikket\.UserState/enums.UserState/g' npm/snikket.d.ts sed -i '1ivar exports = {};' npm/snikket.js echo "export const snikket = exports.snikket;" >> npm/snikket.js diff --git a/npm/index.ts b/npm/index.ts index 5a964cb..a66f7d6 100644 --- a/npm/index.ts +++ b/npm/index.ts @@ -20,6 +20,7 @@ export import jingle = snikket.jingle; export import UiState = enums.UiState; export import MessageStatus = enums.MessageStatus; export import MessageDirection = enums.MessageDirection; +export import UserState = enums.UserState; export namespace persistence { export import browser = browserp; diff --git a/snikket/Chat.hx b/snikket/Chat.hx index 9457452..5fa0926 100644 --- a/snikket/Chat.hx +++ b/snikket/Chat.hx @@ -23,6 +23,14 @@ enum abstract UiState(Int) { var Closed; // Archived } +enum abstract UserState(Int) { + var Gone; + var Inactive; + var Active; + var Composing; + var Paused; +} + #if cpp @:build(HaxeCBridge.expose()) @:build(HaxeSwiftBridge.expose()) @@ -611,7 +619,7 @@ class DirectChat extends Chat { if (typingTimer != null) typingTimer.stop(); client.chatActivity(this); message = prepareOutgoingMessage(message); - final fromStanza = Message.fromStanza(message.asStanza(), client.jid); + final fromStanza = Message.fromStanza(message.asStanza(), client.jid).parsed; switch (fromStanza) { case ChatMessageStanza(_): persistence.storeMessage(client.accountId(), message, (stored) -> { @@ -1021,7 +1029,7 @@ class Channel extends Chat { final stanza = message.asStanza(); // Fake from as it will look on reflection for storage purposes stanza.attr.set("from", getFullJid().asString()); - final fromStanza = Message.fromStanza(stanza, client.jid); + final fromStanza = Message.fromStanza(stanza, client.jid).parsed; stanza.attr.set("from", client.jid.asString()); switch (fromStanza) { case ChatMessageStanza(_): diff --git a/snikket/ChatMessage.hx b/snikket/ChatMessage.hx index 11e7112..03ee468 100644 --- a/snikket/ChatMessage.hx +++ b/snikket/ChatMessage.hx @@ -142,7 +142,7 @@ class ChatMessage { @:allow(snikket) private static function fromStanza(stanza:Stanza, localJid:JID):Null<ChatMessage> { - switch Message.fromStanza(stanza, localJid) { + switch Message.fromStanza(stanza, localJid).parsed { case ChatMessageStanza(message): return message; default: diff --git a/snikket/Client.hx b/snikket/Client.hx index 6f7b0c7..a967bb3 100644 --- a/snikket/Client.hx +++ b/snikket/Client.hx @@ -44,6 +44,7 @@ class Client extends EventEmitter { public var sendAvailable(null, default): Bool = true; private var stream:GenericStream; private var chatMessageHandlers: Array<(ChatMessage)->Void> = []; + private var chatStateHandlers: Array<(String,String,Null<String>,UserState)->Void> = []; @:allow(snikket) private var jid(default,null):JID; private var chats: Array<Chat> = []; @@ -196,7 +197,8 @@ class Client extends EventEmitter { } } - switch (Message.fromStanza(stanza, this.jid)) { + final message = Message.fromStanza(stanza, this.jid); + switch (message.parsed) { case ChatMessageStanza(chatMessage): var chat = getChat(chatMessage.chatId()); if (chat == null && stanza.attr.get("type") != "groupchat") chat = getDirectChat(chatMessage.chatId()); @@ -222,6 +224,23 @@ class Client extends EventEmitter { // ignore } + if (stanza.attr.get("type") != "error") { + final chatState = stanza.getChild(null, "http://jabber.org/protocol/chatstates"); + final userState = switch (chatState?.name) { + case "active": UserState.Active; + case "inactive": UserState.Inactive; + case "gone": UserState.Gone; + case "composing": UserState.Composing; + case "paused": UserState.Paused; + default: null; + }; + if (userState != null) { + for (handler in chatStateHandlers) { + handler(message.senderId, message.chatId, message.threadId, userState); + } + } + } + final pubsubEvent = PubsubEvent.fromStanza(stanza); if (pubsubEvent != null && pubsubEvent.getFrom() != null && pubsubEvent.getNode() == "urn:xmpp:avatar:metadata" && pubsubEvent.getItems().length > 0) { final item = pubsubEvent.getItems()[0]; @@ -857,6 +876,15 @@ class Client extends EventEmitter { }); } + #if !cpp + // TODO: haxe cpp erases enum into int, so using it as a callback arg is hard + // could just use int in C bindings, or need to come up with a good strategy + // for the wrapper + public function addUserStateListener(handler: (String,String,Null<String>,UserState)->Void):Void { + chatStateHandlers.push(handler); + } + #end + /** Event fired when a new ChatMessage comes in on any Chat Also fires when status of a ChatMessage changes, diff --git a/snikket/Message.hx b/snikket/Message.hx index 66f66d2..1c7a079 100644 --- a/snikket/Message.hx +++ b/snikket/Message.hx @@ -23,8 +23,22 @@ enum MessageStanza { @:nullSafety(Strict) class Message { - public static function fromStanza(stanza:Stanza, localJid:JID, ?inputTimestamp: String):MessageStanza { - if (stanza.attr.get("type") == "error") return ErrorMessageStanza(stanza); + public final chatId: String; + public final senderId: String; + public final threadId: Null<String>; + public final parsed: MessageStanza; + + private function new(chatId: String, senderId: String, threadId: Null<String>, parsed: MessageStanza) { + this.chatId = chatId; + this.senderId = senderId; + this.threadId = threadId; + this.parsed = parsed; + } + + public static function fromStanza(stanza:Stanza, localJid:JID, ?inputTimestamp: String):Message { + final fromAttr = stanza.attr.get("from"); + final from = fromAttr == null ? localJid.domain : fromAttr; + if (stanza.attr.get("type") == "error") return new Message(from, from, null, ErrorMessageStanza(stanza)); var msg = new ChatMessage(); final timestamp = stanza.findText("{urn:xmpp:delay}delay@stamp") ?? inputTimestamp ?? Date.format(std.Date.now()); @@ -35,8 +49,7 @@ class Message { if (msg.text != null && (msg.lang == null || msg.lang == "")) { msg.lang = stanza.getChild("body")?.attr.get("xml:lang"); } - final from = stanza.attr.get("from"); - msg.from = from == null ? null : JID.parse(from); + msg.from = JID.parse(from); msg.isGroupchat = stanza.attr.get("type") == "groupchat"; msg.sender = stanza.attr.get("type") == "groupchat" ? msg.from : msg.from?.asBare(); final localJidBare = localJid.asBare(); @@ -97,7 +110,7 @@ class Message { replyTo.clear(); } else if (jid == null) { trace("No support for addressing to non-jid", address); - return UnknownMessageStanza(stanza); + return new Message(msg.chatId(), msg.senderId(), msg.threadId, UnknownMessageStanza(stanza)); } else if (address.attr.get("type") == "to" || address.attr.get("type") == "cc") { recipients[JID.parse(jid).asBare().asString()] = true; if (!anyExtendedReplyTo) replyTo[JID.parse(jid).asString()] = true; // reply all @@ -124,7 +137,7 @@ class Message { final msgFrom = msg.from; if (msg.direction == MessageReceived && msgFrom != null && msg.replyTo.find((r) -> r.asBare().equals(msgFrom.asBare())) == null) { trace("Don't know what chat message without from in replyTo belongs in", stanza); - return UnknownMessageStanza(stanza); + return new Message(msg.chatId(), msg.senderId(), msg.threadId, UnknownMessageStanza(stanza)); } final reactionsEl = stanza.getChild("reactions", "urn:xmpp:reactions:0"); @@ -133,7 +146,7 @@ class Message { final reactions = reactionsEl.allTags("reaction").map((r) -> r.getText()); final reactionId = reactionsEl.attr.get("id"); if (reactionId != null) { - return ReactionUpdateStanza(new ReactionUpdate( + return new Message(msg.chatId(), msg.senderId(), msg.threadId, ReactionUpdateStanza(new ReactionUpdate( stanza.attr.get("id") ?? ID.long(), stanza.attr.get("type") == "groupchat" ? reactionId : null, stanza.attr.get("type") != "groupchat" ? reactionId : null, @@ -141,7 +154,7 @@ class Message { timestamp, msg.senderId(), reactions - )); + ))); } } @@ -156,7 +169,7 @@ class Message { msg.attachSims(sims); } - if (msg.text == null && msg.attachments.length < 1) return UnknownMessageStanza(stanza); + if (msg.text == null && msg.attachments.length < 1) return new Message(msg.chatId(), msg.senderId(), msg.threadId, UnknownMessageStanza(stanza)); for (fallback in stanza.allTags("fallback", "urn:xmpp:fallback:0")) { msg.payloads.push(fallback); @@ -192,6 +205,6 @@ class Message { Reflect.setField(msg, "localId", replaceId); } - return ChatMessageStanza(msg); + return new Message(msg.chatId(), msg.senderId(), msg.threadId, ChatMessageStanza(msg)); } } diff --git a/snikket/MessageSync.hx b/snikket/MessageSync.hx index e834268..dde65c5 100644 --- a/snikket/MessageSync.hx +++ b/snikket/MessageSync.hx @@ -80,7 +80,7 @@ class MessageSync { jmi.set(jmiChildren[0].attr.get("id"), originalMessage); } - var msg = Message.fromStanza(originalMessage, client.jid, timestamp); + final msg = Message.fromStanza(originalMessage, client.jid, timestamp).parsed; switch (msg) { case ChatMessageStanza(chatMessage):