PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/gwibber-3.4.2/gwibber/microblog/plugins/statusnet/__init__.py

#
Python | 331 lines | 311 code | 20 blank | 0 comment | 5 complexity | 4d68449573989fe63979b73e8f9982c8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, LGPL-2.1, GPL-3.0
  1. import re
  2. from gwibber.microblog import network, util
  3. from oauth import oauth
  4. from gwibber.microblog.util import resources
  5. from gettext import lgettext as _
  6. import logging
  7. logger = logging.getLogger("StatusNet")
  8. logger.debug("Initializing.")
  9. PROTOCOL_INFO = {
  10. "name": "StatusNet",
  11. "version": 1.1,
  12. "config": [
  13. "private:secret_token",
  14. "access_token",
  15. "username",
  16. "site_display_name",
  17. "url_prefix",
  18. "color",
  19. "receive_enabled",
  20. "send_enabled",
  21. ],
  22. "authtype": "oauth1a",
  23. "color": "#4E9A06",
  24. "features": [
  25. "send",
  26. "receive",
  27. "reply",
  28. "responses",
  29. "private",
  30. "public",
  31. "delete",
  32. "follow",
  33. "unfollow",
  34. "profile",
  35. "retweet",
  36. "like",
  37. "send_thread",
  38. "send_private",
  39. "user_messages",
  40. "sinceid",
  41. ],
  42. "default_streams": [
  43. "receive",
  44. "images",
  45. "responses",
  46. "private",
  47. "public",
  48. ],
  49. }
  50. class Client:
  51. def __init__(self, acct):
  52. if acct.has_key("url_prefix"):
  53. pref = "" if acct["url_prefix"].startswith("http") else "https://"
  54. self.url_prefix = pref + acct["url_prefix"]
  55. if acct.has_key("secret_token") and acct.has_key("password"): acct.pop("password")
  56. if not acct.has_key("url_prefix") and acct.has_key("domain"): acct.pop("domain")
  57. self.account = acct
  58. def _common(self, data):
  59. m = {}
  60. try:
  61. m["mid"] = str(data["id"])
  62. m["service"] = "statusnet"
  63. m["account"] = self.account["id"]
  64. if data.has_key("created_at"):
  65. m["time"] = util.parsetime(data["created_at"])
  66. m["source"] = data.get("source", False)
  67. m["text"] = util.unescape(data["text"])
  68. m["to_me"] = ("@%s" % self.account["username"]) in data["text"]
  69. m["html"] = util.linkify(m["text"],
  70. ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % self.account["url_prefix"]),
  71. (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % self.account["url_prefix"])), escape=False)
  72. m["content"] = util.linkify(m["text"],
  73. ((util.PARSE_HASH, '#<a href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
  74. (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True)
  75. m["favorited"] = data.get("favorited", False)
  76. images = []
  77. if data.get("attachments", 0):
  78. for a in data["attachments"]:
  79. mime = a.get("mimetype", "")
  80. if mime and mime.startswith("image") and a.get("url", 0):
  81. images.append({"src": a["url"], "url": a["url"]})
  82. images.extend(util.imgpreview(m["text"]))
  83. if images:
  84. m["images"] = images
  85. m["type"] = "photo"
  86. except:
  87. logger.error("%s failure - %s", PROTOCOL_INFO["name"], data)
  88. return m
  89. def _user(self, user):
  90. return {
  91. "name": user.get("name", None),
  92. "nick": user.get("screen_name", None),
  93. "id": user.get("id", None),
  94. "location": user.get("location", None),
  95. "followers": user.get("followers_count", None),
  96. "friends": user.get("friends_count", None),
  97. "description": user.get("description", None),
  98. "following": user.get("following", None),
  99. "protected": user.get("protected", None),
  100. "statuses": user.get("statuses_count", None),
  101. "image": user.get("profile_image_url", None),
  102. "website": user.get("url", None),
  103. "url": "/".join((self.url_prefix, user["screen_name"])) or None,
  104. "is_me": user.get("screen_name", None) == self.account["username"],
  105. }
  106. def _message(self, data):
  107. if type(data) != dict:
  108. logger.error("Cannot parse message data: %s", str(data))
  109. return {}
  110. n = {}
  111. if data.has_key("retweeted_status"):
  112. n["retweeted_by"] = self._user(data["user"] if "user" in data else data["sender"])
  113. if data.has_key("created_at"):
  114. n["time"] = util.parsetime(data["created_at"])
  115. data = data["retweeted_status"]
  116. else:
  117. n["retweeted_by"] = None
  118. if data.has_key("created_at"):
  119. n["time"] = util.parsetime(data["created_at"])
  120. m = self._common(data)
  121. for k in n:
  122. m[k] = n[k]
  123. if data.has_key("in_reply_to_status_id"):
  124. if data["in_reply_to_status_id"]:
  125. m["reply"] = {}
  126. m["reply"]["id"] = data["in_reply_to_status_id"]
  127. m["reply"]["nick"] = data["in_reply_to_screen_name"]
  128. if m["reply"]["id"] and m["reply"]["nick"]:
  129. m["reply"]["url"] = "/".join((self.account["url_prefix"], "notice", str(m["reply"]["id"])))
  130. else:
  131. m["reply"]["url"] = None
  132. m["sender"] = self._user(data["user"] if "user" in data else data["sender"])
  133. m["url"] = "/".join((self.account["url_prefix"], "notice", str(m["mid"])))
  134. return m
  135. def _private(self, data):
  136. m = self._message(data)
  137. m["private"] = True
  138. m["recipient"] = {}
  139. m["recipient"]["name"] = data["recipient"]["name"]
  140. m["recipient"]["nick"] = data["recipient"]["screen_name"]
  141. m["recipient"]["id"] = data["recipient"]["id"]
  142. m["recipient"]["image"] = data["recipient"]["profile_image_url"]
  143. m["recipient"]["location"] = data["recipient"]["location"]
  144. m["recipient"]["url"] = "/".join((self.account["url_prefix"], m["recipient"]["nick"]))
  145. m["recipient"]["is_me"] = m["recipient"]["nick"].lower() == self.account["username"].lower()
  146. m["to_me"] = m["recipient"]["is_me"]
  147. return m
  148. def _result(self, data):
  149. m = self._common(data)
  150. if data["to_user_id"]:
  151. m["reply"] = {}
  152. m["reply"]["id"] = data["to_user_id"]
  153. m["reply"]["nick"] = data["to_user"]
  154. m["sender"] = {}
  155. m["sender"]["nick"] = data["from_user"]
  156. m["sender"]["id"] = data["from_user_id"]
  157. m["sender"]["image"] = data["profile_image_url"]
  158. m["sender"]["url"] = "/".join((self.account["url_prefix"], m["sender"]["nick"]))
  159. m["url"] = "/".join((self.account["url_prefix"], "notice", str(m["mid"])))
  160. return m
  161. def _profile(self, data):
  162. return {
  163. "name": data.get("name", data["screen_name"]),
  164. "service": "statusnet",
  165. "stream": "profile",
  166. "account": self.account["id"],
  167. "mid": data["id"],
  168. "text": data.get("description", ""),
  169. "nick": data["screen_name"],
  170. "url": data.get("url", ""),
  171. "protected": data.get("protected", False),
  172. "statuses": data.get("statuses_count", 0),
  173. "followers": data.get("followers_count", 0),
  174. "friends": data.get("friends_count", 0),
  175. "following": data.get("following", 0),
  176. "favourites": data.get("favourites_count", 0),
  177. "image": data["profile_image_url"],
  178. "utc_offset": data.get("utc_offset", 0),
  179. "id": data["id"],
  180. "lang": data.get("lang", "en"),
  181. "verified": data.get("verified", False),
  182. "geo_enabled": data.get("geo_enabled", False),
  183. "time_zone": data.get("time_zone", "")
  184. }
  185. def _get(self, path, parse="message", post=False, single=False, **args):
  186. if not self.account.has_key("access_token") and not self.account.has_key("secret_token"):
  187. logger.error("%s unexpected result - %s", PROTOCOL_INFO["name"], _("Account needs to be re-authorized"))
  188. return [{"error": {"type": "auth", "account": self.account, "message": _("Account needs to be re-authorized")}}]
  189. url = "/".join((self.account["url_prefix"], "api", path))
  190. self.sigmethod = oauth.OAuthSignatureMethod_HMAC_SHA1()
  191. self.consumer = oauth.OAuthConsumer("anonymous", "anonymous")
  192. self.token = oauth.OAuthToken(self.account["access_token"], self.account["secret_token"])
  193. parameters = util.compact(args)
  194. request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, self.token,
  195. http_method=post and "POST" or "GET", http_url=url, parameters=parameters)
  196. request.sign_request(self.sigmethod, self.consumer, self.token)
  197. if post:
  198. data = network.Download(request.to_url(), parameters, post).get_json()
  199. else:
  200. data = network.Download(request.to_url(), None, post).get_json()
  201. resources.dump(self.account["service"], self.account["id"], data)
  202. if isinstance(data, dict) and data.get("error", 0):
  203. logger.error("%s failure - %s", PROTOCOL_INFO["name"], data["error"])
  204. if "authenticate" in data["error"]:
  205. return [{"error": {"type": "auth", "account": self.account, "message": data["error"]}}]
  206. else:
  207. return [{"error": {"type": "unknown", "account": self.account, "message": data["error"]}}]
  208. elif isinstance(data, str):
  209. logger.error("%s unexpected result - %s", PROTOCOL_INFO["name"], data)
  210. return [{"error": {"type": "unknown", "account": self.account, "message": data}}]
  211. if parse == "follow" or parse == "unfollow":
  212. if isinstance(data, dict) and data.get("error", 0):
  213. logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("%s failed" % parse), data["error"])
  214. logger.error("%s", logstr)
  215. return [{"error": {"type": "auth", "account": self.account, "message": data["error"]}}]
  216. else:
  217. return [["friendships", {"type": parse, "account": self.account["id"], "service": self.account["service"],"user_id": data["id"], "nick": data["screen_name"]}]]
  218. if parse == "profile" and isinstance(data, dict):
  219. return self._profile(data)
  220. if single: return [getattr(self, "_%s" % parse)(data)]
  221. if parse: return [getattr(self, "_%s" % parse)(m) for m in data]
  222. else: return []
  223. return [self._result(m) for m in data]
  224. def _search(self, **args):
  225. data = network.Download("%s/api/search.json" % self.account["url_prefix"], util.compact(args))
  226. data = data.get_json()
  227. if type(data) != dict:
  228. logger.error("Cannot parse search data: %s", str(data))
  229. return []
  230. return [self._result(m) for m in data["results"]]
  231. def __call__(self, opname, **args):
  232. return getattr(self, opname)(**args)
  233. def receive(self, count=util.COUNT, since=None):
  234. return self._get("statuses/friends_timeline.json", count=count, since_id=since, source="Gwibber")
  235. def responses(self, count=util.COUNT, since=None):
  236. return self._get("statuses/mentions.json", count=count, since_id=since, source="Gwibber")
  237. def private(self, count=util.COUNT, since=None):
  238. private = self._get("direct_messages.json", "private", count=count, since_id=since, source="Gwibber") or []
  239. private_sent = self._get("direct_messages/sent.json", "private", count=count, since_id=since, source="Gwibber") or []
  240. return private + private_sent
  241. def public(self, count=util.COUNT, since=None):
  242. return self._get("statuses/public_timeline.json", source="Gwibber")
  243. def search(self, query, count=util.COUNT, since=None):
  244. return self._search(q=query, rpp=count, since_id=since, source="Gwibber")
  245. def tag(self, query, count=util.COUNT, since=None):
  246. return self._search(q="#%s" % query, count=count, since_id=since, source="Gwibber")
  247. def delete(self, message):
  248. self._get("statuses/destroy/%s.json" % message["mid"], None, post=True, do=1, source="Gwibber")
  249. return []
  250. def like(self, message):
  251. self._get("favorites/create/%s.json" % message["mid"], None, post=True, do=1, source="Gwibber")
  252. return []
  253. def send(self, message):
  254. return self._get("statuses/update.json", post=True, single=True, status=message, source="Gwibber")
  255. def send_private(self, message, private):
  256. return self._get("direct_messages/new.json", "private", post=True, single=True,
  257. text=message, screen_name=private["sender"]["nick"], source="Gwibber")
  258. def send_thread(self, message, target):
  259. return self._get("statuses/update.json", post=True, single=True,
  260. status=message, in_reply_to_status_id=target["mid"], source="Gwibber")
  261. def retweet(self, message):
  262. return self._get("statuses/retweet/%s.json" % message["mid"], None, post=True, do=1, source="Gwibber")
  263. def follow(self, screen_name):
  264. return self._get("friendships/create.json", screen_name=screen_name, post=True, parse="follow", source="Gwibber")
  265. def unfollow(self, screen_name):
  266. return self._get("friendships/destroy.json", screen_name=screen_name, post=True, parse="unfollow", source="Gwibber")
  267. def profile(self, id=None, count=None, since=None):
  268. return self._get("users/show.json", screen_name=id, count=count, since_id=since, parse="profile", source="Gwibber")
  269. def user_messages(self, id=None, count=util.COUNT, since=None):
  270. profiles = [self.profile(id)] or []
  271. messages = self._get("statuses/user_timeline.json", id=id, count=count, since_id=since, source="Gwibber") or []
  272. return messages + profiles