Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ihabunek committed Nov 26, 2023
1 parent 1c5abb8 commit c413679
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 2 deletions.
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
packages=['toot', 'toot.tui', 'toot.tui.richtext', 'toot.utils'],
python_requires=">=3.7",
install_requires=[
"click~=8.1",
"requests>=2.13,<3.0",
"beautifulsoup4>=4.5.0,<5.0",
"wcwidth>=0.1.7",
Expand Down
16 changes: 14 additions & 2 deletions toot/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
from .console import main
import sys
from toot.cli import cli
from toot.exceptions import ConsoleError
from toot.output import print_err
from toot.settings import load_settings

main()
try:
defaults = load_settings().get("commands", {})
cli(default_map=defaults)
except ConsoleError as ex:
print_err(str(ex))
sys.exit(1)
except KeyboardInterrupt:
print_err("Aborted")
sys.exit(1)
140 changes: 140 additions & 0 deletions toot/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import logging
import sys
import click
import json as pyjson

from functools import wraps
from toot import App, User, api, config
from toot.entities import Instance, from_dict, Account
from toot.exceptions import ApiError, ConsoleError
from toot.output import print_account, print_instance, print_search_results
from typing import Callable, Concatenate, NamedTuple, Optional, ParamSpec, TypeVar

# Tweak the Click context
# https://click.palletsprojects.com/en/8.1.x/api/#context
CONTEXT = dict(
# Enable using environment variables to set options
auto_envvar_prefix="TOOT",
# Add shorthand -h for invoking help
help_option_names=["-h", "--help"],
# Give help some more room (default is 80)
max_content_width=100,
# Always show default values for options
show_default=True,
)


# Data object to add to Click context
class Context(NamedTuple):
app: Optional[App]
user: Optional[User]
color: bool
debug: bool
quiet: bool


P = ParamSpec("P")
R = TypeVar("R")
T = TypeVar("T")


def pass_context(f: Callable[Concatenate[Context, P], R]) -> Callable[P, R]:
"""Add App and User as first two params, taken from `context.obj`."""
@wraps(f)
def wrapped(*args: P.args, **kwargs: P.kwargs) -> R:
ctx = click.get_current_context()
return f(ctx.obj, *args, **kwargs)

return wrapped


json_option = click.option(
"--json",
is_flag=True,
default=False,
help="Print data as JSON rather than human readable text"
)


@click.group(context_settings=CONTEXT)
@click.option("--debug/--no-debug", default=False, help="Log debug info to stderr")
@click.option("--color/--no-color", default=sys.stdout.isatty(), help="Use ANSI color in output")
@click.option("--quiet/--no-quiet", default=False, help="Don't print anything to stdout")
@click.pass_context
def cli(ctx, color, debug, quiet):
"""Toot is a Mastodon CLI"""
user, app = config.get_active_user_app()
ctx.obj = Context(app, user, color, debug, quiet)

if debug:
logging.basicConfig(level=logging.DEBUG)


@cli.command()
@json_option
@pass_context
def whoami(ctx: Context, json: bool):
"""Display logged in user details"""
response = api.verify_credentials(ctx.app, ctx.user)

if json:
click.echo(response.text)
else:
account = from_dict(Account, response.json())
print_account(account)


@cli.command()
@click.argument("account")
@json_option
@pass_context
def whois(ctx: Context, account: str, json: bool):
"""Display account details"""
account_dict = api.find_account(ctx.app, ctx.user, account)

# Here it's not possible to avoid parsing json since it's needed to find the account.
if json:
click.echo(pyjson.dumps(account_dict))
else:
account_obj = from_dict(Account, account_dict)
print_account(account_obj)


@cli.command()
@click.argument("instance_url", required=False)
@json_option
@pass_context
def instance(ctx: Context, instance_url: Optional[str], json: bool):
"""Display instance details"""
default_url = ctx.app.base_url if ctx.app else None
base_url = instance_url or default_url

if not base_url:
raise ConsoleError("Please specify an instance.")

try:
response = api.get_instance(base_url)
except ApiError:
raise ConsoleError(
f"Instance not found at {base_url}.\n" +
"The given domain probably does not host a Mastodon instance."
)

if json:
print(response.text)
else:
instance = from_dict(Instance, response.json())
print_instance(instance)


@cli.command()
@click.argument("query")
@click.option("-r", "--resolve", is_flag=True, help="Resolve non-local accounts")
@json_option
@pass_context
def search(ctx: Context, query: str, resolve: bool, json: bool):
response = api.search(ctx.app, ctx.user, query, resolve)
if json:
print(response.text)
else:
print_search_results(response.json())

0 comments on commit c413679

Please sign in to comment.