UNPKG

hubot-genesys-cloud

Version:
293 lines (218 loc) 9.62 kB
Controller = require './framework/Controller.coffee' module.exports = class PresenceController extends Controller presences: {} _timeout: 1000 * 60 * 10 @expose: subscribeToPresence: (jids...) -> @_getOrSubscribeToPresence jid for jid in jids return requestPresence: (jids...) -> @_getOrSubscribeToPresence jid for jid in jids return unsubscribeFromPresence: (jids...) -> @realtime.debug 'presence', 'unsubscribeFromPresence', jids for jid in jids @realtime.send new ltx.Element 'presence', from:@realtime.jid.toString(), to:jid, type:'unsubscribe' delete @_presences[jid] return setPresence: (presence) -> @realtime.debug 'presence', 'setPresence', presence, @realtime.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 @realtime.presence then return @realtime.presence = presence @realtime.debug 'presence', 'lib setPresence', presence @_sendPresence() 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 ?= '' @realtime.debug 'presence', 'setStatus', status, @realtime.status if status is @realtime.status then return @realtime.status = status @_sendPresence(callback) return setLocation: (location) -> location ?= '' @realtime.debug 'presence', 'setLocation', location, @realtime.location if location is @realtime.location then return @realtime.location = location @_sendPresence() return @exposeEvents: ['presence'] @stanzas: onMePresence: (stanza) -> stanza.is('presence') and stanza.attrs.from is @realtime.jid?.bare().toString() onPresence: (stanza) -> stanza.is('presence') and stanza.attrs.from isnt @realtime.jid?.bare().toString() and stanza.attrs.type in [undefined, 'unavailable'] and !stanza.attrs.from.match(/@conference/) onPresenceError: (stanza) -> stanza.is('presence') and stanza.attrs.type is "error" and stanza.attrs.to is @realtime.jid?.toString() ignore: (stanza) -> stanza.is('presence') and stanza.attrs.from isnt @realtime.jid?.bare().toString() and stanza.attrs.type in ['subscribed', 'unsubscribed'] extraSubscriptions: [] constructor: -> super @extraSubscriptions = [] @realtime.on 'connect', => debug 'presence', 'connected, sending self subscription' @_presences = {} @_waitingOnReplies = {} @_sendPresenceCallbacks = {} @realtime.presence = null @realtime.status = '' @realtime.location = '' # subscribe to self presence @realtime.send new ltx.Element 'presence', id:'mepresence', from:@realtime.jid.toString(), to:@realtime.jid.bare().toString(), type:'subscribe' for jid in @extraSubscriptions @_requestSubscription jid focusHandler = => @realtime.debug 'focus', 'presence focused' @_focused = true if @_focusTid clearTimeout @_focusTid @_focusTid = null setTimeout (=> if @_beforeIdlePresence @realtime.presence = @_beforeIdlePresence @_beforeIdlePresence = null @_sendPresence() ), 500 if global.window $(window).on 'focus', focusHandler $(window).on 'mouseover', _.debounce(focusHandler, 5000, true) $(window).on 'blur', => @realtime.debug 'focus', 'blur' @_focused = false if @_focusTid then return @_focusTid = setTimeout (=> if (presence = @realtime.presence) in [undefined, 'offline', 'idle'] then return @_beforeIdlePresence = presence @realtime.presence = 'idle' @_sendPresence() ), @_timeout ignore: -> onPresenceError: (stanza) -> @_removeWaitingReply stanza.attrs.id callback = @_sendPresenceCallbacks[stanza.attrs.id] if callback errorNode = stanza.getChild "error" errorText = errorNode.getChild "text" error = new Error errorText error.code = parseInt errorNode.attrs.code @realtime.debug 'presence', 'Send presence failed: #{error.code} - #{errorText}' delete @_sendPresenceCallbacks[stanza.attrs.id] callback error onMePresence: (stanza) -> if stanza.attrs.type in ['subscribed', 'unsubscribed'] return debug 'presence', 'handling self presence' callback = @_sendPresenceCallbacks[stanza.attrs.id] if callback callback(null) #null error to indicate success delete @_sendPresenceCallbacks[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. @_postReplyReact = @_reactToStanza.bind @, stanza @_removeWaitingReply stanza.attrs.id _removeWaitingReply: (id) -> if _.has @_waitingOnReplies, id timer = @_waitingOnReplies[id] if timer clearTimeout timer delete @_waitingOnReplies[id] if 0 is _.size @_waitingOnReplies if @_postReplyReact func = @_postReplyReact @_postReplyReact = null func() _reactToStanza: (stanza) -> if @realtime.disconnected return [presence, status, location] = @_translatePresence stanza @realtime.debug 'presence', 'onMePresence', presence, status, stanza.toString() if presence isnt @realtime.presence and presence in ['online', 'busy', 'away'] if @_beforeIdlePresence? @_beforeIdlePresence = presence else @realtime.presence = presence changed = true unless @realtime.presence @realtime.presence = 'online' changed = true if status isnt @realtime.status @realtime.status = status changed = true if changed @realtime.debug 'presence', 'me presence changed', @realtime.presence, @realtime.status @_sendPresence() onPresence: (stanza) -> @realtime.debug 'presence', stanza.attrs.from, stanza.attrs.type if stanza.attrs.type in ['subscribed', 'unsubscribed'] return [presence, status, location] = @_translatePresence stanza @emit 'presence', @_presences[stanza.attrs.from] = {from:stanza.attrs.from, presence, status, location} onSubscribe: (stanza) -> @realtime.debug 'presence', 'onSubscribe' onUnSubscribe: (stanza) -> @realtime.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 @realtime.connected error = new Error "not connected to the realtime service" error.code = 400 callback error return callback = _.once callback or -> @realtime.debug 'presence', 'controller sending', @realtime.presence, @realtime.status presence = switch @realtime.presence when 'idle' then 'xa' when 'busy' then 'dnd' else @realtime.presence stanza = new ltx.Element 'presence', from:@realtime.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(@realtime.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 => @_removeWaitingReply stanza.attrs.id error = new Error("status update timed out") error.code = 500 callback error delete @_sendPresenceCallbacks[stanza.attrs.id] , 1000 @_waitingOnReplies[stanza.attrs.id] = timer @_sendPresenceCallbacks[stanza.attrs.id] = callback @realtime.send stanza {presence, status, location} = @realtime @emit 'presence', {from:@realtime.jid.bare().toString(), presence, status, location} _getOrSubscribeToPresence: (jid) -> unless jid then return if jid.match /@conference/ then return @realtime.debug 'presence', 'presence requested for', jid if jid is @realtime.jid?.bare().toString() @realtime.debug 'presence', 'self presence requested' {presence, status, location} = @realtime return @emit 'presence', {from:@realtime.jid.bare().toString(), presence, status, location} if @_presences[jid] return @emit 'presence', _.extend(@_presences[jid], {from:jid}) else @realtime.getRoster (err, roster) => if err then return console?.error err unless roster[jid] if 0 > _.indexOf @extraSubscriptions, jid @extraSubscriptions.push jid @_requestSubscription jid # else assume the presence will be here shortly. _requestSubscription: (jid) -> @realtime.send new ltx.Element 'presence', from:@realtime.jid.toString(), to:jid, type:'subscribe'