diff --git a/requirements.txt b/requirements.txt index 5791e8d..1e8d400 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ click>=6.7,<7 +humanize>=0.5.1,<1 objtools>=0.1.0 +python-dateutil>=2.6.1,<3 requests>=2.22 twtxt>=1.2.2 diff --git a/twtxt_registry_client/output.py b/twtxt_registry_client/output.py index ac6d228..e7fbf43 100644 --- a/twtxt_registry_client/output.py +++ b/twtxt_registry_client/output.py @@ -1,6 +1,12 @@ from abc import ABCMeta, abstractmethod +from datetime import datetime, timezone from objtools.registry import ClassRegistry +from twtxt.mentions import format_mentions +from twtxt.parser import parse_iso8601 +import click import json +import humanize +import textwrap class FormatterRegistry(ClassRegistry): @@ -78,3 +84,85 @@ class JSONFormatter(Formatter, key='json'): 'timestamp': timestamp, }) return json.dumps(output) + + +class PrettyFormatter(Formatter, key='pretty'): + + status_colors = { + 1: 'white', + 2: 'green', + 3: 'cyan', + 4: 'red', + 5: 'magenta', + } + + def format_response(self, resp): + return 'HTTP {code} {name}\n\n{body}'.format( + code=click.style( + str(resp.status_code), + fg=self.status_colors.get(resp.status_code // 100), + bold=True, + ), + name=click.style(resp.reason, bold=True), + body=resp.text, + ) + + def format_tweets(self, resp): + if not resp.ok: + return self.format_response(resp) + + # Try to determine the configured character limit and time display + conf = click.get_current_context().obj.conf + abs_time = conf.get('use_abs_time', False) + limit = conf.get('character_limit') + # Prevent AttributeErrors when using twtxt.helper.format_mentions + conf.setdefault('twturl', None) + conf.setdefault('following', []) + + output = [] + for tweet in resp.text.splitlines(): + # Mostly taken from twtxt.helper.style_tweet + nick, url, timestamp, message = tweet.split('\t', maxsplit=3) + if limit: + styled = format_mentions(message) + len_styling = len(styled) - len(click.unstyle(styled)) + message = textwrap.shorten(styled, limit + len_styling) + else: + message = format_mentions(message) + + dt = parse_iso8601(timestamp) + if abs_time: + timestamp = dt.strftime('%c') + tense = None + else: + now = datetime.now(timezone.utc) + timestamp = humanize.naturaldelta(now - dt) + tense = 'from now' if dt > now else 'ago' + + output.append( + '➤ {nick} @ {url} ({timestamp} {tense}):\n{message}'.format( + nick=click.style(nick, bold=True), + url=url, + timestamp=timestamp, + tense=tense, + message=message, + ) + ) + + return '\n\n'.join(output) + + def format_users(self, resp): + if not resp.ok: + return self.format_response(resp) + output = [] + for user in resp.text.splitlines(): + nick, url, timestamp = user.split('\t', maxsplit=2) + dt = parse_iso8601(timestamp) + output.append( + '➤ {nick} @ {url} (last updated on {timestamp})'.format( + nick=click.style(nick, bold=True), + url=url, + timestamp=dt.strftime('%c'), + ) + ) + return '\n'.join(output)