Conflicts:
	forthlift.py
This commit is contained in:
Johann Dreo 2016-05-17 18:43:15 +02:00
commit c3ed359cf2
3 changed files with 236 additions and 101 deletions

View file

@ -3,10 +3,17 @@
import sys
import locale
import argparse
import datetime
import ConfigParser
import tweepy
class AppKeyError(Exception):
def __init__(self, msg):
self.msg = msg
def write(data, stream = sys.stdout):
"""
Write "data" on the given stream, then flush, silently handling broken pipes.
@ -38,24 +45,24 @@ def readline( stream_in ):
raise StopIteration
def setup_adorn( data, asked ):
assert(asked.adorn)
def setup_counter( data, asked ):
assert(asked.counter)
# unroll everything
data = list(data)
nb = len(data)
# " int/int"
adorn_size = len(str(nb))*2 + 1 + 1
counter_size = len(str(nb))*2 + 1 + 1
for i,line in enumerate(data):
adorn = " %i/%i" % (i+1,nb)
curmax = asked.max_len - len(adorn)
counter = " %i/%i" % (i+1,nb)
curmax = asked.max_len - len(counter)
if len(line) > curmax:
if asked.ignore:
data[i] = line
elif asked.trim:
data[i] = line[:curmax]
data[i] = data[i] + adorn
data[i] = data[i] + counter
return data
@ -72,8 +79,8 @@ def setup_hem(data, asked):
def setup( data, asked ):
if asked.adorn:
f = setup_adorn
if asked.counter:
f = setup_counter
else:
f = setup_hem
@ -81,21 +88,33 @@ def setup( data, asked ):
yield line
def operate( func, *args ):
for line in func( readline(sys.stdin), *args ):
write(line)
#
# STDOUT API
#
def on_stdout( data, asked, endline="\n" ):
lines = setup(data, asked)
# You can do something on the whole set of lines if you want.
if asked.chain:
prefix = " "
else:
prefix = ""
for i,line in enumerate(lines):
if endline:
if line[-1] != endline:
line += endline
yield prefix*i + line
if asked.independent:
yield line
else:
yield i + " " + line
#
# TWITTER API
#
def on_twitter( data, api, asked, endline="\n" ):
lines = setup(data, asked)
@ -118,114 +137,195 @@ def on_twitter( data, api, asked, endline="\n" ):
# API.update_status(status[, in_reply_to_status_id][, lat][, long][, source][, place_id])
status = api.update_status(line, prev_status_id)
if asked.chain:
if asked.independent:
prev_status_id = None
else:
prev_status_id = status.id
yield status.text + endline
def operate( func, *args ):
for line in func( readline(sys.stdin), *args ):
write(line)
usage = "Post text read on the standard input to a website or an API."
apis =["stdout", "twitter"] # TODO "http_post", "http_get",
parser = argparse.ArgumentParser( description=usage,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-a", "--api", choices=apis, default="stdout",
help="Name of the API to use.")
# Generic options
parser.add_argument("-m", "--max-len", metavar="MAXLEN", type=int, default=140,
help="Maximum number of characters in the lines.")
parser.add_argument("-i", "--ignore", action="store_true",
help="Ignore lines that are longer than MAXLEN")
parser.add_argument("-t", "--trim", action="store_true",
help="Trim down lines that are longer than MAXLEN.")
parser.add_argument("-d", "--adorn", action="store_true",
help="Add a counter of the form \" x/N\" at the end of the lines, \
with N being the number of lines read and x the current index of the line. \
NOTE: this necessitate to read all the input before processing it.")
parser.add_argument("-q", "--quiet", action="store_true",
help="Do not print errors and warnings on the standard error output.")
# TODO option: rate at which to post lines
# API-dependant options
parser.add_argument("-c", "--chain", action="store_true",
help="Chained actions. Whatever that means depends on the chosen API.")
# Twitter
parser.add_argument("--twitter-images", metavar="FILENAME(S)", nargs="+", type=str,
help="Upload each given image files along with the corresponding tweets in sequence. If there are more images than tweets, they are silently ignored.")
asked = parser.parse_args()
# Consistency checks
if asked.ignore and asked.trim:
if not asked.quiet:
sys.stderr.write("WARNING: asking to trim AND to ignore is not logical, I will ignore.")
assert( not (asked.ignore and asked.trim) )
if asked.twitter_images:
if not asked.api == "twitter":
if not asked.quiet:
sys.stderr.write("WARNING: asking to upload images on twitter while not using the twitter API is not logical, I will ignore.")
assert( not (asked.twitter_images and not asked.api=="twitter") )
else: # Test readable images
cannot=[]
for img in asked.twitter_images:
try:
with open(img) as f:
f.read()
except OSError:
cannot.append(img)
if cannot:
print("Cannot open the following image files, I will not continue: ")
for img in cannot:
print(img)
sys.exit(5) # I/O Error
# APIs
if asked.api == "stdout":
operate( on_stdout, asked )
elif asked.api == "twitter":
import ConfigParser
def setup_twitter(configfile="twitter.conf"):
config = ConfigParser.RawConfigParser()
config.read('twitter.conf')
consumer_key = config.get("Auth","key")
consumer_secret = config.get("Auth","key_secret")
# Authenticate the application.
config.read(configfile)
try:
verifier_code = config.get("Auth","code")
except:
access_token = config.get("Auth","token")
access_token_secret = config.get("Auth","token_secret")
if not config.has_section("App"):
raise AppKeyError("ERROR: did not found application keys, ask your distribution maintainer or get keys from Twitter first.")
auth = tweepy.OAuthHandler(consumer_key, consumer_secret, "https://api.twitter.com/1.1/")
auth.set_access_token(access_token, access_token_secret)
app_key = config.get("App","app_key")
app_secret = config.get("App","app_key_secret")
auth = tweepy.OAuthHandler(app_key, app_secret)
# Authenticate the user.
auth_url = auth.get_authorization_url()
print "Copy and paste this URL in your browser while your are logged in the twitter account where you want to post."
print "Authorization URL: " + auth_url
print "Then paste the Personal Identification Number given by Twitter:"
verifier = raw_input('PIN: ').strip()
auth.get_access_token(verifier)
# print 'ACCESS_KEY = "%s"' % auth.access_token.key
# print 'ACCESS_SECRET = "%s"' % auth.access_token.secret
# Authenticate and get the user name.
auth.set_access_token(auth.access_token.key, auth.access_token.secret)
api = tweepy.API(auth)
username = api.me().name
print "Authentication successful, ready to post to account: " + username
operate( on_twitter, api, asked )
# Save authentication tokens.
if not config.has_section("Info"):
config.add_section('Info')
config.set('Info','account', username)
config.set('Info','auth_date', datetime.datetime.utcnow().isoformat())
if not config.has_section("Auth"):
config.add_section('Auth')
config.set('Auth', 'local_token', auth.access_token.key)
config.set('Auth', 'local_token_secret', auth.access_token.secret)
# Writing our configuration file to 'example.cfg'
with open(configfile, 'wb') as fd:
config.write(fd)
#
# CLI
#
if __name__=="__main__":
errors = {"NO_ERROR":0, "UNKNOWN_ERROR":1, "NO_SETUP_NEEDED":2, "NO_APP_KEY":10}
usage = "Post text read on the standard input to a website or an API."
apis =["stdout", "twitter"] # TODO "http_post", "http_get",
parser = argparse.ArgumentParser( description=usage,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-a", "--api", choices=apis, default="stdout",
help="Name of the API to use.")
# Generic options
parser.add_argument("-m", "--max-len", metavar="MAXLEN", type=int, default=140,
help="Maximum number of characters in the lines.")
parser.add_argument("-i", "--ignore", action="store_true",
help="Ignore lines that are longer than MAXLEN")
parser.add_argument("-t", "--trim", action="store_true",
help="Trim down lines that are longer than MAXLEN.")
parser.add_argument("-c", "--counter", action="store_true",
help="Add a counter of the form \" x/N\" at the end of the lines, \
with N being the number of lines read and x the current index of the line. \
NOTE: this necessitate to read all the input before processing it.")
parser.add_argument("-q", "--quiet", action="store_true",
help="Do not print errors and warnings on the standard error output.")
# TODO option: rate at which to post lines
# API-dependent options
parser.add_argument("-s", "--setup", action="store_true",
help="Setup the selected API (e.g. authenticate and get authorization to post).")
parser.add_argument("-d", "--independent", action="store_true",
help="Post each item independently from the previous one. \
The behaviour of this option depends on the selected API. \
For example on Twitter: do not post a line as an answer to the previous one but as a new tweet.")
# Twitter
parser.add_argument("--twitter-images", metavar="FILENAME(S)", nargs="+", type=str,
help="Upload each given image files along with the corresponding tweets in the sequence. If there are more images than tweets, they are silently ignored.")
asked = parser.parse_args()
# Setup
if asked.setup:
configfile = asked.api+".conf"
if asked.api == "twitter":
try:
setup_twitter(configfile)
except AppKeyError as e:
if not asked.quiet:
sys.stderr.write(e.msg)
sys.exit(errors["NO_APP_KEY"])
except:
print "Unexpected error:", sys.exc_info()[0]
raise
else:
sys.exit(errors["NO_ERROR"])
else: # other API
if not asked.quiet:
sys.stderr.write("This API does not need setup.")
sys.exit(errors["NO_SETUP_NEEDED"])
# Consistency checks
if asked.ignore and asked.trim:
if not asked.quiet:
sys.stderr.write("WARNING: asking to trim AND to ignore is not logical, I will ignore.")
assert( not (asked.ignore and asked.trim) )
if asked.twitter_images:
if not asked.api == "twitter":
if not asked.quiet:
sys.stderr.write("WARNING: asking to upload images on twitter while not using the twitter API is not logical, I will ignore.")
assert( not (asked.twitter_images and not asked.api=="twitter") )
else: # Test readable images
cannot=[]
for img in asked.twitter_images:
try:
with open(img) as f:
f.read()
except OSError:
cannot.append(img)
if cannot:
print("Cannot open the following image files, I will not continue: ")
for img in cannot:
print(img)
sys.exit(5) # I/O Error
# APIs
if asked.api == "stdout":
operate( on_stdout, asked )
elif asked.api == "twitter":
# Authenticate
config = ConfigParser.RawConfigParser()
config.read('twitter.conf')
app_key = config.get("App","app_key")
app_secret = config.get("App","app_key_secret")
try:
verifier_code = config.get("Auth","code")
except:
access_token = config.get("Auth","local_token")
access_token_secret = config.get("Auth","local_token_secret")
auth = tweepy.OAuthHandler(app_key, app_secret, "https://api.twitter.com/1.1/")
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
# Post
operate( on_twitter, api, asked )