-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
155 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |