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)
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()
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))
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)
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)
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')))
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')))
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))
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")
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()
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
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)
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)
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')))
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'])
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
def assert_domain_exists(domain): if not domain_exists(domain): raise ConsoleError("Domain {} not found".format(domain))
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)