This commit is contained in:
Bill Dengler 2015-05-09 16:29:20 -04:00
parent cb94d7f71a
commit 4d4816a61b
5 changed files with 223 additions and 80 deletions

View File

@ -254,8 +254,8 @@ class Session(object):
""" Gets muted users (oh really?)."""
self.db["muted_users"] = self.twitter.twitter.get_muted_users_ids()["ids"]
# self.db["muted_users"] = self.twitter.twitter.get_muted_users_ids()["ids"]
pass
@_require_login
def get_stream(self, name, function, *args, **kwargs):

View File

@ -19,7 +19,7 @@ Questions, comments? ryan@venodesigns.net
"""
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
__version__ = '3.1.2'
__version__ = '3.2.0'
from .api import Twython
from .streaming import TwythonStreamer

View File

@ -9,6 +9,8 @@ Twitter Authentication, and miscellaneous methods that are useful when
dealing with the Twitter API
"""
import warnings
import requests
from requests.auth import HTTPBasicAuth
from requests_oauthlib import OAuth1, OAuth2
@ -20,8 +22,6 @@ from .endpoints import EndpointsMixin
from .exceptions import TwythonError, TwythonAuthError, TwythonRateLimitError
from .helpers import _transparent_params
import warnings
warnings.simplefilter('always', TwythonDeprecationWarning) # For Python 2.7 >
@ -86,8 +86,7 @@ class Twython(EndpointsMixin, object):
self.request_token_url = self.api_url % 'oauth2/token'
self.client_args = client_args or {}
default_headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0'}
# 'Twython v' + __version__}
default_headers = {'User-Agent': 'Twython v' + __version__}
if 'headers' not in self.client_args:
# If they didn't set any headers, set our defaults for them
self.client_args['headers'] = default_headers
@ -193,10 +192,10 @@ class Twython(EndpointsMixin, object):
# app keys/user tokens
ExceptionType = TwythonAuthError
raise ExceptionType(error_message,
raise ExceptionType(
error_message,
error_code=response.status_code,
retry_after=response.headers.get('retry-\
after'))
retry_after=response.headers.get('X-Rate-Limit-Reset'))
try:
content = response.json()
@ -215,6 +214,8 @@ class Twython(EndpointsMixin, object):
# {"errors":[{"code":34,"message":"Sorry,
# that page does not exist"}]}
error_message = content['errors'][0]['message']
except TypeError:
error_message = content['errors']
except ValueError:
# bad json data from Twitter for an error
pass
@ -244,9 +245,12 @@ class Twython(EndpointsMixin, object):
:rtype: dict
"""
if endpoint.startswith('http://'):
raise TwythonError('api.twitter.com is restricted to SSL/TLS traffic.')
# In case they want to pass a full Twitter URL
# i.e. https://api.twitter.com/1.1/search/tweets.json
if endpoint.startswith('http://') or endpoint.startswith('https://'):
if endpoint.startswith('https://'):
url = endpoint
else:
url = '%s/%s.json' % (self.api_url % version, endpoint)
@ -498,8 +502,7 @@ class Twython(EndpointsMixin, object):
# Add 1 to the id because since_id and
# max_id are inclusive
if hasattr(function, 'iter_metadata'):
since_id = content[function.iter_metadata]\
.get('since_id_str')
since_id = content[function.iter_metadata].get('since_id_str')
else:
since_id = content[0]['id_str']
params['since_id'] = (int(since_id) - 1)
@ -576,4 +579,20 @@ class Twython(EndpointsMixin, object):
text = text.replace(tweet['text'][start:end],
url_html % (entity['url'], shown_url))
# Media
if 'media' in entities:
for entity in entities['media']:
start, end = entity['indices'][0], entity['indices'][1]
if use_display_url and entity.get('display_url') \
and not use_expanded_url:
shown_url = entity['display_url']
elif use_expanded_url and entity.get('expanded_url'):
shown_url = entity['expanded_url']
else:
shown_url = entity['url']
url_html = '<a href="%s" class="twython-media">%s</a>'
text = text.replace(tweet['text'][start:end],
url_html % (entity['url'], shown_url))
return text

View File

@ -5,7 +5,8 @@ twython.endpoints
~~~~~~~~~~~~~~~~~
This module provides a mixin for a :class:`Twython <Twython>` instance.
Parameters that need to be embedded in the API url just need to be passed as a keyword argument.
Parameters that need to be embedded in the API url just need to be passed
as a keyword argument.
e.g. Twython.retweet(id=12345)
@ -13,6 +14,10 @@ This map is organized the order functions are documented at:
https://dev.twitter.com/docs/api/1.1
"""
import warnings
from .advisory import TwythonDeprecationWarning
class EndpointsMixin(object):
# Timelines
@ -20,7 +25,8 @@ class EndpointsMixin(object):
"""Returns the 20 most recent mentions (tweets containing a users's
@screen_name) for the authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline
Docs:
https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline
"""
return self.get('statuses/mentions_timeline', params=params)
@ -63,7 +69,8 @@ class EndpointsMixin(object):
Docs: https://dev.twitter.com/docs/api/1.1/get/statuses/retweets/%3Aid
"""
return self.get('statuses/retweets/%s' % params.get('id'), params=params)
return self.get('statuses/retweets/%s' % params.get('id'),
params=params)
def show_status(self, **params):
"""Returns a single Tweet, specified by the id parameter
@ -73,6 +80,16 @@ class EndpointsMixin(object):
"""
return self.get('statuses/show/%s' % params.get('id'), params=params)
def lookup_status(self, **params):
"""Returns fully-hydrated tweet objects for up to 100 tweets per
request, as specified by comma-separated values passed to the id
parameter.
Docs: https://dev.twitter.com/docs/api/1.1/get/statuses/lookup
"""
return self.post('statuses/lookup', params=params)
def destroy_status(self, **params):
"""Destroys the status specified by the required ID parameter
@ -101,11 +118,27 @@ class EndpointsMixin(object):
"""Updates the authenticating user's current status and attaches media
for upload. In other words, it creates a Tweet with a picture attached.
Docs: https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media
Docs:
https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media
"""
warnings.warn(
'This method is deprecated. You should use Twython.upload_media instead.',
TwythonDeprecationWarning,
stacklevel=2
)
return self.post('statuses/update_with_media', params=params)
def upload_media(self, **params):
"""Uploads media file to Twitter servers. The file will be available to be attached
to a status for 60 minutes. To attach to a update, pass a list of returned media ids
to the 'update_status' method using the 'media_ids' param.
Docs:
https://dev.twitter.com/rest/reference/post/media/upload
"""
return self.post('https://upload.twitter.com/1.1/media/upload.json', params=params)
def get_oembed_tweet(self, **params):
"""Returns information allowing the creation of an embedded
representation of a Tweet on third party sites.
@ -174,7 +207,8 @@ class EndpointsMixin(object):
return self.post('direct_messages/destroy', params=params)
def send_direct_message(self, **params):
"""Sends a new direct message to the specified user from the authenticating user.
"""Sends a new direct message to the specified user from the
authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/direct_messages/new
@ -186,7 +220,8 @@ class EndpointsMixin(object):
"""Returns a collection of user_ids that the currently authenticated
user does not want to receive retweets from.
Docs: https://dev.twitter.com/docs/api/1.1/get/friendships/no_retweets/ids
Docs:
https://dev.twitter.com/docs/api/1.1/get/friendships/no_retweets/ids
"""
return self.get('friendships/no_retweets/ids', params=params)
@ -317,7 +352,8 @@ class EndpointsMixin(object):
requesting user if authentication was successful; returns a 401 status
code and an error message if not.
Docs: https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials
Docs:
https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials
"""
return self.get('account/verify_credentials', params=params)
@ -333,13 +369,15 @@ class EndpointsMixin(object):
def update_delivery_service(self, **params):
"""Sets which device Twitter delivers updates to for the authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/update_delivery_device
Docs:
https://dev.twitter.com/docs/api/1.1/post/account/update_delivery_device
"""
return self.post('account/update_delivery_device', params=params)
def update_profile(self, **params):
"""Sets values that users are able to set under the "Account" tab of their settings page.
"""Sets values that users are able to set under the "Account" tab of their
settings page.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/update_profile
@ -349,7 +387,8 @@ class EndpointsMixin(object):
def update_profile_banner_image(self, **params): # pragma: no cover
"""Updates the authenticating user's profile background image.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/update_profile_background_image
Docs:
https://dev.twitter.com/docs/api/1.1/post/account/update_profile_background_image
"""
return self.post('account/update_profile_banner', params=params)
@ -358,7 +397,8 @@ class EndpointsMixin(object):
"""Sets one or more hex values that control the color scheme of the
authenticating user's profile page on twitter.com.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/update_profile_colors
Docs:
https://dev.twitter.com/docs/api/1.1/post/account/update_profile_colors
"""
return self.post('account/update_profile_colors', params=params)
@ -366,13 +406,15 @@ class EndpointsMixin(object):
def update_profile_image(self, **params): # pragma: no cover
"""Updates the authenticating user's profile image.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
Docs:
https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
"""
return self.post('account/update_profile_image', params=params)
def list_blocks(self, **params):
"""Returns a collection of user objects that the authenticating user is blocking.
"""Returns a collection of user objects that the authenticating user
is blocking.
Docs: https://dev.twitter.com/docs/api/1.1/get/blocks/list
@ -400,7 +442,8 @@ class EndpointsMixin(object):
return self.post('blocks/create', params=params)
def destroy_block(self, **params):
"""Un-blocks the user specified in the ID parameter for the authenticating user.
"""Un-blocks the user specified in the ID parameter for the
authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/blocks/destroy
@ -409,7 +452,8 @@ class EndpointsMixin(object):
def lookup_user(self, **params):
"""Returns fully-hydrated user objects for up to 100 users per request,
as specified by comma-separated values passed to the user_id and/or screen_name parameters.
as specified by comma-separated values passed to the user_id and/or
screen_name parameters.
Docs: https://dev.twitter.com/docs/api/1.1/get/users/lookup
@ -426,7 +470,8 @@ class EndpointsMixin(object):
return self.get('users/show', params=params)
def search_users(self, **params):
"""Provides a simple, relevance-based search interface to public user accounts on Twitter.
"""Provides a simple, relevance-based search interface to public user
accounts on Twitter.
Docs: https://dev.twitter.com/docs/api/1.1/get/users/search
@ -453,7 +498,8 @@ class EndpointsMixin(object):
"""Removes the uploaded profile banner for the authenticating user.
Returns HTTP 200 upon success.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/remove_profile_banner
Docs:
https://dev.twitter.com/docs/api/1.1/post/account/remove_profile_banner
"""
return self.post('account/remove_profile_banner', params=params)
@ -461,27 +507,72 @@ class EndpointsMixin(object):
def update_profile_background_image(self, **params):
"""Uploads a profile banner on behalf of the authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/account/update_profile_banner
Docs:
https://dev.twitter.com/docs/api/1.1/post/account/update_profile_banner
"""
return self.post('account/update_profile_background_image', params=params)
return self.post('account/update_profile_background_image',
params=params)
def get_profile_banner_sizes(self, **params):
"""Returns a map of the available size variations of the specified user's profile banner.
"""Returns a map of the available size variations of the specified
user's profile banner.
Docs: https://dev.twitter.com/docs/api/1.1/get/users/profile_banner
"""
return self.get('users/profile_banner', params=params)
def list_mutes(self, **params):
"""Returns a collection of user objects that the authenticating user
is muting.
Docs: https://dev.twitter.com/docs/api/1.1/get/mutes/users/list
"""
return self.get('mutes/users/list', params=params)
list_mutes.iter_mode = 'cursor'
list_mutes.iter_key = 'users'
def list_mute_ids(self, **params):
"""Returns an array of numeric user ids the authenticating user
is muting.
Docs: https://dev.twitter.com/docs/api/1.1/get/mutes/users/ids
"""
return self.get('mutes/users/ids', params=params)
list_mute_ids.iter_mode = 'cursor'
list_mute_ids.iter_key = 'ids'
def create_mute(self, **params):
"""Mutes the specified user, preventing their tweets appearing
in the authenticating user's timeline.
Docs: https://dev.twitter.com/docs/api/1.1/post/mutes/users/create
"""
return self.post('mutes/users/create', params=params)
def destroy_mute(self, **params):
"""Un-mutes the user specified in the user or ID parameter for
the authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/mutes/users/destroy
"""
return self.post('mutes/users/destroy', params=params)
# Suggested Users
def get_user_suggestions_by_slug(self, **params):
"""Access the users in a given category of the Twitter suggested user list.
Docs: https://dev.twitter.com/docs/api/1.1/get/users/suggestions/%3Aslug
Docs:
https://dev.twitter.com/docs/api/1.1/get/users/suggestions/%3Aslug
"""
return self.get('users/suggestions/%s' % params.get('slug'), params=params)
return self.get('users/suggestions/%s' % params.get('slug'),
params=params)
def get_user_suggestions(self, **params):
"""Access to Twitter's suggested user list.
@ -493,16 +584,20 @@ class EndpointsMixin(object):
def get_user_suggestions_statuses_by_slug(self, **params):
"""Access the users in a given category of the Twitter suggested user
list and return their most recent status if they are not a protected user.
list and return their most recent status if they are not a protected
user.
Docs: https://dev.twitter.com/docs/api/1.1/get/users/suggestions/%3Aslug/members
Docs:
https://dev.twitter.com/docs/api/1.1/get/users/suggestions/%3Aslug/members
"""
return self.get('users/suggestions/%s/members' % params.get('slug'), params=params)
return self.get('users/suggestions/%s/members' % params.get('slug'),
params=params)
# Favorites
def get_favorites(self, **params):
"""Returns the 20 most recent Tweets favorited by the authenticating or specified user.
"""Returns the 20 most recent Tweets favorited by the authenticating
or specified user.
Docs: https://dev.twitter.com/docs/api/1.1/get/favorites/list
@ -511,7 +606,8 @@ class EndpointsMixin(object):
get_favorites.iter_mode = 'id'
def destroy_favorite(self, **params):
"""Un-favorites the status specified in the ID parameter as the authenticating user.
"""Un-favorites the status specified in the ID parameter as the
authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/favorites/destroy
@ -519,33 +615,18 @@ class EndpointsMixin(object):
return self.post('favorites/destroy', params=params)
def create_favorite(self, **params):
"""Favorites the status specified in the ID parameter as the authenticating user.
"""Favorites the status specified in the ID parameter as the
authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/favorites/create
"""
return self.post('favorites/create', params=params)
# Mute
def create_mute(self, **params):
return self.post('mutes/users/create', params=params)
def destroy_mute(self, **params):
return self.post('mutes/users/destroy', params=params)
def get_muted_users_ids(self, **params):
return self.get('mutes/users/ids', params=params)
get_muted_users_ids.iter_mode = 'cursor'
get_muted_users_ids.iter_key = 'ids'
def get_muted_users_list(self, **params):
return self.get('mutes/users/list', params=params)
get_muted_users_list.iter_mode = 'cursor'
get_muted_users_list.iter_key = 'users'
# Lists
def show_lists(self, **params):
"""Returns all lists the authenticating or specified user subscribes to, including their own.
"""Returns all lists the authenticating or specified user subscribes to,
including their own.
Docs: https://dev.twitter.com/docs/api/1.1/get/lists/list
@ -592,7 +673,8 @@ class EndpointsMixin(object):
def subscribe_to_list(self, **params):
"""Subscribes the authenticated user to the specified list.
Docs: https://dev.twitter.com/docs/api/1.1/post/lists/subscribers/create
Docs:
https://dev.twitter.com/docs/api/1.1/post/lists/subscribers/create
"""
return self.post('lists/subscribers/create', params=params)
@ -608,7 +690,8 @@ class EndpointsMixin(object):
def unsubscribe_from_list(self, **params):
"""Unsubscribes the authenticated user from the specified list.
Docs: https://dev.twitter.com/docs/api/1.1/post/lists/subscribers/destroy
Docs:
https://dev.twitter.com/docs/api/1.1/post/lists/subscribers/destroy
"""
return self.post('lists/subscribers/destroy', params=params)
@ -617,7 +700,8 @@ class EndpointsMixin(object):
"""Adds multiple members to a list, by specifying a comma-separated
list of member ids or screen names.
Docs: https://dev.twitter.com/docs/api/1.1/post/lists/members/create_all
Docs:
https://dev.twitter.com/docs/api/1.1/post/lists/members/create_all
"""
return self.post('lists/members/create_all', params=params)
@ -694,7 +778,8 @@ class EndpointsMixin(object):
"""Removes multiple members from a list, by specifying a
comma-separated list of member ids or screen names.
Docs: https://dev.twitter.com/docs/api/1.1/post/lists/members/destroy_all
Docs:
https://dev.twitter.com/docs/api/1.1/post/lists/members/destroy_all
"""
return self.post('lists/members/destroy_all', params=params)
@ -721,10 +806,12 @@ class EndpointsMixin(object):
def show_saved_search(self, **params):
"""Retrieve the information for the saved search represented by the given id.
Docs: https://dev.twitter.com/docs/api/1.1/get/saved_searches/show/%3Aid
Docs:
https://dev.twitter.com/docs/api/1.1/get/saved_searches/show/%3Aid
"""
return self.get('saved_searches/show/%s' % params.get('id'), params=params)
return self.get('saved_searches/show/%s' % params.get('id'),
params=params)
def create_saved_search(self, **params):
"""Create a new saved search for the authenticated user.
@ -737,10 +824,12 @@ class EndpointsMixin(object):
def destroy_saved_search(self, **params):
"""Destroys a saved search for the authenticating user.
Docs: https://dev.twitter.com/docs/api/1.1/post/saved_searches/destroy/%3Aid
Docs:
https://dev.twitter.com/docs/api/1.1/post/saved_searches/destroy/%3Aid
"""
return self.post('saved_searches/destroy/%s' % params.get('id'), params=params)
return self.post('saved_searches/destroy/%s' % params.get('id'),
params=params)
# Places & Geo
def get_geo_info(self, **params):
@ -868,7 +957,8 @@ class EndpointsMixin(object):
"""Returns the current rate limits for methods belonging to the
specified resource families.
Docs: https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status
Docs:
https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status
"""
return self.get('application/rate_limit_status', params=params)
@ -878,16 +968,32 @@ class EndpointsMixin(object):
TWITTER_HTTP_STATUS_CODE = {
200: ('OK', 'Success!'),
304: ('Not Modified', 'There was no new data to return.'),
400: ('Bad Request', 'The request was invalid. An accompanying error message will explain why. This is the status code will be returned during rate limiting.'),
401: ('Unauthorized', 'Authentication credentials were missing or incorrect.'),
403: ('Forbidden', 'The request is understood, but it has been refused. An accompanying error message will explain why. This code is used when requests are being denied due to update limits.'),
404: ('Not Found', 'The URI requested is invalid or the resource requested, such as a user, does not exists.'),
406: ('Not Acceptable', 'Returned by the Search API when an invalid format is specified in the request.'),
410: ('Gone', 'This resource is gone. Used to indicate that an API endpoint has been turned off.'),
422: ('Unprocessable Entity', 'Returned when an image uploaded to POST account/update_profile_banner is unable to be processed.'),
429: ('Too Many Requests', 'Returned in API v1.1 when a request cannot be served due to the application\'s rate limit having been exhausted for the resource.'),
500: ('Internal Server Error', 'Something is broken. Please post to the group so the Twitter team can investigate.'),
400: ('Bad Request', 'The request was invalid. An accompanying \
error message will explain why. This is the status code \
will be returned during rate limiting.'),
401: ('Unauthorized', 'Authentication credentials were missing \
or incorrect.'),
403: ('Forbidden', 'The request is understood, but it has been \
refused. An accompanying error message will explain why. \
This code is used when requests are being denied due to \
update limits.'),
404: ('Not Found', 'The URI requested is invalid or the resource \
requested, such as a user, does not exists.'),
406: ('Not Acceptable', 'Returned by the Search API when an \
invalid format is specified in the request.'),
410: ('Gone', 'This resource is gone. Used to indicate that an \
API endpoint has been turned off.'),
422: ('Unprocessable Entity', 'Returned when an image uploaded to \
POST account/update_profile_banner is unable to be processed.'),
429: ('Too Many Requests', 'Returned in API v1.1 when a request cannot \
be served due to the application\'s rate limit having been \
exhausted for the resource.'),
500: ('Internal Server Error', 'Something is broken. Please post to the \
group so the Twitter team can investigate.'),
502: ('Bad Gateway', 'Twitter is down or being upgraded.'),
503: ('Service Unavailable', 'The Twitter servers are up, but overloaded with requests. Try again later.'),
504: ('Gateway Timeout', 'The Twitter servers are up, but the request couldn\'t be serviced due to some failure within our stack. Try again later.'),
503: ('Service Unavailable', 'The Twitter servers are up, but overloaded \
with requests. Try again later.'),
504: ('Gateway Timeout', 'The Twitter servers are up, but the request \
couldn\'t be serviced due to some failure within our stack. Try \
again later.'),
}

View File

@ -51,6 +51,7 @@ class TwythonStreamerTypesStatuses(object):
"""
def __init__(self, streamer):
self.streamer = streamer
self.params = None
def filter(self, **params):
"""Stream statuses/filter
@ -87,3 +88,20 @@ class TwythonStreamerTypesStatuses(object):
url = 'https://stream.twitter.com/%s/statuses/firehose.json' \
% self.streamer.api_version
self.streamer._request(url, params=params)
def set_dynamic_filter(self, **params):
"""Set/update statuses/filter
:param \*\*params: Parameters to send with your stream request
Accepted params found at:
https://dev.twitter.com/docs/api/1.1/post/statuses/filter
"""
self.params = params
def dynamic_filter(self):
"""Stream statuses/filter with dynamic parameters"""
url = 'https://stream.twitter.com/%s/statuses/filter.json' \
% self.streamer.api_version
self.streamer._request(url, 'POST', params=self.params)