X-Git-Url: https://jfr.im/git/z_archive/twitter.git/blobdiff_plain/e896170126f9b38c3d04c0051fcba34f0a99f6c1..ba986560b431abad58c9214a76b4692d88d4158f:/twitter/cmdline.py diff --git a/twitter/cmdline.py b/twitter/cmdline.py index 54a994e..aafe262 100644 --- a/twitter/cmdline.py +++ b/twitter/cmdline.py @@ -16,10 +16,13 @@ ACTIONS: mylist get list of your lists; give a list name to get tweets from that list public get latest public tweets + pyprompt start a Python prompt for interacting with the twitter + object directly replies get latest replies to you search search twitter (Beware: octothorpe, escape it) set set your twitter status shell login to the twitter shell + rate get your current rate limit status (remaining API reqs) OPTIONS: @@ -59,6 +62,8 @@ prompt: '> home directory. """ +from __future__ import print_function + CONSUMER_KEY='uS6hO2sV6tDKIOeVjhnFnQ' CONSUMER_SECRET='MEYTOS97VvlHX7K1rwHPEqVpTSqZ71HtvoK4sVuYk' @@ -68,15 +73,25 @@ from getopt import gnu_getopt as getopt, GetoptError from getpass import getpass import re import os.path -from configparser import SafeConfigParser +import locale +import string + +try: + from ConfigParser import SafeConfigParser +except ImportError: + from configparser import ConfigParser as SafeConfigParser import datetime -from urllib.parse import quote +try: + from urllib.parse import quote +except ImportError: + from urllib2 import quote import webbrowser from .api import Twitter, TwitterError from .oauth import OAuth, write_token_file, read_token_file from .oauth_dance import oauth_dance from . import ansi +from .util import smrt_input, printNicely OPTIONS = { 'action': 'friends', @@ -91,6 +106,7 @@ OPTIONS = { 'datestamp': False, 'extra_args': [], 'secure': True, + 'invert_split': False, } def parse_args(args, options): @@ -99,6 +115,8 @@ def parse_args(args, options): 'datestamp', 'no-ssl'] short_opts = "e:p:f:h?rR:c:l:td" opts, extra_args = getopt(args, short_opts, long_opts) + extra_args = [arg.decode(locale.getpreferredencoding()) + for arg in extra_args] for opt, arg in opts: if opt in ('-f', '--format'): @@ -335,6 +353,7 @@ class Action(object): if (options['refresh'] and isinstance(action, StatusAction)): while True: doAction() + sys.stdout.flush() time.sleep(options['refresh_rate']) else: doAction() @@ -349,10 +368,6 @@ class NoSuchAction(Action): def __call__(self, twitter, options): raise NoSuchActionError("No such action: %s" %(options['action'])) -def printNicely(string): - sys.stdout.buffer.write(string.encode('utf8')) - print() - class StatusAction(Action): def __call__(self, twitter, options): statuses = self.getStatuses(twitter, options) @@ -371,7 +386,7 @@ class SearchAction(Action): # We need to bypass the TwitterCall parameter encoding, so we # don't encode the plus sign, so we have to encode it ourselves query_string = "+".join( - [quote(term.decode(get_term_encoding())) + [quote(term) for term in options['extra_args']]) results = twitter.search(q=query_string)['results'] @@ -448,11 +463,38 @@ class LeaveAction(AdminAction): class SetStatusAction(Action): def __call__(self, twitter, options): - statusTxt = (" ".join(options['extra_args']).decode(get_term_encoding()) + statusTxt = (" ".join(options['extra_args']) if options['extra_args'] else str(input("message: "))) - status = (statusTxt.encode('utf8', 'replace')) - twitter.statuses.update(status=status) + replies = [] + ptr = re.compile("@[\w_]+") + while statusTxt: + s = ptr.match(statusTxt) + if s and s.start() == 0: + replies.append(statusTxt[s.start():s.end()]) + statusTxt = statusTxt[s.end()+1:] + else: + break + replies = " ".join(replies) + if len(replies) >= 140: + # just go back + statusTxt = replies + replies = "" + + splitted = [] + while statusTxt: + limit = 140 - len(replies) + if len(statusTxt) > limit: + end = string.rfind(statusTxt, ' ', 0, limit) + else: + end = limit + splitted.append(" ".join((replies,statusTxt[:end]))) + statusTxt = statusTxt[end:] + + if options['invert_split']: + splitted.reverse() + for status in splitted: + twitter.statuses.update(status=status) class TwitterShell(Action): @@ -501,6 +543,14 @@ class TwitterShell(Action): else: raise SystemExit(0) +class PythonPromptAction(Action): + def __call__(self, twitter, options): + try: + while True: + smrt_input(globals(), locals()) + except EOFError: + pass + class HelpAction(Action): def __call__(self, twitter, options): print(__doc__) @@ -509,6 +559,13 @@ class DoNothingAction(Action): def __call__(self, twitter, options): pass +class RateLimitStatus(Action): + def __call__(self, twitter, options): + rate = twitter.account.rate_limit_status() + print("Remaining API requests: %s / %s (hourly limit)" % (rate['remaining_hits'], rate['hourly_limit'])) + print("Next reset in %ss (%s)" % (int(rate['reset_time_in_seconds']-time.time()), + time.asctime(time.localtime(rate['reset_time_in_seconds'])))) + actions = { 'authorize' : DoNothingAction, 'follow' : FollowAction, @@ -518,10 +575,12 @@ actions = { 'help' : HelpAction, 'leave' : LeaveAction, 'public' : PublicAction, + 'pyprompt' : PythonPromptAction, 'replies' : RepliesAction, 'search' : SearchAction, 'set' : SetStatusAction, 'shell' : TwitterShell, + 'rate' : RateLimitStatus, } def loadConfig(filename): @@ -532,6 +591,10 @@ def loadConfig(filename): for option in ('format', 'prompt'): if cp.has_option('twitter', option): options[option] = cp.get('twitter', option) + # process booleans + for option in ('invert_split',): + if cp.has_option('twitter', option ): + options[option] = cp.getboolean('twitter', option) return options def main(args=sys.argv[1:]):