diff --git a/mitterlib/network/networkbase.py b/mitterlib/network/networkbase.py index 091f479..db2f1fd 100644 --- a/mitterlib/network/networkbase.py +++ b/mitterlib/network/networkbase.py @@ -105,12 +105,9 @@ class MessageTooLongWarning(NetworkWarning): # The classes #-------------------------------------------------------------------- -class NetworkData(object): - """Provides an uniform way to access information about posts. The - following fields should appear [*]_ [*]_: - - **id** - The message identification. *Optional* +class NetworkUser(object): + """Provides an uniform way to access information about users. The + following fields should appear: **name** The name to be displayed as author of the message. *Required* @@ -120,6 +117,22 @@ class NetworkData(object): **avatar** URL to the author avatar. *Optional* + """ + def __init__(self): + self.name = '' + self.username = '' + self.avatar = '' + + +class NetworkData(object): + """Provides an uniform way to access information about posts. The + following fields should appear [*]_ [*]_: + + **id** + The message identification. *Optional* + + **author** + Author of the message. A :class:`NetworkUser` object. **message** The message. *Required* @@ -135,16 +148,17 @@ class NetworkData(object): The parent of this message, in case of a reply. *Optional* **parent_owner** - Username of the owner of the parent message (in other words, "in reply - to".) *Optional* + Owner of the parent message (in other words, "in reply to".) It will be + a :class:`NetworkUser` object. *Optional* **reposted_by** - Username friend of the current user that reposted that message. Some - networks will return the original user in a separate table (Twitter, - in this case, returns the original message with the original user - in a "retweeted_status" structure.) In this case, the network layer - will must return the original user in *name*, *username* and *avatar*, - while the friend username will be moved to *reposted_by*. *Optional* + Author of the reposted message. A :class:`NetworkUser` object. + + Some networks will return the original + user in a separate table (Twitter, in this case, returns the original + message with the original user in a "retweeted_status" structure.) In + this case, the network layer must return the original user in *author* + while the friend will be in *reposted_by*. *Optional* **network** The network id source of the message. Network classes don't need to @@ -162,15 +176,13 @@ class NetworkData(object): def __init__(self): self.id = '' - self.name = '' - self.username = '' - self.avatar = '' + self.author = None self.message = '' self.message_time = None self.favourite = False - self.parent = '' - self.parent_owner = '' - self.reposted_by = '' + self.parent = None + self.parent_owner = None + self.reposted_by = None self.network = '' self.protected = False diff --git a/mitterlib/network/twitter.py b/mitterlib/network/twitter.py index 8c9fe95..f0536a5 100644 --- a/mitterlib/network/twitter.py +++ b/mitterlib/network/twitter.py @@ -30,7 +30,7 @@ import gettext from httplib import BadStatusLine from socket import error as socketError -from networkbase import NetworkBase, NetworkData, auth_options, \ +from networkbase import NetworkBase, NetworkData, NetworkUser, auth_options, \ NetworkDNSError, NetworkBadStatusLineError, NetworkLowLevelError, \ NetworkInvalidResponseError, NetworkPermissionDeniedError, \ MessageTooLongWarning @@ -113,6 +113,15 @@ def _make_datetime(response): return result +class TwitterNetworkUser(NetworkUser): + """A simple wrapper around :class:`NetwokrUser`, to make things easier to + convert twitter user information into a NetworkUser object.""" + + def __init__(self, data): + self.name = data['user']['name'] + self.username = data['user']['screen_name'] + self.avatar = data['user']['profile_image_url'] + class TwitterNetworkData(NetworkData): """A simple wrapper around NetworkData, to make things easier to convert @@ -123,9 +132,7 @@ class TwitterNetworkData(NetworkData): NetworkData.__init__(self) self.id = data['id'] - self.name = data['user']['name'] - self.username = data['user']['screen_name'] - self.avatar = data['user']['profile_image_url'] + self.author = TwitterNetworkUser(data) self.message_time = _to_datetime(data['created_at']) if 'protected' in data['user']: @@ -136,16 +143,14 @@ class TwitterNetworkData(NetworkData): self.favourited = data['favorited'] if 'in_reply_to_status_id' in data and data['in_reply_to_status_id']: + owner = NetworkUser() + owner.username = data['in_reply_to_screen_name'] + # TODO: Check if we have the other data. self.parent = int(data['in_reply_to_status_id']) - self.parent_owner = data['in_reply_to_screen_name'] if 'retweeted_status' in data: - self.reposted_by = self.username - - retweet_user = data['retweeted_status']['user'] - self.name = retweet_user['name'] - self.username = retweet_user['screen_name'] - self.avatar = retweet_user['profile_image_url'] + self.reposted_by = self.author + self.author = TwitterNetworkUser(data['retweeted_status']) self.id = data['retweeted_status']['id'] # also switch the text for the original text. @@ -393,13 +398,13 @@ class Connection(NetworkBase): def link(self, message): """Return a link directly to the message.""" assert(isinstance(message, NetworkData)) - return 'http://twitter.com/%s/status/%s' % (message.username, + return 'http://twitter.com/%s/status/%s' % (message.author.username, message.id) def reply_prefix(self, message): """Returns the prefix needed for a reply.""" assert(isinstance(message, NetworkData)) - return '@' + message.username + ' ' + return '@' + message.author.username + ' ' def replies(self): """Return a list of NetworkData objects for the replies for the user @@ -434,8 +439,8 @@ class Connection(NetworkBase): # start with the username of the original user, we add it # for the user. - if not status.startswith('@' + reply_to.username): - body['status'] = '@' + reply_to.username + ' ' + \ + if not status.startswith('@' + reply_to.author.username): + body['status'] = '@' + reply_to.author.username + ' ' + \ status else: body['in_reply_to_status_id'] = reply_to @@ -486,7 +491,8 @@ class Connection(NetworkBase): """Check if the message belongs to the user. If so, returns True; False otherwise.""" assert(isinstance(message, NetworkData)) - return (message.username == self._options[self.NAMESPACE]['username']) + return (message.author.username == + self._options[self.NAMESPACE]['username']) def can_reply(self, message): """Always return True; Twitter allows replying to any messages, @@ -496,7 +502,7 @@ class Connection(NetworkBase): def can_repost(self, message): """Twitter ignores retweets from the user.""" assert(isinstance(message, NetworkData)) - return not (message.username == + return not (message.author.username == self._options[self.NAMESPACE]['username']) def can_favourite(self, message): diff --git a/mitterlib/ui/ui_pygtk.py b/mitterlib/ui/ui_pygtk.py index d6856fc..b53675e 100644 --- a/mitterlib/ui/ui_pygtk.py +++ b/mitterlib/ui/ui_pygtk.py @@ -608,7 +608,7 @@ class Interface(object): userpic.""" data = store.get_value(position, 0) - pic = data.avatar + pic = data.author.avatar cell.set_property('pixbuf', self._avatars[pic]) return @@ -625,8 +625,8 @@ class Interface(object): # unescape escaped entities that pango is not okay with message_values['message'] = html_escape(data.message) - message_values['username'] = html_escape(data.username) - message_values['full_name'] = html_escape(data.name) + message_values['username'] = html_escape(data.author.username) + message_values['full_name'] = html_escape(data.author.name) message_values['message_age'] = time # highlight URLs @@ -657,11 +657,11 @@ class Interface(object): info = [] if data.reposted_by: info.append(_(' — reposted by %s') % - (data.reposted_by)) + (data.reposted_by.username)) if data.parent_owner: info.append(_(' — in reply to %s') % - (data.parent_owner)) + (data.parent_owner.username)) if data.protected: message_values['protected_status'] = ( @@ -781,7 +781,7 @@ class Interface(object): """Using the results from the request, fill a gtk.Store.""" for message in data: message.read = False - pic = message.avatar + pic = message.author.avatar if not pic in self._avatars: # set the user avatar to the default image, so it won't get # queued again. Once downloaded, the _post_download_pic will @@ -817,7 +817,8 @@ class Interface(object): count = len(text) if self._reply_message_id: - suffix = _('(replying to %s)') % (self._reply_message_id.username) + suffix = _('(replying to %s)') % ( + self._reply_message_id.author.username) else: suffix = '' @@ -1030,7 +1031,7 @@ class Interface(object): (model, iter) = grid.get_selection().get_selected() message = model.get_value(iter, 0) self._update_statusbar(_('Reposting %s message...') % - (message.username)) + (message.author.username)) self._threads.add_work(self._post_repost_message, self._exception_repost_message, self._connection.repost, @@ -1049,7 +1050,7 @@ class Interface(object): display = _('Removing message from %s from favorites...') else: display = _('Marking message from %s as favorite...') - self._update_statusbar(display % (message.username)) + self._update_statusbar(display % (message.author.username)) self._threads.add_work(self._post_favourite_message, self._exception_favourite_message, self._connection.favourite, @@ -1623,4 +1624,4 @@ class Interface(object): 'protected/private.', metavar='CHAR', default=_('(protected)'), - is_cmd_option=False) \ No newline at end of file + is_cmd_option=False)