]> jfr.im git - z_archive/twitter.git/blame - twitter/cmdline.py
Make twitter-log work with py 3.2
[z_archive/twitter.git] / twitter / cmdline.py
CommitLineData
3756d82e 1# encoding: utf-8
7364ea65 2"""
5251ea48 3USAGE:
7364ea65 4
5251ea48 5 twitter [action] [options]
6
086fc282 7
5251ea48 8ACTIONS:
086fc282 9 authorize authorize the command-line tool to interact with Twitter
527c550f 10 follow follow a user
5251ea48 11 friends get latest tweets from your friends (default action)
45688301 12 help print this help text that you are currently reading
527c550f
MV
13 leave stop following a user
14 list get list of a user's lists; give a list name to get
15 tweets from that list
16 mylist get list of your lists; give a list name to get tweets
17 from that list
5251ea48 18 public get latest public tweets
7227ce91
MV
19 pyprompt start a Python prompt for interacting with the twitter
20 object directly
527c550f 21 replies get latest replies to you
6af9fa5f 22 search search twitter (Beware: octothorpe, escape it)
5251ea48 23 set set your twitter status
527c550f 24 shell login to the twitter shell
5251ea48 25
086fc282 26
5251ea48 27OPTIONS:
28
0ea01db7 29 -r --refresh run this command forever, polling every once
30 in a while (default: every 5 minutes)
31 -R --refresh-rate <rate> set the refresh rate (in seconds)
32 -f --format <format> specify the output format for status updates
21e3bd23 33 -c --config <filename> read username and password from given config
39a6f562
MV
34 file (default ~/.twitter)
35 -l --length <count> specify number of status updates shown
36 (default: 20, max: 200)
37 -t --timestamp show time before status lines
527c550f
MV
38 -d --datestamp show date before status lines
39 --no-ssl use less-secure HTTP instead of HTTPS
7f1dd286 40 --oauth <filename> filename to read/store oauth credentials to
086fc282 41
0ea01db7 42FORMATS for the --format option
43
44 default one line per status
45 verbose multiple lines per status, more verbose status info
327e556b
MV
46 urls nothing but URLs
47 ansi ansi colour (rainbow mode)
05b85831 48
086fc282 49
21e3bd23 50CONFIG FILES
51
086fc282
MV
52 The config file should be placed in your home directory and be named .twitter.
53 It must contain a [twitter] header, and all the desired options you wish to
54 set, like so:
21e3bd23 55
56[twitter]
327e556b 57format: <desired_default_format_for_output>
05b85831 58prompt: <twitter_shell_prompt e.g. '[cyan]twitter[R]> '>
086fc282
MV
59
60 OAuth authentication tokens are stored in the file .twitter_oauth in your
61 home directory.
7364ea65 62"""
63
3930cc7b
MV
64from __future__ import print_function
65
6c527e72
MV
66CONSUMER_KEY='uS6hO2sV6tDKIOeVjhnFnQ'
67CONSUMER_SECRET='MEYTOS97VvlHX7K1rwHPEqVpTSqZ71HtvoK4sVuYk'
68
5251ea48 69import sys
0ea01db7 70import time
f2a7ce46 71from getopt import gnu_getopt as getopt, GetoptError
f068ff42 72from getpass import getpass
0ea01db7 73import re
21e3bd23 74import os.path
3930cc7b
MV
75try:
76 from ConfigParser import SafeConfigParser
77except ImportError:
78 from configparser import ConfigParser as SafeConfigParser
a4b5e65b 79import datetime
3930cc7b
MV
80try:
81 from urllib.parse import quote
82except ImportError:
83 from urllib2 import quote
6c527e72 84import webbrowser
5251ea48 85
f7e63802
MV
86from .api import Twitter, TwitterError
87from .oauth import OAuth, write_token_file, read_token_file
88from .oauth_dance import oauth_dance
89from . import ansi
098660ce 90from .util import smrt_input, printNicely
5251ea48 91
327e556b 92OPTIONS = {
5251ea48 93 'action': 'friends',
0ea01db7 94 'refresh': False,
95 'refresh_rate': 600,
96 'format': 'default',
05b85831 97 'prompt': '[cyan]twitter[R]> ',
21e3bd23 98 'config_filename': os.environ.get('HOME', '') + os.sep + '.twitter',
6c527e72 99 'oauth_filename': os.environ.get('HOME', '') + os.sep + '.twitter_oauth',
39a6f562
MV
100 'length': 20,
101 'timestamp': False,
102 'datestamp': False,
9a148ed1 103 'extra_args': [],
6c527e72 104 'secure': True,
5251ea48 105}
106
107def parse_args(args, options):
7f1dd286 108 long_opts = ['help', 'format=', 'refresh', 'oauth=',
8ddd8500 109 'refresh-rate=', 'config=', 'length=', 'timestamp',
9a148ed1 110 'datestamp', 'no-ssl']
39a6f562 111 short_opts = "e:p:f:h?rR:c:l:td"
8ddd8500 112 opts, extra_args = getopt(args, short_opts, long_opts)
efa0ba89 113
5251ea48 114 for opt, arg in opts:
086fc282 115 if opt in ('-f', '--format'):
0ea01db7 116 options['format'] = arg
117 elif opt in ('-r', '--refresh'):
118 options['refresh'] = True
119 elif opt in ('-R', '--refresh-rate'):
120 options['refresh_rate'] = int(arg)
39a6f562
MV
121 elif opt in ('-l', '--length'):
122 options["length"] = int(arg)
123 elif opt in ('-t', '--timestamp'):
124 options["timestamp"] = True
125 elif opt in ('-d', '--datestamp'):
126 options["datestamp"] = True
5251ea48 127 elif opt in ('-?', '-h', '--help'):
05b85831 128 options['action'] = 'help'
21e3bd23 129 elif opt in ('-c', '--config'):
130 options['config_filename'] = arg
9a148ed1
MV
131 elif opt == '--no-ssl':
132 options['secure'] = False
7f1dd286
MV
133 elif opt == '--oauth':
134 options['oauth_filename'] = arg
efa0ba89 135
05b85831 136 if extra_args and not ('action' in options and options['action'] == 'help'):
ae1d86aa 137 options['action'] = extra_args[0]
138 options['extra_args'] = extra_args[1:]
a8b5ad3e 139
87be041f 140def get_time_string(status, options, format="%a %b %d %H:%M:%S +0000 %Y"):
39a6f562
MV
141 timestamp = options["timestamp"]
142 datestamp = options["datestamp"]
87be041f 143 t = time.strptime(status['created_at'], format)
a4b5e65b
MV
144 i_hate_timezones = time.timezone
145 if (time.daylight):
146 i_hate_timezones = time.altzone
147 dt = datetime.datetime(*t[:-3]) - datetime.timedelta(
148 seconds=i_hate_timezones)
149 t = dt.timetuple()
39a6f562
MV
150 if timestamp and datestamp:
151 return time.strftime("%Y-%m-%d %H:%M:%S ", t)
152 elif timestamp:
153 return time.strftime("%H:%M:%S ", t)
154 elif datestamp:
155 return time.strftime("%Y-%m-%d ", t)
8ddd8500 156 return ""
5251ea48 157
158class StatusFormatter(object):
a55c0ac8 159 def __call__(self, status, options):
f7e63802 160 return ("%s%s %s" %(
39a6f562 161 get_time_string(status, options),
0ea01db7 162 status['user']['screen_name'], status['text']))
5251ea48 163
0b9960a3
MV
164class AnsiStatusFormatter(object):
165 def __init__(self):
166 self._colourMap = ansi.ColourMap()
a8b5ad3e 167
39a6f562 168 def __call__(self, status, options):
0b9960a3 169 colour = self._colourMap.colourFor(status['user']['screen_name'])
f7e63802 170 return ("%s%s%s%s %s" %(
39a6f562 171 get_time_string(status, options),
0b9960a3 172 ansi.cmdColour(colour), status['user']['screen_name'],
05b85831
HN
173 ansi.cmdReset(), status['text']))
174
f068ff42 175class VerboseStatusFormatter(object):
39a6f562 176 def __call__(self, status, options):
f7e63802 177 return ("-- %s (%s) on %s\n%s\n" %(
f068ff42 178 status['user']['screen_name'],
179 status['user']['location'],
180 status['created_at'],
0ea01db7 181 status['text']))
f068ff42 182
0ea01db7 183class URLStatusFormatter(object):
184 urlmatch = re.compile(r'https?://\S+')
39a6f562 185 def __call__(self, status, options):
0ea01db7 186 urls = self.urlmatch.findall(status['text'])
f7e63802 187 return '\n'.join(urls) if urls else ""
0ea01db7 188
3c2b5e3e 189
69851f4c
AL
190class ListsFormatter(object):
191 def __call__(self, list):
192 if list['description']:
f7e63802 193 list_str = "%-30s (%s)" % (list['name'], list['description'])
69851f4c 194 else:
f7e63802
MV
195 list_str = "%-30s" % (list['name'])
196 return "%s\n" % list_str
69851f4c
AL
197
198class ListsVerboseFormatter(object):
199 def __call__(self, list):
f7e63802 200 list_str = "%-30s\n description: %s\n members: %s\n mode:%s\n" % (list['name'], list['description'], list['member_count'], list['mode'])
69851f4c
AL
201 return list_str
202
203class AnsiListsFormatter(object):
204 def __init__(self):
205 self._colourMap = ansi.ColourMap()
206
207 def __call__(self, list):
208 colour = self._colourMap.colourFor(list['name'])
f7e63802 209 return ("%s%-15s%s %s" %(
69851f4c
AL
210 ansi.cmdColour(colour), list['name'],
211 ansi.cmdReset(), list['description']))
212
213
1c11e6d7 214class AdminFormatter(object):
efa0ba89 215 def __call__(self, action, user):
f7e63802 216 user_str = "%s (%s)" %(user['screen_name'], user['name'])
da45d039 217 if action == "follow":
f7e63802 218 return "You are now following %s.\n" %(user_str)
da45d039 219 else:
f7e63802 220 return "You are no longer following %s.\n" %(user_str)
efa0ba89 221
1c11e6d7 222class VerboseAdminFormatter(object):
efa0ba89 223 def __call__(self, action, user):
f7e63802 224 return("-- %s: %s (%s): %s" % (
05b85831
HN
225 "Following" if action == "follow" else "Leaving",
226 user['screen_name'],
efa0ba89
MV
227 user['name'],
228 user['url']))
229
87be041f
WD
230class SearchFormatter(object):
231 def __call__(self, result, options):
f7e63802 232 return("%s%s %s" %(
87be041f
WD
233 get_time_string(result, options, "%a, %d %b %Y %H:%M:%S +0000"),
234 result['from_user'], result['text']))
235
236class VerboseSearchFormatter(SearchFormatter):
a8b5ad3e
MV
237 pass #Default to the regular one
238
87be041f
WD
239class URLSearchFormatter(object):
240 urlmatch = re.compile(r'https?://\S+')
241 def __call__(self, result, options):
242 urls = self.urlmatch.findall(result['text'])
f7e63802 243 return '\n'.join(urls) if urls else ""
87be041f
WD
244
245class AnsiSearchFormatter(object):
246 def __init__(self):
247 self._colourMap = ansi.ColourMap()
a8b5ad3e 248
87be041f
WD
249 def __call__(self, result, options):
250 colour = self._colourMap.colourFor(result['from_user'])
f7e63802 251 return ("%s%s%s%s %s" %(
87be041f
WD
252 get_time_string(result, options, "%a, %d %b %Y %H:%M:%S +0000"),
253 ansi.cmdColour(colour), result['from_user'],
254 ansi.cmdReset(), result['text']))
255
5a77e17a
MV
256_term_encoding = None
257def get_term_encoding():
258 global _term_encoding
259 if not _term_encoding:
260 lang = os.getenv('LANG', 'unknown.UTF-8').split('.')
261 if lang[1:]:
262 _term_encoding = lang[1]
263 else:
264 _term_encoding = 'UTF-8'
265 return _term_encoding
266
87be041f 267formatters = {}
1c11e6d7 268status_formatters = {
0ea01db7 269 'default': StatusFormatter,
270 'verbose': VerboseStatusFormatter,
0b9960a3
MV
271 'urls': URLStatusFormatter,
272 'ansi': AnsiStatusFormatter
05b85831 273}
87be041f 274formatters['status'] = status_formatters
1c11e6d7
WD
275
276admin_formatters = {
efa0ba89
MV
277 'default': AdminFormatter,
278 'verbose': VerboseAdminFormatter,
327e556b
MV
279 'urls': AdminFormatter,
280 'ansi': AdminFormatter
1c11e6d7 281}
87be041f 282formatters['admin'] = admin_formatters
efa0ba89 283
87be041f
WD
284search_formatters = {
285 'default': SearchFormatter,
286 'verbose': VerboseSearchFormatter,
287 'urls': URLSearchFormatter,
288 'ansi': AnsiSearchFormatter
289}
290formatters['search'] = search_formatters
291
69851f4c
AL
292lists_formatters = {
293 'default': ListsFormatter,
294 'verbose': ListsVerboseFormatter,
295 'urls': None,
296 'ansi': AnsiListsFormatter
297}
298formatters['lists'] = lists_formatters
299
87be041f
WD
300def get_formatter(action_type, options):
301 formatters_dict = formatters.get(action_type)
302 if (not formatters_dict):
a8b5ad3e 303 raise TwitterError(
87be041f
WD
304 "There was an error finding a class of formatters for your type (%s)"
305 %(action_type))
306 f = formatters_dict.get(options['format'])
307 if (not f):
efa0ba89 308 raise TwitterError(
87be041f
WD
309 "Unknown formatter '%s' for status actions" %(options['format']))
310 return f()
efa0ba89 311
0ea01db7 312class Action(object):
ec894371
MV
313
314 def ask(self, subject='perform this action', careful=False):
05b85831
HN
315 '''
316 Requests fromt he user using `raw_input` if `subject` should be
317 performed. When `careful`, the default answer is NO, otherwise YES.
318 Returns the user answer in the form `True` or `False`.
319 '''
f47ab046
MV
320 sample = '(y/N)'
321 if not careful:
322 sample = '(Y/n)'
a8b5ad3e 323
05b85831
HN
324 prompt = 'You really want to %s %s? ' %(subject, sample)
325 try:
f7e63802 326 answer = input(prompt).lower()
05b85831 327 if careful:
f47ab046 328 return answer in ('yes', 'y')
05b85831 329 else:
f47ab046 330 return answer not in ('no', 'n')
05b85831 331 except EOFError:
f7e63802 332 print(file=sys.stderr) # Put Newline since Enter was never pressed
05b85831
HN
333 # TODO:
334 # Figure out why on OS X the raw_input keeps raising
335 # EOFError and is never able to reset and get more input
336 # Hint: Look at how IPython implements their console
f47ab046
MV
337 default = True
338 if careful:
339 default = False
05b85831 340 return default
a8b5ad3e 341
05b85831
HN
342 def __call__(self, twitter, options):
343 action = actions.get(options['action'], NoSuchAction)()
344 try:
345 doAction = lambda : action(twitter, options)
346 if (options['refresh'] and isinstance(action, StatusAction)):
347 while True:
348 doAction()
349 time.sleep(options['refresh_rate'])
350 else:
351 doAction()
352 except KeyboardInterrupt:
f7e63802 353 print('\n[Keyboard Interrupt]', file=sys.stderr)
05b85831
HN
354 pass
355
356class NoSuchActionError(Exception):
0ea01db7 357 pass
358
359class NoSuchAction(Action):
360 def __call__(self, twitter, options):
05b85831 361 raise NoSuchActionError("No such action: %s" %(options['action']))
0ea01db7 362
363class StatusAction(Action):
364 def __call__(self, twitter, options):
39a6f562 365 statuses = self.getStatuses(twitter, options)
87be041f 366 sf = get_formatter('status', options)
0ea01db7 367 for status in statuses:
39a6f562 368 statusStr = sf(status, options)
0ea01db7 369 if statusStr.strip():
862cce81 370 printNicely(statusStr)
1c11e6d7 371
87be041f
WD
372class SearchAction(Action):
373 def __call__(self, twitter, options):
374 # We need to be pointing at search.twitter.com to work, and it is less
375 # tangly to do it here than in the main()
376 twitter.domain="search.twitter.com"
e15fbef3 377 twitter.uriparts=()
fd2bc885
WD
378 # We need to bypass the TwitterCall parameter encoding, so we
379 # don't encode the plus sign, so we have to encode it ourselves
5a77e17a 380 query_string = "+".join(
7fd7f08a 381 [quote(term)
5a77e17a 382 for term in options['extra_args']])
fd2bc885 383
e15fbef3 384 results = twitter.search(q=query_string)['results']
87be041f
WD
385 f = get_formatter('search', options)
386 for result in results:
387 resultStr = f(result, options)
388 if resultStr.strip():
389 printNicely(resultStr)
a8b5ad3e 390
1c11e6d7 391class AdminAction(Action):
efa0ba89 392 def __call__(self, twitter, options):
ec894371 393 if not (options['extra_args'] and options['extra_args'][0]):
e02facc9 394 raise TwitterError("You need to specify a user (screen name)")
87be041f 395 af = get_formatter('admin', options)
e02facc9
MV
396 try:
397 user = self.getUser(twitter, options['extra_args'][0])
f7e63802
MV
398 except TwitterError as e:
399 print("There was a problem following or leaving the specified user.")
400 print("You may be trying to follow a user you are already following;")
401 print("Leaving a user you are not currently following;")
402 print("Or the user may not exist.")
403 print("Sorry.")
404 print()
405 print(e)
e02facc9 406 else:
862cce81 407 printNicely(af(options['action'], user))
efa0ba89 408
69851f4c
AL
409class ListsAction(StatusAction):
410 def getStatuses(self, twitter, options):
3c2b5e3e
MV
411 if not options['extra_args']:
412 raise TwitterError("Please provide a user to query for lists")
413
414 screen_name = options['extra_args'][0]
415
416 if not options['extra_args'][1:]:
417 lists = twitter.user.lists(user=screen_name)['lists']
418 if not lists:
419 printNicely("This user has no lists.")
420 for list in lists:
69851f4c
AL
421 lf = get_formatter('lists', options)
422 printNicely(lf(list))
3c2b5e3e
MV
423 return []
424 else:
425 return reversed(twitter.user.lists.list.statuses(
426 user=screen_name, list=options['extra_args'][1]))
427
428
429class MyListsAction(ListsAction):
430 def getStatuses(self, twitter, options):
431 screen_name = twitter.account.verify_credentials()['screen_name']
432 options['extra_args'].insert(0, screen_name)
433 return ListsAction.getStatuses(self, twitter, options)
434
69851f4c 435
0ea01db7 436class FriendsAction(StatusAction):
39a6f562
MV
437 def getStatuses(self, twitter, options):
438 return reversed(twitter.statuses.friends_timeline(count=options["length"]))
efa0ba89 439
0ea01db7 440class PublicAction(StatusAction):
39a6f562
MV
441 def getStatuses(self, twitter, options):
442 return reversed(twitter.statuses.public_timeline(count=options["length"]))
0ea01db7 443
9a9f7ae7 444class RepliesAction(StatusAction):
39a6f562
MV
445 def getStatuses(self, twitter, options):
446 return reversed(twitter.statuses.replies(count=options["length"]))
9a9f7ae7 447
1c11e6d7 448class FollowAction(AdminAction):
efa0ba89 449 def getUser(self, twitter, user):
70955aae 450 return twitter.friendships.create(id=user)
efa0ba89 451
1c11e6d7 452class LeaveAction(AdminAction):
efa0ba89 453 def getUser(self, twitter, user):
70955aae 454 return twitter.friendships.destroy(id=user)
1c11e6d7 455
0ea01db7 456class SetStatusAction(Action):
457 def __call__(self, twitter, options):
a87e8a6c 458 statusTxt = (" ".join(options['extra_args'])
05b85831 459 if options['extra_args']
f7e63802 460 else str(input("message: ")))
24e8d939 461 twitter.statuses.update(status=statusTxt)
5251ea48 462
05b85831 463class TwitterShell(Action):
ec894371
MV
464
465 def render_prompt(self, prompt):
05b85831
HN
466 '''Parses the `prompt` string and returns the rendered version'''
467 prompt = prompt.strip("'").replace("\\'","'")
468 for colour in ansi.COLOURS_NAMED:
469 if '[%s]' %(colour) in prompt:
470 prompt = prompt.replace(
a8b5ad3e 471 '[%s]' %(colour), ansi.cmdColourNamed(colour))
05b85831
HN
472 prompt = prompt.replace('[R]', ansi.cmdReset())
473 return prompt
a8b5ad3e 474
05b85831
HN
475 def __call__(self, twitter, options):
476 prompt = self.render_prompt(options.get('prompt', 'twitter> '))
477 while True:
ec894371 478 options['action'] = ""
05b85831 479 try:
f7e63802 480 args = input(prompt).split()
05b85831
HN
481 parse_args(args, options)
482 if not options['action']:
483 continue
484 elif options['action'] == 'exit':
485 raise SystemExit(0)
486 elif options['action'] == 'shell':
f7e63802 487 print('Sorry Xzibit does not work here!', file=sys.stderr)
05b85831
HN
488 continue
489 elif options['action'] == 'help':
f7e63802 490 print('''\ntwitter> `action`\n
a8b5ad3e 491 The Shell Accepts all the command line actions along with:
05b85831 492
a8b5ad3e 493 exit Leave the twitter shell (^D may also be used)
05b85831 494
f7e63802 495 Full CMD Line help is appended below for your convinience.''', file=sys.stderr)
05b85831
HN
496 Action()(twitter, options)
497 options['action'] = ''
f7e63802
MV
498 except NoSuchActionError as e:
499 print(e, file=sys.stderr)
05b85831 500 except KeyboardInterrupt:
f7e63802 501 print('\n[Keyboard Interrupt]', file=sys.stderr)
05b85831 502 except EOFError:
f7e63802 503 print(file=sys.stderr)
05b85831
HN
504 leaving = self.ask(subject='Leave')
505 if not leaving:
f7e63802 506 print('Excellent!', file=sys.stderr)
05b85831
HN
507 else:
508 raise SystemExit(0)
509
a5e40197
MV
510class PythonPromptAction(Action):
511 def __call__(self, twitter, options):
512 try:
513 while True:
514 smrt_input(globals(), locals())
515 except EOFError:
516 pass
517
45688301
MV
518class HelpAction(Action):
519 def __call__(self, twitter, options):
f7e63802 520 print(__doc__)
45688301 521
086fc282
MV
522class DoNothingAction(Action):
523 def __call__(self, twitter, options):
524 pass
525
5251ea48 526actions = {
086fc282 527 'authorize' : DoNothingAction,
05b85831
HN
528 'follow' : FollowAction,
529 'friends' : FriendsAction,
69851f4c 530 'list' : ListsAction,
3c2b5e3e 531 'mylist' : MyListsAction,
05b85831
HN
532 'help' : HelpAction,
533 'leave' : LeaveAction,
534 'public' : PublicAction,
a5e40197 535 'pyprompt' : PythonPromptAction,
05b85831 536 'replies' : RepliesAction,
87be041f 537 'search' : SearchAction,
05b85831
HN
538 'set' : SetStatusAction,
539 'shell' : TwitterShell,
5251ea48 540}
541
21e3bd23 542def loadConfig(filename):
327e556b 543 options = dict(OPTIONS)
21e3bd23 544 if os.path.exists(filename):
545 cp = SafeConfigParser()
546 cp.read([filename])
086fc282 547 for option in ('format', 'prompt'):
327e556b
MV
548 if cp.has_option('twitter', option):
549 options[option] = cp.get('twitter', option)
550 return options
ae1d86aa 551
327e556b
MV
552def main(args=sys.argv[1:]):
553 arg_options = {}
44405280 554 try:
327e556b 555 parse_args(args, arg_options)
f7e63802
MV
556 except GetoptError as e:
557 print("I can't do that, %s." %(e), file=sys.stderr)
558 print(file=sys.stderr)
05b85831 559 raise SystemExit(1)
21e3bd23 560
7f22c021 561 config_path = os.path.expanduser(
327e556b 562 arg_options.get('config_filename') or OPTIONS.get('config_filename'))
7f22c021 563 config_options = loadConfig(config_path)
efa0ba89 564
327e556b
MV
565 # Apply the various options in order, the most important applied last.
566 # Defaults first, then what's read from config file, then command-line
567 # arguments.
568 options = dict(OPTIONS)
569 for d in config_options, arg_options:
f7e63802 570 for k,v in list(d.items()):
327e556b 571 if v: options[k] = v
05b85831 572
e02facc9
MV
573 if options['refresh'] and options['action'] not in (
574 'friends', 'public', 'replies'):
f7e63802
MV
575 print("You can only refresh the friends, public, or replies actions.", file=sys.stderr)
576 print("Use 'twitter -h' for help.", file=sys.stderr)
086fc282 577 return 1
05b85831 578
7f22c021
MV
579 oauth_filename = os.path.expanduser(options['oauth_filename'])
580
086fc282 581 if (options['action'] == 'authorize'
7f22c021 582 or not os.path.exists(oauth_filename)):
1b31d642
MV
583 oauth_dance(
584 "the Command-Line Tool", CONSUMER_KEY, CONSUMER_SECRET,
585 options['oauth_filename'])
6c527e72 586
7f22c021 587 oauth_token, oauth_token_secret = read_token_file(oauth_filename)
8ddd8500 588
9a148ed1 589 twitter = Twitter(
086fc282
MV
590 auth=OAuth(
591 oauth_token, oauth_token_secret, CONSUMER_KEY, CONSUMER_SECRET),
1cc9ab0b 592 secure=options['secure'],
9c07e547
MV
593 api_version='1',
594 domain='api.twitter.com')
086fc282 595
5251ea48 596 try:
05b85831 597 Action()(twitter, options)
f7e63802
MV
598 except NoSuchActionError as e:
599 print(e, file=sys.stderr)
05b85831 600 raise SystemExit(1)
f7e63802
MV
601 except TwitterError as e:
602 print(str(e), file=sys.stderr)
603 print("Use 'twitter -h' for help.", file=sys.stderr)
05b85831 604 raise SystemExit(1)