api.py 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348
  1. # Tweepy
  2. # Copyright 2009-2010 Joshua Roesslein
  3. # See LICENSE for details.
  4. from __future__ import print_function
  5. import os
  6. import mimetypes
  7. import six
  8. from tweepy.binder import bind_api
  9. from tweepy.error import TweepError
  10. from tweepy.parsers import ModelParser, Parser
  11. from tweepy.utils import list_to_csv
  12. class API(object):
  13. """Twitter API"""
  14. def __init__(self, auth_handler=None,
  15. host='api.twitter.com', search_host='search.twitter.com',
  16. upload_host='upload.twitter.com', cache=None, api_root='/1.1',
  17. search_root='', upload_root='/1.1', retry_count=0,
  18. retry_delay=0, retry_errors=None, timeout=60, parser=None,
  19. compression=False, wait_on_rate_limit=False,
  20. wait_on_rate_limit_notify=False, proxy=''):
  21. """ Api instance Constructor
  22. :param auth_handler:
  23. :param host: url of the server of the rest api, default:'api.twitter.com'
  24. :param search_host: url of the search server, default:'search.twitter.com'
  25. :param upload_host: url of the upload server, default:'upload.twitter.com'
  26. :param cache: Cache to query if a GET method is used, default:None
  27. :param api_root: suffix of the api version, default:'/1.1'
  28. :param search_root: suffix of the search version, default:''
  29. :param upload_root: suffix of the upload version, default:'/1.1'
  30. :param retry_count: number of allowed retries, default:0
  31. :param retry_delay: delay in second between retries, default:0
  32. :param retry_errors: default:None
  33. :param timeout: delay before to consider the request as timed out in seconds, default:60
  34. :param parser: ModelParser instance to parse the responses, default:None
  35. :param compression: If the response is compressed, default:False
  36. :param wait_on_rate_limit: If the api wait when it hits the rate limit, default:False
  37. :param wait_on_rate_limit_notify: If the api print a notification when the rate limit is hit, default:False
  38. :param proxy: Url to use as proxy during the HTTP request, default:''
  39. :raise TypeError: If the given parser is not a ModelParser instance.
  40. """
  41. self.auth = auth_handler
  42. self.host = host
  43. self.search_host = search_host
  44. self.upload_host = upload_host
  45. self.api_root = api_root
  46. self.search_root = search_root
  47. self.upload_root = upload_root
  48. self.cache = cache
  49. self.compression = compression
  50. self.retry_count = retry_count
  51. self.retry_delay = retry_delay
  52. self.retry_errors = retry_errors
  53. self.timeout = timeout
  54. self.wait_on_rate_limit = wait_on_rate_limit
  55. self.wait_on_rate_limit_notify = wait_on_rate_limit_notify
  56. self.parser = parser or ModelParser()
  57. self.proxy = {}
  58. if proxy:
  59. self.proxy['https'] = proxy
  60. # Attempt to explain more clearly the parser argument requirements
  61. # https://github.com/tweepy/tweepy/issues/421
  62. #
  63. parser_type = Parser
  64. if not isinstance(self.parser, parser_type):
  65. raise TypeError(
  66. '"parser" argument has to be an instance of "{required}".'
  67. ' It is currently a {actual}.'.format(
  68. required=parser_type.__name__,
  69. actual=type(self.parser)
  70. )
  71. )
  72. @property
  73. def home_timeline(self):
  74. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/home_timeline
  75. :allowed_param:'since_id', 'max_id', 'count'
  76. """
  77. return bind_api(
  78. api=self,
  79. path='/statuses/home_timeline.json',
  80. payload_type='status', payload_list=True,
  81. allowed_param=['since_id', 'max_id', 'count'],
  82. require_auth=True
  83. )
  84. def statuses_lookup(self, id_, include_entities=None,
  85. trim_user=None, map_=None):
  86. return self._statuses_lookup(list_to_csv(id_), include_entities,
  87. trim_user, map_)
  88. @property
  89. def _statuses_lookup(self):
  90. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/lookup
  91. :allowed_param:'id', 'include_entities', 'trim_user', 'map'
  92. """
  93. return bind_api(
  94. api=self,
  95. path='/statuses/lookup.json',
  96. payload_type='status', payload_list=True,
  97. allowed_param=['id', 'include_entities', 'trim_user', 'map'],
  98. require_auth=True
  99. )
  100. @property
  101. def user_timeline(self):
  102. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/user_timeline
  103. :allowed_param:'id', 'user_id', 'screen_name', 'since_id'
  104. """
  105. return bind_api(
  106. api=self,
  107. path='/statuses/user_timeline.json',
  108. payload_type='status', payload_list=True,
  109. allowed_param=['id', 'user_id', 'screen_name', 'since_id',
  110. 'max_id', 'count', 'include_rts']
  111. )
  112. @property
  113. def mentions_timeline(self):
  114. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/mentions_timeline
  115. :allowed_param:'since_id', 'max_id', 'count'
  116. """
  117. return bind_api(
  118. api=self,
  119. path='/statuses/mentions_timeline.json',
  120. payload_type='status', payload_list=True,
  121. allowed_param=['since_id', 'max_id', 'count'],
  122. require_auth=True
  123. )
  124. @property
  125. def related_results(self):
  126. """ :reference: https://dev.twitter.com/docs/api/1.1/get/related_results/show/%3id.format
  127. :allowed_param:'id'
  128. """
  129. return bind_api(
  130. api=self,
  131. path='/related_results/show/{id}.json',
  132. payload_type='relation', payload_list=True,
  133. allowed_param=['id'],
  134. require_auth=False
  135. )
  136. @property
  137. def retweets_of_me(self):
  138. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/retweets_of_me
  139. :allowed_param:'since_id', 'max_id', 'count'
  140. """
  141. return bind_api(
  142. api=self,
  143. path='/statuses/retweets_of_me.json',
  144. payload_type='status', payload_list=True,
  145. allowed_param=['since_id', 'max_id', 'count'],
  146. require_auth=True
  147. )
  148. @property
  149. def get_status(self):
  150. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/show/%3Aid
  151. :allowed_param:'id'
  152. """
  153. return bind_api(
  154. api=self,
  155. path='/statuses/show.json',
  156. payload_type='status',
  157. allowed_param=['id']
  158. )
  159. def update_status(self, *args, **kwargs):
  160. """ :reference: https://dev.twitter.com/rest/reference/post/statuses/update
  161. :allowed_param:'status', 'in_reply_to_status_id', 'lat', 'long', 'source', 'place_id', 'display_coordinates', 'media_ids'
  162. """
  163. post_data = {}
  164. media_ids = kwargs.pop("media_ids", None)
  165. if media_ids is not None:
  166. post_data["media_ids"] = list_to_csv(media_ids)
  167. return bind_api(
  168. api=self,
  169. path='/statuses/update.json',
  170. method='POST',
  171. payload_type='status',
  172. allowed_param=['status', 'in_reply_to_status_id', 'lat', 'long', 'source', 'place_id', 'display_coordinates'],
  173. require_auth=True
  174. )(post_data=post_data, *args, **kwargs)
  175. def media_upload(self, filename, *args, **kwargs):
  176. """ :reference: https://dev.twitter.com/rest/reference/post/media/upload
  177. :allowed_param:
  178. """
  179. f = kwargs.pop('file', None)
  180. headers, post_data = API._pack_image(filename, 3072, form_field='media', f=f)
  181. kwargs.update({'headers': headers, 'post_data': post_data})
  182. return bind_api(
  183. api=self,
  184. path='/media/upload.json',
  185. method='POST',
  186. payload_type='media',
  187. allowed_param=[],
  188. require_auth=True,
  189. upload_api=True
  190. )(*args, **kwargs)
  191. def update_with_media(self, filename, *args, **kwargs):
  192. """ :reference: https://dev.twitter.com/rest/reference/post/statuses/update_with_media
  193. :allowed_param:'status', 'possibly_sensitive', 'in_reply_to_status_id', 'lat', 'long', 'place_id', 'display_coordinates'
  194. """
  195. f = kwargs.pop('file', None)
  196. headers, post_data = API._pack_image(filename, 3072, form_field='media[]', f=f)
  197. kwargs.update({'headers': headers, 'post_data': post_data})
  198. return bind_api(
  199. api=self,
  200. path='/statuses/update_with_media.json',
  201. method='POST',
  202. payload_type='status',
  203. allowed_param=[
  204. 'status', 'possibly_sensitive', 'in_reply_to_status_id', 'lat', 'long',
  205. 'place_id', 'display_coordinates'
  206. ],
  207. require_auth=True
  208. )(*args, **kwargs)
  209. @property
  210. def destroy_status(self):
  211. """ :reference: https://dev.twitter.com/rest/reference/post/statuses/destroy/%3Aid
  212. :allowed_param:'id'
  213. """
  214. return bind_api(
  215. api=self,
  216. path='/statuses/destroy/{id}.json',
  217. method='POST',
  218. payload_type='status',
  219. allowed_param=['id'],
  220. require_auth=True
  221. )
  222. @property
  223. def retweet(self):
  224. """ :reference: https://dev.twitter.com/rest/reference/post/statuses/retweet/%3Aid
  225. :allowed_param:'id'
  226. """
  227. return bind_api(
  228. api=self,
  229. path='/statuses/retweet/{id}.json',
  230. method='POST',
  231. payload_type='status',
  232. allowed_param=['id'],
  233. require_auth=True
  234. )
  235. @property
  236. def retweets(self):
  237. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/retweets/%3Aid
  238. :allowed_param:'id', 'count'
  239. """
  240. return bind_api(
  241. api=self,
  242. path='/statuses/retweets/{id}.json',
  243. payload_type='status', payload_list=True,
  244. allowed_param=['id', 'count'],
  245. require_auth=True
  246. )
  247. @property
  248. def retweeters(self):
  249. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/retweeters/ids
  250. :allowed_param:'id', 'cursor', 'stringify_ids
  251. """
  252. return bind_api(
  253. api=self,
  254. path='/statuses/retweeters/ids.json',
  255. payload_type='ids',
  256. allowed_param=['id', 'cursor', 'stringify_ids']
  257. )
  258. @property
  259. def get_user(self):
  260. """ :reference: https://dev.twitter.com/rest/reference/get/users/show
  261. :allowed_param:'id', 'user_id', 'screen_name'
  262. """
  263. return bind_api(
  264. api=self,
  265. path='/users/show.json',
  266. payload_type='user',
  267. allowed_param=['id', 'user_id', 'screen_name']
  268. )
  269. @property
  270. def get_oembed(self):
  271. """ :reference: https://dev.twitter.com/rest/reference/get/statuses/oembed
  272. :allowed_param:'id', 'url', 'maxwidth', 'hide_media', 'omit_script', 'align', 'related', 'lang'
  273. """
  274. return bind_api(
  275. api=self,
  276. path='/statuses/oembed.json',
  277. payload_type='json',
  278. allowed_param=['id', 'url', 'maxwidth', 'hide_media', 'omit_script', 'align', 'related', 'lang']
  279. )
  280. def lookup_users(self, user_ids=None, screen_names=None, include_entities=None):
  281. """ Perform bulk look up of users from user ID or screenname """
  282. post_data = {}
  283. if include_entities is not None:
  284. include_entities = 'true' if include_entities else 'false'
  285. post_data['include_entities'] = include_entities
  286. if user_ids:
  287. post_data['user_id'] = list_to_csv(user_ids)
  288. if screen_names:
  289. post_data['screen_name'] = list_to_csv(screen_names)
  290. return self._lookup_users(post_data=post_data)
  291. @property
  292. def _lookup_users(self):
  293. """ :reference: https://dev.twitter.com/rest/reference/get/users/lookup
  294. allowed_param='user_id', 'screen_name', 'include_entities'
  295. """
  296. return bind_api(
  297. api=self,
  298. path='/users/lookup.json',
  299. payload_type='user', payload_list=True,
  300. method='POST',
  301. )
  302. def me(self):
  303. """ Get the authenticated user """
  304. return self.get_user(screen_name=self.auth.get_username())
  305. @property
  306. def search_users(self):
  307. """ :reference: https://dev.twitter.com/rest/reference/get/users/search
  308. :allowed_param:'q', 'count', 'page'
  309. """
  310. return bind_api(
  311. api=self,
  312. path='/users/search.json',
  313. payload_type='user', payload_list=True,
  314. require_auth=True,
  315. allowed_param=['q', 'count', 'page']
  316. )
  317. @property
  318. def suggested_users(self):
  319. """ :reference: https://dev.twitter.com/rest/reference/get/users/suggestions/%3Aslug
  320. :allowed_param:'slug', 'lang'
  321. """
  322. return bind_api(
  323. api=self,
  324. path='/users/suggestions/{slug}.json',
  325. payload_type='user', payload_list=True,
  326. require_auth=True,
  327. allowed_param=['slug', 'lang']
  328. )
  329. @property
  330. def suggested_categories(self):
  331. """ :reference: https://dev.twitter.com/rest/reference/get/users/suggestions
  332. :allowed_param:'lang'
  333. """
  334. return bind_api(
  335. api=self,
  336. path='/users/suggestions.json',
  337. payload_type='category', payload_list=True,
  338. allowed_param=['lang'],
  339. require_auth=True
  340. )
  341. @property
  342. def suggested_users_tweets(self):
  343. """ :reference: https://dev.twitter.com/rest/reference/get/users/suggestions/%3Aslug/members
  344. :allowed_param:'slug'
  345. """
  346. return bind_api(
  347. api=self,
  348. path='/users/suggestions/{slug}/members.json',
  349. payload_type='status', payload_list=True,
  350. allowed_param=['slug'],
  351. require_auth=True
  352. )
  353. @property
  354. def direct_messages(self):
  355. """ :reference: https://dev.twitter.com/rest/reference/get/direct_messages
  356. :allowed_param:'since_id', 'max_id', 'count', 'full_text'
  357. """
  358. return bind_api(
  359. api=self,
  360. path='/direct_messages.json',
  361. payload_type='direct_message', payload_list=True,
  362. allowed_param=['since_id', 'max_id', 'count', 'full_text'],
  363. require_auth=True
  364. )
  365. @property
  366. def get_direct_message(self):
  367. """ :reference: https://dev.twitter.com/rest/reference/get/direct_messages/show
  368. :allowed_param:'id', 'full_text'
  369. """
  370. return bind_api(
  371. api=self,
  372. path='/direct_messages/show/{id}.json',
  373. payload_type='direct_message',
  374. allowed_param=['id', 'full_text'],
  375. require_auth=True
  376. )
  377. @property
  378. def sent_direct_messages(self):
  379. """ :reference: https://dev.twitter.com/rest/reference/get/direct_messages/sent
  380. :allowed_param:'since_id', 'max_id', 'count', 'page', 'full_text'
  381. """
  382. return bind_api(
  383. api=self,
  384. path='/direct_messages/sent.json',
  385. payload_type='direct_message', payload_list=True,
  386. allowed_param=['since_id', 'max_id', 'count', 'page', 'full_text'],
  387. require_auth=True
  388. )
  389. @property
  390. def send_direct_message(self):
  391. """ :reference: https://dev.twitter.com/rest/reference/post/direct_messages/new
  392. :allowed_param:'user', 'screen_name', 'user_id', 'text'
  393. """
  394. return bind_api(
  395. api=self,
  396. path='/direct_messages/new.json',
  397. method='POST',
  398. payload_type='direct_message',
  399. allowed_param=['user', 'screen_name', 'user_id', 'text'],
  400. require_auth=True
  401. )
  402. @property
  403. def destroy_direct_message(self):
  404. """ :reference: https://dev.twitter.com/rest/reference/post/direct_messages/destroy
  405. :allowed_param:'id'
  406. """
  407. return bind_api(
  408. api=self,
  409. path='/direct_messages/destroy.json',
  410. method='POST',
  411. payload_type='direct_message',
  412. allowed_param=['id'],
  413. require_auth=True
  414. )
  415. @property
  416. def create_friendship(self):
  417. """ :reference: https://dev.twitter.com/rest/reference/post/friendships/create
  418. :allowed_param:'id', 'user_id', 'screen_name', 'follow'
  419. """
  420. return bind_api(
  421. api=self,
  422. path='/friendships/create.json',
  423. method='POST',
  424. payload_type='user',
  425. allowed_param=['id', 'user_id', 'screen_name', 'follow'],
  426. require_auth=True
  427. )
  428. @property
  429. def destroy_friendship(self):
  430. """ :reference: https://dev.twitter.com/rest/reference/post/friendships/destroy
  431. :allowed_param:'id', 'user_id', 'screen_name'
  432. """
  433. return bind_api(
  434. api=self,
  435. path='/friendships/destroy.json',
  436. method='POST',
  437. payload_type='user',
  438. allowed_param=['id', 'user_id', 'screen_name'],
  439. require_auth=True
  440. )
  441. @property
  442. def show_friendship(self):
  443. """ :reference: https://dev.twitter.com/rest/reference/get/friendships/show
  444. :allowed_param:'source_id', 'source_screen_name'
  445. """
  446. return bind_api(
  447. api=self,
  448. path='/friendships/show.json',
  449. payload_type='friendship',
  450. allowed_param=['source_id', 'source_screen_name',
  451. 'target_id', 'target_screen_name']
  452. )
  453. def lookup_friendships(self, user_ids=None, screen_names=None):
  454. """ Perform bulk look up of friendships from user ID or screenname """
  455. return self._lookup_friendships(list_to_csv(user_ids), list_to_csv(screen_names))
  456. @property
  457. def _lookup_friendships(self):
  458. """ :reference: https://dev.twitter.com/rest/reference/get/friendships/lookup
  459. :allowed_param:'user_id', 'screen_name'
  460. """
  461. return bind_api(
  462. api=self,
  463. path='/friendships/lookup.json',
  464. payload_type='relationship', payload_list=True,
  465. allowed_param=['user_id', 'screen_name'],
  466. require_auth=True
  467. )
  468. @property
  469. def friends_ids(self):
  470. """ :reference: https://dev.twitter.com/rest/reference/get/friends/ids
  471. :allowed_param:'id', 'user_id', 'screen_name', 'cursor'
  472. """
  473. return bind_api(
  474. api=self,
  475. path='/friends/ids.json',
  476. payload_type='ids',
  477. allowed_param=['id', 'user_id', 'screen_name', 'cursor']
  478. )
  479. @property
  480. def friends(self):
  481. """ :reference: https://dev.twitter.com/rest/reference/get/friends/list
  482. :allowed_param:'id', 'user_id', 'screen_name', 'cursor', 'skip_status', 'include_user_entities'
  483. """
  484. return bind_api(
  485. api=self,
  486. path='/friends/list.json',
  487. payload_type='user', payload_list=True,
  488. allowed_param=['id', 'user_id', 'screen_name', 'cursor', 'skip_status', 'include_user_entities']
  489. )
  490. @property
  491. def friendships_incoming(self):
  492. """ :reference: https://dev.twitter.com/rest/reference/get/friendships/incoming
  493. :allowed_param:'cursor'
  494. """
  495. return bind_api(
  496. api=self,
  497. path='/friendships/incoming.json',
  498. payload_type='ids',
  499. allowed_param=['cursor']
  500. )
  501. @property
  502. def friendships_outgoing(self):
  503. """ :reference: https://dev.twitter.com/rest/reference/get/friendships/outgoing
  504. :allowed_param:'cursor'
  505. """
  506. return bind_api(
  507. api=self,
  508. path='/friendships/outgoing.json',
  509. payload_type='ids',
  510. allowed_param=['cursor']
  511. )
  512. @property
  513. def followers_ids(self):
  514. """ :reference: https://dev.twitter.com/rest/reference/get/followers/ids
  515. :allowed_param:'id', 'user_id', 'screen_name', 'cursor', 'count'
  516. """
  517. return bind_api(
  518. api=self,
  519. path='/followers/ids.json',
  520. payload_type='ids',
  521. allowed_param=['id', 'user_id', 'screen_name', 'cursor', 'count']
  522. )
  523. @property
  524. def followers(self):
  525. """ :reference: https://dev.twitter.com/rest/reference/get/followers/list
  526. :allowed_param:'id', 'user_id', 'screen_name', 'cursor', 'count', 'skip_status', 'include_user_entities'
  527. """
  528. return bind_api(
  529. api=self,
  530. path='/followers/list.json',
  531. payload_type='user', payload_list=True,
  532. allowed_param=['id', 'user_id', 'screen_name', 'cursor', 'count',
  533. 'skip_status', 'include_user_entities']
  534. )
  535. @property
  536. def get_settings(self):
  537. """ :reference: https://dev.twitter.com/rest/reference/get/account/settings
  538. """
  539. return bind_api(
  540. api=self,
  541. path='/account/settings.json',
  542. payload_type='json',
  543. use_cache=False
  544. )
  545. @property
  546. def set_settings(self):
  547. """ :reference: https://dev.twitter.com/rest/reference/post/account/settings
  548. :allowed_param:'sleep_time_enabled', 'start_sleep_time',
  549. 'end_sleep_time', 'time_zone', 'trend_location_woeid',
  550. 'allow_contributor_request', 'lang'
  551. """
  552. return bind_api(
  553. api=self,
  554. path='/account/settings.json',
  555. method='POST',
  556. payload_type='json',
  557. allowed_param=['sleep_time_enabled', 'start_sleep_time',
  558. 'end_sleep_time', 'time_zone',
  559. 'trend_location_woeid', 'allow_contributor_request',
  560. 'lang'],
  561. use_cache=False
  562. )
  563. def verify_credentials(self, **kargs):
  564. """ :reference: https://dev.twitter.com/rest/reference/get/account/verify_credentials
  565. :allowed_param:'include_entities', 'skip_status', 'include_email'
  566. """
  567. try:
  568. return bind_api(
  569. api=self,
  570. path='/account/verify_credentials.json',
  571. payload_type='user',
  572. require_auth=True,
  573. allowed_param=['include_entities', 'skip_status', 'include_email'],
  574. )(**kargs)
  575. except TweepError as e:
  576. if e.response and e.response.status == 401:
  577. return False
  578. raise
  579. @property
  580. def rate_limit_status(self):
  581. """ :reference: https://dev.twitter.com/rest/reference/get/application/rate_limit_status
  582. :allowed_param:'resources'
  583. """
  584. return bind_api(
  585. api=self,
  586. path='/application/rate_limit_status.json',
  587. payload_type='json',
  588. allowed_param=['resources'],
  589. use_cache=False
  590. )
  591. @property
  592. def set_delivery_device(self):
  593. """ :reference: https://dev.twitter.com/rest/reference/post/account/update_delivery_device
  594. :allowed_param:'device'
  595. """
  596. return bind_api(
  597. api=self,
  598. path='/account/update_delivery_device.json',
  599. method='POST',
  600. allowed_param=['device'],
  601. payload_type='user',
  602. require_auth=True
  603. )
  604. @property
  605. def update_profile_colors(self):
  606. """ :reference: https://dev.twitter.com/docs/api/1.1/post/account/update_profile_colors
  607. :allowed_param:'profile_background_color', 'profile_text_color',
  608. 'profile_link_color', 'profile_sidebar_fill_color',
  609. 'profile_sidebar_border_color'],
  610. """
  611. return bind_api(
  612. api=self,
  613. path='/account/update_profile_colors.json',
  614. method='POST',
  615. payload_type='user',
  616. allowed_param=['profile_background_color', 'profile_text_color',
  617. 'profile_link_color', 'profile_sidebar_fill_color',
  618. 'profile_sidebar_border_color'],
  619. require_auth=True
  620. )
  621. def update_profile_image(self, filename, file_=None):
  622. """ :reference: https://dev.twitter.com/rest/reference/post/account/update_profile_image
  623. :allowed_param:'include_entities', 'skip_status'
  624. """
  625. headers, post_data = API._pack_image(filename, 700, f=file_)
  626. return bind_api(
  627. api=self,
  628. path='/account/update_profile_image.json',
  629. method='POST',
  630. payload_type='user',
  631. allowed_param=['include_entities', 'skip_status'],
  632. require_auth=True
  633. )(self, post_data=post_data, headers=headers)
  634. def update_profile_background_image(self, filename, **kargs):
  635. """ :reference: https://dev.twitter.com/rest/reference/post/account/update_profile_background_image
  636. :allowed_param:'tile', 'include_entities', 'skip_status', 'use'
  637. """
  638. f = kargs.pop('file', None)
  639. headers, post_data = API._pack_image(filename, 800, f=f)
  640. bind_api(
  641. api=self,
  642. path='/account/update_profile_background_image.json',
  643. method='POST',
  644. payload_type='user',
  645. allowed_param=['tile', 'include_entities', 'skip_status', 'use'],
  646. require_auth=True
  647. )(post_data=post_data, headers=headers)
  648. def update_profile_banner(self, filename, **kargs):
  649. """ :reference: https://dev.twitter.com/rest/reference/post/account/update_profile_banner
  650. :allowed_param:'width', 'height', 'offset_left', 'offset_right'
  651. """
  652. f = kargs.pop('file', None)
  653. headers, post_data = API._pack_image(filename, 700, form_field="banner", f=f)
  654. bind_api(
  655. api=self,
  656. path='/account/update_profile_banner.json',
  657. method='POST',
  658. allowed_param=['width', 'height', 'offset_left', 'offset_right'],
  659. require_auth=True
  660. )(post_data=post_data, headers=headers)
  661. @property
  662. def update_profile(self):
  663. """ :reference: https://dev.twitter.com/rest/reference/post/account/update_profile
  664. :allowed_param:'name', 'url', 'location', 'description'
  665. """
  666. return bind_api(
  667. api=self,
  668. path='/account/update_profile.json',
  669. method='POST',
  670. payload_type='user',
  671. allowed_param=['name', 'url', 'location', 'description'],
  672. require_auth=True
  673. )
  674. @property
  675. def favorites(self):
  676. """ :reference: https://dev.twitter.com/rest/reference/get/favorites/list
  677. :allowed_param:'screen_name', 'user_id', 'max_id', 'count', 'since_id', 'max_id'
  678. """
  679. return bind_api(
  680. api=self,
  681. path='/favorites/list.json',
  682. payload_type='status', payload_list=True,
  683. allowed_param=['screen_name', 'user_id', 'max_id', 'count', 'since_id', 'max_id']
  684. )
  685. @property
  686. def create_favorite(self):
  687. """ :reference:https://dev.twitter.com/rest/reference/post/favorites/create
  688. :allowed_param:'id'
  689. """
  690. return bind_api(
  691. api=self,
  692. path='/favorites/create.json',
  693. method='POST',
  694. payload_type='status',
  695. allowed_param=['id'],
  696. require_auth=True
  697. )
  698. @property
  699. def destroy_favorite(self):
  700. """ :reference: https://dev.twitter.com/rest/reference/post/favorites/destroy
  701. :allowed_param:'id'
  702. """
  703. return bind_api(
  704. api=self,
  705. path='/favorites/destroy.json',
  706. method='POST',
  707. payload_type='status',
  708. allowed_param=['id'],
  709. require_auth=True
  710. )
  711. @property
  712. def create_block(self):
  713. """ :reference: https://dev.twitter.com/rest/reference/post/blocks/create
  714. :allowed_param:'id', 'user_id', 'screen_name'
  715. """
  716. return bind_api(
  717. api=self,
  718. path='/blocks/create.json',
  719. method='POST',
  720. payload_type='user',
  721. allowed_param=['id', 'user_id', 'screen_name'],
  722. require_auth=True
  723. )
  724. @property
  725. def destroy_block(self):
  726. """ :reference: https://dev.twitter.com/rest/reference/post/blocks/destroy
  727. :allowed_param:'id', 'user_id', 'screen_name'
  728. """
  729. return bind_api(
  730. api=self,
  731. path='/blocks/destroy.json',
  732. method='POST',
  733. payload_type='user',
  734. allowed_param=['id', 'user_id', 'screen_name'],
  735. require_auth=True
  736. )
  737. @property
  738. def blocks(self):
  739. """ :reference: https://dev.twitter.com/rest/reference/get/blocks/list
  740. :allowed_param:'cursor'
  741. """
  742. return bind_api(
  743. api=self,
  744. path='/blocks/list.json',
  745. payload_type='user', payload_list=True,
  746. allowed_param=['cursor'],
  747. require_auth=True
  748. )
  749. @property
  750. def blocks_ids(self):
  751. """ :reference: https://dev.twitter.com/rest/reference/get/blocks/ids """
  752. return bind_api(
  753. api=self,
  754. path='/blocks/ids.json',
  755. payload_type='json',
  756. require_auth=True
  757. )
  758. @property
  759. def report_spam(self):
  760. """ :reference: https://dev.twitter.com/rest/reference/post/users/report_spam
  761. :allowed_param:'user_id', 'screen_name'
  762. """
  763. return bind_api(
  764. api=self,
  765. path='/users/report_spam.json',
  766. method='POST',
  767. payload_type='user',
  768. allowed_param=['user_id', 'screen_name'],
  769. require_auth=True
  770. )
  771. @property
  772. def saved_searches(self):
  773. """ :reference: https://dev.twitter.com/rest/reference/get/saved_searches/show/%3Aid """
  774. return bind_api(
  775. api=self,
  776. path='/saved_searches/list.json',
  777. payload_type='saved_search', payload_list=True,
  778. require_auth=True
  779. )
  780. @property
  781. def get_saved_search(self):
  782. """ :reference: https://dev.twitter.com/rest/reference/get/saved_searches/show/%3Aid
  783. :allowed_param:'id'
  784. """
  785. return bind_api(
  786. api=self,
  787. path='/saved_searches/show/{id}.json',
  788. payload_type='saved_search',
  789. allowed_param=['id'],
  790. require_auth=True
  791. )
  792. @property
  793. def create_saved_search(self):
  794. """ :reference: https://dev.twitter.com/rest/reference/post/saved_searches/create
  795. :allowed_param:'query'
  796. """
  797. return bind_api(
  798. api=self,
  799. path='/saved_searches/create.json',
  800. method='POST',
  801. payload_type='saved_search',
  802. allowed_param=['query'],
  803. require_auth=True
  804. )
  805. @property
  806. def destroy_saved_search(self):
  807. """ :reference: https://dev.twitter.com/rest/reference/post/saved_searches/destroy/%3Aid
  808. :allowed_param:'id'
  809. """
  810. return bind_api(
  811. api=self,
  812. path='/saved_searches/destroy/{id}.json',
  813. method='POST',
  814. payload_type='saved_search',
  815. allowed_param=['id'],
  816. require_auth=True
  817. )
  818. @property
  819. def create_list(self):
  820. """ :reference: https://dev.twitter.com/rest/reference/post/lists/create
  821. :allowed_param:'name', 'mode', 'description'
  822. """
  823. return bind_api(
  824. api=self,
  825. path='/lists/create.json',
  826. method='POST',
  827. payload_type='list',
  828. allowed_param=['name', 'mode', 'description'],
  829. require_auth=True
  830. )
  831. @property
  832. def destroy_list(self):
  833. """ :reference: https://dev.twitter.com/rest/reference/post/lists/destroy
  834. :allowed_param:'owner_screen_name', 'owner_id', 'list_id', 'slug'
  835. """
  836. return bind_api(
  837. api=self,
  838. path='/lists/destroy.json',
  839. method='POST',
  840. payload_type='list',
  841. allowed_param=['owner_screen_name', 'owner_id', 'list_id', 'slug'],
  842. require_auth=True
  843. )
  844. @property
  845. def update_list(self):
  846. """ :reference: https://dev.twitter.com/rest/reference/post/lists/update
  847. :allowed_param: list_id', 'slug', 'name', 'mode', 'description', 'owner_screen_name', 'owner_id'
  848. """
  849. return bind_api(
  850. api=self,
  851. path='/lists/update.json',
  852. method='POST',
  853. payload_type='list',
  854. allowed_param=['list_id', 'slug', 'name', 'mode', 'description', 'owner_screen_name', 'owner_id'],
  855. require_auth=True
  856. )
  857. @property
  858. def lists_all(self):
  859. """ :reference: https://dev.twitter.com/rest/reference/get/lists/list
  860. :allowed_param:'screen_name', 'user_id'
  861. """
  862. return bind_api(
  863. api=self,
  864. path='/lists/list.json',
  865. payload_type='list', payload_list=True,
  866. allowed_param=['screen_name', 'user_id'],
  867. require_auth=True
  868. )
  869. @property
  870. def lists_memberships(self):
  871. """ :reference: https://dev.twitter.com/rest/reference/get/lists/memberships
  872. :allowed_param:'screen_name', 'user_id', 'filter_to_owned_lists', 'cursor'
  873. """
  874. return bind_api(
  875. api=self,
  876. path='/lists/memberships.json',
  877. payload_type='list', payload_list=True,
  878. allowed_param=['screen_name', 'user_id', 'filter_to_owned_lists', 'cursor'],
  879. require_auth=True
  880. )
  881. @property
  882. def lists_subscriptions(self):
  883. """ :reference: https://dev.twitter.com/rest/reference/get/lists/subscriptions
  884. :allowed_param:'screen_name', 'user_id', 'cursor'
  885. """
  886. return bind_api(
  887. api=self,
  888. path='/lists/subscriptions.json',
  889. payload_type='list', payload_list=True,
  890. allowed_param=['screen_name', 'user_id', 'cursor'],
  891. require_auth=True
  892. )
  893. @property
  894. def list_timeline(self):
  895. """ :reference: https://dev.twitter.com/docs/api/1.1/get/lists/statuses
  896. :allowed_param:'owner_screen_name', 'slug', 'owner_id', 'list_id',
  897. 'since_id', 'max_id', 'count', 'include_rts
  898. """
  899. return bind_api(
  900. api=self,
  901. path='/lists/statuses.json',
  902. payload_type='status', payload_list=True,
  903. allowed_param=['owner_screen_name', 'slug', 'owner_id',
  904. 'list_id', 'since_id', 'max_id', 'count',
  905. 'include_rts']
  906. )
  907. @property
  908. def get_list(self):
  909. """ :reference: https://dev.twitter.com/rest/reference/get/lists/show
  910. :allowed_param:'owner_screen_name', 'owner_id', 'slug', 'list_id'
  911. """
  912. return bind_api(
  913. api=self,
  914. path='/lists/show.json',
  915. payload_type='list',
  916. allowed_param=['owner_screen_name', 'owner_id', 'slug', 'list_id']
  917. )
  918. @property
  919. def add_list_member(self):
  920. """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/create
  921. :allowed_param:'screen_name', 'user_id', 'owner_screen_name',
  922. 'owner_id', 'slug', 'list_id'
  923. """
  924. return bind_api(
  925. api=self,
  926. path='/lists/members/create.json',
  927. method='POST',
  928. payload_type='list',
  929. allowed_param=['screen_name', 'user_id', 'owner_screen_name',
  930. 'owner_id', 'slug', 'list_id'],
  931. require_auth=True
  932. )
  933. @property
  934. def remove_list_member(self):
  935. """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/destroy
  936. :allowed_param:'screen_name', 'user_id', 'owner_screen_name',
  937. 'owner_id', 'slug', 'list_id'
  938. """
  939. return bind_api(
  940. api=self,
  941. path='/lists/members/destroy.json',
  942. method='POST',
  943. payload_type='list',
  944. allowed_param=['screen_name', 'user_id', 'owner_screen_name',
  945. 'owner_id', 'slug', 'list_id'],
  946. require_auth=True
  947. )
  948. def add_list_members(self, screen_name=None, user_id=None, slug=None,
  949. list_id=None, owner_id=None, owner_screen_name=None):
  950. """ Perform bulk add of list members from user ID or screenname """
  951. return self._add_list_members(list_to_csv(screen_name),
  952. list_to_csv(user_id),
  953. slug, list_id, owner_id,
  954. owner_screen_name)
  955. @property
  956. def _add_list_members(self):
  957. """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/create_all
  958. :allowed_param:'screen_name', 'user_id', 'slug', 'list_id',
  959. 'owner_id', 'owner_screen_name'
  960. """
  961. return bind_api(
  962. api=self,
  963. path='/lists/members/create_all.json',
  964. method='POST',
  965. payload_type='list',
  966. allowed_param=['screen_name', 'user_id', 'slug', 'list_id',
  967. 'owner_id', 'owner_screen_name'],
  968. require_auth=True
  969. )
  970. def remove_list_members(self, screen_name=None, user_id=None, slug=None,
  971. list_id=None, owner_id=None, owner_screen_name=None):
  972. """ Perform bulk remove of list members from user ID or screenname """
  973. return self._remove_list_members(list_to_csv(screen_name),
  974. list_to_csv(user_id),
  975. slug, list_id, owner_id,
  976. owner_screen_name)
  977. @property
  978. def _remove_list_members(self):
  979. """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/destroy_all
  980. :allowed_param:'screen_name', 'user_id', 'slug', 'list_id',
  981. 'owner_id', 'owner_screen_name'
  982. """
  983. return bind_api(
  984. api=self,
  985. path='/lists/members/destroy_all.json',
  986. method='POST',
  987. payload_type='list',
  988. allowed_param=['screen_name', 'user_id', 'slug', 'list_id',
  989. 'owner_id', 'owner_screen_name'],
  990. require_auth=True
  991. )
  992. @property
  993. def list_members(self):
  994. """ :reference: https://dev.twitter.com/docs/api/1.1/get/lists/members
  995. :allowed_param:'owner_screen_name', 'slug', 'list_id',
  996. 'owner_id', 'cursor
  997. """
  998. return bind_api(
  999. api=self,
  1000. path='/lists/members.json',
  1001. payload_type='user', payload_list=True,
  1002. allowed_param=['owner_screen_name', 'slug', 'list_id',
  1003. 'owner_id', 'cursor']
  1004. )
  1005. @property
  1006. def show_list_member(self):
  1007. """ :reference: https://dev.twitter.com/docs/api/1.1/get/lists/members/show
  1008. :allowed_param:'list_id', 'slug', 'user_id', 'screen_name',
  1009. 'owner_screen_name', 'owner_id
  1010. """
  1011. return bind_api(
  1012. api=self,
  1013. path='/lists/members/show.json',
  1014. payload_type='user',
  1015. allowed_param=['list_id', 'slug', 'user_id', 'screen_name',
  1016. 'owner_screen_name', 'owner_id']
  1017. )
  1018. @property
  1019. def subscribe_list(self):
  1020. """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/subscribers/create
  1021. :allowed_param:'owner_screen_name', 'slug', 'owner_id',
  1022. 'list_id'
  1023. """
  1024. return bind_api(
  1025. api=self,
  1026. path='/lists/subscribers/create.json',
  1027. method='POST',
  1028. payload_type='list',
  1029. allowed_param=['owner_screen_name', 'slug', 'owner_id',
  1030. 'list_id'],
  1031. require_auth=True
  1032. )
  1033. @property
  1034. def unsubscribe_list(self):
  1035. """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/subscribers/destroy
  1036. :allowed_param:'owner_screen_name', 'slug', 'owner_id',
  1037. 'list_id'
  1038. """
  1039. return bind_api(
  1040. api=self,
  1041. path='/lists/subscribers/destroy.json',
  1042. method='POST',
  1043. payload_type='list',
  1044. allowed_param=['owner_screen_name', 'slug', 'owner_id',
  1045. 'list_id'],
  1046. require_auth=True
  1047. )
  1048. @property
  1049. def list_subscribers(self):
  1050. """ :reference: https://dev.twitter.com/docs/api/1.1/get/lists/subscribers
  1051. :allowed_param:'owner_screen_name', 'slug', 'owner_id',
  1052. 'list_id', 'cursor
  1053. """
  1054. return bind_api(
  1055. api=self,
  1056. path='/lists/subscribers.json',
  1057. payload_type='user', payload_list=True,
  1058. allowed_param=['owner_screen_name', 'slug', 'owner_id',
  1059. 'list_id', 'cursor']
  1060. )
  1061. @property
  1062. def show_list_subscriber(self):
  1063. """ :reference: https://dev.twitter.com/docs/api/1.1/get/lists/subscribers/show
  1064. :allowed_param:'owner_screen_name', 'slug', 'screen_name',
  1065. 'owner_id', 'list_id', 'user_id
  1066. """
  1067. return bind_api(
  1068. api=self,
  1069. path='/lists/subscribers/show.json',
  1070. payload_type='user',
  1071. allowed_param=['owner_screen_name', 'slug', 'screen_name',
  1072. 'owner_id', 'list_id', 'user_id']
  1073. )
  1074. @property
  1075. def trends_available(self):
  1076. """ :reference: https://dev.twitter.com/rest/reference/get/trends/available """
  1077. return bind_api(
  1078. api=self,
  1079. path='/trends/available.json',
  1080. payload_type='json'
  1081. )
  1082. @property
  1083. def trends_place(self):
  1084. """ :reference: https://dev.twitter.com/rest/reference/get/trends/place
  1085. :allowed_param:'id', 'exclude'
  1086. """
  1087. return bind_api(
  1088. api=self,
  1089. path='/trends/place.json',
  1090. payload_type='json',
  1091. allowed_param=['id', 'exclude']
  1092. )
  1093. @property
  1094. def trends_closest(self):
  1095. """ :reference: https://dev.twitter.com/rest/reference/get/trends/closest
  1096. :allowed_param:'lat', 'long'
  1097. """
  1098. return bind_api(
  1099. api=self,
  1100. path='/trends/closest.json',
  1101. payload_type='json',
  1102. allowed_param=['lat', 'long']
  1103. )
  1104. @property
  1105. def search(self):
  1106. """ :reference: https://dev.twitter.com/rest/reference/get/search/tweets
  1107. :allowed_param:'q', 'lang', 'locale', 'since_id', 'geocode',
  1108. 'max_id', 'since', 'until', 'result_type', 'count',
  1109. 'include_entities', 'from', 'to', 'source']
  1110. """
  1111. return bind_api(
  1112. api=self,
  1113. path='/search/tweets.json',
  1114. payload_type='search_results',
  1115. allowed_param=['q', 'lang', 'locale', 'since_id', 'geocode',
  1116. 'max_id', 'since', 'until', 'result_type',
  1117. 'count', 'include_entities', 'from',
  1118. 'to', 'source']
  1119. )
  1120. @property
  1121. def reverse_geocode(self):
  1122. """ :reference: https://dev.twitter.com/rest/reference/get/geo/reverse_geocode
  1123. :allowed_param:'lat', 'long', 'accuracy', 'granularity', 'max_results'
  1124. """
  1125. return bind_api(
  1126. api=self,
  1127. path='/geo/reverse_geocode.json',
  1128. payload_type='place', payload_list=True,
  1129. allowed_param=['lat', 'long', 'accuracy', 'granularity',
  1130. 'max_results']
  1131. )
  1132. @property
  1133. def geo_id(self):
  1134. """ :reference: https://dev.twitter.com/rest/reference/get/geo/id/%3Aplace_id
  1135. :allowed_param:'id'
  1136. """
  1137. return bind_api(
  1138. api=self,
  1139. path='/geo/id/{id}.json',
  1140. payload_type='place',
  1141. allowed_param=['id']
  1142. )
  1143. @property
  1144. def geo_search(self):
  1145. """ :reference: https://dev.twitter.com/docs/api/1.1/get/geo/search
  1146. :allowed_param:'lat', 'long', 'query', 'ip', 'granularity',
  1147. 'accuracy', 'max_results', 'contained_within
  1148. """
  1149. return bind_api(
  1150. api=self,
  1151. path='/geo/search.json',
  1152. payload_type='place', payload_list=True,
  1153. allowed_param=['lat', 'long', 'query', 'ip', 'granularity',
  1154. 'accuracy', 'max_results', 'contained_within']
  1155. )
  1156. @property
  1157. def geo_similar_places(self):
  1158. """ :reference: https://dev.twitter.com/rest/reference/get/geo/similar_places
  1159. :allowed_param:'lat', 'long', 'name', 'contained_within'
  1160. """
  1161. return bind_api(
  1162. api=self,
  1163. path='/geo/similar_places.json',
  1164. payload_type='place', payload_list=True,
  1165. allowed_param=['lat', 'long', 'name', 'contained_within']
  1166. )
  1167. @property
  1168. def supported_languages(self):
  1169. """ :reference: https://dev.twitter.com/rest/reference/get/help/languages """
  1170. return bind_api(
  1171. api=self,
  1172. path='/help/languages.json',
  1173. payload_type='json',
  1174. require_auth=True
  1175. )
  1176. @property
  1177. def configuration(self):
  1178. """ :reference: https://dev.twitter.com/rest/reference/get/help/configuration """
  1179. return bind_api(
  1180. api=self,
  1181. path='/help/configuration.json',
  1182. payload_type='json',
  1183. require_auth=True
  1184. )
  1185. """ Internal use only """
  1186. @staticmethod
  1187. def _pack_image(filename, max_size, form_field="image", f=None):
  1188. """Pack image from file into multipart-formdata post body"""
  1189. # image must be less than 700kb in size
  1190. if f is None:
  1191. try:
  1192. if os.path.getsize(filename) > (max_size * 1024):
  1193. raise TweepError('File is too big, must be less than %skb.' % max_size)
  1194. except os.error as e:
  1195. raise TweepError('Unable to access file: %s' % e.strerror)
  1196. # build the mulitpart-formdata body
  1197. fp = open(filename, 'rb')
  1198. else:
  1199. f.seek(0, 2) # Seek to end of file
  1200. if f.tell() > (max_size * 1024):
  1201. raise TweepError('File is too big, must be less than %skb.' % max_size)
  1202. f.seek(0) # Reset to beginning of file
  1203. fp = f
  1204. # image must be gif, jpeg, or png
  1205. file_type = mimetypes.guess_type(filename)
  1206. if file_type is None:
  1207. raise TweepError('Could not determine file type')
  1208. file_type = file_type[0]
  1209. if file_type not in ['image/gif', 'image/jpeg', 'image/png']:
  1210. raise TweepError('Invalid file type for image: %s' % file_type)
  1211. if isinstance(filename, six.text_type):
  1212. filename = filename.encode("utf-8")
  1213. BOUNDARY = b'Tw3ePy'
  1214. body = list()
  1215. body.append(b'--' + BOUNDARY)
  1216. body.append('Content-Disposition: form-data; name="{0}";'
  1217. ' filename="{1}"'.format(form_field, filename)
  1218. .encode('utf-8'))
  1219. body.append('Content-Type: {0}'.format(file_type).encode('utf-8'))
  1220. body.append(b'')
  1221. body.append(fp.read())
  1222. body.append(b'--' + BOUNDARY + b'--')
  1223. body.append(b'')
  1224. fp.close()
  1225. body = b'\r\n'.join(body)
  1226. # build headers
  1227. headers = {
  1228. 'Content-Type': 'multipart/form-data; boundary=Tw3ePy',
  1229. 'Content-Length': str(len(body))
  1230. }
  1231. return headers, body