Add instance command - toot - Unnamed repository; edit this file 'description' to name the repository.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit dfdad045f00b3faa9679989031d0a4dabe4753ce
 (DIR) parent 5c0a797b66faf3d6ef14549301bfd56df79dfc07
 (HTM) Author: Ivan Habunek <ivan@habunek.com>
       Date:   Fri, 29 Dec 2017 14:26:40 +0100
       
       Add instance command
       
       Diffstat:
         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(-)
       ---
 (DIR) diff --git 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)**
        
 (DIR) diff --git 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)
       +        )
 (DIR) diff --git 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)
 (DIR) diff --git a/toot/console.py b/toot/console.py
       @@ -96,6 +96,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",
                arguments=[
 (DIR) diff --git 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 = ["<red>{}</red>".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("<green>{}</green>".format(instance['title']))
       +    print_out("<blue>{}</blue>".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()
 (DIR) diff --git 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