From 9c7887fae6a346492339961857bd2032a01e631e Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Mon, 2 Sep 2019 23:34:49 +0700 Subject: [PATCH] Implement a sendRoomMessage function (#18) --- Emulsion.Tests/Xmpp/SharpXmppHelper.fs | 2 +- Emulsion/Xmpp/AsyncXmppClient.fs | 6 ----- Emulsion/Xmpp/SharpXmppClient.fs | 32 ++++++++++++++++++++++++++ Emulsion/Xmpp/SharpXmppHelper.fs | 8 ++++++- Emulsion/Xmpp/XmppClient.fs | 2 +- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/Emulsion.Tests/Xmpp/SharpXmppHelper.fs b/Emulsion.Tests/Xmpp/SharpXmppHelper.fs index 2cd38324..a7cb773a 100644 --- a/Emulsion.Tests/Xmpp/SharpXmppHelper.fs +++ b/Emulsion.Tests/Xmpp/SharpXmppHelper.fs @@ -10,7 +10,7 @@ open Emulsion.Xmpp [] let ``Message body has a proper namespace``() = - let message = SharpXmppHelper.message "cthulhu@test" "text" + let message = SharpXmppHelper.message None "cthulhu@test" "text" let body = Seq.exactlyOne(message.Descendants()) Assert.Equal(XNamespace.Get "jabber:client", body.Name.Namespace) diff --git a/Emulsion/Xmpp/AsyncXmppClient.fs b/Emulsion/Xmpp/AsyncXmppClient.fs index 66bd03ff..9fdea825 100644 --- a/Emulsion/Xmpp/AsyncXmppClient.fs +++ b/Emulsion/Xmpp/AsyncXmppClient.fs @@ -31,12 +31,6 @@ type MessageDeliveryInfo = Async // Resolves after the message is guarante type IAsyncXmppClient = // TODO[F]: Implement the remaining functions in SharpXmppClient - /// Enter the room, returning the in-room lifetime. Will terminate if kicked or left the room. - abstract member EnterRoom : RoomInfo -> Async - - /// Sends the message to the room. - abstract member SendMessage : MessageInfo -> Async - /// Waits for the message to be delivered. abstract member AwaitMessageDelivery : MessageDeliveryInfo -> Async diff --git a/Emulsion/Xmpp/SharpXmppClient.fs b/Emulsion/Xmpp/SharpXmppClient.fs index b1af5196..031db26f 100644 --- a/Emulsion/Xmpp/SharpXmppClient.fs +++ b/Emulsion/Xmpp/SharpXmppClient.fs @@ -8,6 +8,7 @@ open SharpXMPP.XMPP open Emulsion open Emulsion.Lifetimes +open Emulsion.Xmpp open Emulsion.Xmpp.AsyncXmppClient open SharpXMPP.XMPP.Client.Elements @@ -53,6 +54,12 @@ let private extractException (roomInfo: RoomInfo) (presence: XMPPPresence) = |> Option.map (fun e -> Exception(sprintf "Error: %A" e)) else None +let private addMessageHandler (lifetime: Lifetime) (client: XmppClient) handler = + let handlerDelegate = XmppConnection.MessageHandler handler + client.add_Message handlerDelegate + lifetime.OnTermination(fun () -> client.remove_Message handlerDelegate) + +/// Enter the room, returning the in-room lifetime. Will terminate if kicked or left the room. let enterRoom (client: XmppClient) (lifetime: Lifetime) (roomInfo: RoomInfo): Async = async { use connectionLifetimeDefinition = lifetime.CreateNested() let connectionLifetime = connectionLifetimeDefinition.Lifetime @@ -92,3 +99,28 @@ let enterRoom (client: XmppClient) (lifetime: Lifetime) (roomInfo: RoomInfo): As roomLifetimeDefinition.Terminate() return ExceptionUtils.reraise ex } + +let private hasMessageId messageId message = + SharpXmppHelper.getMessageId message = Some messageId + +let private awaitMessageReceival (lifetime: Lifetime) client messageId = async { + use messageLifetimeDefinition = lifetime.CreateNested() + let messageLifetime = messageLifetimeDefinition.Lifetime + let messageReceivedTask = nestedTaskCompletionSource messageLifetime + addMessageHandler lifetime client (fun _ message -> + if hasMessageId messageId message then + messageReceivedTask.SetResult() + ) + + do! Async.AwaitTask messageReceivedTask.Task +} + +/// Sends the message to the room. Returns an object that allows to track the message receival. +let sendRoomMessage (lifetime: Lifetime) (client: XmppClient) (messageInfo: MessageInfo): Async = + async { + let messageId = Guid.NewGuid().ToString() // TODO[F]: Move to a new function + let message = SharpXmppHelper.message (Some messageId) messageInfo.RecipientJid messageInfo.Text + let! result = Async.StartChild <| awaitMessageReceival lifetime client messageId + client.Send message + return result + } diff --git a/Emulsion/Xmpp/SharpXmppHelper.fs b/Emulsion/Xmpp/SharpXmppHelper.fs index bb1d52fa..286c77ba 100644 --- a/Emulsion/Xmpp/SharpXmppHelper.fs +++ b/Emulsion/Xmpp/SharpXmppHelper.fs @@ -17,6 +17,7 @@ module Namespaces = module Attributes = let Code = XName.Get "code" let From = XName.Get "from" + let Id = XName.Get "id" let Jid = XName.Get "jid" let Stamp = XName.Get "stamp" let To = XName.Get "to" @@ -45,8 +46,10 @@ let joinRoom (client: XmppClient) (roomJid: string) (nickname: string): unit = let room = bookmark roomJid nickname client.BookmarkManager.Join(room) -let message (toAddr : string) (text : string) = +let message (id: string option) (toAddr: string) (text: string): XMPPMessage = + // TODO[F]: Make id a mandatory parameter? let m = XMPPMessage() + id |> Option.iter (fun id -> m.SetAttributeValue(Id, id)) m.SetAttributeValue(Type, "groupchat") m.SetAttributeValue(To, toAddr) let body = XElement(Body) @@ -81,6 +84,9 @@ let isGroupChatMessage(message: XMPPMessage): bool = let isEmptyMessage(message: XMPPMessage): bool = String.IsNullOrWhiteSpace message.Text +let getMessageId(message: XMPPMessage): string option = + getAttributeValue message Id + let parseMessage (message: XMPPMessage): Message = let nickname = getAttributeValue message From diff --git a/Emulsion/Xmpp/XmppClient.fs b/Emulsion/Xmpp/XmppClient.fs index 7c20b2b3..ec29bd4b 100644 --- a/Emulsion/Xmpp/XmppClient.fs +++ b/Emulsion/Xmpp/XmppClient.fs @@ -79,5 +79,5 @@ let run (logger: ILogger) (client: XmppClient): Async = let send (settings: XmppSettings) (client: XmppClient) (message: Message): unit = let text = sprintf "<%s> %s" message.author message.text - SharpXmppHelper.message settings.Room text + SharpXmppHelper.message None settings.Room text |> client.Send