Add search command - toot - Unnamed repository; edit this file 'description' to name the repository.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 64d46955e2b827c406f503cabc1be8b6169e72dd
 (DIR) parent d53849fe4b5c5b3f84a0ffc787d187bb641a51a5
 (HTM) Author: Ivan Habunek <ivan@habunek.com>
       Date:   Sun, 16 Apr 2017 15:07:27 +0200
       
       Add search command
       
       Diffstat:
         CHANGELOG.md                        |       5 +++++
         README.rst                          |      51 +++++++++++++------------------
         tests/test_console.py               |      34 ++++++++++++++++++++++++++++++-
         toot/api.py                         |       7 +++++++
         toot/console.py                     |      48 ++++++++++++++++++++++++++++---
       
       5 files changed, 110 insertions(+), 35 deletions(-)
       ---
 (DIR) diff --git a/CHANGELOG.md b/CHANGELOG.md
       @@ -1,6 +1,11 @@
        Changelog
        ---------
        
       +**0.5.0 (2016-04-16)**
       +
       +* Add `search` command
       +* Migrate from `optparse` to `argparse`
       +
        **0.4.0 (2016-04-15)**
        
        * Add `upload` command to post media
 (DIR) diff --git a/README.rst b/README.rst
       @@ -4,6 +4,8 @@ Toot - Mastodon CLI interface
        
        Interact with Mastodon social networks from the command line.
        
       +.. image:: https://img.shields.io/travis/ihabunek/toot.svg?maxAge=3600&style=flat-square
       +   :target: https://travis-ci.org/ihabunek/toot
        .. image:: https://img.shields.io/badge/author-%40ihabunek-blue.svg?maxAge=3600&style=flat-square
           :target: https://mastodon.social/@ihabunek
        .. image:: https://img.shields.io/github/license/ihabunek/pdf417-py.svg?maxAge=3600&style=flat-square
       @@ -21,11 +23,28 @@ Install using pip:
        
            pip install toot
        
       -
        Usage
        -----
        
       -Firstly, you will need to login to a Mastodon instance:
       +Running ``toot`` displays a list of available commands.
       +
       +Running ``toot <command> -h`` shows the documentation for the given command.
       +
       +===================  ===============================================================
       + Command              Description
       +===================  ===============================================================
       + ``toot login``       Log into a Mastodon instance, saves access keys for later use.
       + ``toot logout``      Log out, deletes stored access keys.
       + ``toot auth``        Display current login details.
       + ``toot post``        Post a status to your timeline.
       + ``toot search``      Search for accounts or hashtags.
       + ``toot timeline``    Display recent items in your public timeline.
       +===================  ===============================================================
       +
       +Authentication
       +--------------
       +
       +Before tooting, you need to login to a Mastodon instance:
        
        .. code-block::
        
       @@ -51,31 +70,3 @@ And you can logout which will remove the stored access tokens:
        .. code-block::
        
            toot logout
       -
       -Show timeline
       -~~~~~~~~~~~~~
       -
       -To show recent items in your public timeline:
       -
       -.. code-block::
       -
       -    toot timeline
       -
       -Post status
       -~~~~~~~~~~~
       -
       -To post a new status to your timeline:
       -
       -.. code-block::
       -
       -    toot post "Hello world!"
       -
       -Optionally attach an image or video to the status:
       -
       -    toot post "Hello world!" --media=path/to/world.jpg
       -
       -To set post visibility:
       -
       -    toot post "Hello world!" --visibility=unlisted
       -
       -Possible visibility values are: ``public`` (default), ``unlisted``, ``private``, ``direct``. They are documented  `here <https://github.com/tootsuite/documentation/blob/aa20089756c8cf9ff5a52fb35ad1a9472f10970c/Using-Mastodon/User-guide.md#toot-privacy>`_.
 (DIR) diff --git a/tests/test_console.py b/tests/test_console.py
       @@ -3,7 +3,7 @@ import pytest
        import requests
        
        from toot import User, App
       -from toot.console import print_usage, cmd_post_status, cmd_timeline, cmd_upload
       +from toot.console import print_usage, cmd_post_status, cmd_timeline, cmd_upload, cmd_search
        
        from tests.utils import MockResponse
        
       @@ -138,3 +138,35 @@ def test_upload(monkeypatch, capsys):
            out, err = capsys.readouterr()
            assert "Uploading media" in out
            assert __file__ in out
       +
       +
       +def test_search(monkeypatch, capsys):
       +    def mock_get(url, params, headers=None):
       +        assert url == 'https://habunek.com/api/v1/search'
       +        assert headers == {'Authorization': 'Bearer xxx'}
       +        assert params == {
       +            'q': 'freddy',
       +            'resolve': False,
       +        }
       +
       +        return MockResponse({
       +            'hashtags': ['foo', 'bar', 'baz'],
       +            'accounts': [{
       +                'acct': 'thequeen',
       +                'display_name': 'Freddy Mercury'
       +            }, {
       +                'acct': 'thequeen@other.instance',
       +                'display_name': 'Mercury Freddy'
       +            }],
       +            'statuses': [],
       +        })
       +
       +    monkeypatch.setattr(requests, 'get', mock_get)
       +
       +    cmd_search(app, user, ['freddy'])
       +
       +    out, err = capsys.readouterr()
       +    assert "Hashtags:\n\033[32m#foo\033[0m, \033[32m#bar\033[0m, \033[32m#baz\033[0m" in out
       +    assert "Accounts:" in out
       +    assert "\033[32m@thequeen\033[0m Freddy Mercury" in out
       +    assert "\033[32m@thequeen@other.instance\033[0m Mercury Freddy" in out
 (DIR) diff --git a/toot/api.py b/toot/api.py
       @@ -108,3 +108,10 @@ def upload_media(app, user, file):
            return _post(app, user, '/api/v1/media', files={
                'file': file
            })
       +
       +
       +def search(app, user, query, resolve):
       +    return _get(app, user, '/api/v1/search', {
       +        'q': query,
       +        'resolve': resolve,
       +    })
 (DIR) diff --git a/toot/console.py b/toot/console.py
       @@ -16,7 +16,7 @@ from argparse import ArgumentParser, FileType
        from textwrap import TextWrapper
        
        from toot import DEFAULT_INSTANCE
       -from toot.api import create_app, login, post_status, timeline_home, upload_media
       +from toot.api import create_app, login, post_status, timeline_home, upload_media, search
        from toot.config import save_user, load_user, load_app, save_app, CONFIG_APP_FILE, CONFIG_USER_FILE
        
        
       @@ -84,6 +84,7 @@ def print_usage():
            print("  toot logout     - log out (delete saved access tokens)")
            print("  toot auth       - shows currently logged in user and instance")
            print("  toot post <msg> - toot a new post to your timeline")
       +    print("  toot search     - search for accounts or hashtags")
            print("  toot timeline   - shows your public timeline")
            print("")
            print("To get help for each command run:")
       @@ -233,6 +234,42 @@ def cmd_upload(app, user, args):
            print("Text URL:     " + green(response['text_url']))
        
        
       +def _print_accounts(accounts):
       +    if not accounts:
       +        return
       +
       +    print("\nAccounts:")
       +    for account in accounts:
       +        acct = green("@{}".format(account['acct']))
       +        display_name = account['display_name']
       +        print("* {} {}".format(acct, display_name))
       +
       +
       +def _print_hashtags(hashtags):
       +    if not hashtags:
       +        return
       +
       +    print("\nHashtags:")
       +    print(", ".join([green("#" + t) for t in hashtags]))
       +
       +
       +def cmd_search(app, user, args):
       +    parser = ArgumentParser(prog="toot serach",
       +                            description="Search for content",
       +                            epilog="https://github.com/ihabunek/toot")
       +
       +    parser.add_argument("query", help="The search query")
       +    parser.add_argument("-r", "--resolve", action='store_true', default=False,
       +                        help="Whether to resolve non-local accounts")
       +
       +    args = parser.parse_args(args)
       +
       +    response = search(app, user, args.query, args.resolve)
       +
       +    _print_accounts(response['accounts'])
       +    _print_hashtags(response['hashtags'])
       +
       +
        def do_upload(app, user, file):
            print("Uploading media: {}".format(green(file.name)))
            return upload_media(app, user, file)
       @@ -251,8 +288,8 @@ def run_command(command, args):
        
            # Commands which require user to be logged in
            if not app or not user:
       -        print(red("You are not logged in."))
       -        print(red("Please run `toot login` first."))
       +        print_error("You are not logged in.")
       +        print_error("Please run `toot login` first.")
                return
        
            if command == 'logout':
       @@ -267,7 +304,10 @@ def run_command(command, args):
            if command == 'upload':
                return cmd_upload(app, user, args)
        
       -    print(red("Unknown command '{}'\n".format(command)))
       +    if command == 'search':
       +        return cmd_search(app, user, args)
       +
       +    print_error("Unknown command '{}'\n".format(command))
            print_usage()