From b219a181c284fcc7b74465adb7b33ae4e1d02557 Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Thu, 6 Jan 2011 11:59:26 -0800 Subject: [PATCH 1/8] Modified maybe_auto_subscribe to use conn argument, if available, for get_channel --- hookbox/server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hookbox/server.py b/hookbox/server.py index 655909b..a40f7a9 100644 --- a/hookbox/server.py +++ b/hookbox/server.py @@ -333,12 +333,13 @@ def get_channel(self, conn, channel_name): def maybe_auto_subscribe(self, user, options, conn=None): #print 'maybe autosubscribe....' + use_conn = conn if conn else user for destination in options.get('auto_subscribe', ()): #print 'subscribing to', destination - channel = self.get_channel(user, destination) + channel = self.get_channel(use_conn, destination) channel.subscribe(user, conn=conn, needs_auth=False) for destination in options.get('auto_unsubscribe', ()): - channel = self.get_channel(user, destination) + channel = self.get_channel(use_conn, destination) channel.unsubscribe(user, conn=conn, needs_auth=False) From 84810aa09ff9b76953d63456c9e59c92aae959ae Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Thu, 6 Jan 2011 14:24:55 -0800 Subject: [PATCH 2/8] Added 'auto_subscribe' option for users object --- hookbox/user.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hookbox/user.py b/hookbox/user.py index 254b796..1664ef5 100644 --- a/hookbox/user.py +++ b/hookbox/user.py @@ -13,6 +13,7 @@ class User(object): _options = { 'reflective': True, 'moderated_message': True, + 'auto_subscribe': [] } def __init__(self, server, name, **options): From 9651080eeeba04d97d60963ab64f53be9f19fdf0 Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Sat, 8 Jan 2011 15:54:52 -0800 Subject: [PATCH 3/8] Added first cut at EXISTS_USERS protocol --- hookbox/protocol.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/hookbox/protocol.py b/hookbox/protocol.py index 545067a..01daf61 100644 --- a/hookbox/protocol.py +++ b/hookbox/protocol.py @@ -60,7 +60,7 @@ def run(self): # print 'read a frame...' self.logger.debug('%s waiting for a frame', self) fid, fname, fargs= self._rtjp_conn.recv_frame().wait() -# print 'got frame', fid, fname, fargs + print 'got frame', fid, fname, fargs except rtjp_eventlet.errors.ConnectionLost, e: self.logger.debug('received connection lost error') # print 'connection lost' @@ -135,7 +135,24 @@ def frame_MESSAGE(self, fid, fargs): if 'name' not in fargs: return self.send_error(fid, "name required") self.user.send_message(fargs['name'], fargs.get('payload', 'null'), conn=self) - + + def frame_EXISTS_USERS(self, fid, fargs): + if self.state != 'connected': + return self.send_error(fid, "Not connected") + if 'name' not in fargs: + return self.send_error(fid, "name required") + if 'response_channel' not in fargs: + return self.send_error(fid, "response_channel required") + if 'users' not in fargs: + return self.send_error(fid, "users required") + + exists_users = [] + for user in fargs['users']: + exists_users.append({'name':user, 'status':user in self.server.users}) + + payload = {'frame_type':'exists_users', 'data':exists_users} + + self.send_frame('PUBLISH', {'channel_name':fargs['response_channel'], 'payload':payload}) def parse_cookies(cookieString): output = {} From a7bf9d809d433fbacb2bd3b5a70aad9ddfb6da37 Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Sat, 8 Jan 2011 16:26:09 -0800 Subject: [PATCH 4/8] Remove print statement in protocol.py --- hookbox/protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hookbox/protocol.py b/hookbox/protocol.py index 01daf61..271d8ac 100644 --- a/hookbox/protocol.py +++ b/hookbox/protocol.py @@ -60,7 +60,7 @@ def run(self): # print 'read a frame...' self.logger.debug('%s waiting for a frame', self) fid, fname, fargs= self._rtjp_conn.recv_frame().wait() - print 'got frame', fid, fname, fargs +# print 'got frame', fid, fname, fargs except rtjp_eventlet.errors.ConnectionLost, e: self.logger.debug('received connection lost error') # print 'connection lost' From 5242380bcc3196db85b79f5012c11a46192f59df Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Sat, 8 Jan 2011 19:57:54 -0800 Subject: [PATCH 5/8] Added 'state_multi_set' to set multiple keys at once --- hookbox/channel.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hookbox/channel.py b/hookbox/channel.py index 3207a1d..54b31d9 100644 --- a/hookbox/channel.py +++ b/hookbox/channel.py @@ -242,7 +242,18 @@ def state_set(self, key, val): return self.state[key] = val self.state_broadcast(updates={ key: val }) - + + def state_multi_set(self, state): + #Set multiple state values at once + updates = {} + for k,v in state.iteritems(): + if k in self.state and self.state[k]==v: + pass + else: + self.state[k] = v + updates[k] = v + self.state_broadcast(updates=updates) + def state_broadcast(self, updates={}, deletes=[]): frame = { "channel_name": self.name, From 4decdf749394e7dbb2f1cdb9934afc892276ab73 Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Sat, 8 Jan 2011 19:59:44 -0800 Subject: [PATCH 6/8] copy options argument on input of create_channel and use that. Otherwise dict conflicts can arise. --- hookbox/server.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hookbox/server.py b/hookbox/server.py index a40f7a9..8e29f60 100644 --- a/hookbox/server.py +++ b/hookbox/server.py @@ -300,6 +300,7 @@ def remove_user(self, name): self.logger.warn("Unexpected error when removing user: %s", e, exc_info=True) def create_channel(self, conn, channel_name, options={}, needs_auth=True): + local_options = options.copy() if channel_name in self.channels: raise ExpectedException("Channel already exists") if needs_auth: @@ -309,10 +310,11 @@ def create_channel(self, conn, channel_name, options={}, needs_auth=True): } success, callback_options = self.http_request('create_channel', cookie_string, form) if success: - options.update(callback_options) + local_options.update(callback_options) else: raise ExpectedException(callback_options.get('error', 'Unauthorized')) - chan = self.channels[channel_name] = channel.Channel(self, channel_name, **options) + + chan = self.channels[channel_name] = channel.Channel(self, channel_name, **local_options) self.admin.channel_event('create_channel', channel_name, chan.serialize()) def destroy_channel(self, channel_name, needs_auth=True): From 7d3f5e9337713166efca0fe62a2e6eb677e75e17 Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Sat, 8 Jan 2011 20:00:35 -0800 Subject: [PATCH 7/8] Added server_presenceful and server_user_presence options to channel. Thi is done so that user presence on server can be pushed out to channels that request it. --- hookbox/channel.py | 2 ++ hookbox/server.py | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/hookbox/channel.py b/hookbox/channel.py index 54b31d9..ef55f95 100644 --- a/hookbox/channel.py +++ b/hookbox/channel.py @@ -22,6 +22,8 @@ class Channel(object): 'moderated_subscribe': False, 'moderated_unsubscribe': False, 'presenceful': False, + 'server_presenceful': False, + 'server_user_presence': [], 'anonymous': False, 'polling': { 'mode': "", diff --git a/hookbox/server.py b/hookbox/server.py index 8e29f60..f486c72 100644 --- a/hookbox/server.py +++ b/hookbox/server.py @@ -75,6 +75,7 @@ def __init__(self, bound_socket, bound_api_socket, config, outputter): self.conns_by_cookie = {} self.conns = {} self.users = {} + self.user_channel_presence = {} self.pool = EventletPool() def _ws_wrapper(self, environ, start_response): @@ -284,6 +285,9 @@ def get_user(self, name): if name not in self.users: self.users[name] = User(self, name) self.admin.user_event('create', name, self.users[name].serialize()) + if name in self.user_channel_presence: + for channel in self.user_channel_presence[name]: + channel.state_set(name, True) return self.users[name] def remove_user(self, name): @@ -298,6 +302,10 @@ def remove_user(self, name): pass except Exception, e: self.logger.warn("Unexpected error when removing user: %s", e, exc_info=True) + + if name in self.user_channel_presence: + for channel in self.user_channel_presence[name]: + channel.state_set(name, False) def create_channel(self, conn, channel_name, options={}, needs_auth=True): local_options = options.copy() @@ -315,6 +323,18 @@ def create_channel(self, conn, channel_name, options={}, needs_auth=True): raise ExpectedException(callback_options.get('error', 'Unauthorized')) chan = self.channels[channel_name] = channel.Channel(self, channel_name, **local_options) + + #If channel needs to know about server presence, register channel with users + if chan.server_presenceful: + user_state = {} + for user in chan.server_user_presence: + user_state[user] = user in self.users + if user not in self.user_channel_presence: + self.user_channel_presence[user] = [chan] + else: + self.user_channel_presence[user].append(chan) + chan.state_multi_set(user_state) + self.admin.channel_event('create_channel', channel_name, chan.serialize()) def destroy_channel(self, channel_name, needs_auth=True): @@ -322,6 +342,10 @@ def destroy_channel(self, channel_name, needs_auth=True): return None channel = self.channels[channel_name] if channel.destroy(needs_auth): + if channel.server_presenceful: + for user in channel.server_user_presence: + self.user_channel_presence[user].remove(channel) + del self.channels[channel_name] self.admin.channel_event('destroy_channel', channel_name, None) From 63df4d3a8782cc32806ddbae5528cd8c9b1683a0 Mon Sep 17 00:00:00 2001 From: Michael Viamari Date: Sat, 8 Jan 2011 20:02:15 -0800 Subject: [PATCH 8/8] Reversing previous changes made to protocol.py --- hookbox/protocol.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/hookbox/protocol.py b/hookbox/protocol.py index 271d8ac..545067a 100644 --- a/hookbox/protocol.py +++ b/hookbox/protocol.py @@ -135,24 +135,7 @@ def frame_MESSAGE(self, fid, fargs): if 'name' not in fargs: return self.send_error(fid, "name required") self.user.send_message(fargs['name'], fargs.get('payload', 'null'), conn=self) - - def frame_EXISTS_USERS(self, fid, fargs): - if self.state != 'connected': - return self.send_error(fid, "Not connected") - if 'name' not in fargs: - return self.send_error(fid, "name required") - if 'response_channel' not in fargs: - return self.send_error(fid, "response_channel required") - if 'users' not in fargs: - return self.send_error(fid, "users required") - - exists_users = [] - for user in fargs['users']: - exists_users.append({'name':user, 'status':user in self.server.users}) - - payload = {'frame_type':'exists_users', 'data':exists_users} - - self.send_frame('PUBLISH', {'channel_name':fargs['response_channel'], 'payload':payload}) + def parse_cookies(cookieString): output = {}