hubot-genesys-cloud
Version:
Genesys Cloud adapter for Hubot
293 lines (218 loc) • 9.62 kB
text/coffeescript
Controller = require './framework/Controller.coffee'
module.exports = class PresenceController extends Controller
presences: {}
_timeout: 1000 * 60 * 10
:
subscribeToPresence: (jids...) ->
jid for jid in jids
return
requestPresence: (jids...) ->
jid for jid in jids
return
unsubscribeFromPresence: (jids...) ->
.debug 'presence', 'unsubscribeFromPresence', jids
for jid in jids
.send new ltx.Element 'presence', from:.jid.toString(), to:jid, type:'unsubscribe'
delete [jid]
return
setPresence: (presence) ->
.debug 'presence', 'setPresence', presence, .presence
if presence is 'offline' then throw Error('Do not set your presence to offline, please use disconnect() instead.')
unless presence in ['online', 'away', 'busy'] then throw Error('invalid presence '+presence)
if presence is .presence then return
.presence = presence
.debug 'presence', 'lib setPresence', presence
return
#callback will be called when the status change succeeds or fails, with a first argument being an Error with a message and a "code" property with the numeric status code
setStatus: (status, callback) ->
status ?= ''
.debug 'presence', 'setStatus', status, .status
if status is .status then return
.status = status
return
setLocation: (location) ->
location ?= ''
.debug 'presence', 'setLocation', location, .location
if location is .location then return
.location = location
return
: ['presence']
:
onMePresence: (stanza) -> stanza.is('presence') and stanza.attrs.from is .jid?.bare().toString()
onPresence: (stanza) -> stanza.is('presence') and stanza.attrs.from isnt .jid?.bare().toString() and stanza.attrs.type in [undefined, 'unavailable'] and !stanza.attrs.from.match(//)
onPresenceError: (stanza) -> stanza.is('presence') and stanza.attrs.type is "error" and stanza.attrs.to is .jid?.toString()
ignore: (stanza) -> stanza.is('presence') and stanza.attrs.from isnt .jid?.bare().toString() and stanza.attrs.type in ['subscribed', 'unsubscribed']
extraSubscriptions: []
constructor: ->
super
= []
.on 'connect', =>
debug 'presence', 'connected, sending self subscription'
= {}
= {}
= {}
.presence = null
.status = ''
.location = ''
# subscribe to self presence
.send new ltx.Element 'presence', id:'mepresence', from:.jid.toString(), to:.jid.bare().toString(), type:'subscribe'
for jid in
jid
focusHandler = =>
.debug 'focus', 'presence focused'
= true
if
clearTimeout
= null
setTimeout (=>
if
.presence =
= null
), 500
if global.window
$(window).on 'focus', focusHandler
$(window).on 'mouseover', _.debounce(focusHandler, 5000, true)
$(window).on 'blur', =>
.debug 'focus', 'blur'
= false
if then return
= setTimeout (=>
if (presence = .presence) in [undefined, 'offline', 'idle'] then return
= presence
.presence = 'idle'
),
ignore: ->
onPresenceError: (stanza) ->
stanza.attrs.id
callback = [stanza.attrs.id]
if callback
errorNode = stanza.getChild "error"
errorText = errorNode.getChild "text"
error = new Error errorText
error.code = parseInt errorNode.attrs.code
.debug 'presence', 'Send presence failed: #{error.code} - #{errorText}'
delete [stanza.attrs.id]
callback error
onMePresence: (stanza) ->
if stanza.attrs.type in ['subscribed', 'unsubscribed']
return
debug 'presence', 'handling self presence'
callback = [stanza.attrs.id]
if callback
callback(null) #null error to indicate success
delete [stanza.attrs.id]
# The goal of these two lines is to save the last seen incoming stanza
# for processing, and the process it once the server has replied to any
# presence changes.
= .bind @, stanza
stanza.attrs.id
_removeWaitingReply: (id) ->
if _.has , id
timer = [id]
if timer
clearTimeout timer
delete [id]
if 0 is _.size
if
func =
= null
func()
_reactToStanza: (stanza) ->
if .disconnected
return
[presence, status, location] = stanza
.debug 'presence', 'onMePresence', presence, status, stanza.toString()
if presence isnt .presence and presence in ['online', 'busy', 'away']
if ?
= presence
else
.presence = presence
changed = true
unless .presence
.presence = 'online'
changed = true
if status isnt .status
.status = status
changed = true
if changed
.debug 'presence', 'me presence changed', .presence, .status
onPresence: (stanza) ->
.debug 'presence', stanza.attrs.from, stanza.attrs.type
if stanza.attrs.type in ['subscribed', 'unsubscribed']
return
[presence, status, location] = stanza
'presence', [stanza.attrs.from] = {from:stanza.attrs.from, presence, status, location}
onSubscribe: (stanza) ->
.debug 'presence', 'onSubscribe'
onUnSubscribe: (stanza) ->
.debug 'presence', 'onUnSubscribe'
_translatePresence: (stanza) ->
show = stanza.getChild('show')?.text() or 'online'
if stanza.attrs.type is 'unavailable' then show = 'offline'
status = stanza.getChild('status')?.text() or ''
location = stanza.getChild('location')?.text() or ''
presence = switch show
when 'offline' then 'offline'
when 'chat' then 'online'
when 'away' then 'away'
when 'dnd' then 'busy'
when 'xa' then 'idle'
else 'online'
return [presence, status, location]
_sendPresence: (callback) ->
unless .connected
error = new Error "not connected to the realtime service"
error.code = 400
callback error
return
callback = _.once callback or ->
.debug 'presence', 'controller sending', .presence, .status
presence = switch .presence
when 'idle' then 'xa'
when 'busy' then 'dnd'
else .presence
stanza = new ltx.Element 'presence', from:.jid.toString(), 'xml:lang':'en'
if presence is 'offline' then stanza.attrs.type = 'unavailable'
else if presence isnt 'online' then stanza.c('show').t presence
stanza.c('status').t(.status)
stanza.attrs.id = uuid.generate()
# If a reply from the server has not been seen in a second, then assume
# that the stanza was lost and remove it and avoid waiting forever
timer = setTimeout =>
stanza.attrs.id
error = new Error("status update timed out")
error.code = 500
callback error
delete [stanza.attrs.id]
, 1000
[stanza.attrs.id] = timer
[stanza.attrs.id] = callback
.send stanza
{presence, status, location} =
'presence', {from:.jid.bare().toString(), presence, status, location}
_getOrSubscribeToPresence: (jid) ->
unless jid then return
if jid.match // then return
.debug 'presence', 'presence requested for', jid
if jid is .jid?.bare().toString()
.debug 'presence', 'self presence requested'
{presence, status, location} =
return 'presence', {from:.jid.bare().toString(), presence, status, location}
if [jid]
return 'presence', _.extend([jid], {from:jid})
else
.getRoster (err, roster) =>
if err then return console?.error err
unless roster[jid]
if 0 > _.indexOf , jid
.push jid
jid
# else assume the presence will be here shortly.
_requestSubscription: (jid) ->
.send new ltx.Element 'presence', from:.jid.toString(), to:jid, type:'subscribe'