~ubuntu-branches/ubuntu/precise/gwibber-service-sina/precise-201203201005

« back to all changes in this revision

Viewing changes to __init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Ken VanDine
  • Date: 2011-06-10 09:11:33 UTC
  • Revision ID: james.westby@ubuntu.com-20110610091133-04usv8rnmraq5f4j
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from gwibber.microblog import network, util
 
2
from htmlentitydefs import name2codepoint
 
3
import re
 
4
import gnomekeyring
 
5
from oauth import oauth
 
6
from gwibber.microblog.util import log, resources
 
7
from gettext import lgettext as _
 
8
import sina.utils
 
9
log.logger.name = "Sina"
 
10
 
 
11
PROTOCOL_INFO = {
 
12
  "name": "Sina",
 
13
  "version": "1.0",
 
14
  
 
15
  "config": [
 
16
    "private:secret_token",
 
17
    "access_token",
 
18
    "username",
 
19
    "color",
 
20
    "receive_enabled",
 
21
    "send_enabled",
 
22
  ],
 
23
 
 
24
  "authtype": "oauth1a",
 
25
  "color": "#729FCF",
 
26
 
 
27
  "features": [
 
28
    "send",
 
29
    "receive",
 
30
    "search",
 
31
    "tag",
 
32
    "reply",
 
33
    "responses",
 
34
    "private",
 
35
    "public",
 
36
    "delete",
 
37
    "retweet",
 
38
    "like",
 
39
    "send_thread",
 
40
    "send_private",
 
41
    "user_messages",
 
42
    "sinceid",
 
43
    "lists",
 
44
    "list",
 
45
  ],
 
46
 
 
47
  "default_streams": [
 
48
    "receive",
 
49
    "images",
 
50
    "responses",
 
51
    "private",
 
52
    "lists",
 
53
  ],
 
54
}
 
55
 
 
56
URL_PREFIX = "http://t.sina.com.cn"
 
57
API_PREFIX = "http://api.t.sina.com.cn"
 
58
 
 
59
 
 
60
def unescape(s):
 
61
  return re.sub('&(%s);' % '|'.join(name2codepoint), 
 
62
    lambda m: unichr(name2codepoint[m.group(1)]), s)
 
63
 
 
64
class Client:
 
65
  def __init__(self, acct):
 
66
    self.service = util.getbus("Service")
 
67
    if acct.has_key("secret_token") and acct.has_key("password"): acct.pop("password")
 
68
    self.account = acct
 
69
 
 
70
    if not acct.has_key("access_token") and not acct.has_key("secret_token"):
 
71
      return [{"error": {"type": "auth", "account": self.account, "message": _("Failed to find credentials")}}]
 
72
 
 
73
    self.sigmethod = oauth.OAuthSignatureMethod_HMAC_SHA1()
 
74
    self.consumer = oauth.OAuthConsumer(*sina.utils.get_sina_keys())
 
75
    self.token = oauth.OAuthToken(acct["access_token"], acct["secret_token"])
 
76
 
 
77
  def _common(self, data):
 
78
    m = {};
 
79
    try:
 
80
      m["mid"] = str(data["id"])
 
81
      m["service"] = "sina"
 
82
      m["account"] = self.account["id"]
 
83
      m["time"] = util.parsetime(data["created_at"])
 
84
      m["text"] = unescape(data["text"])
 
85
      m["to_me"] = ("@%s" % self.account["username"]) in data["text"]
 
86
 
 
87
      m["html"] = util.linkify(data["text"],
 
88
        ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % URL_PREFIX),
 
89
        (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX)), escape=False)
 
90
 
 
91
      m["content"] = util.linkify(data["text"],
 
92
        ((util.PARSE_HASH, '#<a class="hash" href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
 
93
        (util.PARSE_NICK, '@<a class="nick" href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=False)
 
94
 
 
95
      if data.has_key("retweeted_status"):
 
96
        m["retweeted_status"] = data["retweeted_status"]
 
97
      else:
 
98
        m["retweeted_status"] = None
 
99
 
 
100
      images = []
 
101
      if data.get("original_pic", 0):
 
102
        images.append({"src": data["thumbnail_pic"], "url": data["original_pic"]})
 
103
      if data.get("retweeted_status", 0):
 
104
        if data["retweeted_status"].get("original_pic"):
 
105
          images.append({"src": data["retweeted_status"]["thumbnail_pic"], "url": data["retweeted_status"]["original_pic"]})
 
106
      if images:
 
107
        m["images"] = images
 
108
        m["type"] = "photo"
 
109
    except: 
 
110
      log.logger.error("%s failure - %s", PROTOCOL_INFO["name"], data)
 
111
      return {}
 
112
 
 
113
    return m
 
114
 
 
115
  def _user(self, user):
 
116
    return {
 
117
        "name": user["name"],
 
118
        "nick": user["screen_name"],
 
119
        "id": user["id"],
 
120
        "location": user["location"],
 
121
        "followers": user.get("followers", None),
 
122
        "image": user["profile_image_url"],
 
123
        "url": "/".join((API_PREFIX, str(user["id"]))),
 
124
        "is_me": user["screen_name"] == self.account["username"],
 
125
    }
 
126
    
 
127
  def _message(self, data):
 
128
    if type(data) == type(None):
 
129
      return []
 
130
 
 
131
    m = self._common(data)
 
132
    m["source"] = data.get("source", False)
 
133
    
 
134
    if data.has_key("in_reply_to_status_id"):
 
135
      if data["in_reply_to_status_id"]:
 
136
        m["reply"] = {}
 
137
        m["reply"]["id"] = data["in_reply_to_status_id"]
 
138
        m["reply"]["nick"] = data["in_reply_to_screen_name"]
 
139
        if m["reply"]["id"] and m["reply"]["nick"]:
 
140
          m["reply"]["url"] = "/".join((URL_PREFIX, m["reply"]["nick"], "statuses", str(m["reply"]["id"])))
 
141
        else:
 
142
          m["reply"]["url"] = None
 
143
 
 
144
    m["sender"] = self._user(data["user"] if "user" in data else data["sender"])
 
145
    m["url"] = "/".join((m["sender"]["url"], "statuses", str(m["mid"])))
 
146
 
 
147
    return m
 
148
 
 
149
  def _private(self, data):
 
150
    m = self._message(data)
 
151
    m["private"] = True
 
152
 
 
153
    m["recipient"] = {}
 
154
    m["recipient"]["name"] = data["recipient"]["name"]
 
155
    m["recipient"]["nick"] = data["recipient"]["screen_name"]
 
156
    m["recipient"]["id"] = data["recipient"]["id"]
 
157
    m["recipient"]["image"] = data["recipient"]["profile_image_url"]
 
158
    m["recipient"]["location"] = data["recipient"]["location"]
 
159
    m["recipient"]["url"] = "/".join((URL_PREFIX, m["recipient"]["nick"]))
 
160
    m["recipient"]["is_me"] = m["recipient"]["nick"] == self.account["username"]
 
161
    m["to_me"] = m["recipient"]["is_me"]
 
162
 
 
163
    return m
 
164
 
 
165
  def _result(self, data):
 
166
    m = self._common(data)
 
167
    
 
168
    if data["to_user_id"]:
 
169
      m["reply"] = {}
 
170
      m["reply"]["id"] = data["to_user_id"]
 
171
      m["reply"]["nick"] = data["to_user"]
 
172
 
 
173
    m["sender"] = {}
 
174
    m["sender"]["nick"] = data["from_user"]
 
175
    m["sender"]["id"] = data["from_user_id"]
 
176
    m["sender"]["image"] = data["profile_image_url"]
 
177
    m["sender"]["url"] = "/".join((URL_PREFIX, m["sender"]["nick"]))
 
178
    m["sender"]["is_me"] = m["sender"]["nick"] == self.account["username"]
 
179
    m["url"] = "/".join((m["sender"]["url"], "statuses", str(m["mid"])))
 
180
    return m
 
181
 
 
182
  def _list(self, data):
 
183
    return {
 
184
        "mid": data["id"],
 
185
        "service": "sina",
 
186
        "account": self.account["id"],
 
187
        "time": 0,
 
188
        "text": data["description"],
 
189
        "html": data["description"],
 
190
        "content": data["description"],
 
191
        "url": "/".join((URL_PREFIX, data["uri"])),
 
192
        "sender": self._user(data["user"]),
 
193
        "name": data["name"],
 
194
        "nick": data["slug"],
 
195
        "key": data["slug"],
 
196
        "full": data["full_name"],
 
197
        "uri": data["uri"],
 
198
        "mode": data["mode"],
 
199
        "members": data["member_count"],
 
200
        "followers": data["subscriber_count"],
 
201
        "kind": "list",
 
202
    }
 
203
 
 
204
  def _get(self, path, parse="message", post=False, single=False, **args):
 
205
    url = "/".join((API_PREFIX, path))
 
206
 
 
207
    request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, self.token,
 
208
        http_method="POST" if post else "GET", http_url=url, parameters=util.compact(args))
 
209
    request.sign_request(self.sigmethod, self.consumer, self.token)
 
210
    
 
211
    if post:
 
212
      data = network.Download(request.http_url, None, post, body=request.to_postdata()).get_json()
 
213
      #data = network.Download(request.to_url(), util.compact(args), post).get_json()
 
214
    else:
 
215
      data = network.Download(request.to_url(), None, post).get_json()
 
216
 
 
217
    resources.dump(self.account["service"], self.account["id"], data)
 
218
 
 
219
    if isinstance(data, dict) and data.get("errors", 0):
 
220
      if "authenticate" in data["errors"][0]["message"]:
 
221
        logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Authentication failed"), error["message"])
 
222
        log.logger.error("%s", logstr)
 
223
        return [{"error": {"type": "auth", "account": self.account, "message": data["errors"][0]["message"]}}]
 
224
      else:
 
225
        for error in data["errors"]:
 
226
          logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Unknown failure"), error["message"])
 
227
          return [{"error": {"type": "unknown", "account": self.account, "message": error["message"]}}]
 
228
    elif isinstance(data, dict) and data.get("error", 0):
 
229
      if "Incorrect signature" in data["error"]:
 
230
        logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Request failed"), data["error"])
 
231
        log.logger.error("%s", logstr)
 
232
        return [{"error": {"type": "auth", "account": self.account, "message": data["error"]}}]
 
233
    elif isinstance(data, str):
 
234
      logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Request failed"), data)
 
235
      log.logger.error("%s", logstr)
 
236
      return [{"error": {"type": "request", "account": self.account, "message": data}}]
 
237
    
 
238
    if parse == "list":
 
239
      return [self._list(l) for l in data["lists"]]
 
240
    if single: return [getattr(self, "_%s" % parse)(data)]
 
241
    if parse: return [getattr(self, "_%s" % parse)(m) for m in data]
 
242
    else: return []
 
243
 
 
244
  def _search(self, **args):
 
245
    data = network.Download("http://api.t.sina.com.cn/search.json", util.compact(args))
 
246
    data = data.get_json()["results"]
 
247
 
 
248
    return [self._result(m) for m in data]
 
249
 
 
250
  def __call__(self, opname, **args):
 
251
    return getattr(self, opname)(**args)
 
252
  
 
253
  def receive(self, count=util.COUNT, since=None):
 
254
    return self._get("statuses/home_timeline.json", count=count, since_id=since)
 
255
 
 
256
  def user_messages(self, id=None, count=util.COUNT, since=None):
 
257
    return self._get("statuses/user_timeline.json", id=id, count=count, since_id=since)
 
258
 
 
259
  def responses(self, count=util.COUNT, since=None):
 
260
    return self._get("statuses/mentions.json", count=count, since_id=since)
 
261
 
 
262
  def private(self, count=util.COUNT, since=None):
 
263
    private = self._get("direct_messages.json", "private", count=count, since_id=since) or []
 
264
    private_sent = self._get("direct_messages/sent.json", "private", count=count, since_id=since) or []
 
265
    return private + private_sent
 
266
 
 
267
  def public(self):
 
268
    return self._get("statuses/public_timeline.json")
 
269
 
 
270
  def lists(self, **args):
 
271
    following = self._get("%s/lists/subscriptions.json" % self.account["username"], "list") or []
 
272
    lists = self._get("%s/lists.json" % self.account["username"], "list") or []
 
273
    return following + lists
 
274
 
 
275
  def list(self, user, id, count=util.COUNT, since=None):
 
276
    return self._get("%s/lists/%s/statuses.json" % (user, id), per_page=count, since_id=since)
 
277
 
 
278
  def search(self, query, count=util.COUNT, since=None):
 
279
    return self._search(q=query, rpp=count, since_id=since)
 
280
 
 
281
  def tag(self, query, count=util.COUNT, since=None):
 
282
    return self._search(q="#%s" % query, count=count, since_id=since)
 
283
 
 
284
  def delete(self, message):
 
285
    return self._get("statuses/destroy/%s.json" % message["mid"], None, post=True, do=1)
 
286
 
 
287
  def like(self, message):
 
288
    return self._get("favorites/create/%s.json" % message["mid"], None, post=True, do=1)
 
289
 
 
290
  def send(self, message):
 
291
    return self._get("statuses/update.json", post=True, single=True,
 
292
        status=message)
 
293
  
 
294
  def send_private(self, message, private):
 
295
    return self._get("direct_messages/new.json", "private", post=True, single=True,
 
296
        text=message, screen_name=private["sender"]["nick"])
 
297
 
 
298
  def send_thread(self, message, target):
 
299
    return self._get("statuses/update.json", post=True, single=True,
 
300
        status=message, in_reply_to_status_id=target["mid"])