Files
Nuliga2/env/lib/python3.6/site-packages/twitter/follow.py
2024-11-18 08:59:34 +01:00

256 lines
9.3 KiB
Python

"""USAGE
twitter-follow [options] <user>
DESCRIPTION
Display all following/followers of a user, one user per line.
OPTIONS
-o --oauth authenticate to Twitter using OAuth (default no)
-r --followers display followers of the given user (default)
-g --following display users the given user is following
-a --api-rate see your current API rate limit status
-i --ids prepend user id to each line. useful to tracking renames
AUTHENTICATION
Authenticate to Twitter using OAuth to see following/followers of private
profiles and have higher API rate limits. OAuth authentication tokens
are stored in the file .twitter-follow_oauth in your home directory.
"""
from __future__ import print_function
import os, sys, time, calendar
from getopt import gnu_getopt as getopt, GetoptError
try:
import urllib.request as urllib2
import http.client as httplib
except ImportError:
import urllib2
import httplib
# T-Follow (Twitter-Follow) application registered by @stalkr_
CONSUMER_KEY='USRZQfvFFjB6UvZIN2Edww'
CONSUMER_SECRET='AwGAaSzZa5r0TDL8RKCDtffnI9H9mooZUdOa95nw8'
from .api import Twitter, TwitterError
from .oauth import OAuth, read_token_file
from .oauth_dance import oauth_dance
from .auth import NoAuth
from .util import Fail, err
def parse_args(args, options):
"""Parse arguments from command-line to set options."""
long_opts = ['help', 'oauth', 'followers', 'following', 'api-rate', 'ids']
short_opts = "horgai"
opts, extra_args = getopt(args, short_opts, long_opts)
for opt, arg in opts:
if opt in ('-h', '--help'):
print(__doc__)
raise SystemExit(1)
elif opt in ('-o', '--oauth'):
options['oauth'] = True
elif opt in ('-r', '--followers'):
options['followers'] = True
elif opt in ('-g', '--following'):
options['followers'] = False
elif opt in ('-a', '--api-rate'):
options['api-rate' ] = True
elif opt in ('-i', '--ids'):
options['show_id'] = True
options['extra_args'] = extra_args
def lookup_portion(twitter, user_ids):
"""Resolve a limited list of user ids to screen names."""
users = {}
kwargs = dict(user_id=",".join(map(str, user_ids)), skip_status=1)
for u in twitter.users.lookup(**kwargs):
users[int(u['id'])] = u['screen_name']
return users
def lookup(twitter, user_ids):
"""Resolve an entire list of user ids to screen names."""
users = {}
api_limit = 100
for i in range(0, len(user_ids), api_limit):
fail = Fail()
while True:
try:
portion = lookup_portion(twitter, user_ids[i:][:api_limit])
except TwitterError as e:
if e.e.code == 429:
err("Fail: %i API rate limit exceeded" % e.e.code)
rls = twitter.application.rate_limit_status()
reset = rls.rate_limit_reset
reset = time.asctime(time.localtime(reset))
delay = int(rls.rate_limit_reset
- time.time()) + 5 # avoid race
err("Interval limit of %i requests reached, next reset on "
"%s: going to sleep for %i secs"
% (rls.rate_limit_limit, reset, delay))
fail.wait(delay)
continue
elif e.e.code == 502:
err("Fail: %i Service currently unavailable, retrying..."
% e.e.code)
else:
err("Fail: %s\nRetrying..." % str(e)[:500])
fail.wait(3)
except urllib2.URLError as e:
err("Fail: urllib2.URLError %s - Retrying..." % str(e))
fail.wait(3)
except httplib.error as e:
err("Fail: httplib.error %s - Retrying..." % str(e))
fail.wait(3)
except KeyError as e:
err("Fail: KeyError %s - Retrying..." % str(e))
fail.wait(3)
else:
users.update(portion)
err("Resolving user ids to screen names: %i/%i"
% (len(users), len(user_ids)))
break
return users
def follow_portion(twitter, screen_name, cursor=-1, followers=True):
"""Get a portion of followers/following for a user."""
kwargs = dict(screen_name=screen_name, cursor=cursor)
if followers:
t = twitter.followers.ids(**kwargs)
else: # following
t = twitter.friends.ids(**kwargs)
return t['ids'], t['next_cursor']
def follow(twitter, screen_name, followers=True):
"""Get the entire list of followers/following for a user."""
user_ids = []
cursor = -1
fail = Fail()
while True:
try:
portion, cursor = follow_portion(twitter, screen_name, cursor,
followers)
except TwitterError as e:
if e.e.code == 401:
reason = ("follow%s of that user are protected"
% ("ers" if followers else "ing"))
err("Fail: %i Unauthorized (%s)" % (e.e.code, reason))
break
elif e.e.code == 429:
err("Fail: %i API rate limit exceeded" % e.e.code)
rls = twitter.application.rate_limit_status()
reset = rls.rate_limit_reset
reset = time.asctime(time.localtime(reset))
delay = int(rls.rate_limit_reset
- time.time()) + 5 # avoid race
err("Interval limit of %i requests reached, next reset on %s: "
"going to sleep for %i secs" % (rls.rate_limit_limit,
reset, delay))
fail.wait(delay)
continue
elif e.e.code == 502:
err("Fail: %i Service currently unavailable, retrying..."
% e.e.code)
else:
err("Fail: %s\nRetrying..." % str(e)[:500])
fail.wait(3)
except urllib2.URLError as e:
err("Fail: urllib2.URLError %s - Retrying..." % str(e))
fail.wait(3)
except httplib.error as e:
err("Fail: httplib.error %s - Retrying..." % str(e))
fail.wait(3)
except KeyError as e:
err("Fail: KeyError %s - Retrying..." % str(e))
fail.wait(3)
else:
new = -len(user_ids)
user_ids = list(set(user_ids + portion))
new += len(user_ids)
what = "follow%s" % ("ers" if followers else "ing")
err("Browsing %s %s, new: %i" % (screen_name, what, new))
if cursor == 0:
break
fail = Fail()
return user_ids
def rate_limit_status(twitter):
"""Print current Twitter API rate limit status."""
rls = twitter.application.rate_limit_status()
print("Remaining API requests: %i/%i (interval limit)"
% (rls.rate_limit_remaining, rls.rate_limit_limit))
print("Next reset in %is (%s)"
% (int(rls.rate_limit_reset - time.time()),
time.asctime(time.localtime(rls.rate_limit_reset))))
def main(args=sys.argv[1:]):
options = {
'oauth': False,
'followers': True,
'api-rate': False,
'show_id': False
}
try:
parse_args(args, options)
except GetoptError as e:
err("I can't do that, %s." % e)
raise SystemExit(1)
# exit if no user or given, except if asking for API rate
if not options['extra_args'] and not options['api-rate']:
print(__doc__)
raise SystemExit(1)
# authenticate using OAuth, asking for token if necessary
if options['oauth']:
oauth_filename = (os.getenv("HOME", "") + os.sep
+ ".twitter-follow_oauth")
if not os.path.exists(oauth_filename):
oauth_dance("Twitter-Follow", CONSUMER_KEY, CONSUMER_SECRET,
oauth_filename)
oauth_token, oauth_token_secret = read_token_file(oauth_filename)
auth = OAuth(oauth_token, oauth_token_secret, CONSUMER_KEY,
CONSUMER_SECRET)
else:
auth = NoAuth()
twitter = Twitter(auth=auth, api_version='1.1', domain='api.twitter.com')
if options['api-rate']:
rate_limit_status(twitter)
return
# obtain list of followers (or following) for every given user
for user in options['extra_args']:
user_ids, users = [], {}
try:
user_ids = follow(twitter, user, options['followers'])
users = lookup(twitter, user_ids)
except KeyboardInterrupt as e:
err()
err("Interrupted.")
raise SystemExit(1)
for uid in user_ids:
if options['show_id']:
try:
print(str(uid) + "\t" + users[uid].encode("utf-8"))
except KeyError:
pass
else:
try:
print(users[uid].encode("utf-8"))
except KeyError:
pass
# print total on stderr to separate from user list on stdout
if options['followers']:
err("Total followers for %s: %i" % (user, len(user_ids)))
else:
err("Total users %s is following: %i" % (user, len(user_ids)))