2

I'm trying to interface with Slack's RTM API with Aleph.

Currently I have this code:

(defn connect-socket []
  (let [conn @(http/websocket-client (get-websocket-url))]
    (s/consume #(prn %) conn)
    (send-message conn {:type "ping"}) ;; just to check if send-message works
    (s/on-closed conn (prn "closed"))))

It works well for the first few times, and then conn stops receiving messages from Slack after some inactivity. Neither does it print "closed", which seems to signify that the stream isn't closed. I'm not quite sure what is happening here.

How can I keep-alive the websocket connection, or auto-reconnect if disconnected? I've seen some code in the wild doing pings, but I don't think I understand the code well enough to adapt it.

1 Answers1

0

I think that you have to send ping messages because :

  • websocket deconnections are signaled by a special packet
  • there is no way to send this packet in case of ex. a network error
  • there is no "auto reconnect" feature, unless you maybe use a library that does it for you (I would personally love sente to support Aleph).

Therefore you have to do some "pings" regularly. Regarding the code, on-closed wants a function as second argument, so : (s/on-closed! conn #(prn "closed"))

nha
  • 17,623
  • 13
  • 87
  • 133
  • I'm currently pinging it as such: ``` (defn connect-socket [] (let [conn @(http/websocket-client (get-websocket-url))] (s/consume #(prn %) conn) (send-message conn {:type "ping"}) (d/loop [] (Thread/sleep 5000) (send-message conn {:type "ping"}) (d/recur)) (s/on-closed conn (prn "closed")))) ``` – jethro kuan Jan 19 '16 at 08:31
  • I don't see why this would not work for the pings (although I would have used `core.async` myself). I updated my answer to show the "closed" message. – nha Jan 19 '16 at 09:38
  • It works, but now it leaves me wondering how I should manage the websocket connection. Would you store the connection in an agent? I probably need to spawn an async loop checking for pongs, and if so reset the connection somehow. – jethro kuan Jan 19 '16 at 09:51
  • You probably meant storing the connections in an atom ? Yes, this is a sensible option. You could have a look at how https://github.com/ptaoussanis/sente does it for instance (they have a SubscriberStore protocol). You could also handle deconnections/reconnections by handling send errors. – nha Jan 19 '16 at 09:54
  • Thanks! I think I need to play around with aleph more. Will mark it as correct because pinging does keep the connection alive for the most part. – jethro kuan Jan 19 '16 at 10:38
  • Thanks ! One thing to keep in mind is that the client has to cooperate to make the reconnections. – nha Jan 19 '16 at 10:58
  • what slack does is you send another GET req with a token, and it'll return another WS url for reconnection. Storing it in an atom works, I can swap! it in a new connection, but I feel like I should just model it as a Component with a lifecycle, perhaps both. – jethro kuan Jan 20 '16 at 01:37
  • @jethrokuan even if you model it as a component you will need to have to store them in an atom or something else at some point. I do this with aleph to store SSE connections : a component to hold them, a store backed up by a protocol, and a memory implementation using an atom (this way I should be able to change the storage to something else. At least that's the idea). – nha Jan 20 '16 at 08:22
  • @jethrokuan Also that's interesting about slack, do you have more information about how it works ? – nha Jan 20 '16 at 08:23
  • 1
    to get the websocket url in slack you first need to sent an authenticated request to their rtm.start method. https://api.slack.com/methods/rtm.start . It does talk about rate limits, but not about disconnecting due to inactivity. – jethro kuan Jan 20 '16 at 10:46