]>
jfr.im git - z_archive/twitter.git/blob - twitter/follow.py
2 twitter-follow [options] <user>
5 Display all following/followers of a user, one user per line.
8 -o --oauth authenticate to Twitter using OAuth (default no)
9 -r --followers display followers of the given user (default)
10 -g --following display users the given user is following
11 -a --api-rate see your current API rate limit status
12 -i --ids prepend user id to each line. useful to tracking renames
15 Authenticate to Twitter using OAuth to see following/followers of private
16 profiles and have higher API rate limits. OAuth authentication tokens
17 are stored in the file .twitter-follow_oauth in your home directory.
20 from __future__
import print_function
22 import os
, sys
, time
, calendar
23 from getopt
import gnu_getopt
as getopt
, GetoptError
26 import urllib
.request
as urllib2
27 import http
.client
as httplib
32 # T-Follow (Twitter-Follow) application registered by @stalkr_
33 CONSUMER_KEY
='USRZQfvFFjB6UvZIN2Edww'
34 CONSUMER_SECRET
='AwGAaSzZa5r0TDL8RKCDtffnI9H9mooZUdOa95nw8'
36 from .api
import Twitter
, TwitterError
37 from .oauth
import OAuth
, read_token_file
38 from .oauth_dance
import oauth_dance
39 from .auth
import NoAuth
40 from .util
import Fail
, err
43 def parse_args(args
, options
):
44 """Parse arguments from command-line to set options."""
45 long_opts
= ['help', 'oauth', 'followers', 'following', 'api-rate', 'ids']
47 opts
, extra_args
= getopt(args
, short_opts
, long_opts
)
50 if opt
in ('-h', '--help'):
53 elif opt
in ('-o', '--oauth'):
54 options
['oauth'] = True
55 elif opt
in ('-r', '--followers'):
56 options
['followers'] = True
57 elif opt
in ('-g', '--following'):
58 options
['followers'] = False
59 elif opt
in ('-a', '--api-rate'):
60 options
['api-rate' ] = True
61 elif opt
in ('-i', '--ids'):
62 options
['show_id'] = True
64 options
['extra_args'] = extra_args
66 def lookup_portion(twitter
, user_ids
):
67 """Resolve a limited list of user ids to screen names."""
69 kwargs
= dict(user_id
=",".join(map(str, user_ids
)), skip_status
=1)
70 for u
in twitter
.users
.lookup(**kwargs
):
71 users
[int(u
['id'])] = u
['screen_name']
74 def lookup(twitter
, user_ids
):
75 """Resolve an entire list of user ids to screen names."""
78 for i
in range(0, len(user_ids
), api_limit
):
82 portion
= lookup_portion(twitter
, user_ids
[i
:][:api_limit
])
83 except TwitterError
as e
:
85 err("Fail: %i API rate limit exceeded" % e
.e
.code
)
86 rate
= twitter
.account
.rate_limit_status()
87 reset
= rate
['reset_time_in_seconds']
88 reset
= time
.asctime(time
.localtime(reset
))
89 delay
= int(rate
['reset_time_in_seconds']
90 - time
.time()) + 5 # avoid race
91 err("Hourly limit of %i requests reached, next reset on "
92 "%s: going to sleep for %i secs"
93 % (rate
['hourly_limit'], reset
, delay
))
97 err("Fail: %i Service currently unavailable, retrying..."
100 err("Fail: %s\nRetrying..." % str(e
)[:500])
102 except urllib2
.URLError
as e
:
103 err("Fail: urllib2.URLError %s - Retrying..." % str(e
))
105 except httplib
.error
as e
:
106 err("Fail: httplib.error %s - Retrying..." % str(e
))
108 except KeyError as e
:
109 err("Fail: KeyError %s - Retrying..." % str(e
))
112 users
.update(portion
)
113 err("Resolving user ids to screen names: %i/%i"
114 % (len(users
), len(user_ids
)))
118 def follow_portion(twitter
, screen_name
, cursor
=-1, followers
=True):
119 """Get a portion of followers/following for a user."""
120 kwargs
= dict(screen_name
=screen_name
, cursor
=cursor
)
122 t
= twitter
.followers
.ids(**kwargs
)
124 t
= twitter
.friends
.ids(**kwargs
)
125 return t
['ids'], t
['next_cursor']
127 def follow(twitter
, screen_name
, followers
=True):
128 """Get the entire list of followers/following for a user."""
134 portion
, cursor
= follow_portion(twitter
, screen_name
, cursor
,
136 except TwitterError
as e
:
138 reason
= ("follow%s of that user are protected"
139 % ("ers" if followers
else "ing"))
140 err("Fail: %i Unauthorized (%s)" % (e
.e
.code
, reason
))
142 elif e
.e
.code
== 400:
143 err("Fail: %i API rate limit exceeded" % e
.e
.code
)
144 rate
= twitter
.account
.rate_limit_status()
145 reset
= rate
['reset_time_in_seconds']
146 reset
= time
.asctime(time
.localtime(reset
))
147 delay
= int(rate
['reset_time_in_seconds']
148 - time
.time()) + 5 # avoid race
149 err("Hourly limit of %i requests reached, next reset on %s: "
150 "going to sleep for %i secs" % (rate
['hourly_limit'],
154 elif e
.e
.code
== 502:
155 err("Fail: %i Service currently unavailable, retrying..."
158 err("Fail: %s\nRetrying..." % str(e
)[:500])
160 except urllib2
.URLError
as e
:
161 err("Fail: urllib2.URLError %s - Retrying..." % str(e
))
163 except httplib
.error
as e
:
164 err("Fail: httplib.error %s - Retrying..." % str(e
))
166 except KeyError as e
:
167 err("Fail: KeyError %s - Retrying..." % str(e
))
171 user_ids
= list(set(user_ids
+ portion
))
173 what
= "follow%s" % ("ers" if followers
else "ing")
174 err("Browsing %s %s, new: %i" % (screen_name
, what
, new
))
181 def rate_limit_status(twitter
):
182 """Print current Twitter API rate limit status."""
183 r
= twitter
.account
.rate_limit_status()
184 print("Remaining API requests: %i/%i (hourly limit)"
185 % (r
['remaining_hits'], r
['hourly_limit']))
186 print("Next reset in %is (%s)"
187 % (int(r
['reset_time_in_seconds'] - time
.time()),
188 time
.asctime(time
.localtime(r
['reset_time_in_seconds']))))
190 def main(args
=sys
.argv
[1:]):
198 parse_args(args
, options
)
199 except GetoptError
as e
:
200 err("I can't do that, %s." % e
)
203 # exit if no user or given, except if asking for API rate
204 if not options
['extra_args'] and not options
['api-rate']:
208 # authenticate using OAuth, asking for token if necessary
210 oauth_filename
= (os
.getenv("HOME", "") + os
.sep
211 + ".twitter-follow_oauth")
212 if not os
.path
.exists(oauth_filename
):
213 oauth_dance("Twitter-Follow", CONSUMER_KEY
, CONSUMER_SECRET
,
215 oauth_token
, oauth_token_secret
= read_token_file(oauth_filename
)
216 auth
= OAuth(oauth_token
, oauth_token_secret
, CONSUMER_KEY
,
221 twitter
= Twitter(auth
=auth
, api_version
='1', domain
='api.twitter.com')
223 if options
['api-rate']:
224 rate_limit_status(twitter
)
227 # obtain list of followers (or following) for every given user
228 for user
in options
['extra_args']:
229 user_ids
, users
= [], {}
231 user_ids
= follow(twitter
, user
, options
['followers'])
232 users
= lookup(twitter
, user_ids
)
233 except KeyboardInterrupt as e
:
239 if options
['show_id']:
241 print(str(uid
) + "\t" + users
[uid
].encode("utf-8"))
247 print(users
[uid
].encode("utf-8"))
251 # print total on stderr to separate from user list on stdout
252 if options
['followers']:
253 err("Total followers for %s: %i" % (user
, len(user_ids
)))
255 err("Total users %s is following: %i" % (user
, len(user_ids
)))