示例#1
0
def get_timeline_generator(app, user, args):
    # Make sure tag, list and public are not used simultaneously
    if len([arg for arg in [args.tag, args.list, args.public] if arg]) > 1:
        raise ConsoleError(
            "Only one of --public, --tag, or --list can be used at one time.")

    if args.local and not (args.public or args.tag):
        raise ConsoleError(
            "The --local option is only valid alongside --public or --tag.")

    if args.instance and not (args.public or args.tag):
        raise ConsoleError(
            "The --instance option is only valid alongside --public or --tag.")

    if args.public:
        instance = args.instance or app.instance
        return api.public_timeline_generator(instance,
                                             local=args.local,
                                             limit=args.count)
    elif args.tag:
        instance = args.instance or app.instance
        return api.tag_timeline_generator(instance,
                                          args.tag,
                                          local=args.local,
                                          limit=args.count)
    elif args.list:
        return api.timeline_list_generator(app,
                                           user,
                                           args.list,
                                           limit=args.count)
    else:
        return api.home_timeline_generator(app, user, limit=args.count)
示例#2
0
文件: commands.py 项目: mugcake/toot
def curses(app, user, args):
    from toot.ui.app import TimelineApp

    # Make sure tag, list and public are not used simultaneously
    if len([arg for arg in [args.tag, args.public] if arg]) > 1:
        raise ConsoleError(
            "Only one of --public or --tag can be used at one time.")

    if args.local and not (args.public or args.tag):
        raise ConsoleError(
            "The --local option is only valid alongside --public or --tag.")

    if not args.public and (not app or not user):
        raise ConsoleError("You must be logged in to view the home timeline.")

    if args.public:
        instance = args.instance or app.instance
        generator = api.public_timeline_generator(instance, local=args.local)
    elif args.tag:
        generator = api.tag_timeline_generator(app,
                                               user,
                                               args.tag,
                                               local=args.local)
    else:
        generator = api.home_timeline_generator(app, user)

    TimelineApp(generator).run()
示例#3
0
文件: commands.py 项目: mugcake/toot
def instance(app, user, args):
    name = args.instance or (app and app.instance)
    if not name:
        raise ConsoleError("Please specify instance name.")

    assert_domain_exists(name)

    try:
        instance = api.get_instance(name, args.scheme)
        print_instance(instance)
    except NotFoundError:
        raise ConsoleError(
            "Instance not found at {}.\n"
            "The given domain probably does not host a Mastodon instance.".
            format(name))
示例#4
0
文件: app.py 项目: l-n-s/toot
    def setup_windows(self):
        screen_height, screen_width = self.stdscr.getmaxyx()

        if screen_width < 60:
            raise ConsoleError(
                "Terminal screen is too narrow, toot curses requires at least 60 columns to display properly."
            )

        header_height = 1
        footer_height = 2
        footer_top = screen_height - footer_height

        left_width = max(min(screen_width // 3, 60), 30)
        main_height = screen_height - header_height - footer_height
        main_width = screen_width - left_width

        self.header = HeaderWindow(self.stdscr, header_height, screen_width, 0,
                                   0)
        self.footer = FooterWindow(self.stdscr, footer_height, screen_width,
                                   footer_top, 0)
        self.left = StatusListWindow(self.stdscr, main_height, left_width,
                                     header_height, 0)
        self.right = StatusDetailWindow(self.stdscr, main_height, main_width,
                                        header_height, left_width)

        self.help_modal = HelpModal(self.stdscr,
                                    resize_callback=self.on_resize)
示例#5
0
文件: console.py 项目: mugcake/toot
def run_command(app, user, name, args):
    command = next((c for c in COMMANDS if c.name == name), None)

    if not command:
        print_err("Unknown command '{}'\n".format(name))
        print_usage()
        return

    parser = get_argument_parser(name, command)
    parsed_args = parser.parse_args(args)

    # Override the active account if 'using' option is given
    if command.require_auth and parsed_args.using:
        user, app = config.get_user_app(parsed_args.using)
        if not user or not app:
            raise ConsoleError("User '{}' not found".format(parsed_args.using))

    if command.require_auth and (not user or not app):
        print_err("This command requires that you are logged in.")
        print_err("Please run `toot login` first.")
        return

    fn = commands.__dict__.get(name)

    if not fn:
        raise NotImplementedError("Command '{}' does not have an implementation.".format(name))

    return fn(app, user, parsed_args)
示例#6
0
文件: commands.py 项目: mugcake/toot
def post(app, user, args):
    if args.media:
        media = _do_upload(app, user, args.media)
        media_ids = [media['id']]
    else:
        media = None
        media_ids = None

    if media and not args.text:
        args.text = media['text_url']

    if not args.text:
        print_out(
            "Write or paste your toot. Press <yellow>{}</yellow> to post it.".
            format(EOF_KEY))
        args.text = multiline_input()

    if not args.text:
        raise ConsoleError("You must specify either text or media to post.")

    response = api.post_status(
        app,
        user,
        args.text,
        visibility=args.visibility,
        media_ids=media_ids,
        sensitive=args.sensitive,
        spoiler_text=args.spoiler_text,
        in_reply_to_id=args.reply_to,
    )

    print_out("Toot posted: <green>{}</green>".format(response.get('url')))
示例#7
0
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]
    else:
        media = None
        media_ids = None

    if media and not args.text:
        args.text = "\n".join(m['text_url'] for m in media)

    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()

    if not args.text:
        raise ConsoleError("You must specify either text or media to post.")

    response = api.post_status(
        app,
        user,
        args.text,
        visibility=args.visibility,
        media_ids=media_ids,
        sensitive=args.sensitive,
        spoiler_text=args.spoiler_text,
        in_reply_to_id=args.reply_to,
        language=args.language,
    )

    print_out("Toot posted: <green>{}</green>".format(response.get('url')))
示例#8
0
def load_user(user_id, throw=False):
    config = load_config()

    if user_id in config['users']:
        return User(**config['users'][user_id])

    if throw:
        raise ConsoleError("User '{}' not found".format(user_id))
示例#9
0
文件: commands.py 项目: mugcake/toot
def _find_account(app, user, account_name):
    """For a given account name, returns the Account object.

    Raises an exception if not found.
    """
    if not account_name:
        raise ConsoleError("Empty account name given")

    accounts = api.search_accounts(app, user, account_name)

    if account_name[0] == "@":
        account_name = account_name[1:]

    for account in accounts:
        if account['acct'] == account_name:
            return account

    raise ConsoleError("Account not found")
示例#10
0
def curses(app, user, args):
    from toot.ui.app import TimelineApp

    if not args.public and (not app or not user):
        raise ConsoleError("You must be logged in to view the home timeline.")

    if args.public:
        instance = args.instance or app.instance
        generator = api.public_timeline_generator(instance)
    else:
        generator = api.home_timeline_generator(app, user)

    TimelineApp(generator).run()
示例#11
0
def timeline(app, user, args):
    # Make sure tag, list and public are not used simultaneously
    if len([arg for arg in [args.tag, args.list, args.public] if arg]) > 1:
        raise ConsoleError(
            "Only one of --public, --tag, or --list can be used at one time.")

    if args.local and not (args.public or args.tag):
        raise ConsoleError(
            "The --local option is only valid alongside --public or --tag.")

    if args.public:
        gen = api.public_timeline_generator(app.instance,
                                            local=args.local,
                                            limit=args.count)
    elif args.tag:
        gen = api.tag_timeline_generator(app,
                                         user,
                                         args.tag,
                                         local=args.local,
                                         limit=args.count)
    elif args.list:
        gen = api.timeline_list_genertor(app, user, args.list)
    else:
        gen = api.home_timeline_generator(app, user, limit=args.count)

    while (True):
        items = next(gen)

        if args.reverse:
            items = reversed(items)

        print_timeline(items)

        if args.once:
            break

        char = input("\nContinue? [Y/n] ")
        if char.lower() == "n":
            break
示例#12
0
文件: commands.py 项目: mugcake/toot
def timeline(app, user, args):
    # Make sure tag, list and public are not used simultaneously
    if len([arg for arg in [args.tag, args.list, args.public] if arg]) > 1:
        raise ConsoleError(
            "Only one of --public, --tag, or --list can be used at one time.")

    if args.local and not (args.public or args.tag):
        raise ConsoleError(
            "The --local option is only valid alongside --public or --tag.")

    if args.public:
        items = api.timeline_public(app, user, local=args.local)
    elif args.tag:
        items = api.timeline_tag(app, user, args.tag, local=args.local)
    elif args.list:
        items = api.timeline_list(app, user, args.list)
    else:
        items = api.timeline_home(app, user)

    if args.reverse:
        items = reversed(items)

    print_timeline(items)
示例#13
0
    def setup_windows(self):
        screen_height, screen_width = self.stdscr.getmaxyx()

        if screen_width < 60:
            raise ConsoleError("Terminal screen is too narrow, toot curses requires at least 60 columns to display properly.")

        left_width = max(min(screen_width // 3, 60), 30)
        right_width = screen_width - left_width

        self.header = HeaderWindow(self.stdscr, 2, screen_width, 0, 0)
        self.footer = FooterWindow(self.stdscr, 2, screen_width, screen_height - 2, 0)
        self.left = StatusListWindow(self.stdscr, screen_height - 4, left_width, 2, 0)
        self.right = StatusDetailWindow(self.stdscr, screen_height - 4, right_width, 2, left_width)

        self.help_modal = HelpModal(self.stdscr)
示例#14
0
def post(app, user, args):
    if args.media:
        media = _do_upload(app, user, args.media)
        media_ids = [media['id']]
    else:
        media = None
        media_ids = None

    if media and not args.text:
        args.text = media['text_url']

    if not args.text:
        raise ConsoleError("You must specify either text or media to post.")

    response = api.post_status(app, user, args.text, args.visibility, media_ids)

    print_out("Toot posted: <green>{}</green>".format(response.get('url')))
示例#15
0
def login_interactive(app, email=None):
    print_out("Log in to <green>{}</green>".format(app.instance))

    if email:
        print_out("Email: <green>{}</green>".format(email))

    while not email:
        email = input('Email: ')

    password = getpass('Password: '******'access_token'])
示例#16
0
def register_app(domain, scheme='https'):
    print_out("Looking up instance info...")
    instance = api.get_instance(domain)

    print_out(
        "Found instance <blue>{}</blue> running Mastodon version <yellow>{}</yellow>"
        .format(instance['title'], instance['version']))

    try:
        print_out("Registering application...")
        response = api.create_app(domain, scheme)
    except ApiError:
        raise ConsoleError("Registration failed.")

    base_url = scheme + '://' + domain

    app = App(domain, base_url, response['client_id'],
              response['client_secret'])
    config.save_app(app)

    print_out("Application tokens saved.")

    return app
示例#17
0
文件: utils.py 项目: marnanel/toot
def assert_domain_exists(domain):
    if not domain_exists(domain):
        raise ConsoleError("Domain {} not found".format(domain))
示例#18
0
文件: app.py 项目: anjandev/toot
import webbrowser

from toot import __version__

from toot.exceptions import ConsoleError
from toot.ui.parsers import parse_status
from toot.ui.utils import draw_horizontal_divider, draw_lines
from toot.utils import trunc

# Attempt to load curses, which is not available on windows
try:
    import curses
    import curses.panel
except ImportError as e:
    raise ConsoleError("Curses is not available on this platform")


class Color:
    @classmethod
    def setup_palette(class_):
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
        curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
        curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK)
        curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK)
        curses.init_pair(5, curses.COLOR_RED, curses.COLOR_BLACK)
        curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)
        curses.init_pair(7, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
        curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(9, curses.COLOR_WHITE, curses.COLOR_RED)