Implement composing tweets using an editor

fixes #90
This commit is contained in:
Ivan Habunek 2019-08-22 17:10:37 +02:00
parent 840b2fd476
commit d21cad892c
No known key found for this signature in database
GPG Key ID: CDBD63C43A30BB95
3 changed files with 66 additions and 11 deletions

View File

@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
import sys
from toot import api, config
from toot.auth import login_interactive, login_browser_interactive, create_app_interactive
from toot.exceptions import ConsoleError, NotFoundError
from toot.output import (print_out, print_instance, print_account,
print_search_results, print_timeline, print_notifications)
from toot.utils import assert_domain_exists, multiline_input, EOF_KEY
from toot.utils import assert_domain_exists, editor_input, multiline_input, EOF_KEY
def get_timeline_generator(app, user, args):
@ -76,9 +78,17 @@ def curses(app, user, args):
def post(app, user, args):
# TODO: this might be achievable, explore options
if args.editor and not sys.stdin.isatty():
raise ConsoleError("Cannot run editor if not in tty.")
if args.media and len(args.media) > 4:
raise ConsoleError("Cannot attach more than 4 files.")
# Read any text that might be piped to stdin
if not args.text and not sys.stdin.isatty():
args.text = sys.stdin.read().rstrip()
if args.media:
media = [_do_upload(app, user, file) for file in args.media]
media_ids = [m["id"] for m in media]
@ -89,7 +99,9 @@ def post(app, user, args):
if media and not args.text:
args.text = "\n".join(m['text_url'] for m in media)
if not args.text:
if args.editor:
args.text = editor_input(args.editor, args.text)
elif not args.text:
print_out("Write or paste your toot. Press <yellow>{}</yellow> to post it.".format(EOF_KEY))
args.text = multiline_input()

View File

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
import os
import sys
import logging
import os
import shutil
import sys
from argparse import ArgumentParser, FileType, ArgumentTypeError
from collections import namedtuple
@ -39,6 +40,21 @@ def timeline_count(value):
return n
def editor(value):
if not value:
raise ArgumentTypeError(
"Editor not specified in --editor option and $EDITOR environment "
"variable not set."
)
# Check editor executable exists
exe = shutil.which(value)
if not exe:
raise ArgumentTypeError(f"Editor `{value}` not found")
return exe
Command = namedtuple("Command", ["name", "description", "require_auth", "arguments"])
@ -298,6 +314,13 @@ POST_COMMANDS = [
"type": language,
"help": "ISO 639-2 language code of the toot, to skip automatic detection",
}),
(["-e", "--editor"], {
"type": editor,
"nargs": "?",
"const": os.getenv("EDITOR", ""), # option given without value
"help": "Specify an editor to compose your toot, "
"defaults to editor defined in $EDITOR env variable.",
}),
],
require_auth=True,
),
@ -500,12 +523,6 @@ def main():
filename = os.getenv("TOOT_LOG_FILE")
logging.basicConfig(level=logging.DEBUG, filename=filename)
# If something is piped in, append it to commandline arguments
if not sys.stdin.isatty():
stdin = sys.stdin.read()
if stdin:
sys.argv.append(stdin)
command_name = sys.argv[1] if len(sys.argv) > 1 else None
args = sys.argv[2:]
@ -519,5 +536,5 @@ def main():
except (ConsoleError, ApiError) as e:
print_err(str(e))
sys.exit(1)
except KeyboardInterrupt as e:
except KeyboardInterrupt:
pass

View File

@ -3,6 +3,8 @@
import os
import re
import socket
import subprocess
import tempfile
import unicodedata
import warnings
@ -88,3 +90,27 @@ def multiline_input():
break
return "\n".join(lines).strip()
EDITOR_INPUT_INSTRUCTIONS = """
# Please enter your toot. Lines starting with '#' will be ignored, and an empty
# message aborts the post.
"""
def editor_input(editor, initial_text):
"""Lets user input text using an editor."""
initial_text = (initial_text or "") + EDITOR_INPUT_INSTRUCTIONS
with tempfile.NamedTemporaryFile() as f:
f.write(initial_text.encode())
f.flush()
subprocess.run([editor, f.name])
f.seek(0)
text = f.read().decode()
lines = text.strip().splitlines()
lines = (l for l in lines if not l.startswith("#"))
return "\n".join(lines)