Skip to content

Commit

Permalink
initial presence implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Robert Marklund <[email protected]>
  • Loading branch information
trollkarlen committed Sep 22, 2014
1 parent 8bf1c61 commit 5476357
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 7 deletions.
21 changes: 17 additions & 4 deletions hangups/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def get_submissions(self, new_data_bytes):
def _parse_sid_response(res):
"""Parse response format for request for new channel SID.
Returns (SID, header_client, gsessionid).
Returns (SID, email, header_client, gsessionid).
"""
sid = None
header_client = None
Expand All @@ -112,11 +112,11 @@ def _parse_sid_response(res):
elif message[0] == 'c':
type_ = message[1][1][0]
if type_ == 'cfj':
header_client = message[1][1][1].split('/')[1]
email, header_client = message[1][1][1].split('/')
elif type_ == 'ei':
gsessionid = message[1][1][1]

return(sid, header_client, gsessionid)
return(sid, email, header_client, gsessionid)


class Channel(object):
Expand Down Expand Up @@ -165,6 +165,17 @@ def __init__(self, cookies, path, clid, ec, prop, connector):
self._sid_param = None
self._gsessionid_param = None

self._email = None
self._header_client = None

@property
def header_client(self):
return self._header_client

@property
def email(self):
return self._email

@asyncio.coroutine
def listen(self):
"""Listen for messages on the channel.
Expand Down Expand Up @@ -243,10 +254,12 @@ def _fetch_channel_sid(self):
raise exceptions.HangupsError('Failed to request SID: {}'.format(e))
# TODO: Re-write the function we're calling here to use a schema so we
# can easily catch its failure.
self._sid_param, _, self._gsessionid_param = (
self._sid_param, self._email, self._header_client, self._gsessionid_param = (
_parse_sid_response(res.body)
)
logger.info('New SID: {}'.format(self._sid_param))
logger.info('New email: {}'.format(self._email))
logger.info('New client: {}'.format(self._header_client))
logger.info('New gsessionid: {}'.format(self._gsessionid_param))

@asyncio.coroutine
Expand Down
67 changes: 64 additions & 3 deletions hangups/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def __init__(self, cookies):
# Event fired when the client connects for the first time with
# arguments (initial_data).
self.on_connect = event.Event('Client.on_connect')
self.__on_pre_connect = event.Event('Client.on_pre_connect')
self.__on_pre_connect.add_observer(self._on_pre_connect)

# Event fired when the client reconnects after being disconnected with
# arguments ().
self.on_reconnect = event.Event('Client.on_reconnect')
Expand All @@ -72,8 +75,9 @@ def __init__(self, cookies):
self._header_date = None
self._header_version = None
self._header_id = None
# TODO This one isn't being set anywhere:
# Like the jabber client ID
self._header_client = None
self._email = None
# Parameters needed to create the Channel:
self._channel_path = None
self._clid = None
Expand All @@ -92,18 +96,29 @@ def connect(self):
self._cookies, self._channel_path, self._clid,
self._channel_ec_param, self._channel_prop_param, self._connector
)

self._channel.on_connect.add_observer(
lambda: self.on_connect.fire(initial_data)
lambda: self.__on_pre_connect.fire(initial_data)
)
self._channel.on_reconnect.add_observer(self.on_reconnect.fire)
self._channel.on_disconnect.add_observer(self.on_disconnect.fire)
self._channel.on_message.add_observer(self._on_push_data)
yield from self._channel.listen()


##########################################################################
# Private methods
##########################################################################

def _on_pre_connect(self, initial_data):
"""Called before on_connect to setup presence/client
"""
logger.debug("_on_pre_connect")
asyncio.async(self.setactiveclient(True)).add_done_callback(
lambda res: asyncio.async(self.setpresence(True)).add_done_callback(
lambda res: self.on_connect.fire(initial_data)))

@asyncio.coroutine
def _initialize_chat(self):
"""Request push channel creation and initial chat data.
Expand Down Expand Up @@ -208,7 +223,7 @@ def _get_request_header(self):
"""Return request header for chat API request."""
return [
[3, 3, self._header_version, self._header_date],
[self._header_client, self._header_id],
[self._channel.header_client, self._header_id],
None,
"en"
]
Expand Down Expand Up @@ -238,6 +253,9 @@ def _request(self, endpoint, body_json, use_json=True):
'key': self._api_key,
'alt': 'json' if use_json else 'protojson',
}

logger.debug("Fetching '{}' with '{}'".format(url, body_json))

res = yield from http_utils.fetch(
'post', url, headers=headers, cookies=cookies, params=params,
data=json.dumps(body_json), connector=self._connector
Expand Down Expand Up @@ -360,6 +378,49 @@ def searchentities(self, search_string, max_results):
return json.loads(res.body.decode())

@asyncio.coroutine
def setpresence(self, online):
"""Set the presence of the client and also the mood
"""
res = yield from self._request('presence/setpresence', [
self._get_request_header(),
None,
None,
None,
[not online]
])
res = json.loads(res.body.decode())
res_status = res['response_header']['status']
if res_status != 'OK':
raise exceptions.NetworkError('Unexpected status: {}'
.format(res_status))
res = yield from self._request('presence/setpresence', [
self._get_request_header(),
[720, 1 if online else 40 ]
])
res = json.loads(res.body.decode())
res_status = res['response_header']['status']
if res_status != 'OK':
raise exceptions.NetworkError('Unexpected status: {}'
.format(res_status))

@asyncio.coroutine
def setactiveclient(self, online):
"""Set the active client.
"""
res = yield from self._request('clients/setactiveclient', [
self._get_request_header(),
online,
"{}/{}".format(self._channel.email, self._channel.header_client),
120,
])
res = json.loads(res.body.decode())
res_status = res['response_header']['status']
if res_status != 'OK':
raise exceptions.NetworkError('Unexpected status: {}'
.format(res_status))
@asyncio.coroutine
def querypresence(self, chat_id):
"""Check someone's presence status.
Expand Down
9 changes: 9 additions & 0 deletions hangups/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def from_conv_part_data(conv_part_data, self_user_id):
return User(user_id, conv_part_data.fallback_name, None, None, [],
(self_user_id == user_id) or (self_user_id is None))

def is_online(self):
"""Ask if the user is online.
"""
pass

class UserList(object):

Expand Down Expand Up @@ -80,6 +84,11 @@ def __init__(self, client, self_entity, entities, conv_parts):

self._client.on_state_update.add_observer(self._on_state_update)

def get_online_users(self):
"""Return a list of users currently online.
"""
pass

def get_user(self, user_id):
"""Return a User by their UserID.
Expand Down

0 comments on commit 5476357

Please sign in to comment.