]>
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
14 Authenticate to Twitter using OAuth to see following/followers of private
15 profiles and have higher API rate limits. OAuth authentication tokens
16 are stored in the file .twitter-follow_oauth in your home directory.
19 from __future__
import print_function
21 import os
, sys
, time
, calendar
, urllib2
, httplib
22 from getopt
import gnu_getopt
as getopt
, GetoptError
24 # T-Follow (Twitter-Follow) application registered by @stalkr_
25 CONSUMER_KEY
='USRZQfvFFjB6UvZIN2Edww'
26 CONSUMER_SECRET
='AwGAaSzZa5r0TDL8RKCDtffnI9H9mooZUdOa95nw8'
28 from .api
import Twitter
, TwitterError
29 from .oauth
import OAuth
, read_token_file
30 from .oauth_dance
import oauth_dance
31 from .auth
import NoAuth
32 from .util
import Fail
, err
34 def parse_args(args
, options
):
35 """Parse arguments from command-line to set options."""
36 long_opts
= ['help', 'oauth', 'followers', 'following', 'api-rate']
38 opts
, extra_args
= getopt(args
, short_opts
, long_opts
)
41 if opt
in ('-h', '--help'):
44 elif opt
in ('-o', '--oauth'):
45 options
['oauth'] = True
46 elif opt
in ('-r', '--followers'):
47 options
['followers'] = True
48 elif opt
in ('-g', '--following'):
49 options
['followers'] = False
50 elif opt
in ('-a', '--api-rate'):
51 options
['api-rate' ] = True
53 options
['extra_args'] = extra_args
55 def lookup_portion(twitter
, user_ids
):
56 """Resolve a limited list of user ids to screen names."""
58 kwargs
= dict(user_id
=",".join(map(str, user_ids
)), skip_status
=1)
59 for u
in twitter
.users
.lookup(**kwargs
):
60 users
[int(u
['id'])] = u
['screen_name']
63 def lookup(twitter
, user_ids
):
64 """Resolve an entire list of user ids to screen names."""
67 for i
in range(0, len(user_ids
), api_limit
):
71 portion
= lookup_portion(twitter
, user_ids
[i
:][:api_limit
])
72 except TwitterError
as e
:
74 err("Fail: %i API rate limit exceeded" % e
.e
.code
)
75 rate
= twitter
.account
.rate_limit_status()
76 reset
= rate
['reset_time_in_seconds']
77 reset
= time
.asctime(time
.localtime(reset
))
78 delay
= int(rate
['reset_time_in_seconds']
79 - time
.time()) + 5 # avoid race
80 err("Hourly limit of %i requests reached, next reset on "
81 "%s: going to sleep for %i secs"
82 % (rate
['hourly_limit'], reset
, delay
))
86 err("Fail: %i Service currently unavailable, retrying..."
89 err("Fail: %s\nRetrying..." % str(e
)[:500])
91 except urllib2
.URLError
as e
:
92 err("Fail: urllib2.URLError %s - Retrying..." % str(e
))
94 except httplib
.error
as e
:
95 err("Fail: httplib.error %s - Retrying..." % str(e
))
98 err("Fail: KeyError %s - Retrying..." % str(e
))
101 users
.update(portion
)
102 err("Resolving user ids to screen names: %i/%i"
103 % (len(users
), len(user_ids
)))
107 def follow_portion(twitter
, screen_name
, cursor
=-1, followers
=True):
108 """Get a portion of followers/following for a user."""
109 kwargs
= dict(screen_name
=screen_name
, cursor
=cursor
)
111 t
= twitter
.followers
.ids(**kwargs
)
113 t
= twitter
.friends
.ids(**kwargs
)
114 return t
['ids'], t
['next_cursor']
116 def follow(twitter
, screen_name
, followers
=True):
117 """Get the entire list of followers/following for a user."""
123 portion
, cursor
= follow_portion(twitter
, screen_name
, cursor
,
125 except TwitterError
as e
:
127 reason
= ("follow%s of that user are protected"
128 % ("ers" if followers
else "ing"))
129 err("Fail: %i Unauthorized (%s)" % (e
.e
.code
, reason
))
131 elif e
.e
.code
== 400:
132 err("Fail: %i API rate limit exceeded" % e
.e
.code
)
133 rate
= twitter
.account
.rate_limit_status()
134 reset
= rate
['reset_time_in_seconds']
135 reset
= time
.asctime(time
.localtime(reset
))
136 delay
= int(rate
['reset_time_in_seconds']
137 - time
.time()) + 5 # avoid race
138 err("Hourly limit of %i requests reached, next reset on %s: "
139 "going to sleep for %i secs" % (rate
['hourly_limit'],
143 elif e
.e
.code
== 502:
144 err("Fail: %i Service currently unavailable, retrying..."
147 err("Fail: %s\nRetrying..." % str(e
)[:500])
149 except urllib2
.URLError
as e
:
150 err("Fail: urllib2.URLError %s - Retrying..." % str(e
))
152 except httplib
.error
as e
:
153 err("Fail: httplib.error %s - Retrying..." % str(e
))
155 except KeyError as e
:
156 err("Fail: KeyError %s - Retrying..." % str(e
))
160 user_ids
= list(set(user_ids
+ portion
))
162 what
= "follow%s" % ("ers" if followers
else "ing")
163 err("Browsing %s %s, new: %i" % (screen_name
, what
, new
))
170 def rate_limit_status(twitter
):
171 """Print current Twitter API rate limit status."""
172 r
= twitter
.account
.rate_limit_status()
173 print("Remaining API requests: %i/%i (hourly limit)"
174 % (r
['remaining_hits'], r
['hourly_limit']))
175 print("Next reset in %is (%s)"
176 % (int(r
['reset_time_in_seconds'] - time
.time()),
177 time
.asctime(time
.localtime(r
['reset_time_in_seconds']))))
179 def main(args
=sys
.argv
[1:]):
186 parse_args(args
, options
)
187 except GetoptError
as e
:
188 err("I can't do that, %s." % e
)
191 # exit if no user or given, except if asking for API rate
192 if not options
['extra_args'] and not options
['api-rate']:
196 # authenticate using OAuth, asking for token if necessary
198 oauth_filename
= (os
.getenv("HOME", "") + os
.sep
199 + ".twitter-follow_oauth")
200 if not os
.path
.exists(oauth_filename
):
201 oauth_dance("Twitter-Follow", CONSUMER_KEY
, CONSUMER_SECRET
,
203 oauth_token
, oauth_token_secret
= read_token_file(oauth_filename
)
204 auth
= OAuth(oauth_token
, oauth_token_secret
, CONSUMER_KEY
,
209 twitter
= Twitter(auth
=auth
, api_version
='1', domain
='api.twitter.com')
211 if options
['api-rate']:
212 rate_limit_status(twitter
)
215 # obtain list of followers (or following) for every given user
216 for user
in options
['extra_args']:
217 user_ids
, users
= [], {}
219 user_ids
= follow(twitter
, user
, options
['followers'])
220 users
= lookup(twitter
, user_ids
)
221 except KeyboardInterrupt as e
:
227 print(users
[uid
].encode("utf-8"))
229 # print total on stderr to separate from user list on stdout
230 if options
['followers']:
231 err("Total followers for %s: %i" % (user
, len(user_ids
)))
233 err("Total users %s is following: %i" % (user
, len(user_ids
)))