From dfdad045f00b3faa9679989031d0a4dabe4753ce Mon Sep 17 00:00:00 2001 From: Ivan Habunek Date: Fri, 29 Dec 2017 14:26:40 +0100 Subject: [PATCH] Add instance command --- CHANGELOG.md | 5 +++-- toot/api.py | 30 ++++++++++++++++++++++++++++-- toot/commands.py | 11 ++++++++++- toot/console.py | 12 ++++++++++++ toot/output.py | 20 ++++++++++++++++++++ toot/utils.py | 9 +++++++++ 6 files changed, 82 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 582aeb0..0cfb05e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ Changelog **0.16.0 (TBA)** -* **Dropped support for Python 2** because it's a pain to support and was - causing bugs with handling unicode. +* **Drop support for Python 2** because it's a pain to support and caused bugs + with handling unicode. * Remove hacky `login_2fa` command, use `login_browser` instead +* Add `instance` command **0.15.1 (2017-12-12)** diff --git a/toot/api.py b/toot/api.py index 72491c0..2127942 100644 --- a/toot/api.py +++ b/toot/api.py @@ -4,10 +4,11 @@ import logging import re import requests -from urllib.parse import urlparse, urlencode from requests import Request, Session +from urllib.parse import urlparse, urlencode from toot import CLIENT_NAME, CLIENT_WEBSITE +from toot.utils import domain_exists SCOPES = 'read write follow' @@ -28,7 +29,9 @@ class AuthenticationError(ApiError): def _log_request(request): logger.debug(">>> \033[32m{} {}\033[0m".format(request.method, request.url)) - logger.debug(">>> HEADERS: \033[33m{}\033[0m".format(request.headers)) + + if request.headers: + logger.debug(">>> HEADERS: \033[33m{}\033[0m".format(request.headers)) if request.data: logger.debug(">>> DATA: \033[33m{}\033[0m".format(request.data)) @@ -83,6 +86,14 @@ def _get(app, user, url, params=None): return _process_response(response) +def _unauthorized_get(url, params=None): + _log_request(Request('GET', url, None, params=params)) + + response = requests.get(url, params) + + return _process_response(response) + + def _post(app, user, url, data=None, files=None): url = app.base_url + url headers = {"Authorization": "Bearer " + user.access_token} @@ -239,3 +250,18 @@ def verify_credentials(app, user): def get_notifications(app, user): return _get(app, user, '/api/v1/notifications').json() + + +def get_instance(app, user, domain): + if not domain_exists(domain): + raise ApiError("Domain {} not found".format(domain)) + + url = "http://{}/api/v1/instance".format(domain) + + try: + return _unauthorized_get(url).json() + except NotFoundError: + raise ApiError( + "Instance info not found at {}.\n" + "The given domain probably does not host a Mastodon instance.".format(url) + ) diff --git a/toot/commands.py b/toot/commands.py index 5608f54..95b08ce 100644 --- a/toot/commands.py +++ b/toot/commands.py @@ -11,7 +11,7 @@ from itertools import chain from textwrap import TextWrapper, wrap from toot import api, config, DEFAULT_INSTANCE, User, App, ConsoleError -from toot.output import print_out +from toot.output import print_out, print_instance def register_app(instance): @@ -329,3 +329,12 @@ def whoami(app, user, args): def whois(app, user, args): account = _find_account(app, user, args.account) _print_account(account) + + +def instance(app, user, args): + name = args.instance or (app and app.instance) + if not name: + raise ConsoleError("Please specify instance name.") + + instance = api.get_instance(app, user, name) + print_instance(instance) diff --git a/toot/console.py b/toot/console.py index 0a6317a..d29a151 100644 --- a/toot/console.py +++ b/toot/console.py @@ -95,6 +95,18 @@ READ_COMMANDS = [ ], require_auth=True, ), + Command( + name="instance", + description="Display instance details", + arguments=[ + (["instance"], { + "help": "instance domain (e.g. 'mastodon.social') or blank to use current", + "nargs": "?", + }), + + ], + require_auth=False, + ), Command( name="search", description="Search for users or hashtags", diff --git a/toot/output.py b/toot/output.py index 0c1590c..8ca0ff7 100644 --- a/toot/output.py +++ b/toot/output.py @@ -3,6 +3,9 @@ import sys import re +from textwrap import wrap +from toot.utils import format_content + START_CODES = { 'red': '\033[31m', 'green': '\033[32m', @@ -50,3 +53,20 @@ def print_err(*args, **kwargs): args = ["{}".format(a) for a in args] args = [colorize(a) if USE_ANSI_COLOR else strip_tags(a) for a in args] print(*args, file=sys.stderr, **kwargs) + + +def print_instance(instance): + print_out("{}".format(instance['title'])) + print_out("{}".format(instance['uri'])) + print_out("running Mastodon {}".format(instance['version'])) + print_out("") + + description = instance['description'].strip() + if not description: + return + + lines = [line.strip() for line in format_content(description) if line.strip()] + for line in lines: + for l in wrap(line.strip()): + print(l) + print() diff --git a/toot/utils.py b/toot/utils.py index 2ce04a6..dcc7c29 100644 --- a/toot/utils.py +++ b/toot/utils.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import re +import socket from bs4 import BeautifulSoup @@ -41,3 +42,11 @@ def format_content(content): yield line first = False + + +def domain_exists(name): + try: + socket.gethostbyname(name) + return True + except OSError: + return False