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