diff -up gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/__init__.py.sina gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/__init__.py --- gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/__init__.py.sina 2011-05-12 11:59:10.214024023 -0400 +++ gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/__init__.py 2011-05-12 11:59:10.214024023 -0400 @@ -0,0 +1 @@ + diff -up gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py.sina gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py --- gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py.sina 2011-05-12 11:59:10.227023883 -0400 +++ gwibber-3.0.0.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py 2011-05-12 12:04:16.120705165 -0400 @@ -0,0 +1,174 @@ +import gtk, pango, webkit, gnomekeyring +import urllib, urllib2, json, urlparse, uuid +from oauth import oauth + +from gtk import Builder +from gwibber.microblog.util import resources +import gettext +from gettext import gettext as _ +if hasattr(gettext, 'bind_textdomain_codeset'): + gettext.bind_textdomain_codeset('gwibber','UTF-8') +gettext.textdomain('gwibber') + +gtk.gdk.threads_init() + +sigmeth = oauth.OAuthSignatureMethod_HMAC_SHA1() + +class AccountWidget(gtk.VBox): + """AccountWidget: A widget that provides a user interface for configuring sina accounts in Gwibber + """ + + def __init__(self, account=None, dialog=None): + """Creates the account pane for configuring Sina accounts""" + gtk.VBox.__init__( self, False, 20 ) + self.ui = gtk.Builder() + self.ui.set_translation_domain ("gwibber") + self.ui.add_from_file (resources.get_ui_asset("gwibber-accounts-sina.ui")) + self.ui.connect_signals(self) + self.vbox_settings = self.ui.get_object("vbox_settings") + self.pack_start(self.vbox_settings, False, False) + self.show_all() + + self.account = account or {} + self.dialog = dialog + has_secret_key = True + if self.account.has_key("id"): + try: + value = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET, {"id": str("%s/%s" % (self.account["id"], "secret_token"))})[0].secret + except gnomekeyring.NoMatchError: + has_secret_key = False + + try: + if self.account.has_key("access_token") and self.account.has_key("secret_token") and self.account.has_key("username") and has_secret_key and not self.dialog.condition: + self.ui.get_object("hbox_sina_auth").hide() + self.ui.get_object("sina_auth_done_label").set_label(_("%s has been authorized by Sina") % self.account["username"]) + self.ui.get_object("hbox_sina_auth_done").show() + else: + self.ui.get_object("hbox_sina_auth_done").hide() + if self.dialog.ui: + self.dialog.ui.get_object('vbox_create').hide() + except: + self.ui.get_object("hbox_sina_auth_done").hide() + if self.dialog.ui: + self.dialog.ui.get_object("vbox_create").hide() + + + def on_sina_auth_clicked(self, widget, data=None): + self.winsize = self.window.get_size() + + web = webkit.WebView() + web.get_settings().set_property("enable-plugins", False) + web.load_html_string(_("<p>Please wait...</p>"), "file:///") + + self.consumer = oauth.OAuthConsumer(*resources.get_sina_keys()) + + request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_method="POST", + http_url="http://api.t.sina.com.cn/oauth/request_token") + + request.sign_request(sigmeth, self.consumer, token=None) + + tokendata = urllib2.urlopen(request.http_url, request.to_postdata()).read() + self.token = oauth.OAuthToken.from_string(tokendata) + + url = "http://api.t.sina.com.cn/oauth/authorize?oauth_token=%s&oauth_callback=%s&display=popup" % ( self.token.key, "http://gwibber.com/0/auth.html" ) + + web.load_uri(url) + web.set_size_request(550, 400) + web.connect("title-changed", self.on_sina_auth_title_change) + + self.scroll = gtk.ScrolledWindow() + self.scroll.add(web) + + self.pack_start(self.scroll, True, True, 0) + self.show_all() + + self.ui.get_object("vbox1").hide() + self.ui.get_object("vbox_advanced").hide() + self.dialog.infobar.set_message_type(gtk.MESSAGE_INFO) + + def on_sina_auth_title_change(self, web=None, title=None, data=None): + saved = False + if title.get_title() == "Success": + + if hasattr(self.dialog, "infobar_content_area"): + for child in self.dialog.infobar_content_area.get_children(): child.destroy() + self.dialog.infobar_content_area = self.dialog.infobar.get_content_area() + self.dialog.infobar_content_area.show() + self.dialog.infobar.show() + + message_label = gtk.Label(_("Verifying")) + message_label.set_use_markup(True) + message_label.set_ellipsize(pango.ELLIPSIZE_END) + self.dialog.infobar_content_area.add(message_label) + self.dialog.infobar.show_all() + self.scroll.hide() + url = web.get_main_frame().get_uri() + data = urlparse.parse_qs(url.split("?", 1)[1]) + + self.ui.get_object("vbox1").show() + self.ui.get_object("vbox_advanced").show() + + token = data["oauth_token"][0] + verifier = data["oauth_verifier"][0] + + request = oauth.OAuthRequest.from_consumer_and_token( + self.consumer, self.token, + http_url="http://api.t.sina.com.cn/oauth/access_token", + parameters={"oauth_verifier": str(verifier)}) + request.sign_request(sigmeth, self.consumer, self.token) + + tokendata = urllib2.urlopen(request.http_url, request.to_postdata()).read() + data = urlparse.parse_qs(tokendata) + + atok = oauth.OAuthToken.from_string(tokendata) + + self.account["access_token"] = data["oauth_token"][0] + self.account["secret_token"] = data["oauth_token_secret"][0] + self.account["username"] = data["screen_name"][0] + self.account["user_id"] = data["user_id"][0] + + apireq = oauth.OAuthRequest.from_consumer_and_token( + self.consumer, atok, + http_method="GET", + http_url="http://api.t.sina.com.cn/account/verify_credentials.json", parameters=None) + + apireq.sign_request(sigmeth, self.consumer, atok) + + account_data = json.loads(urllib2.urlopen(apireq.to_url()).read()) + + if isinstance(account_data, dict): + if account_data.has_key("id"): + saved = self.dialog.on_edit_account_save() + else: + print "Failed" + self.dialog.infobar.set_message_type(gtk.MESSAGE_ERROR) + message_label.set_text(_("Authorization failed. Please try again.")) + else: + print "Failed" + self.dialog.infobar.set_message_type(gtk.MESSAGE_ERROR) + message_label.set_text(_("Authorization failed. Please try again.")) + + if saved: + message_label.set_text(_("Successful")) + self.dialog.infobar.set_message_type(gtk.MESSAGE_INFO) + #self.dialog.infobar.hide() + + self.ui.get_object("hbox_sina_auth").hide() + self.ui.get_object("sina_auth_done_label").set_label(_("%s has been authorized by Sina") % str(self.account["username"])) + self.ui.get_object("hbox_sina_auth_done").show() + if self.dialog.ui and self.account.has_key("id") and not saved: + self.dialog.ui.get_object("vbox_save").show() + elif self.dialog.ui and not saved: + self.dialog.ui.get_object("vbox_create").show() + + self.window.resize(*self.winsize) + + if title.get_title() == "Failure": + web.hide() + self.dialog.infobar.set_message_type(gtk.MESSAGE_ERROR) + message_label.set_text(_("Authorization failed. Please try again.")) + self.dialog.infobar.show_all() + + self.ui.get_object("vbox1").show() + self.ui.get_object("vbox_advanced").show() + self.window.resize(*self.winsize) diff -up gwibber-3.0.0.1/gwibber/microblog/plugins/sina/__init__.py.sina gwibber-3.0.0.1/gwibber/microblog/plugins/sina/__init__.py --- gwibber-3.0.0.1/gwibber/microblog/plugins/sina/__init__.py.sina 2011-05-12 11:59:10.233023817 -0400 +++ gwibber-3.0.0.1/gwibber/microblog/plugins/sina/__init__.py 2011-05-12 11:59:10.233023817 -0400 @@ -0,0 +1,294 @@ +from gwibber.microblog import network, util +from htmlentitydefs import name2codepoint +import re +import gnomekeyring +from oauth import oauth +from gwibber.microblog.util import log, resources +from gettext import lgettext as _ +from kitchen.text.converters import to_unicode +log.logger.name = "Sina" + +PROTOCOL_INFO = { + "name": "Sina", + "version": "1.0", + + "config": [ + "private:secret_token", + "access_token", + "username", + "color", + "receive_enabled", + "send_enabled", + ], + + "authtype": "oauth1a", + "color": "#E61217", + + "features": [ + "send", + "receive", + "search", + "tag", + "reply", + "responses", + "private", + "public", + "delete", + "retweet", + "like", + "send_thread", + "send_private", + "user_messages", + "sinceid", + "lists", + "list", + ], + + "default_streams": [ + "receive", + "images", + "responses", + "private", + "lists", + ], +} + +URL_PREFIX = "http://t.sina.com.cn" +API_PREFIX = "http://api.t.sina.com.cn" + +def unescape(s): + return re.sub('&(%s);' % '|'.join(name2codepoint), + lambda m: unichr(name2codepoint[m.group(1)]), s) + +class Client: + def __init__(self, acct): + self.service = util.getbus("Service") + if acct.has_key("secret_token") and acct.has_key("password"): acct.pop("password") + self.account = acct + + if not acct.has_key("access_token") and not acct.has_key("secret_token"): + return [{"error": {"type": "auth", "account": self.account, "message": _("Failed to find credentials")}}] + + self.sigmethod = oauth.OAuthSignatureMethod_HMAC_SHA1() + self.consumer = oauth.OAuthConsumer(*util.resources.get_sina_keys()) + self.token = oauth.OAuthToken(acct["access_token"], acct["secret_token"]) + + def _common(self, data): + m = {}; + try: + m["mid"] = str(data["id"]) + m["service"] = u"sina" + m["account"] = self.account["id"] + m["time"] = util.parsetime(data["created_at"]) + m["text"] = to_unicode(unescape(data["text"])) + m["to_me"] = ("@%s" % self.account["username"]) in data["text"] + + m["html"] = to_unicode(util.linkify(data["text"], + ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % URL_PREFIX), + (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX)), escape=False)) + + m["content"] = to_unicode(util.linkify(data["text"], + ((util.PARSE_HASH, '#<a class="hash" href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]), + (util.PARSE_NICK, '@<a class="nick" href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=False)) + + if data.has_key("retweeted_status"): + m["retweeted_status"] = data["retweeted_status"] + else: + m["retweeted_status"] = None + + images = util.imgpreview(m["text"]) + if images: + m["images"] = images + m["type"] = "photo" + except: + log.logger.error("%s failure - %s", PROTOCOL_INFO["name"], data) + return {} + + return m + + def _user(self, user): + return { + "name": to_unicode(user["name"]), + "nick": to_unicode(user["screen_name"]), + "id": user["id"], + "location": to_unicode(user["location"]), + "followers": user.get("followers", None), + "image": to_unicode(user["profile_image_url"]), + "url": to_unicode("/".join((URL_PREFIX, user["screen_name"]))), + "is_me": user["screen_name"] == self.account["username"], + } + + def _message(self, data): + if type(data) == type(None): + return [] + + m = self._common(data) + m["source"] = data.get("source", False) + + if data.has_key("in_reply_to_status_id"): + if data["in_reply_to_status_id"]: + m["reply"] = {} + m["reply"]["id"] = data["in_reply_to_status_id"] + m["reply"]["nick"] = to_unicode(data["in_reply_to_screen_name"]) + if m["reply"]["id"] and m["reply"]["nick"]: + m["reply"]["url"] = to_unicode("/".join((URL_PREFIX, m["reply"]["nick"], "statuses", str(m["reply"]["id"])))) + else: + m["reply"]["url"] = None + + m["sender"] = self._user(data["user"] if "user" in data else data["sender"]) + m["url"] = to_unicode("/".join((m["sender"]["url"], "statuses", str(m["mid"])))) + + return m + + def _private(self, data): + m = self._message(data) + m["private"] = True + + m["recipient"] = {} + m["recipient"]["name"] = to_unicode(data["recipient"]["name"]) + m["recipient"]["nick"] = to_unicode(data["recipient"]["screen_name"]) + m["recipient"]["id"] = data["recipient"]["id"] + m["recipient"]["image"] = to_unicode(data["recipient"]["profile_image_url"]) + m["recipient"]["location"] = to_unicode(data["recipient"]["location"]) + m["recipient"]["url"] = to_unicode("/".join((URL_PREFIX, m["recipient"]["nick"]))) + m["recipient"]["is_me"] = m["recipient"]["nick"] == self.account["username"] + m["to_me"] = m["recipient"]["is_me"] + + return m + + def _result(self, data): + m = self._common(data) + + if data["to_user_id"]: + m["reply"] = {} + m["reply"]["id"] = data["to_user_id"] + m["reply"]["nick"] = to_unicode(data["to_user"]) + + m["sender"] = {} + m["sender"]["nick"] = to_unicode(data["from_user"]) + m["sender"]["id"] = data["from_user_id"] + m["sender"]["image"] = to_unicode(data["profile_image_url"]) + m["sender"]["url"] = to_unicode("/".join((URL_PREFIX, m["sender"]["nick"]))) + m["sender"]["is_me"] = m["sender"]["nick"] == self.account["username"] + m["url"] = to_unicode("/".join((m["sender"]["url"], "statuses", str(m["mid"])))) + return m + + def _list(self, data): + return { + "mid": data["id"], + "service": u"sina", + "account": self.account["id"], + "time": 0, + "text": to_unicode(data["description"]), + "html": to_unicode(data["description"]), + "content": to_unicode(data["description"]), + "url": to_unicode("/".join((URL_PREFIX, data["uri"]))), + "sender": to_unicode(self._user(data["user"])), + "name": to_unicode(data["name"]), + "nick": to_unicode(data["slug"]), + "key": data["slug"], + "full": to_unicode(data["full_name"]), + "uri": to_unicode(data["uri"]), + "mode": data["mode"], + "members": data["member_count"], + "followers": data["subscriber_count"], + "kind": u"list", + } + + def _get(self, path, parse="message", post=False, single=False, **args): + url = "/".join((API_PREFIX, path)) + + request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, self.token, + http_method="POST" if post else "GET", http_url=url, parameters=util.compact(args)) + request.sign_request(self.sigmethod, self.consumer, self.token) + + if post: + data = network.Download(request.http_url, None, post, body=request.to_postdata()).get_json() + #data = network.Download(request.to_url(), util.compact(args), post).get_json() + else: + data = network.Download(request.to_url(), None, post).get_json() + + resources.dump(self.account["service"], self.account["id"], data) + + if isinstance(data, dict) and data.get("errors", 0): + if "authenticate" in data["errors"][0]["message"]: + logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Authentication failed"), error["message"]) + log.logger.error("%s", logstr) + return [{"error": {"type": "auth", "account": self.account, "message": data["errors"][0]["message"]}}] + else: + for error in data["errors"]: + logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Unknown failure"), error["message"]) + return [{"error": {"type": "unknown", "account": self.account, "message": error["message"]}}] + elif isinstance(data, dict) and data.get("error", 0): + if "Incorrect signature" in data["error"]: + logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Request failed"), data["error"]) + log.logger.error("%s", logstr) + return [{"error": {"type": "auth", "account": self.account, "message": data["error"]}}] + elif isinstance(data, str): + logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Request failed"), data) + log.logger.error("%s", logstr) + return [{"error": {"type": "request", "account": self.account, "message": data}}] + + if parse == "list": + return [self._list(l) for l in data["lists"]] + if single: return [getattr(self, "_%s" % parse)(data)] + if parse: return [getattr(self, "_%s" % parse)(m) for m in data] + else: return [] + + def _search(self, **args): + data = network.Download("http://api.t.sina.com.cn/search.json", util.compact(args)) + data = data.get_json()["results"] + + return [self._result(m) for m in data] + + def __call__(self, opname, **args): + return getattr(self, opname)(**args) + + def receive(self, count=util.COUNT, since=None): + return self._get("statuses/home_timeline.json", count=count, since_id=since) + + def user_messages(self, id=None, count=util.COUNT, since=None): + return self._get("statuses/user_timeline.json", id=id, count=count, since_id=since) + + def responses(self, count=util.COUNT, since=None): + return self._get("statuses/mentions.json", count=count, since_id=since) + + def private(self, count=util.COUNT, since=None): + private = self._get("direct_messages.json", "private", count=count, since_id=since) or [] + private_sent = self._get("direct_messages/sent.json", "private", count=count, since_id=since) or [] + return private + private_sent + + def public(self): + return self._get("statuses/public_timeline.json") + + def lists(self, **args): + following = self._get("%s/lists/subscriptions.json" % self.account["username"], "list") or [] + lists = self._get("%s/lists.json" % self.account["username"], "list") or [] + return following + lists + + def list(self, user, id, count=util.COUNT, since=None): + return self._get("%s/lists/%s/statuses.json" % (user, id), per_page=count, since_id=since) + + def search(self, query, count=util.COUNT, since=None): + return self._search(q=query, rpp=count, since_id=since) + + def tag(self, query, count=util.COUNT, since=None): + return self._search(q="#%s" % query, count=count, since_id=since) + + def delete(self, message): + return self._get("statuses/destroy/%s.json" % message["mid"], None, post=True, do=1) + + def like(self, message): + return self._get("favorites/create/%s.json" % message["mid"], None, post=True, do=1) + + def send(self, message): + return self._get("statuses/update.json", post=True, single=True, + status=message) + + def send_private(self, message, private): + return self._get("direct_messages/new.json", "private", post=True, single=True, + text=message, screen_name=private["sender"]["nick"]) + + def send_thread(self, message, target): + return self._get("statuses/update.json", post=True, single=True, + status=message, in_reply_to_status_id=target["mid"]) diff -up gwibber-3.0.0.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui.sina gwibber-3.0.0.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui --- gwibber-3.0.0.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui.sina 2011-05-12 11:59:10.234023806 -0400 +++ gwibber-3.0.0.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui 2011-05-12 11:59:10.233023817 -0400 @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy toplevel-contextual --> + <object class="GtkVBox" id="vbox_settings"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <child> + <object class="GtkHBox" id="hbox_sina_auth"> + <property name="visible">True</property> + <child> + <object class="GtkButton" id="sina_auth_button"> + <property name="label" translatable="yes">_Authorize</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="on_sina_auth_clicked"/> + </object> + <packing> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="sina_auth_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Authorize with sina</property> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox_sina_auth_done"> + <property name="visible">True</property> + <child> + <object class="GtkLabel" id="sina_auth_done_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Sina authorized</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHSeparator" id="hseparator1"> + <property name="visible">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="vbox_advanced"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Account Settings:</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkTable" id="table_advanced_settings"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkCheckButton" id="send_enabled"> + <property name="label" translatable="yes">_Send Messages</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="tooltip_text" translatable="yes">Allow sending posts to this account</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="receive_enabled"> + <property name="label" translatable="yes">_Receive Messages</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="tooltip_text" translatable="yes">Include this account when downloading messages</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="right_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="tooltip_text" translatable="yes">Color used to help distinguish accounts</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Account Color:</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkColorButton" id="color"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="tooltip_text" translatable="yes">Color used to help distinguish accounts</property> + <property name="color">#000000000000</property> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + </object> +</interface> diff -up gwibber-3.0.0.1/gwibber/microblog/util/const.py.sina gwibber-3.0.0.1/gwibber/microblog/util/const.py --- gwibber-3.0.0.1/gwibber/microblog/util/const.py.sina 2011-05-12 11:59:10.145024769 -0400 +++ gwibber-3.0.0.1/gwibber/microblog/util/const.py 2011-05-12 11:59:10.234023806 -0400 @@ -15,6 +15,9 @@ else: TWITTER_OAUTH_KEY = "VDOuA5qCJ1XhjaSa4pl76g" TWITTER_OAUTH_SECRET = "BqHlB8sMz5FhZmmFimwgiIdB0RiBr72Y0bio49IVJM" +SINA_OAUTH_KEY = "4014350411" +SINA_OAUTH_SECRET = "92e7877ad12d59a8410d850c19787701" + # Gwibber MAX_MESSAGE_LENGTH = 140 MAX_MESSAGE_COUNT = 20000 diff -up gwibber-3.0.0.1/gwibber/microblog/util/resources.py.sina gwibber-3.0.0.1/gwibber/microblog/util/resources.py --- gwibber-3.0.0.1/gwibber/microblog/util/resources.py.sina 2011-04-05 17:06:50.000000000 -0400 +++ gwibber-3.0.0.1/gwibber/microblog/util/resources.py 2011-05-12 11:59:10.234023806 -0400 @@ -79,6 +79,10 @@ def get_twitter_keys(): # Distros should register their own keys and not rely on the defaults return TWITTER_OAUTH_KEY, TWITTER_OAUTH_SECRET +def get_sina_keys(): + # Distros should register their own keys and not rely on the defaults + return SINA_OAUTH_KEY, SINA_OAUTH_SECRET + def get_avatar_path(url): avatar_cache_dir = realpath(join(CACHE_BASE_DIR, "gwibber", "avatars")) if not isdir(avatar_cache_dir): diff -up gwibber-3.0.0.1/po/POTFILES.in.sina gwibber-3.0.0.1/po/POTFILES.in --- gwibber-3.0.0.1/po/POTFILES.in.sina 2011-05-12 11:59:10.200024174 -0400 +++ gwibber-3.0.0.1/po/POTFILES.in 2011-05-12 11:59:10.235023795 -0400 @@ -18,6 +18,7 @@ gwibber/microblog/plugins/buzz/gtk/buzz/ gwibber/microblog/plugins/digg/gtk/digg/__init__.py gwibber/microblog/plugins/pingfm/gtk/pingfm/__init__.py gwibber/microblog/plugins/qaiku/gtk/qaiku/__init__.py +gwibber/microblog/plugins/sina/gtk/sina/__init__.py gwibber/preferences.py gwibber/util.py [type: gettext/glade] ui/gwibber-about-dialog.ui @@ -35,6 +36,7 @@ gwibber/util.py [type: gettext/glade] gwibber/microblog/plugins/qaiku/ui/gwibber-accounts-qaiku.ui [type: gettext/glade] gwibber/microblog/plugins/foursquare/ui/gwibber-accounts-foursquare.ui [type: gettext/glade] gwibber/microblog/plugins/gowalla/ui/gwibber-accounts-gowalla.ui +[type: gettext/glade] gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui ui/templates/base.mako ui/templates/targetbar.mako bin/gwibber-poster diff -up gwibber-3.0.0.1/setup.py.sina gwibber-3.0.0.1/setup.py --- gwibber-3.0.0.1/setup.py.sina 2011-05-12 11:59:10.201024163 -0400 +++ gwibber-3.0.0.1/setup.py 2011-05-12 11:59:10.235023795 -0400 @@ -69,6 +69,14 @@ setup(name="gwibber", ('share/gwibber/plugins/gowalla/ui/icons/22x22', glob("gwibber/microblog/plugins/gowalla/ui/icons/22x22/*.*")), ('share/gwibber/plugins/gowalla/ui/icons/32x32', glob("gwibber/microblog/plugins/gowalla/ui/icons/32x32/*.*")), ('share/gwibber/plugins/gowalla/ui/icons/scalable', glob("gwibber/microblog/plugins/gowalla/ui/icons/scalable/*.*")), + ('share/gwibber/plugins/sina', glob("gwibber/microblog/plugins/sina/*.*")), + ('share/gwibber/plugins/sina/gtk', glob("gwibber/microblog/plugins/sina/gtk/*.*")), + ('share/gwibber/plugins/sina/gtk/sina', glob("gwibber/microblog/plugins/sina/gtk/sina/*.*")), + ('share/gwibber/plugins/sina/ui', glob("gwibber/microblog/plugins/sina/ui/*.*")), + ('share/gwibber/plugins/sina/ui/icons/16x16', glob("gwibber/microblog/plugins/sina/ui/icons/16x16/*.*")), + ('share/gwibber/plugins/sina/ui/icons/22x22', glob("gwibber/microblog/plugins/sina/ui/icons/22x22/*.*")), + ('share/gwibber/plugins/sina/ui/icons/32x32', glob("gwibber/microblog/plugins/sina/ui/icons/32x32/*.*")), + ('share/gwibber/plugins/sina/ui/icons/scalable', glob("gwibber/microblog/plugins/sina/ui/icons/scalable/*.*")), ('share/gwibber/plugins/buzz', glob("gwibber/microblog/plugins/buzz/*.*")), ('share/gwibber/plugins/buzz/gtk', glob("gwibber/microblog/plugins/buzz/gtk/*.*")), ('share/gwibber/plugins/buzz/gtk/buzz', glob("gwibber/microblog/plugins/buzz/gtk/buzz/*.*")),