]>
jfr.im git - z_archive/twitter.git/blob - twitter/cmdline.py
4 twitter [action] [options]
7 follow add the specified user to your follow list
8 friends get latest tweets from your friends (default action)
9 help print this help text that you are currently reading
10 leave remove the specified user from your following list
11 public get latest public tweets
12 replies get latest replies
13 set set your twitter status
17 -e --email <email> your email to login to twitter
18 -p --password <password> your twitter password
19 -r --refresh run this command forever, polling every once
20 in a while (default: every 5 minutes)
21 -R --refresh-rate <rate> set the refresh rate (in seconds)
22 -f --format <format> specify the output format for status updates
23 -c --config <filename> read username and password from given config
24 file (default ~/.twitter)
26 FORMATS for the --format option
28 default one line per status
29 verbose multiple lines per status, more verbose status info
31 ansi ansi colour (rainbow mode)
35 The config file should contain a [twitter] header, and all the desired options
36 you wish to set, like so:
41 format: <desired_default_format_for_output>
46 from getopt
import getopt
, GetoptError
47 from getpass
import getpass
50 from ConfigParser
import SafeConfigParser
52 from api
import Twitter
, TwitterError
55 # Please don't change this, it was provided by the fine folks at Twitter.
56 # If you change it, it will not work.
57 AGENT_STR
= "twittercommandlinetoolpy"
66 'config_filename': os
.environ
.get('HOME', '') + os
.sep
+ '.twitter',
70 def parse_args(args
, options
):
71 long_opts
= ['email', 'password', 'help', 'format', 'refresh',
72 'refresh-rate', 'config']
73 short_opts
= "e:p:f:h?rR:c:"
74 opts
, extra_args
= getopt(args
, short_opts
, long_opts
)
77 if opt
in ('-e', '--email'):
78 options
['email'] = arg
79 elif opt
in ('-p', '--password'):
80 options
['password'] = arg
81 elif opt
in ('-f', '--format'):
82 options
['format'] = arg
83 elif opt
in ('-r', '--refresh'):
84 options
['refresh'] = True
85 elif opt
in ('-R', '--refresh-rate'):
86 options
['refresh_rate'] = int(arg
)
87 elif opt
in ('-?', '-h', '--help'):
90 elif opt
in ('-c', '--config'):
91 options
['config_filename'] = arg
94 options
['action'] = extra_args
[0]
95 options
['extra_args'] = extra_args
[1:]
97 class StatusFormatter(object):
98 def __call__(self
, status
):
100 status
['user']['screen_name'], status
['text']))
102 class AnsiStatusFormatter(object):
104 self
._colourMap
= ansi
.ColourMap()
106 def __call__(self
, status
):
107 colour
= self
._colourMap
.colourFor(status
['user']['screen_name'])
108 return (u
"%s%s%s %s" %(
109 ansi
.cmdColour(colour
), status
['user']['screen_name'],
110 ansi
.cmdReset(), status
['text']))
112 class VerboseStatusFormatter(object):
113 def __call__(self
, status
):
114 return (u
"-- %s (%s) on %s\n%s\n" %(
115 status
['user']['screen_name'],
116 status
['user']['location'],
117 status
['created_at'],
120 class URLStatusFormatter(object):
121 urlmatch
= re
.compile(r
'https?://\S+')
122 def __call__(self
, status
):
123 urls
= self
.urlmatch
.findall(status
['text'])
124 return u
'\n'.join(urls
) if urls
else ""
126 class AdminFormatter(object):
127 def __call__(self
, action
, user
):
128 user_str
= u
"%s (%s)" %(user
['screen_name'], user
['name'])
129 if action
== "follow":
130 return u
"You are now following %s.\n" %(user_str)
132 return u
"You are no longer following %s.\n" %(user_str)
134 class VerboseAdminFormatter(object):
135 def __call__(self
, action
, user
):
136 return(u
"-- %s: %s (%s): %s" % (
137 "Following" if action
== "follow" else "Leaving",
142 status_formatters
= {
143 'default': StatusFormatter
,
144 'verbose': VerboseStatusFormatter
,
145 'urls': URLStatusFormatter
,
146 'ansi': AnsiStatusFormatter
150 'default': AdminFormatter
,
151 'verbose': VerboseAdminFormatter
,
152 'urls': AdminFormatter
,
153 'ansi': AdminFormatter
156 def get_status_formatter(options
):
157 sf
= status_formatters
.get(options
['format'])
160 "Unknown formatter '%s'" %(options
['format']))
163 def get_admin_formatter(options
):
164 sf
= admin_formatters
.get(options
['format'])
167 "Unknown formatter '%s'" %(options
['format']))
170 class Action(object):
173 class NoSuchAction(Action
):
174 def __call__(self
, twitter
, options
):
175 print >> sys
.stderr
, "No such action: ", options
['action']
178 class StatusAction(Action
):
179 def __call__(self
, twitter
, options
):
180 statuses
= self
.getStatuses(twitter
)
181 sf
= get_status_formatter(options
)
182 for status
in statuses
:
183 statusStr
= sf(status
)
184 if statusStr
.strip():
185 print statusStr
.encode(sys
.stdout
.encoding
, 'replace')
187 class AdminAction(Action
):
188 def __call__(self
, twitter
, options
):
189 if not options
['extra_args'][0]:
190 raise TwitterError("You need to specify a user (screen name)")
191 af
= get_admin_formatter(options
)
193 user
= self
.getUser(twitter
, options
['extra_args'][0])
194 except TwitterError
, e
:
195 print "There was a problem following or leaving the specified user."
196 print " You may be trying to follow a user you are already following;"
197 print " Leaving a user you are not currently following;"
198 print " Or the user may not exist."
203 print af(options
['action'], user
).encode(sys
.stdout
.encoding
, 'replace')
205 class FriendsAction(StatusAction
):
206 def getStatuses(self
, twitter
):
207 return reversed(twitter
.statuses
.friends_timeline())
209 class PublicAction(StatusAction
):
210 def getStatuses(self
, twitter
):
211 return reversed(twitter
.statuses
.public_timeline())
213 class RepliesAction(StatusAction
):
214 def getStatuses(self
, twitter
):
215 return reversed(twitter
.statuses
.replies())
217 class FollowAction(AdminAction
):
218 def getUser(self
, twitter
, user
):
219 return twitter
.friendships
.create(id=user
)
221 class LeaveAction(AdminAction
):
222 def getUser(self
, twitter
, user
):
223 return twitter
.friendships
.destroy(id=user
)
225 class SetStatusAction(Action
):
226 def __call__(self
, twitter
, options
):
227 statusTxt
= (u
" ".join(options
['extra_args'])
228 if options
['extra_args']
229 else unicode(raw_input("message: ")))
230 status
= (statusTxt
.encode('utf8', 'replace'))
231 twitter
.statuses
.update(status
=status
)
233 class HelpAction(Action
):
234 def __call__(self
, twitter
, options
):
238 'follow': FollowAction
,
239 'friends': FriendsAction
,
241 'leave': LeaveAction
,
242 'public': PublicAction
,
243 'replies': RepliesAction
,
244 'set': SetStatusAction
,
247 def loadConfig(filename
):
248 options
= dict(OPTIONS
)
249 if os
.path
.exists(filename
):
250 cp
= SafeConfigParser()
252 for option
in ('email', 'password', 'format'):
253 if cp
.has_option('twitter', option
):
254 options
[option
] = cp
.get('twitter', option
)
257 def main(args
=sys
.argv
[1:]):
260 parse_args(args
, arg_options
)
261 except GetoptError
, e
:
262 print >> sys
.stderr
, "I can't do that, %s." %(e)
266 config_options
= loadConfig(
267 arg_options
.get('config_filename') or OPTIONS
.get('config_filename'))
269 # Apply the various options in order, the most important applied last.
270 # Defaults first, then what's read from config file, then command-line
272 options
= dict(OPTIONS
)
273 for d
in config_options
, arg_options
:
274 for k
,v
in d
.items():
277 if options
['refresh'] and options
['action'] not in (
278 'friends', 'public', 'replies'):
279 print >> sys
.stderr
, "You can only refresh the friends, public, or replies actions."
280 print >> sys
.stderr
, "Use 'twitter -h' for help."
283 if options
['email'] and not options
['password']:
284 options
['password'] = getpass("Twitter password: ")
286 twitter
= Twitter(options
['email'], options
['password'], agent
=AGENT_STR
)
287 action
= actions
.get(options
['action'], NoSuchAction
)()
290 doAction
= lambda : action(twitter
, options
)
292 if (options
['refresh'] and isinstance(action
, StatusAction
)):
295 time
.sleep(options
['refresh_rate'])
299 except TwitterError
, e
:
300 print >> sys
.stderr
, e
.args
[0]
301 print >> sys
.stderr
, "Use 'twitter -h' for help."
303 except KeyboardInterrupt: