Skip to content

Instantly share code, notes, and snippets.

@daniel-barlow
Created September 29, 2016 11:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daniel-barlow/16d959fc41f03c3b31ab2056ef8cccfb to your computer and use it in GitHub Desktop.
Save daniel-barlow/16d959fc41f03c3b31ab2056ef8cccfb to your computer and use it in GitHub Desktop.
WIP slightly pseudocode
;; Piezo is not a real bell
(ns piezo.core
(:require [clojure.core.async :as async :refer [chan >! >!! <! <!! go]]))
;; stubs to be filled in later
(defn http-listening-chan
"A channel which sends ring request maps on incoming HTTP requests
whose uri paths begin with `prefix`"
[prefix]
nil)
(defn twilio-start-call
"Cause Twilio to make an outgoing call to `dest`, registering
`webhook` as the URL for StatusCallback"
[dest webhook]
nil)
(defn call-leg-handler [destination]
(let [out-ch (chan)
id (make-uuid)
uri (str "/webook/call_leg/" id)
twilio-webhook-chan (http-listening-chan uri)]
(go
(twilio-start-call destination uri)
(loop [state {:destination destination}
event {}]
(let [new-state (merge state event)
status (:CallStatus new-state)]
(mongo-write-document new-state)
;; we send the new status out whatever it is, the receiver
;; can discard messages it doesn't want
(>! out-ch status)
;; keep processing until we get a 'final' status, then
;; close the channel
(if (#{"completed" "failed" "busy" "no-answer"} status)
(close out-ch)
(recur new-state (<! twilio-webhook-chan)))
)))
out-ch))
;; a conference is in one of these states -
;; setup: until the moderator (= consultant) has been called & is online
;; normal operation: non-moderator participants may join, leave, etc
;; ending: there are fewer than two participants left, kick everyone out
;; ended, close the channel and finish up
(defn conference-handler [moderator invited]
(let [out-ch (chan)
id (make-uuid)
uri (str "/webhook/conference/" id)
webhook-chan (http-listening-chan uri)
mod-chan (call-leg-handler moderator)
ui-chan (http-listening-chan (str "/ajax/" id))]
(go
(loop [s {:mod-status nil
:legs {moderator mod-chan}}]
(let [leg-chans (map :chan (vals legs))
chans (conj leg-chans webhook-chan ui-chan)
[ch v] (alts chans)]
(let [new-state
(cond-> s
;; is the moderator channel up and running? if so,
;; we can start to call the other legs - if we haven't
;; already
(and (= ch mod-chan) (= v "in-progress"))
(assoc :legs
(merge (:legs s)
(reduce
(fn [m d]
(assoc m d {:chan (call-leg-handler d)}))
(legs-with-no-chan))))
;; has one of the leg channels closed? This means the
;; associated call hung up, so mark it as completed
(and (some (partial = ch) leg-chans)
(nil? v))
(assoc-in [:legs (get-dest-for-ch ch) :completed?] true)
;; message from the UI channel. Do something ...
(= ch ui-chan) (do-ui-message v)
;; message from the webhook channel. Do something ...
(= ch webhook-chan) (do-webhook-message v))
;; tot up the member count to find out if it's time for
;; talk-time-bye-bye
active-members (count (reject :completed?
(vals (:legs new-state))))]
(if (< active-members 2)
;; kill the remaining conection, perhaps send some messages?
(dum-de-dum)
;; otherwise continue around the loop
(recur new-state)))))
(async/close out-chan))))
@daniel-barlow
Copy link
Author

The point where I got to with this: each channel is unidirectional. So for example we have a channel on which the call leg emits status messages and the conference can deal with them (e.g. by inviting customers to the call or by shutting down when people leave) but we can't use that same channel for commands back to the call-leg if we need to tell it to do stuff. Should we have another channel for that? The call leg is already taking "commands" (= "requests to change state") from twilio, but it's doing that on a channel that it creates for tself rather than one that's passed in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment