PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/pybitly/api.py

https://github.com/mattoufoutu/pybitly
Python | 317 lines | 305 code | 2 blank | 10 comment | 7 complexity | cd2d1b8126006020f3052cf6239c441a MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. """
  3. Module to interact with the bit.ly API functions.
  4. All the functions provided by the API are implemented except 0Auth functions.
  5. """
  6. #TODO: in methods allowing shorturls or urlhashs: autodetect hashes and urls
  7. #TODO: in methods allowing multiple args: limit to 15 args max (API restriction)
  8. #TODO: implement OAuth methods
  9. from urllib import urlencode
  10. from urllib2 import build_opener, HTTPSHandler
  11. try:
  12. # Python >= 2.6
  13. from json import load as json_load
  14. except ImportError:
  15. # Python < 2.6
  16. try:
  17. from simplejson import load as json_load
  18. except ImportError:
  19. print("""Could not import the simplejson module.
  20. You can download simplejson from PyPI or
  21. install it using your package manager.""")
  22. from errors import ArgTypeError, ArgumentError
  23. ALLOWED_API_DOMAINS = ['bit.ly', 'j.mp']
  24. class RespStatus(object):
  25. """
  26. Contains an API response status.
  27. """
  28. def __init__(self, status_code, status_txt):
  29. self.code = status_code
  30. self.txt = status_txt
  31. def __str__(self):
  32. return "<RespStatus(status_code=%d, status_txt='%s')>" % (
  33. self.code, self.txt
  34. )
  35. def is_ok(self):
  36. return (self.code == 200) and (self.txt == 'OK')
  37. class BitlyApi(object):
  38. """
  39. Interact with the bit.ly API functions.
  40. """
  41. base_url = "http://api.bit.ly/v3"
  42. def __init__(self, login, key):
  43. self.login = login
  44. self.key = key
  45. self.opener = build_opener()
  46. self.has_ssl = False
  47. for handler in self.opener.handlers:
  48. if isinstance(handler, HTTPSHandler):
  49. self.has_ssl = True
  50. break
  51. def __repr__(self):
  52. return "BitlyApi(login='%s', key='%s')" % (self.login, self.key)
  53. def __str__(self):
  54. return "<BitlyApi(login='%s', key='%s') at %s>" % (
  55. self.login, self.key, hex(id(self))
  56. )
  57. def _get_resp(self, url, data=None):
  58. """
  59. Send a query to bit.ly and return the received data.
  60. """
  61. resp = self.opener.open(url, data)
  62. json_data = json_load(resp)
  63. status = RespStatus(json_data['status_code'], json_data['status_txt'])
  64. data = json_data['data']
  65. return status, data
  66. def _multi_args(self, argname, args):
  67. """
  68. Format URL arguments for queries allowing multiple ones.
  69. """
  70. if not args:
  71. return ''
  72. arglist = []
  73. for arg in args:
  74. arglist.append('%s=%s' % (argname, arg))
  75. arglist = '&'+'&'.join(arglist)
  76. return arglist
  77. def shorten(self, longUrl, domain=None):
  78. """
  79. Shorten a given URL.
  80. """
  81. if domain is None:
  82. domain = 'bit.ly'
  83. if domain not in ALLOWED_API_DOMAINS:
  84. raise ArgumentError("Unknown domain '%s' "
  85. "(allowed: 'bit.ly' and 'j.mp')" % domain)
  86. url = "%s/shorten?login=%s&apiKey=%s&longUrl=%s&domain=%s" % (
  87. self.base_url, self.login, self.key, longUrl, domain
  88. )
  89. resp = self._get_resp(url)
  90. return resp
  91. def expand(self, shortUrls=None, urlHashs=None):
  92. """
  93. Expand short URLs/hashs.
  94. """
  95. if (shortUrls is None) and (urlHashs is None):
  96. return {}
  97. if shortUrls is None:
  98. urlarg = ''
  99. else:
  100. if not isinstance(shortUrls, list):
  101. raise ArgTypeError('shortUrls', obj_type(shortUrls), 'list')
  102. urlarg = self._multi_args('shortUrl', shortUrls)
  103. if urlHashs is None:
  104. hasharg = ''
  105. else:
  106. if not isinstance(urlHashs, list):
  107. raise ArgTypeError('urlHashs', obj_type(urlHashs), 'list')
  108. hasharg = self._multi_args('hash', urlHashs)
  109. url = "%s/expand?login=%s&apiKey=%s%s%s" % (
  110. self.base_url, self.login, self.key, urlarg, hasharg
  111. )
  112. resp = self._get_resp(url)
  113. return resp
  114. def validate(self, login, key):
  115. """
  116. Validate a bit.ly API account.
  117. """
  118. url = "%s/validate?login=%s&apiKey=%s&x_login=%s&x_apiKey=%s" % (
  119. self.base_url, self.login, self.key, login, key
  120. )
  121. resp = self._get_resp(url)
  122. return resp
  123. def clicks(self, shortUrls=None, urlHashs=None):
  124. """
  125. Get statistics about short URLs/hashs.
  126. """
  127. if (shortUrls is None) and (urlHashs is None):
  128. return {}
  129. if shortUrls is None:
  130. urlarg = ''
  131. else:
  132. if not isinstance(shortUrls, list):
  133. raise ArgTypeError('shortUrls', obj_type(shortUrls), 'list')
  134. urlarg = self._multi_args('shortUrl', shortUrls)
  135. if urlHashs is None:
  136. hasharg = ''
  137. else:
  138. if not isinstance(urlHashs, list):
  139. raise ArgTypeError('urlHashs', obj_type(urlHashs), 'list')
  140. hasharg = self._multi_args('hash', urlHashs)
  141. url = '%s/clicks?login=%s&apiKey=%s%s%s' % (
  142. self.base_url, self.login, self.key, urlarg, hasharg
  143. )
  144. resp = self._get_resp(url)
  145. return resp
  146. def referrers(self, shortUrl=None, urlHash=None):
  147. """
  148. Get referring sites and number of clicks per referrer
  149. for a given short URL or hash.
  150. """
  151. if (shortUrl is None) and (urlHash is None):
  152. return {}
  153. if (shortUrl is not None) and (urlHash is not None):
  154. raise ArgumentError("You can submit only one URL or "
  155. "hash, not both.")
  156. if shortUrl is not None:
  157. url = "%s/referrers?login=%s&apiKey=%s&shortUrl=%s" % (
  158. self.base_url, self.login, self.key, shortUrl
  159. )
  160. if urlHash is not None:
  161. url = "%s/referrers?login=%s&apiKey=%s&hash=%s" % (
  162. self.base_url, self.login, self.key, urlHash
  163. )
  164. resp = self._get_resp(url)
  165. return resp
  166. def countries(self, shortUrl=None, urlHash=None):
  167. """
  168. Get a list of countries from which clicks have originated for a given URL or hash.
  169. """
  170. if (shortUrl is None) and (urlHash is None):
  171. return {}
  172. if (shortUrl is not None) and (urlHash is not None):
  173. raise ArgumentError("You can submit only one URL or "
  174. "hash, not both.")
  175. if shortUrl is not None:
  176. url = "%s/countries?login=%s&apiKey=%s&shortUrl=%s" % (
  177. self.base_url, self.login, self.key, shortUrl
  178. )
  179. if urlHash is not None:
  180. url = "%s/countries?login=%s&apiKey=%s&hash=%s" % (
  181. self.base_url, self.login, self.key, urlHash
  182. )
  183. resp = self._get_resp(url)
  184. return resp
  185. def clicks_by_minute(self, shortUrls=None, urlHashs=None):
  186. """
  187. Get time series clicks per minute for the last hour
  188. (most recent to least recent) about short URLs/hashs.
  189. """
  190. if (shortUrls is None) and (urlHashs is None):
  191. return {}
  192. if shortUrls is None:
  193. urlarg = ''
  194. else:
  195. if not isinstance(shortUrls, list):
  196. raise ArgTypeError('shortUrls', obj_type(shortUrls), 'list')
  197. urlarg = self._multi_args('shortUrl', shortUrls)
  198. if urlHashs is None:
  199. hasharg = ''
  200. else:
  201. if not isinstance(urlHashs, list):
  202. raise ArgTypeError('urlHashs', obj_type(urlHashs), 'list')
  203. hasharg = self._multi_args('hash', urlHashs)
  204. url = "%s/clicks_by_minute?login=%s&apiKey=%s%s%s" % (
  205. self.base_url, self.login, self.key, urlarg, hasharg
  206. )
  207. resp = self._get_resp(url)
  208. return resp
  209. def bitly_pro_domain(self, domain):
  210. """
  211. Check whether a given short domain is assigned for bitly Pro.
  212. """
  213. url = "%s/bitly_pro_domain?login=%s&apiKey=%s&domain=%s" % (
  214. self.base_url, self.login, self.key, domain
  215. )
  216. resp = self._get_resp(url)
  217. return resp
  218. def lookup(self, longUrls=None):
  219. """
  220. Find short URLs corresponding to given long URLs.
  221. """
  222. if longUrls is None:
  223. return {}
  224. if not isinstance(longUrls, list):
  225. raise ArgTypeError('longUrls', obj_type(longUrls), 'list')
  226. urlarg = self._multi_args('url', longUrls)
  227. url = "%s/lookup?login=%s&apiKey=%s%s" % (
  228. self.base_url, self.login, self.key, urlarg
  229. )
  230. resp = self._get_resp(url)
  231. return resp
  232. def authenticate(self, login, password):
  233. """
  234. Lookup a bit.ly API key given an account username and password.
  235. Access to this function is restricted and must be requested by
  236. email at api@bit.ly.
  237. """
  238. url = "%s/authenticate" % self.base_url
  239. data = urlencode({
  240. 'login': self.login,
  241. 'apiKey': self.key,
  242. 'x_login': login,
  243. 'x_password': password,
  244. })
  245. resp = self._get_resp(url, data)
  246. return resp
  247. def info(self, shortUrls=None, urlHashs=None):
  248. """
  249. Get informations about short URLs/hashs (creator, page title, ...).
  250. """
  251. if (shortUrls is None) and (urlHashs is None):
  252. return {}
  253. if shortUrls is None:
  254. urlarg = ''
  255. else:
  256. if not isinstance(shortUrls, list):
  257. raise ArgTypeError('shortUrls', obj_type(shortUrls), 'list')
  258. urlarg = self._multi_args('shortUrl', shortUrls)
  259. if urlHashs is None:
  260. hasharg = ''
  261. else:
  262. if not isinstance(urlHashs, list):
  263. raise ArgTypeError('urlHashs', obj_type(urlHashs), 'list')
  264. hasharg = self._multi_args('hash', urlHashs)
  265. url = "%s/info?login=%s&apiKey=%s%s%s" % (
  266. self.base_url, self.login, self.key, urlarg, hasharg
  267. )
  268. resp = self._get_resp(url)
  269. return resp
  270. def oauth_access_token(self, client_id, client_secret, **kwargs):
  271. """
  272. Request an OAuth access token (used for OAuth Web Flow
  273. and Oauth XAuth Flow).
  274. """
  275. if not self.has_ssl:
  276. msg = "Your Python installation doesn't have SSL support."
  277. raise NotImplementedError(msg)
  278. data = {'client_id': client_id, 'client_secret': client_secret}
  279. for argname in kwargs:
  280. data[argname] = kwargs[argname]
  281. data = urlencode(data)
  282. resp = self.opener.open('https://api-ssl.bit.ly/oauth/access_token',
  283. data)
  284. return resp.read()
  285. def obj_type(obj):
  286. """Return the string representation of a given object's type."""
  287. return obj.__class__.__name__