├── setup.cfg ├── imgur_cli ├── exceptions.py ├── __init__.py ├── utils.py ├── cli.py └── cli_api.py ├── .travis.yml ├── requirements.txt ├── tests ├── __init__.py └── test_cli.py ├── .gitignore ├── LICENSE ├── setup.py └── README.md /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /imgur_cli/exceptions.py: -------------------------------------------------------------------------------- 1 | class CommandError(Exception): 2 | """Error in CLI tool""" 3 | pass 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.4" 4 | install: 5 | - pip install -r requirements.txt 6 | before_script: 7 | - pep8 --max-line-length=85 imgur_cli 8 | script: 9 | - nosetests 10 | -------------------------------------------------------------------------------- /imgur_cli/__init__.py: -------------------------------------------------------------------------------- 1 | __title__ = 'imgur-cli' 2 | __author__ = 'Usman Ehtesham Gul' 3 | __email__ = 'uehtesham90@gmail.com' 4 | __license__ = 'MIT' 5 | __version__ = '0.0.1' 6 | __url__ = 'https://github.com/ueg1990/imgur-cli.git' 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | coverage==4.0.1 2 | extras==0.0.3 3 | fixtures==1.3.1 4 | imgurpython==1.1.6 5 | linecache2==1.0.0 6 | nose==1.3.7 7 | pbr==1.8.0 8 | pep8==1.6.2 9 | python-mimeparse==0.1.4 10 | requests==2.20.0 11 | six==1.9.0 12 | testtools==1.8.0 13 | traceback2==1.4.0 14 | unittest2==1.1.0 15 | yolk3k==0.8.7 16 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | class InitializationTests(unittest.TestCase): 5 | 6 | def test_initialization(self): 7 | """ 8 | Check the test suite runs by affirming 2+2=4 9 | """ 10 | self.assertEqual(2 + 2, 4) 11 | 12 | def test_import(self): 13 | """ 14 | Ensure the test suite can import our module 15 | """ 16 | try: 17 | import imgur_cli 18 | except ImportError: 19 | self.fail("Was not able to import imgur_cli") 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Usman Ehtesham Gul 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from setuptools import setup, find_packages 4 | 5 | with open('imgur_cli/__init__.py', 'r') as fd: 6 | meta_data = fd.read() 7 | version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', 8 | meta_data, re.MULTILINE).group(1) 9 | if not version: 10 | raise RuntimeError('Cannot find version information') 11 | name = re.search(r'^__title__\s*=\s*[\'"]([^\'"]*)[\'"]', 12 | meta_data, re.MULTILINE).group(1) 13 | author = re.search(r'^__author__\s*=\s*[\'"]([^\'"]*)[\'"]', 14 | meta_data, re.MULTILINE).group(1) 15 | email = re.search(r'^__email__\s*=\s*[\'"]([^\'"]*)[\'"]', 16 | meta_data, re.MULTILINE).group(1) 17 | license = re.search(r'^__license__\s*=\s*[\'"]([^\'"]*)[\'"]', 18 | meta_data, re.MULTILINE).group(1) 19 | url = re.search(r'^__url__\s*=\s*[\'"]([^\'"]*)[\'"]', 20 | meta_data, re.MULTILINE).group(1) 21 | 22 | requires = ['imgurpython >= 1.1.6', 'requests >= 2.7.0'] 23 | classifiers = [ 24 | # How mature is this project? Common values are 25 | # 3 - Alpha 26 | # 4 - Beta 27 | # 5 - Production/Stablegit 28 | 'Development Status :: 3 - Alpha', 29 | 30 | # Indicate who your project is intended for 31 | 'Intended Audience :: Developers', 32 | 'Topic :: Software Development :: Build Tools', 33 | 34 | # Pick your license as you wish (should match "license" above) 35 | 'License :: OSI Approved :: MIT License', 36 | 37 | # Specify the Python versions you support here. In particular, ensure 38 | # that you indicate whether you support Python 2, Python 3 or both. 39 | 'Programming Language :: Python :: 3.4', 40 | ] 41 | 42 | setup( 43 | name=name, 44 | version=version, 45 | description='Imgur CLI', 46 | author=author, 47 | license=license, 48 | classifiers=classifiers, 49 | keywords="imgur cli imgurpython meme memes", 50 | author_email=email, 51 | url=url, 52 | packages=find_packages(exclude='tests'), 53 | install_requires=requires, 54 | entry_points={ 55 | 'console_scripts': [ 56 | 'imgur = imgur_cli.cli:main' 57 | ], 58 | } 59 | ) 60 | -------------------------------------------------------------------------------- /imgur_cli/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def cli_arg(*args, **kwargs): 5 | """Decorator for CLI args""" 6 | def _decorator(func): 7 | add_arg(func, *args, **kwargs) 8 | return func 9 | return _decorator 10 | 11 | 12 | def add_arg(func, *args, **kwargs): 13 | """Bind CLI arguments to a 'cmd_' format function""" 14 | if not hasattr(func, 'arguments'): 15 | func.arguments = [] 16 | 17 | if (args, kwargs) not in func.arguments: 18 | # Because of the semantics of decorator composition if we just append 19 | # to the options list positional options will appear to be backwards. 20 | func.arguments.insert(0, (args, kwargs)) 21 | 22 | 23 | def cli_subparser(*args, **kwargs): 24 | """Decorator for CLI subparsers""" 25 | def _decorator(func): 26 | add_subparser(func, *args, **kwargs) 27 | return func 28 | return _decorator 29 | 30 | 31 | def add_subparser(func, *args, **kwargs): 32 | """Bind a subparser to a 'cmd_' format function""" 33 | if not hasattr(func, 'subparser'): 34 | func.subparser = args[0] 35 | 36 | 37 | def generate_output(result, output_filename=None): 38 | """Generate JSON output and either print it to console or save to a file""" 39 | if output_filename: 40 | with open(output_filename, 'w') as json_file: 41 | data = json.dumps(result, json_file, indent=4, separators=(',', ': ')) 42 | json_file.write(data) 43 | else: 44 | print(json.dumps(result, indent=4, separators=(',', ': '))) 45 | 46 | 47 | def data_fields(args, allowed_fields): 48 | """Generate data fields dictionary required by some client methods""" 49 | data = {} 50 | for field in allowed_fields: 51 | field_value = getattr(args, field, None) 52 | if field_value: 53 | data[field] = field_value 54 | return data 55 | 56 | 57 | def format_comment_tree(comments): 58 | if isinstance(comments, list): 59 | result = [] 60 | for comment in comments: 61 | comment_dict = comment.__dict__ 62 | comment_dict['children'] = build_comment_tree(comment_dict['children']) 63 | result.append(comment_dict) 64 | else: 65 | result = comments.__dict__ 66 | result['children'] = build_comment_tree(result['children']) 67 | return result 68 | 69 | 70 | def build_comment_tree(children): 71 | children_objects = [] 72 | for child in children: 73 | child_dict = child .__dict__ 74 | child_dict['children'] = build_comment_tree(child_dict['children']) 75 | children_objects.append(child_dict) 76 | return children_objects 77 | -------------------------------------------------------------------------------- /imgur_cli/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Imgur CLI 3 | """ 4 | 5 | import argparse 6 | import logging 7 | import os 8 | import traceback 9 | import sys 10 | 11 | from collections import namedtuple 12 | 13 | import imgurpython 14 | 15 | from imgur_cli import __version__ 16 | from imgur_cli import cli_api 17 | from imgur_cli import exceptions 18 | from imgur_cli.utils import cli_arg 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | 23 | def imgur_credentials(): 24 | ImgurCredentials = namedtuple('ImgurCredentials', 25 | ['client_id', 'client_secret', 'access_token', 26 | 'refresh_token', 'mashape_key']) 27 | 28 | client_id = os.environ.get('IMGUR_CLIENT_ID') 29 | client_secret = os.environ.get('IMGUR_CLIENT_SECRET') 30 | access_token = os.environ.get('IMGUR_ACCESS_TOKEN') 31 | refresh_token = os.environ.get('IMGUR_REFRESH_TOKEN') 32 | mashape_key = os.environ.get('IMGUR_MASHAPE_KEY') 33 | 34 | if not client_id or not client_secret: 35 | raise imgurpython.client.ImgurClientError('Client credentials not found. ' 36 | 'Ensure you have both client id ' 37 | 'and client secret') 38 | 39 | return ImgurCredentials(client_id, client_secret, access_token, 40 | refresh_token, mashape_key) 41 | 42 | 43 | class ImgurCli: 44 | 45 | def __init__(self): 46 | self.subcommands = {} 47 | self.parser = None 48 | self.client = None 49 | self.subparsers = {} 50 | 51 | @property 52 | def base_parser(self): 53 | parser = argparse.ArgumentParser(prog='imgur', description=__doc__.strip(), 54 | epilog='See "imgur help ' 55 | '" for help on a specific ' 56 | 'subparser or subcommand') 57 | 58 | # Global arguments 59 | parser.add_argument('-v', '--version', action='version', 60 | version='%(prog)s {0}'.format(__version__)) 61 | 62 | return parser 63 | 64 | def generate_parser(self): 65 | self.parser = self.base_parser 66 | base_subparser = self.parser.add_subparsers(metavar='') 67 | self._add_help(self, base_subparser) 68 | self._add_subparsers(base_subparser) 69 | self._add_subcommands(cli_api) 70 | 71 | def _generate_subcommand(self, name, callback, subparser): 72 | description = callback.__doc__ or '' 73 | action_help = description.strip() 74 | arguments = getattr(callback, 'arguments', []) 75 | subcommand = subparser.add_parser(name, help=action_help, 76 | description=description, 77 | add_help=False) 78 | subcommand.add_argument('-h', '--help', action='help', 79 | help=argparse.SUPPRESS) 80 | for args, kwargs in arguments: 81 | subcommand.add_argument(*args, **kwargs) 82 | subcommand.set_defaults(func=callback) 83 | self.subcommands[name] = subcommand 84 | 85 | def _add_help(self, cli_api, subparser): 86 | callback = getattr(self, 'cmd_help') 87 | self._generate_subcommand('help', callback, subparser) 88 | 89 | def _add_subparsers(self, subparser): 90 | for name, description in cli_api.SUBPARSERS.items(): 91 | parser = subparser.add_parser(name, help=description, 92 | description=description, 93 | add_help=False) 94 | parser.add_argument('-h', '--help', action='help', 95 | help=argparse.SUPPRESS) 96 | self.subparsers[name] = {} 97 | self.subparsers[name]['parser'] = parser 98 | self.subparsers[name]['subparser'] = ( 99 | parser.add_subparsers(metavar='') 100 | ) 101 | 102 | def _add_subcommands(self, cli_api): 103 | for attr in (action for action in dir(cli_api) 104 | if action.startswith('cmd_')): 105 | callback = getattr(cli_api, attr) 106 | subparser_name = getattr(callback, 'subparser', None) 107 | name = (attr[len('cmd_{0}_'.format(subparser_name.replace('-', '_'))):] 108 | .replace('_', '-')) 109 | subparser = self.subparsers[subparser_name]['subparser'] 110 | self._generate_subcommand(name, callback, subparser) 111 | 112 | @cli_arg('subparser', metavar='', nargs='?', 113 | help='Display help for ') 114 | @cli_arg('subcommand', metavar='', nargs='?', 115 | help='Display help for ') 116 | def cmd_help(self, args): 117 | """Display help about this program or one of its subparsers""" 118 | if args.subparser: 119 | if args.subcommand: 120 | try: 121 | self.subcommands[args.subcommand].print_help() 122 | except KeyError: 123 | raise exceptions.CommandError('{0} is not valid subcommand' 124 | .format(args.subcommand)) 125 | else: 126 | try: 127 | self.subparsers[args.subparser]['parser'].print_help() 128 | except KeyError: 129 | raise exceptions.CommandError('{0} is not valid subparser' 130 | .format(args.subparser)) 131 | else: 132 | self.parser.print_help() 133 | 134 | def main(self, argv): 135 | self.generate_parser() 136 | if not argv: 137 | self.parser.print_help() 138 | return 0 139 | args = self.parser.parse_args(argv) 140 | try: 141 | # Short-circuit and deal with help right away 142 | if args.func == self.cmd_help: 143 | self.cmd_help(args) 144 | return 0 145 | credentials = imgur_credentials() 146 | self.client = imgurpython.ImgurClient(*credentials) 147 | args.func(self.client, args) 148 | except AttributeError: 149 | self.subparsers[argv[0]]['parser'].print_help() 150 | 151 | 152 | def main(): 153 | try: 154 | imgur_cli = ImgurCli() 155 | imgur_cli.main(sys.argv[1:]) 156 | except imgurpython.client.ImgurClientError as e: 157 | print(e, file=sys.stderr) 158 | sys.exit(1) 159 | except Exception as e: 160 | print(traceback.format_exc()) 161 | sys.exit(1) 162 | 163 | if __name__ == '__main__': 164 | main() 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imgur-cli 2 | 3 | A command line interface for Imgur 4 | 5 | #### Note: 6 | Need Python 3. I decided to stick with only Python 3 in order to encourage other developers to use Python 3. You can (and should) use a virtual environment to ensure there are no dependency issues with other tools and libraries. 7 | 8 | ## Installation 9 | 10 | pip install imgurpython 11 | 12 | 13 | ## Imgur API Documentation 14 | 15 | 16 | Documentation for the Imgur API can be found [here](https://api.imgur.com/). 17 | 18 | ## Authentication 19 | 20 | You must [register](http://api.imgur.com/oauth2/addclient) the CLI with the Imgur API, and provide the Client-ID to make *any* request to the API (see the [Authentication](https://api.imgur.com/#authentication) note). If you want to perform actions as a user, the user will have to authorize your CLI through OAuth2. 21 | 22 | To use the CLI from a strictly anonymous context (no actions on behalf of a user), you need only the client id and client secret. Once you have these, you need to set them up as environment variables. 23 | 24 | export IMGUR_CLIENT_ID= 25 | export IMGUR_CLIENT_SECRET= 26 | 27 | 28 | To take actions as a logged in user, you will additionally need an access token and refresh token. You can use the Imgur CLI to do the following: 29 | 30 | 1. Get authorization url: 31 | 32 | ``` 33 | imgur auth url 34 | ``` 35 | 36 | 2. Go to authorization url to retrieve PIN and then run the following command: 37 | 38 | ``` 39 | imgur auth set-user-auth 40 | ``` 41 | 42 | 3. Even though Imgur will set the access token and refresh token, you should also set them as environment variables. After running the command from (2), you will receive the access token and the refresh token. You can set them as follows: 43 | 44 | export IMGUR_ACCESS_TOKEN= 45 | export IMGUR_REFRESH_TOKEN= 46 | 47 | #### Note: 48 | To set environment variables on a Windows system, you can follow instructions [here](https://docs.python.org/2/using/windows.html#excursus-setting-environment-variables) 49 | 50 | ## Library Usage 51 | 52 | ### Help 53 | 54 | Run ```imgur help``` to see available subparsers: 55 | 56 | imgur help 57 | 58 | **Output** 59 | 60 | imgur help 61 | usage: imgur [-h] [-v] ... 62 | 63 | Imgur CLI 64 | 65 | positional arguments: 66 | 67 | help Display help about this program or one of its subparsers 68 | gallery Gallery subparser 69 | image Image subparser 70 | notification 71 | Notification subparser 72 | memegen Memegen subparser 73 | auth Authentication subparser 74 | comment Comment subparser 75 | conversation 76 | Conversation subparser 77 | account Account subparser 78 | album Album subparser 79 | 80 | optional arguments: 81 | -h, --help show this help message and exit 82 | -v, --version show program's version number and exit 83 | 84 | See "imgur help " for help on a specific subparser or subcommand 85 | 86 | For help on each subparser, you can run: 87 | 88 | imgur help 89 | 90 | For help of each subcommandm you can run: 91 | 92 | imgur help 93 | 94 | ### Examples 95 | 96 | #### Gallery Items 97 | 98 | imgur gallery items 99 | 100 | **Sample output** 101 | 102 | { 103 | "gallery": [ 104 | { 105 | "layout": "blog", 106 | "cover_height": 360, 107 | "account_id": 147142, 108 | "id": "AzrRs", 109 | "cover": "gW3whO8", 110 | "is_album": true, 111 | "comment_count": 184, 112 | "title": "5 Salads with More Calories Than a Big Mac", 113 | "section": "", 114 | "topic_id": 11, 115 | "downs": 279, 116 | "nsfw": false, 117 | "link": "http://imgur.com/a/AzrRs", 118 | "account_url": "Housemaster", 119 | "topic": "The More You Know", 120 | "comment_preview": null, 121 | "score": 6999, 122 | "images_count": 6, 123 | "cover_width": 640, 124 | "ups": 7282, 125 | "favorite": false, 126 | "views": 109548, 127 | "privacy": "hidden", 128 | "datetime": 1446568913, 129 | "points": 7003, 130 | "description": null, 131 | "vote": null 132 | }, 133 | { 134 | "layout": "blog", 135 | "cover_height": 3264, 136 | "account_id": null, 137 | "id": "Oz5SW", 138 | "cover": "lJJZVRv", 139 | "is_album": true, 140 | "comment_count": 264, 141 | "title": "It has never been harder to leave my apartment", 142 | "section": "pics", 143 | "topic_id": 0, 144 | "downs": 171, 145 | "nsfw": false, 146 | "link": "http://imgur.com/a/Oz5SW", 147 | "account_url": null, 148 | "topic": null, 149 | "comment_preview": null, 150 | "score": 12858, 151 | "images_count": 3, 152 | "cover_width": 2448, 153 | "ups": 12070, 154 | "favorite": false, 155 | "views": 1058140, 156 | "privacy": "public", 157 | "datetime": 1446555875, 158 | "points": 11899, 159 | "description": null, 160 | "vote": null 161 | } 162 | ... 163 | ] 164 | } 165 | 166 | 167 | #### Gallery Items with different arguments 168 | 169 | imgur gallery items --section hot --sort top --page 2 170 | 171 | #### Save output of gallery items to a file 172 | 173 | Some commands allow to store output in a file (you run the -h option with each command to check). 174 | 175 | imgur gallery items --output-file 176 | 177 | ## Development 178 | It is suggested to do development in a virtual environment using virtualenvwrapper/virtualenv 179 | 180 | ### Requirements 181 | - Python 3 182 | - imgurpython (>= 1.1.6) 183 | - pip - instructions [here](https://pip.pypa.io/en/latest/installing.html) 184 | - virtualenvwrapper - instructions [here](https://virtualenvwrapper.readthedocs.org/en/latest/install.html) (can also use [virtualenv](https://virtualenv.readthedocs.org/en/latest/installation.html)) 185 | 186 | For tests, additional requirements: 187 | 188 | - testtools 189 | - nose 190 | - coverage 191 | 192 | ### Fork and Clone 193 | 1. Fork the repo 194 | 2. Clone the fork repo: 195 | 196 | ``` 197 | git clone https://github.com/ueg1990/imgur-cli.git 198 | ``` 199 | 3. Run setup.py: 200 | 201 | ``` 202 | python setup.py develop 203 | ```` 204 | 4. Start developing and contributing! :) 205 | 206 | ## Author 207 | 208 | Usman Ehtesham Gul (ueg1990) - 209 | 210 | ## Contribute 211 | 212 | If you want to add any new features, or improve existing ones, feel free to send a pull request. If you have any questions or need help/mentoring with contributions, feel free to contact via email 213 | 214 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import fixtures 4 | import imgurpython 5 | import testtools 6 | 7 | from unittest import mock 8 | 9 | import imgur_cli.cli as cli 10 | from imgur_cli import exceptions 11 | 12 | FAKE_ENV = {'IMGUR_CLIENT_ID': 'client_id', 13 | 'IMGUR_CLIENT_SECRET': 'client_secret', 14 | 'IMGUR_ACCESS_TOKEN': 'access_token', 15 | 'IMGUR_REFRESH_TOKEN': 'refresh_token', 16 | 'IMGUR_MASHAPE_KEY': 'mashape_key'} 17 | 18 | 19 | class DevNull: 20 | def write(self, data): 21 | pass 22 | 23 | 24 | class TestImgurCli(testtools.TestCase): 25 | 26 | def setUp(self): 27 | super(TestImgurCli, self).setUp() 28 | self.mock_client = mock.patch('imgur_cli.cli.imgurpython.ImgurClient') 29 | self._client = self.mock_client.start() 30 | self.mock_output = mock.patch('imgur_cli.cli_api.generate_output') 31 | self.mock_output.start() 32 | self.old_stderr = sys.stderr 33 | sys.stderr = DevNull() 34 | 35 | def tearDown(self): 36 | super(TestImgurCli, self).tearDown() 37 | self.mock_client.stop() 38 | self.mock_output.stop() 39 | sys.stderr = self.old_stderr 40 | 41 | def cli(self, argv, exclude=None): 42 | self.make_env(exclude) 43 | _cli = cli.ImgurCli() 44 | _cli.main(argv) 45 | return _cli 46 | 47 | def assertParser(self, _cli, parser_args, argv): 48 | """ 49 | Assertion method for subparsers, subcommands and positional arguments. 50 | Parameter argv is a list where the first item is the name of the subparser, 51 | followed by the subcommand and the remaining items are positional arguments 52 | """ 53 | number_of_parsing_levels = 2 54 | cmd = 'cmd_{0}_{1}'.format(argv[0], argv[1]).replace('-', '_') 55 | self.assertEqual(parser_args.func.__name__, cmd) 56 | self.assertTrue(argv[1] in _cli.subcommands) 57 | self.assertTrue(argv[0] in _cli.subparsers) 58 | number_of_positional_arguments = len(argv) - number_of_parsing_levels 59 | if number_of_positional_arguments: 60 | positional_args = [] 61 | for item in parser_args.func.arguments: 62 | arg = item[0][0] 63 | if arg.startswith('--'): 64 | break 65 | positional_args.append(arg) 66 | for index, arg in enumerate(positional_args, 67 | start=number_of_parsing_levels): 68 | args_type = ( 69 | parser_args.func.arguments[index - number_of_parsing_levels][1] 70 | .get('type') 71 | ) 72 | if args_type: 73 | self.assertEqual(getattr(parser_args, arg), 74 | args_type(argv[index])) 75 | else: 76 | self.assertEqual(getattr(parser_args, arg), argv[index]) 77 | self.assertRaises(SystemExit, self.cli, argv[:index]) 78 | 79 | def make_env(self, exclude=None): 80 | env = {key: value for key, value in FAKE_ENV.items() if key != exclude} 81 | self.useFixture(fixtures.MonkeyPatch('os.environ', env)) 82 | 83 | def test_imgur_credentials_env(self): 84 | self.make_env() 85 | expected = ('client_id', 'client_secret', 'access_token', 'refresh_token', 86 | 'mashape_key') 87 | imgur_credentials = cli.imgur_credentials() 88 | self.assertEqual(expected, imgur_credentials) 89 | self.make_env(exclude='IMGUR_MASHAPE_KEY') 90 | expected = ('client_id', 'client_secret', 'access_token', 'refresh_token', 91 | None) 92 | imgur_credentials = cli.imgur_credentials() 93 | self.assertEqual(expected, imgur_credentials) 94 | self.make_env(exclude='IMGUR_CLIENT_ID') 95 | self.assertRaises(imgurpython.client.ImgurClientError, 96 | cli.imgur_credentials) 97 | self.make_env(exclude='IMGUR_CLIENT_SECRET') 98 | self.assertRaises(imgurpython.client.ImgurClientError, 99 | cli.imgur_credentials) 100 | 101 | def test_help_unknown_command(self): 102 | self.assertRaises(exceptions.CommandError, self.cli, ['help', 'foofoo']) 103 | 104 | def test_account_user(self): 105 | argv = ['account', 'user', 'me'] 106 | _cli = self.cli(argv) 107 | parser_args = _cli.parser.parse_args(argv) 108 | self.assertParser(_cli, parser_args, argv) 109 | self.assertTrue(_cli.client.get_account.called) 110 | 111 | def test_account_gallery_favorites(self): 112 | argv = ['account', 'gallery-favorites', 'me'] 113 | _cli = self.cli(argv) 114 | parser_args = _cli.parser.parse_args(argv) 115 | self.assertParser(_cli, parser_args, argv) 116 | self.assertTrue(_cli.client.get_gallery_favorites.called) 117 | 118 | def test_account_favorites(self): 119 | argv = ['account', 'favorites', 'me'] 120 | _cli = self.cli(argv) 121 | parser_args = _cli.parser.parse_args(argv) 122 | self.assertParser(_cli, parser_args, argv) 123 | self.assertTrue(_cli.client.get_account_favorites.called) 124 | 125 | def test_account_submissions(self): 126 | argv = ['account', 'submissions', 'me'] 127 | _cli = self.cli(argv) 128 | parser_args = _cli.parser.parse_args(argv) 129 | self.assertParser(_cli, parser_args, argv) 130 | self.assertTrue(_cli.client.get_account_submissions.called) 131 | 132 | def test_account_settings(self): 133 | argv = ['account', 'settings', 'me'] 134 | _cli = self.cli(argv) 135 | parser_args = _cli.parser.parse_args(argv) 136 | self.assertParser(_cli, parser_args, argv) 137 | self.assertTrue(_cli.client.get_account_settings.called) 138 | 139 | def test_account_change_settings(self): 140 | argv = ['account', 'change-settings', 'ue90', '--username', 'ueg1990'] 141 | self._client.return_value.allowed_account_fields = {'bio', 'public_images', 142 | 'messaging_enabled', 143 | 'album_privacy', 144 | 'accepted_gallery_terms', 145 | 'username'} 146 | _cli = self.cli(argv) 147 | parser_args = _cli.parser.parse_args(argv) 148 | self.assertParser(_cli, parser_args, argv) 149 | self.assertTrue(_cli.client.change_account_settings.called) 150 | expected_args = 'ue90', {'username': 'ueg1990'} 151 | _cli.client.change_account_settings.assert_called_with(*expected_args) 152 | 153 | def test_account_email_verification_status(self): 154 | argv = ['account', 'verification-status', 'me'] 155 | _cli = self.cli(argv) 156 | parser_args = _cli.parser.parse_args(argv) 157 | self.assertParser(_cli, parser_args, argv) 158 | self.assertTrue(_cli.client.get_email_verification_status.called) 159 | 160 | def test_account_send_verification_email(self): 161 | argv = ['account', 'send-verification', 'me'] 162 | _cli = self.cli(argv) 163 | parser_args = _cli.parser.parse_args(argv) 164 | self.assertParser(_cli, parser_args, argv) 165 | self.assertTrue(_cli.client.send_verification_email.called) 166 | 167 | def test_account_albums(self): 168 | argv = ['account', 'albums', 'me'] 169 | _cli = self.cli(argv) 170 | parser_args = _cli.parser.parse_args(argv) 171 | self.assertParser(_cli, parser_args, argv) 172 | self.assertTrue(_cli.client.get_account_albums.called) 173 | 174 | def test_account_album_ids(self): 175 | argv = ['account', 'album-ids', 'me'] 176 | _cli = self.cli(argv) 177 | parser_args = _cli.parser.parse_args(argv) 178 | self.assertParser(_cli, parser_args, argv) 179 | self.assertTrue(_cli.client.get_account_album_ids.called) 180 | 181 | def test_account_album_count(self): 182 | argv = ['account', 'album-count', 'me'] 183 | _cli = self.cli(argv) 184 | parser_args = _cli.parser.parse_args(argv) 185 | self.assertParser(_cli, parser_args, argv) 186 | self.assertTrue(_cli.client.get_account_album_count.called) 187 | 188 | @mock.patch('imgur_cli.cli_api.format_comment_tree') 189 | def test_account_comments(self, mock_format_comment_tree): 190 | argv = ['account', 'comments', 'me'] 191 | _cli = self.cli(argv) 192 | parser_args = _cli.parser.parse_args(argv) 193 | self.assertParser(_cli, parser_args, argv) 194 | self.assertTrue(_cli.client.get_account_comments.called) 195 | 196 | def test_account_comment_ids(self): 197 | argv = ['account', 'comment-ids', 'me'] 198 | _cli = self.cli(argv) 199 | parser_args = _cli.parser.parse_args(argv) 200 | self.assertParser(_cli, parser_args, argv) 201 | self.assertTrue(_cli.client.get_account_comment_ids.called) 202 | 203 | def test_account_comment_count(self): 204 | argv = ['account', 'comment-count', 'me'] 205 | _cli = self.cli(argv) 206 | parser_args = _cli.parser.parse_args(argv) 207 | self.assertParser(_cli, parser_args, argv) 208 | self.assertTrue(_cli.client.get_account_comment_count.called) 209 | 210 | def test_account_images(self): 211 | argv = ['account', 'images', 'me'] 212 | _cli = self.cli(argv) 213 | parser_args = _cli.parser.parse_args(argv) 214 | self.assertParser(_cli, parser_args, argv) 215 | self.assertTrue(_cli.client.get_account_images.called) 216 | 217 | def test_account_image_ids(self): 218 | argv = ['account', 'image-ids', 'me'] 219 | _cli = self.cli(argv) 220 | parser_args = _cli.parser.parse_args(argv) 221 | self.assertParser(_cli, parser_args, argv) 222 | self.assertTrue(_cli.client.get_account_image_ids.called) 223 | 224 | def test_account_image_count(self): 225 | argv = ['account', 'image-count', 'me'] 226 | _cli = self.cli(argv) 227 | parser_args = _cli.parser.parse_args(argv) 228 | self.assertParser(_cli, parser_args, argv) 229 | self.assertTrue(_cli.client.get_account_images_count.called) 230 | 231 | def test_album(self): 232 | argv = ['album', 'id', '123'] 233 | _cli = self.cli(argv) 234 | parser_args = _cli.parser.parse_args(argv) 235 | self.assertParser(_cli, parser_args, argv) 236 | self.assertTrue(_cli.client.get_album.called) 237 | 238 | def test_album_images(self): 239 | argv = ['album', 'images', '123'] 240 | _cli = self.cli(argv) 241 | parser_args = _cli.parser.parse_args(argv) 242 | self.assertParser(_cli, parser_args, argv) 243 | self.assertEqual(parser_args.output_file, None) 244 | self.assertTrue(_cli.client.get_album_images.called) 245 | self.assertRaises(SystemExit, self.cli, 246 | [argv[0], '--output-file', 'dummy.json']) 247 | 248 | def test_album_create(self): 249 | argv = ['album', 'create', '--title', 'test'] 250 | self._client.return_value.allowed_album_fields = {'ids', 'title', 251 | 'description', 'privacy', 252 | 'layout', 'cover'} 253 | _cli = self.cli(argv) 254 | parser_args = _cli.parser.parse_args(argv) 255 | self.assertParser(_cli, parser_args, argv) 256 | self.assertTrue(_cli.client.create_album.called) 257 | _cli.client.create_album.assert_called_with({'title': 'test'}) 258 | 259 | def test_album_update(self): 260 | argv = ['album', 'update', '123', '--title', 'test'] 261 | self._client.return_value.allowed_album_fields = {'ids', 'title', 262 | 'description', 'privacy', 263 | 'layout', 'cover'} 264 | _cli = self.cli(argv) 265 | parser_args = _cli.parser.parse_args(argv) 266 | self.assertParser(_cli, parser_args, argv) 267 | self.assertTrue(_cli.client.update_album.called) 268 | _cli.client.update_album.assert_called_with('123', {'title': 'test'}) 269 | 270 | def test_album_delete(self): 271 | argv = ['album', 'delete', '123'] 272 | _cli = self.cli(argv) 273 | parser_args = _cli.parser.parse_args(argv) 274 | self.assertParser(_cli, parser_args, argv) 275 | self.assertTrue(_cli.client.album_delete.called) 276 | 277 | def test_album_favorite(self): 278 | argv = ['album', 'favorite', '123'] 279 | _cli = self.cli(argv) 280 | parser_args = _cli.parser.parse_args(argv) 281 | self.assertParser(_cli, parser_args, argv) 282 | self.assertTrue(_cli.client.album_favorite.called) 283 | 284 | def test_album_set_images(self): 285 | argv = ['album', 'set-images', '123', 'abc'] 286 | _cli = self.cli(argv) 287 | parser_args = _cli.parser.parse_args(argv) 288 | self.assertParser(_cli, parser_args, argv) 289 | self.assertTrue(_cli.client.album_set_images.called) 290 | 291 | def test_album_add_images(self): 292 | argv = ['album', 'add-images', '123', 'abc'] 293 | _cli = self.cli(argv) 294 | parser_args = _cli.parser.parse_args(argv) 295 | self.assertParser(_cli, parser_args, argv) 296 | self.assertTrue(_cli.client.album_add_images.called) 297 | 298 | def test_album_remove_images(self): 299 | argv = ['album', 'remove-images', '123', 'abc'] 300 | _cli = self.cli(argv) 301 | parser_args = _cli.parser.parse_args(argv) 302 | self.assertParser(_cli, parser_args, argv) 303 | self.assertTrue(_cli.client.album_remove_images.called) 304 | 305 | def test_comment_id(self): 306 | argv = ['comment', 'id', '123'] 307 | _cli = self.cli(argv) 308 | parser_args = _cli.parser.parse_args(argv) 309 | self.assertParser(_cli, parser_args, argv) 310 | self.assertTrue(_cli.client.get_comment.called) 311 | 312 | def test_comment_delete(self): 313 | argv = ['comment', 'delete', '123'] 314 | _cli = self.cli(argv) 315 | parser_args = _cli.parser.parse_args(argv) 316 | self.assertParser(_cli, parser_args, argv) 317 | self.assertTrue(_cli.client.delete_comment.called) 318 | 319 | @mock.patch('imgur_cli.cli_api.format_comment_tree') 320 | def test_comment_replies(self, mock_format_comment_tree): 321 | argv = ['comment', 'replies', '123'] 322 | _cli = self.cli(argv) 323 | parser_args = _cli.parser.parse_args(argv) 324 | self.assertParser(_cli, parser_args, argv) 325 | self.assertTrue(_cli.client.get_comment_replies.called) 326 | 327 | def test_comment_reply(self): 328 | argv = ['comment', 'reply', '123', '456', 'Test comment'] 329 | _cli = self.cli(argv) 330 | parser_args = _cli.parser.parse_args(argv) 331 | self.assertParser(_cli, parser_args, argv) 332 | self.assertTrue(_cli.client.post_comment_reply.called) 333 | 334 | def test_comment_vote(self): 335 | argv = ['comment', 'vote', '123'] 336 | _cli = self.cli(argv) 337 | parser_args = _cli.parser.parse_args(argv) 338 | self.assertParser(_cli, parser_args, argv) 339 | self.assertTrue(_cli.client.comment_vote.called) 340 | 341 | def test_comment_report(self): 342 | argv = ['comment', 'report', '123'] 343 | _cli = self.cli(argv) 344 | parser_args = _cli.parser.parse_args(argv) 345 | self.assertParser(_cli, parser_args, argv) 346 | self.assertTrue(_cli.client.comment_report.called) 347 | 348 | def test_conversation_list(self): 349 | argv = ['conversation', 'list'] 350 | _cli = self.cli(argv) 351 | parser_args = _cli.parser.parse_args(argv) 352 | self.assertParser(_cli, parser_args, argv) 353 | self.assertTrue(_cli.client.conversation_list.called) 354 | 355 | def test_conversation_id_1(self): 356 | argv = ['conversation', 'id', '123'] 357 | self._client.return_value.get_conversation.return_value = \ 358 | mock.Mock(messages=[]) 359 | _cli = self.cli(argv) 360 | parser_args = _cli.parser.parse_args(argv) 361 | self.assertParser(_cli, parser_args, argv) 362 | self.assertTrue(_cli.client.get_conversation.called) 363 | 364 | def test_conversation_id_2(self): 365 | argv = ['conversation', 'id', '123'] 366 | self._client.return_value.get_conversation.return_value = \ 367 | mock.Mock(items=[]) 368 | _cli = self.cli(argv) 369 | parser_args = _cli.parser.parse_args(argv) 370 | self.assertParser(_cli, parser_args, argv) 371 | self.assertTrue(_cli.client.get_conversation.called) 372 | 373 | def test_conversation_create(self): 374 | argv = ['conversation', 'create', 'ue90', 'Test message'] 375 | _cli = self.cli(argv) 376 | parser_args = _cli.parser.parse_args(argv) 377 | self.assertParser(_cli, parser_args, argv) 378 | self.assertTrue(_cli.client.create_message.called) 379 | 380 | def test_conversation_delete(self): 381 | argv = ['conversation', 'delete', '123'] 382 | _cli = self.cli(argv) 383 | parser_args = _cli.parser.parse_args(argv) 384 | self.assertParser(_cli, parser_args, argv) 385 | self.assertTrue(_cli.client.delete_conversation.called) 386 | 387 | def test_conversation_report(self): 388 | argv = ['conversation', 'report', 'ue90'] 389 | _cli = self.cli(argv) 390 | parser_args = _cli.parser.parse_args(argv) 391 | self.assertParser(_cli, parser_args, argv) 392 | self.assertTrue(_cli.client.report_sender.called) 393 | 394 | def test_conversation_block(self): 395 | argv = ['conversation', 'block', 'ue90'] 396 | _cli = self.cli(argv) 397 | parser_args = _cli.parser.parse_args(argv) 398 | self.assertParser(_cli, parser_args, argv) 399 | self.assertTrue(_cli.client.block_sender.called) 400 | 401 | def test_gallery(self): 402 | argv = ['gallery', 'items'] 403 | _cli = self.cli(argv) 404 | parser_args = _cli.parser.parse_args(argv) 405 | self.assertParser(_cli, parser_args, argv) 406 | self.assertTrue(_cli.client.gallery.called) 407 | expected_args = {'section': 'hot', 'sort': 'viral', 'page': 0, 408 | 'window': 'day', 'show_viral': False, 'output_file': None} 409 | self.assertTrue(all(getattr(parser_args, key) == value 410 | for key, value in expected_args.items())) 411 | argv.extend(['--show-viral']) 412 | _cli = self.cli(argv) 413 | parser_args = _cli.parser.parse_args(argv) 414 | expected_args['show_viral'] = True 415 | self.assertTrue(all(getattr(parser_args, key) == value 416 | for key, value in expected_args.items())) 417 | 418 | def test_gallery_memes_subgallery(self): 419 | argv = ['gallery', 'memes-subgallery'] 420 | _cli = self.cli(argv) 421 | parser_args = _cli.parser.parse_args(argv) 422 | self.assertParser(_cli, parser_args, argv) 423 | self.assertTrue(_cli.client.memes_subgallery.called) 424 | expected_args = {'sort': 'viral', 'page': 0, 'window': 'week', 425 | 'output_file': None} 426 | self.assertTrue(all(getattr(parser_args, key) == value 427 | for key, value in expected_args.items())) 428 | 429 | def test_gallery_subreddit_gallery(self): 430 | argv = ['gallery', 'subreddit-gallery', 'soccer'] 431 | _cli = self.cli(argv) 432 | parser_args = _cli.parser.parse_args(argv) 433 | self.assertParser(_cli, parser_args, argv) 434 | self.assertTrue(_cli.client.subreddit_gallery.called) 435 | expected_args = {'sort': 'time', 'page': 0, 'window': 'week', 436 | 'output_file': None} 437 | self.assertTrue(all(getattr(parser_args, key) == value 438 | for key, value in expected_args.items())) 439 | 440 | def test_gallery_subreddit_image(self): 441 | argv = ['gallery', 'subreddit-image', 'soccer', '123'] 442 | _cli = self.cli(argv) 443 | parser_args = _cli.parser.parse_args(argv) 444 | self.assertParser(_cli, parser_args, argv) 445 | self.assertTrue(_cli.client.subreddit_image.called) 446 | 447 | def test_gallery_tag(self): 448 | argv = ['gallery', 'tag', 'dogs'] 449 | self._client.return_value.gallery_tag.return_value = mock.Mock(items=[]) 450 | _cli = self.cli(argv) 451 | parser_args = _cli.parser.parse_args(argv) 452 | self.assertParser(_cli, parser_args, argv) 453 | self.assertTrue(_cli.client.gallery_tag.called) 454 | expected_args = {'tag': argv[2], 'sort': 'viral', 'page': 0, 455 | 'window': 'week', 'output_file': None} 456 | self.assertTrue(all(getattr(parser_args, key) == value 457 | for key, value in expected_args.items())) 458 | 459 | def test_gallery_tag_image(self): 460 | argv = ['gallery', 'tag-image', 'dogs', '123'] 461 | _cli = self.cli(argv) 462 | parser_args = _cli.parser.parse_args(argv) 463 | self.assertParser(_cli, parser_args, argv) 464 | self.assertTrue(_cli.client.gallery_tag_image.called) 465 | 466 | def test_gallery_item_tags(self): 467 | argv = ['gallery', 'item-tags', '123'] 468 | _cli = self.cli(argv) 469 | parser_args = _cli.parser.parse_args(argv) 470 | self.assertParser(_cli, parser_args, argv) 471 | self.assertTrue(_cli.client.gallery_item_tags.called) 472 | 473 | def test_gallery_tag_vote(self): 474 | argv = ['gallery', 'tag-vote', '123', 'funny', 'up'] 475 | _cli = self.cli(argv) 476 | parser_args = _cli.parser.parse_args(argv) 477 | self.assertParser(_cli, parser_args, argv) 478 | self.assertTrue(_cli.client.gallery_tag_vote.called) 479 | argv[-1] = 'left' 480 | self.assertRaises(SystemExit, self.cli, argv) 481 | 482 | def test_gallery_search(self): 483 | argv = ['gallery', 'search', 'soccer'] 484 | _cli = self.cli(argv) 485 | parser_args = _cli.parser.parse_args(argv) 486 | self.assertParser(_cli, parser_args, argv) 487 | self.assertTrue(_cli.client.gallery_search.called) 488 | expected_args = {'advanced': None, 'sort': 'time', 'page': 0, 489 | 'window': 'all', 'output_file': None} 490 | self.assertTrue(all(getattr(parser_args, key) == value 491 | for key, value in expected_args.items())) 492 | 493 | def test_gallery_random(self): 494 | argv = ['gallery', 'random'] 495 | _cli = self.cli(argv) 496 | parser_args = _cli.parser.parse_args(argv) 497 | self.assertParser(_cli, parser_args, argv) 498 | self.assertTrue(_cli.client.gallery_random.called) 499 | self.assertEqual(parser_args.page, 0) 500 | self.assertEqual(parser_args.output_file, None) 501 | argv.extend(['--page', '12', '--output-file', 'dummy.json']) 502 | _cli = self.cli(argv) 503 | parser_args = _cli.parser.parse_args(argv) 504 | self.assertTrue(_cli.client.gallery_random.called) 505 | self.assertEqual(parser_args.page, int(argv[3])) 506 | self.assertEqual(parser_args.output_file, argv[5]) 507 | self.assertTrue(isinstance(parser_args.page, int)) 508 | 509 | def test_gallery_publish(self): 510 | argv = ['gallery', 'publish', '123', 'Title'] 511 | _cli = self.cli(argv) 512 | parser_args = _cli.parser.parse_args(argv) 513 | self.assertParser(_cli, parser_args, argv) 514 | self.assertTrue(_cli.client.share_on_imgur.called) 515 | 516 | def test_gallery_remove(self): 517 | argv = ['gallery', 'remove', '123'] 518 | _cli = self.cli(argv) 519 | parser_args = _cli.parser.parse_args(argv) 520 | self.assertParser(_cli, parser_args, argv) 521 | self.assertTrue(_cli.client.remove_from_gallery.called) 522 | 523 | def test_gallery_item(self): 524 | argv = ['gallery', 'item', '123'] 525 | _cli = self.cli(argv) 526 | parser_args = _cli.parser.parse_args(argv) 527 | self.assertParser(_cli, parser_args, argv) 528 | self.assertTrue(_cli.client.gallery_item .called) 529 | 530 | def test_gallery_report(self): 531 | argv = ['gallery', 'report', '123'] 532 | _cli = self.cli(argv) 533 | parser_args = _cli.parser.parse_args(argv) 534 | self.assertParser(_cli, parser_args, argv) 535 | self.assertTrue(_cli.client.report_gallery_item.called) 536 | 537 | def test_gallery_item_vote(self): 538 | argv = ['gallery', 'item-vote', '123'] 539 | _cli = self.cli(argv) 540 | parser_args = _cli.parser.parse_args(argv) 541 | self.assertParser(_cli, parser_args, argv) 542 | self.assertTrue(_cli.client.gallery_item_vote.called) 543 | self.assertEqual(parser_args.vote, 'up') 544 | argv.extend(['--vote', 'down']) 545 | _cli = self.cli(argv) 546 | parser_args = _cli.parser.parse_args(argv) 547 | self.assertEqual(parser_args.vote, 'down') 548 | argv[-1] = 'left' 549 | self.assertRaises(SystemExit, self.cli, argv) 550 | 551 | @mock.patch('imgur_cli.cli_api.format_comment_tree') 552 | def test_gallery_comments(self, mock_format_comment_tree): 553 | argv = ['gallery', 'comments', '123'] 554 | _cli = self.cli(argv) 555 | parser_args = _cli.parser.parse_args(argv) 556 | self.assertParser(_cli, parser_args, argv) 557 | self.assertTrue(_cli.client.gallery_item_comments.called) 558 | self.assertEqual(parser_args.sort, 'best') 559 | 560 | def test_gallery_create_comment(self): 561 | argv = ['gallery', 'create-comment', '123', 'test'] 562 | _cli = self.cli(argv) 563 | parser_args = _cli.parser.parse_args(argv) 564 | self.assertParser(_cli, parser_args, argv) 565 | self.assertTrue(_cli.client.gallery_comment.called) 566 | 567 | def test_gallery_comment_ids(self): 568 | argv = ['gallery', 'comment-ids', '123'] 569 | _cli = self.cli(argv) 570 | parser_args = _cli.parser.parse_args(argv) 571 | self.assertParser(_cli, parser_args, argv) 572 | self.assertTrue(_cli.client.gallery_comment_ids.called) 573 | 574 | def test_gallery_comment_count(self): 575 | argv = ['gallery', 'comment-count', '123'] 576 | _cli = self.cli(argv) 577 | parser_args = _cli.parser.parse_args(argv) 578 | self.assertParser(_cli, parser_args, argv) 579 | self.assertTrue(_cli.client.gallery_comment_count.called) 580 | 581 | def test_image(self): 582 | argv = ['image', 'id', '123'] 583 | _cli = self.cli(argv) 584 | parser_args = _cli.parser.parse_args(argv) 585 | self.assertParser(_cli, parser_args, argv) 586 | self.assertTrue(_cli.client.get_image.called) 587 | 588 | def test_image_upload(self): 589 | argv = ['image', 'upload', 'file', 'test.jpg'] 590 | _cli = self.cli(argv) 591 | parser_args = _cli.parser.parse_args(argv) 592 | self.assertParser(_cli, parser_args, argv) 593 | self.assertTrue(_cli.client.upload_from_path.called) 594 | argv[2] = 'url' 595 | _cli = self.cli(argv) 596 | parser_args = _cli.parser.parse_args(argv) 597 | self.assertParser(_cli, parser_args, argv) 598 | self.assertTrue(_cli.client.upload_from_url.called) 599 | 600 | def test_image_delete(self): 601 | argv = ['image', 'delete', '123'] 602 | _cli = self.cli(argv) 603 | parser_args = _cli.parser.parse_args(argv) 604 | self.assertParser(_cli, parser_args, argv) 605 | self.assertTrue(_cli.client.delete_image.called) 606 | 607 | def test_image_favorite(self): 608 | argv = ['image', 'favorite', '123'] 609 | _cli = self.cli(argv) 610 | parser_args = _cli.parser.parse_args(argv) 611 | self.assertParser(_cli, parser_args, argv) 612 | self.assertTrue(_cli.client.favorite_image.called) 613 | 614 | def test_memegen_default_memes(self): 615 | argv = ['memegen', 'default-memes'] 616 | _cli = self.cli(argv) 617 | parser_args = _cli.parser.parse_args(argv) 618 | self.assertParser(_cli, parser_args, argv) 619 | self.assertTrue(_cli.client.default_memes.called) 620 | 621 | @mock.patch('imgur_cli.cli_api.format_comment_tree') 622 | def test_notification_all(self, mock_format_comment_tree): 623 | argv = ['notification', 'all'] 624 | self._client.return_value.get_notifications.return_value = \ 625 | {'messages': [mock.Mock()], 'replies': [mock.Mock(content=[])]} 626 | _cli = self.cli(argv) 627 | parser_args = _cli.parser.parse_args(argv) 628 | self.assertParser(_cli, parser_args, argv) 629 | self.assertTrue(_cli.client.get_notifications.called) 630 | 631 | def test_notification_id_1(self): 632 | argv = ['notification', 'id', '123'] 633 | self._client.return_value.get_notification.return_value = \ 634 | mock.Mock(content=[]) 635 | _cli = self.cli(argv) 636 | parser_args = _cli.parser.parse_args(argv) 637 | self.assertParser(_cli, parser_args, argv) 638 | self.assertTrue(_cli.client.get_notification.called) 639 | 640 | @mock.patch('imgur_cli.cli_api.format_comment_tree') 641 | def test_notification_id_2(self, mock_format_comment_tree): 642 | argv = ['notification', 'id', '123'] 643 | self._client.return_value.get_notification.return_value = \ 644 | mock.Mock(content={'comment': []}) 645 | _cli = self.cli(argv) 646 | parser_args = _cli.parser.parse_args(argv) 647 | self.assertParser(_cli, parser_args, argv) 648 | self.assertTrue(_cli.client.get_notification.called) 649 | 650 | def test_notification_mark(self): 651 | argv = ['notification', 'mark', '1,2,3'] 652 | _cli = self.cli(argv) 653 | parser_args = _cli.parser.parse_args(argv) 654 | self.assertParser(_cli, parser_args, argv) 655 | self.assertTrue(_cli.client.mark_notifications_as_read.called) 656 | 657 | def test_auth_url(self): 658 | argv = ['auth', 'url'] 659 | _cli = self.cli(argv) 660 | parser_args = _cli.parser.parse_args(argv) 661 | self.assertParser(_cli, parser_args, argv) 662 | self.assertTrue(_cli.client.get_auth_url.called) 663 | 664 | def test_auth_set_user_auth(self): 665 | argv = ['auth', 'set-user-auth', '123'] 666 | _cli = self.cli(argv) 667 | parser_args = _cli.parser.parse_args(argv) 668 | self.assertParser(_cli, parser_args, argv) 669 | self.assertTrue(_cli.client.authorize.called) 670 | -------------------------------------------------------------------------------- /imgur_cli/cli_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import imgurpython 4 | 5 | from imgur_cli import exceptions 6 | from imgur_cli.utils import (cli_arg, cli_subparser, data_fields, generate_output, 7 | format_comment_tree) 8 | from imgur_cli.utils import cli_subparser 9 | from imgur_cli.utils import data_fields 10 | from imgur_cli.utils import generate_output 11 | 12 | 13 | SUBPARSERS = {'gallery': 'Gallery subparser', 'album': 'Album subparser', 14 | 'image': 'Image subparser', 'comment': 'Comment subparser', 15 | 'memegen': 'Memegen subparser', 'account': 'Account subparser', 16 | 'conversation': 'Conversation subparser', 17 | 'notification': 'Notification subparser', 18 | 'auth': 'Authentication subparser'} 19 | 20 | 21 | @cli_subparser('account') 22 | @cli_arg('username', help='Username of Account') 23 | def cmd_account_user(client, args): 24 | """ 25 | Request standard user information. If you need the username for the account 26 | that is logged in, it is returned in the request for an access token 27 | """ 28 | account_user = client.get_account(args.username) 29 | data = account_user.__dict__ 30 | generate_output({'account_user': data}) 31 | 32 | 33 | @cli_subparser('account') 34 | @cli_arg('username', help='Username of Account') 35 | @cli_arg('--output-file', default=None, metavar='', 36 | help='Save output to a JSON file') 37 | def cmd_account_gallery_favorites(client, args): 38 | """Return the images the user has favorited in the gallery""" 39 | gallery_favorites = client.get_gallery_favorites(args.username) 40 | data = [item.__dict__ for item in gallery_favorites] 41 | generate_output({'gallery_favorites': data}, args.output_file) 42 | 43 | 44 | @cli_subparser('account') 45 | @cli_arg('username', help='Username of Account') 46 | @cli_arg('--output-file', default=None, metavar='', 47 | help='Save output to a JSON file') 48 | def cmd_account_favorites(client, args): 49 | """ 50 | Returns the users favorited images, only accessible if you're logged 51 | in as the user 52 | """ 53 | account_favorites = client.get_account_favorites(args.username) 54 | data = [item.__dict__ for item in account_favorites] 55 | generate_output({'account_favorites': data}, args.output_file) 56 | 57 | 58 | @cli_subparser('account') 59 | @cli_arg('username', help='Username of Account') 60 | @cli_arg('--page', default=0, metavar='', type=int, 61 | help='The data paging number (defaults to %(default)s)') 62 | @cli_arg('--output-file', default=None, metavar='', 63 | help='Save output to a JSON file') 64 | def cmd_account_submissions(client, args): 65 | """Return the images a user has submitted to the gallery""" 66 | account_submissions = client.get_account_submissions(args.username, args.page) 67 | data = [item.__dict__ for item in account_submissions] 68 | generate_output({'account_submissions': data}, args.output_file) 69 | 70 | 71 | @cli_subparser('account') 72 | @cli_arg('username', help='Username of Account') 73 | def cmd_account_settings(client, args): 74 | """ 75 | Returns the account settings, only accessible if you're logged 76 | in as the user 77 | """ 78 | account_settings = client.get_account_settings(args.username) 79 | data = account_settings.__dict__ 80 | generate_output({'account_settings': data}) 81 | 82 | 83 | @cli_subparser('account') 84 | @cli_arg('user', help='Username of Account') 85 | @cli_arg('--bio', metavar='', help='The biography of the user, ' 86 | 'is displayed in the gallery profile page') 87 | @cli_arg('--public-images', metavar='', 88 | choices=['true', 'false'], help='Set the users images to private ' 89 | 'or public by default') 90 | @cli_arg('--messaging-enabled', metavar='', 91 | choices=['true', 'false'], help='Allows the user to enable or ' 92 | 'disable private messages') 93 | @cli_arg('--album-privacy', metavar='', 94 | choices=['public', 'hidden', 'secret'], help='public | hidden | secret - ' 95 | 'Sets the default privacy level of albums the users creates') 96 | @cli_arg('--accepted-gallery-terms', metavar='', 97 | choices=['true', 'false'], help='The user agreement to the Imgur ' 98 | 'Gallery terms') 99 | @cli_arg('--username', metavar='', 100 | help='A valid Imgur username (between 4 and 63 alphanumeric characters)') 101 | def cmd_account_change_settings(client, args): 102 | """Updates the account settings for a given user, the user must be logged in""" 103 | fields = data_fields(args, client.allowed_account_fields) 104 | account_settings = client.change_account_settings(args.user, fields) 105 | generate_output({'account_settings': account_settings}) 106 | 107 | 108 | @cli_subparser('account') 109 | @cli_arg('username', help='Username of Account') 110 | def cmd_account_verification_status(client, args): 111 | """Checks to see if user has verified their email address""" 112 | email_verification_status = client.get_email_verification_status(args.username) 113 | generate_output({'email_verification_status': email_verification_status}) 114 | 115 | 116 | @cli_subparser('account') 117 | @cli_arg('username', help='Username of Account') 118 | def cmd_account_send_verification(client, args): 119 | """ 120 | Sends an email to the user to verify that their email is valid to upload to 121 | gallery. Must be logged in as the user to send 122 | """ 123 | verification_email = client.send_verification_email(args.username) 124 | generate_output({'verification_email': verification_email}) 125 | 126 | 127 | @cli_subparser('account') 128 | @cli_arg('username', help='Username of Account') 129 | @cli_arg('--page', default=0, metavar='', type=int, 130 | help='Page number (defaults to %(default)s)') 131 | @cli_arg('--output-file', default=None, metavar='', 132 | help='Save output to a JSON file') 133 | def cmd_account_albums(client, args): 134 | """ 135 | Get all the albums associated with the account. Must be logged in as the user 136 | to see secret and hidden albums 137 | """ 138 | account_albums = client.get_account_albums(args.username, args.page) 139 | data = [item.__dict__ for item in account_albums] 140 | generate_output({'account_albums': data}, args.output_file) 141 | 142 | 143 | @cli_subparser('account') 144 | @cli_arg('username', help='Username of Account') 145 | @cli_arg('--page', default=0, metavar='', type=int, 146 | help='Page number (defaults to %(default)s)') 147 | def cmd_account_album_ids(client, args): 148 | """Return an array of all of the album IDs""" 149 | account_album_ids = client.get_account_album_ids(args.username, args.page) 150 | generate_output({'account_album_ids': account_album_ids}) 151 | 152 | 153 | @cli_subparser('account') 154 | @cli_arg('username', help='Username of Account') 155 | def cmd_account_album_count(client, args): 156 | """Return the total number of albums associated with the account""" 157 | account_album_count = client.get_account_album_count(args.username) 158 | generate_output({'account_album_count': account_album_count}) 159 | 160 | 161 | @cli_subparser('account') 162 | @cli_arg('username', help='Username of Account') 163 | @cli_arg('--sort', default='newest', metavar='', 164 | choices=['best', 'worst', 'oldest', 'newest'], 165 | help='best | worst | oldest | newest - defaults to %(default)s') 166 | @cli_arg('--page', default=0, metavar='', type=int, 167 | help='Page number (defaults to %(default)s)') 168 | def cmd_account_comments(client, args): 169 | """Return the comments the user has created""" 170 | account_comments = client.get_account_comments(args.username, args.sort, 171 | args.page) 172 | data = format_comment_tree(account_comments) 173 | generate_output({'account_comments': data}) 174 | 175 | 176 | @cli_subparser('account') 177 | @cli_arg('username', help='Username of Account') 178 | @cli_arg('--sort', default='newest', metavar='', 179 | choices=['best', 'worst', 'oldest', 'newest'], 180 | help='best | worst | oldest | newest - defaults to %(default)s') 181 | @cli_arg('--page', default=0, metavar='', type=int, 182 | help='Page number (defaults to %(default)s)') 183 | def cmd_account_comment_ids(client, args): 184 | """Return an array of all of the comment IDs""" 185 | account_comment_ids = client.get_account_comment_ids(args.username, args.sort, 186 | args.page) 187 | generate_output({'account_comment_ids': account_comment_ids}) 188 | 189 | 190 | @cli_subparser('account') 191 | @cli_arg('username', help='Username of Account') 192 | def cmd_account_comment_count(client, args): 193 | """Return a count of all of the comments associated with the account""" 194 | account_comment_count = client.get_account_comment_count(args.username) 195 | generate_output({'account_comment_count': account_comment_count}) 196 | 197 | 198 | @cli_subparser('account') 199 | @cli_arg('username', help='Username of Account') 200 | @cli_arg('--page', default=0, metavar='', type=int, 201 | help='Page number (defaults to %(default)s)') 202 | @cli_arg('--output-file', default=None, metavar='', 203 | help='Save output to a JSON file') 204 | def cmd_account_images(client, args): 205 | """Return all of the images associated with the account""" 206 | account_images = client.get_account_images(args.username, args.page) 207 | data = [item.__dict__ for item in account_images] 208 | generate_output({'account_images': data}, args.output_file) 209 | 210 | 211 | @cli_subparser('account') 212 | @cli_arg('username', help='Username of Account') 213 | @cli_arg('--page', default=0, metavar='', type=int, 214 | help='Page number (defaults to %(default)s)') 215 | def cmd_account_image_ids(client, args): 216 | """Returns an array of Image IDs that are associated with the account""" 217 | account_image_ids = client.get_account_image_ids(args.username, args.page) 218 | generate_output({'account_image_ids': account_image_ids}) 219 | 220 | 221 | @cli_subparser('account') 222 | @cli_arg('username', help='Username of Account') 223 | def cmd_account_image_count(client, args): 224 | """Return the total number of albums associated with the account""" 225 | account_image_count = client.get_account_images_count(args.username) 226 | generate_output({'account_image_count': account_image_count}) 227 | 228 | 229 | @cli_subparser('album') 230 | @cli_arg('album_id', help='Album ID') 231 | def cmd_album_id(client, args): 232 | """Get information about a specific album""" 233 | album = client.get_album(args.album_id) 234 | data = album.__dict__ 235 | generate_output({'album': data}) 236 | 237 | 238 | @cli_subparser('album') 239 | @cli_arg('album_id', help='Album ID') 240 | @cli_arg('--output-file', default=None, metavar='', 241 | help='Save output to a JSON file') 242 | def cmd_album_images(client, args): 243 | """Return all of the images in the album""" 244 | album_images = client.get_album_images(args.album_id) 245 | data = [item.__dict__ for item in album_images] 246 | generate_output({'album_images': data}, args.output_file) 247 | 248 | 249 | @cli_subparser('album') 250 | @cli_arg('--ids', metavar='', help='Comma separated list of image ids that you ' 251 | 'want to be included in the album; you have to be logged in as the user ' 252 | 'for adding the image ids') 253 | @cli_arg('--title', metavar='', help='The title of the album') 254 | @cli_arg('--description', metavar='<description>', 255 | help='The description of the album') 256 | @cli_arg('--privacy', metavar='<privacy>', choices=['public', 'hidden', 'secret'], 257 | help="Sets the privacy level of the album." 258 | "Values are : public | hidden | secret." 259 | "Defaults to user's privacy settings for logged in users") 260 | @cli_arg('--layout', metavar='<layout>', 261 | choices=['blog', 'grid', 'horizontal', 'vertical'], 262 | help='Sets the layout to display the album. ' 263 | 'Values are : blog | grid | horizontal | vertical') 264 | @cli_arg('--cover', metavar='<cover>', 265 | help='The ID of an image that you want to be the cover of the album; ' 266 | 'you have to be logged in as the user') 267 | def cmd_album_create(client, args): 268 | """ 269 | Create a new album. Optional parameter of ids is an array of image ids to 270 | add to the album; you have to be logged in as the user for adding the image ids. 271 | 272 | This method is available without authenticating an account, and may be used 273 | merely by sending "Authorization: Client-ID {client_id}" in the request headers. 274 | Doing so will create an anonymous album which is not tied to an account 275 | """ 276 | fields = data_fields(args, client.allowed_album_fields) 277 | album = client.create_album(fields) 278 | generate_output({'album': album}) 279 | 280 | 281 | @cli_subparser('album') 282 | @cli_arg('album_id', help='Album ID') 283 | @cli_arg('--ids', metavar='<ids>', help='Comma separated list of image ids that you ' 284 | 'want to be included in the album; you have to be logged in as the user ' 285 | 'for adding the image ids') 286 | @cli_arg('--title', metavar='<title>', help='The title of the album') 287 | @cli_arg('--description', metavar='<description>', 288 | help='The description of the album') 289 | @cli_arg('--privacy', metavar='<privacy>', choices=['public', 'hidden', 'secret'], 290 | help="Sets the privacy level of the album." 291 | "Values are : public | hidden | secret." 292 | "Defaults to user's privacy settings for logged in users") 293 | @cli_arg('--layout', metavar='<layout>', 294 | choices=['blog', 'grid', 'horizontal', 'vertical'], 295 | help='Sets the layout to display the album. ' 296 | 'Values are : blog | grid | horizontal | vertical') 297 | @cli_arg('--cover', metavar='<cover>', 298 | help='The ID of an image that you want to be the cover of the album; ' 299 | 'you have to be logged in as the user') 300 | def cmd_album_update(client, args): 301 | """ 302 | Update the information of an album. For anonymous albums, {album} should be the 303 | deletehash that is returned at creation 304 | """ 305 | fields = data_fields(args, client.allowed_album_fields) 306 | album = client.update_album(args.album_id, fields) 307 | generate_output({'album': album}) 308 | 309 | 310 | @cli_subparser('album') 311 | @cli_arg('album_id', help='Album ID') 312 | def cmd_album_delete(client, args): 313 | """ 314 | Delete an album with a given ID. You are required to be logged in as the user 315 | to delete the album. For anonymous albums, {album} should be the deletehash 316 | that is returned at creation 317 | """ 318 | delete_album = client.album_delete(args.album_id) 319 | generate_output({'delete_album': delete_album}) 320 | 321 | 322 | @cli_subparser('album') 323 | @cli_arg('album_id', help='Album ID') 324 | def cmd_album_favorite(client, args): 325 | """ 326 | Favorite an album with a given ID. The user is required to be logged in to 327 | favorite the album 328 | """ 329 | favorite_album = client.album_favorite(args.album_id) 330 | generate_output({'favorite_album': favorite_album}) 331 | 332 | 333 | @cli_subparser('album') 334 | @cli_arg('album_id', help='Album ID') 335 | @cli_arg('ids', help='Comma separated list of image ids that you want to be added ' 336 | 'to the album') 337 | def cmd_album_set_images(client, args): 338 | """ 339 | Sets the images for an album, removes all other images and only uses the images 340 | in this request. For anonymous albums, {album} should be the deletehash that 341 | is returned at creation 342 | """ 343 | set_images = client.album_set_images(args.album_id, args.ids) 344 | generate_output({'set_images': set_images}) 345 | 346 | 347 | @cli_subparser('album') 348 | @cli_arg('album_id', help='Album ID') 349 | @cli_arg('ids', help='Comma separated list of image ids that you want to be added ' 350 | 'to the album') 351 | def cmd_album_add_images(client, args): 352 | """ 353 | Add images for an album from a given comma separated list of image ids. 354 | For anonymous albums, {album} should be the deletehash that is returned 355 | at creation 356 | """ 357 | add_images = client.album_add_images(args.album_id, args.ids) 358 | generate_output({'add_images': add_images}) 359 | 360 | 361 | @cli_subparser('album') 362 | @cli_arg('album_id', help='Album ID') 363 | @cli_arg('ids', help='Comma separated list of image ids that you want to be removed ' 364 | 'to the album') 365 | def cmd_album_remove_images(client, args): 366 | """ 367 | Remove images for an album from a given comma separated list of image ids. 368 | For anonymous albums, {album} should be the deletehash that is returned 369 | at creation 370 | """ 371 | remove_images = client.album_remove_images(args.album_id, args.ids) 372 | generate_output({'remove_images': remove_images}) 373 | 374 | 375 | @cli_subparser('comment') 376 | @cli_arg('comment_id', type=int, help='Comment ID') 377 | def cmd_comment_id(client, args): 378 | """Get information about a specific comment""" 379 | comment = client.get_comment(args.comment_id) 380 | data = comment.__dict__ 381 | generate_output({'comment': data}) 382 | 383 | 384 | @cli_subparser('comment') 385 | @cli_arg('comment_id', type=int, help='Comment ID') 386 | def cmd_comment_delete(client, args): 387 | """Delete a comment by the given id""" 388 | delete_comment = client.delete_comment(args.comment_id) 389 | generate_output({'delete_comment': delete_comment}) 390 | 391 | 392 | @cli_subparser('comment') 393 | @cli_arg('comment_id', type=int, help='Comment ID') 394 | @cli_arg('--output-file', default=None, metavar='<output_file>', 395 | help='Save output to a JSON file') 396 | def cmd_comment_replies(client, args): 397 | """Get the comment with all of the replies for the comment""" 398 | comment_replies = client.get_comment_replies(args.comment_id) 399 | data = format_comment_tree(comment_replies) 400 | generate_output({'comment_replies': data}, args.output_file) 401 | 402 | 403 | @cli_subparser('comment') 404 | @cli_arg('comment_id', type=int, help='Comment ID') 405 | @cli_arg('image_id', help='Image ID') 406 | @cli_arg('comment', help='The comment text, this is what will be displayed') 407 | def cmd_comment_reply(client, args): 408 | """Create a reply for the given comment""" 409 | comment_reply = client.post_comment_reply(args.comment_id, args.image_id, 410 | args.comment) 411 | generate_output({'comment_reply': comment_reply}) 412 | 413 | 414 | @cli_subparser('comment') 415 | @cli_arg('comment_id', type=int, help='Comment ID') 416 | @cli_arg('--vote', default='up', metavar='<vote>', choices=['up', 'down'], 417 | help="'up' or 'down'") 418 | def cmd_comment_vote(client, args): 419 | """Vote on a comment. The {vote} variable can only be set as "up" or "down""" 420 | comment_vote = client.comment_vote(args.comment_id, args.vote) 421 | generate_output({'comment_vote': comment_vote}) 422 | 423 | 424 | @cli_subparser('comment') 425 | @cli_arg('comment_id', type=int, help='Comment ID') 426 | def cmd_comment_report(client, args): 427 | """Report a comment for being inappropriate""" 428 | comment_report = client.comment_report(args.comment_id) 429 | generate_output({'comment_report': comment_report}) 430 | 431 | 432 | @cli_subparser('conversation') 433 | @cli_arg('--output-file', default=None, metavar='<output_file>', 434 | help='Save output to a JSON file') 435 | def cmd_conversation_list(client, args): 436 | """Get list of all conversations for the logged in user""" 437 | conversation_list = client.conversation_list() 438 | data = [item.__dict__ for item in conversation_list] 439 | generate_output({'conversation_list': data}, args.output_file) 440 | 441 | 442 | @cli_subparser('conversation') 443 | @cli_arg('conversation_id', type=int, help='Conversation ID') 444 | @cli_arg('--page', default=1, metavar='<page>', type=int, 445 | help='Page of message thread. Starting at 1 for the most recent 25 ' 446 | 'messages and counting upwards (defaults to %(default)s)') 447 | @cli_arg('--offset', default=0, metavar='<offset>', type=int, 448 | help='Additional offset in current page (defaults to %(default)s)') 449 | @cli_arg('--output-file', default=None, metavar='<output_file>', 450 | help='Save output to a JSON file') 451 | def cmd_conversation_id(client, args): 452 | """Get information about a specific conversation. Includes messages""" 453 | conversation = client.get_conversation(args.conversation_id, 454 | args.page, args.offset) 455 | data = conversation.__dict__ 456 | try: 457 | data['messages'] = [item.__dict__ for item in data['messages']] 458 | except KeyError: 459 | pass 460 | generate_output({'conversation': data}) 461 | 462 | 463 | @cli_subparser('conversation') 464 | @cli_arg('recipient', help='The recipient username, this person will receive ' 465 | 'the message') 466 | @cli_arg('body', help='The message body') 467 | def cmd_conversation_create(client, args): 468 | """Create a new message""" 469 | create_message = client.create_message(args.recipient, args.body) 470 | generate_output({'create_message': create_message}) 471 | 472 | 473 | @cli_subparser('conversation') 474 | @cli_arg('conversation_id', type=int, help='Conversation ID') 475 | def cmd_conversation_delete(client, args): 476 | """Delete a conversation by the given ID""" 477 | delete_conversation = client.delete_conversation(args.conversation_id) 478 | generate_output({'delete_conversation': delete_conversation}) 479 | 480 | 481 | @cli_subparser('conversation') 482 | @cli_arg('username', help='Username of sender to report') 483 | def cmd_conversation_report(client, args): 484 | """Report a user for sending messages that are against the Terms of Service""" 485 | report_sender = client.report_sender(args.username) 486 | generate_output({'report_sender': report_sender}) 487 | 488 | 489 | @cli_subparser('conversation') 490 | @cli_arg('username', help='Username of sender to block') 491 | def cmd_conversation_block(client, args): 492 | """Block the user from sending messages to the user that is logged in""" 493 | block_sender = client.block_sender(args.username) 494 | generate_output({'block_sender': block_sender}) 495 | 496 | 497 | @cli_subparser('gallery') 498 | @cli_arg('--section', default='hot', metavar='<section>', 499 | choices=['hot', 'top', 'user'], 500 | help='hot | top | user - defaults to hot') 501 | @cli_arg('--sort', default='viral', metavar='<sort>', 502 | choices=['viral', 'top', 'time', 'rising'], 503 | help='viral | top | time | rising (only available with user section) - ' 504 | 'defaults to viral') 505 | @cli_arg('--page', default=0, metavar='<page>', type=int, 506 | help='The data paging number (defaults to %(default)s)') 507 | @cli_arg('--window', default='day', metavar='<window>', 508 | choices=['day', 'week', 'month', 'year', 'all'], 509 | help='Change the date range of the request if the section is "top", ' 510 | 'day | week | month | year | all (Defaults to %(default)s)') 511 | @cli_arg('--show-viral', action='store_true', help='Show or hide viral images ' 512 | 'from the "user" section (Defaults to %(default)s)') 513 | @cli_arg('--output-file', default=None, metavar='<output_file>', 514 | help='Save output to a JSON file') 515 | def cmd_gallery_items(client, args): 516 | """View items in the gallery""" 517 | gallery = client.gallery(args.section, args.sort, args.page, args.window, 518 | args.show_viral) 519 | data = [item.__dict__ for item in gallery] 520 | generate_output({'gallery': data}, args.output_file) 521 | 522 | 523 | @cli_subparser('gallery') 524 | @cli_arg('--sort', default='viral', metavar='<sort>', 525 | choices=['viral', 'top', 'time'], 526 | help='viral | top | time - defaults to %(default)s') 527 | @cli_arg('--page', default=0, metavar='<page>', type=int, 528 | help='The data paging number (defaults to %(default)s)') 529 | @cli_arg('--window', default='week', metavar='<window>', 530 | choices=['day', 'week', 'month', 'year', 'all'], 531 | help='Change the date range of the request if the sort is "top", ' 532 | 'day | week | month | year | all (Defaults to %(default)s)') 533 | @cli_arg('--output-file', default=None, metavar='<output_file>', 534 | help='Save output to a JSON file') 535 | def cmd_gallery_memes_subgallery(client, args): 536 | """View images for memes subgallery""" 537 | memes_subgallery = client.memes_subgallery(args.sort, args.page, args.window) 538 | data = [item.__dict__ for item in memes_subgallery] 539 | generate_output({'memes_subgallery': data}, args.output_file) 540 | 541 | 542 | @cli_subparser('gallery') 543 | @cli_arg('subreddit', help='A valid subreddit name') 544 | @cli_arg('--sort', default='time', metavar='<sort>', 545 | choices=['top', 'time'], 546 | help='top | time - defaults to %(default)s') 547 | @cli_arg('--page', default=0, metavar='<page>', type=int, 548 | help='The data paging number (defaults to %(default)s)') 549 | @cli_arg('--window', default='week', metavar='<window>', 550 | choices=['day', 'week', 'month', 'year', 'all'], 551 | help='Change the date range of the request if the sort is "top", ' 552 | 'day | week | month | year | all (Defaults to %(default)s)') 553 | @cli_arg('--output-file', default=None, metavar='<output_file>', 554 | help='Save output to a JSON file') 555 | def cmd_gallery_subreddit_gallery(client, args): 556 | """View gallery images for a subreddit""" 557 | subreddit_gallery = client.subreddit_gallery(args.subreddit, args.sort, 558 | args.window, args.page) 559 | data = [item.__dict__ for item in subreddit_gallery] 560 | generate_output({'subreddit_gallery': data}, args.output_file) 561 | 562 | 563 | @cli_subparser('gallery') 564 | @cli_arg('subreddit', help='A valid subreddit name') 565 | @cli_arg('image_id', help='Image ID') 566 | def cmd_gallery_subreddit_image(client, args): 567 | """View a single image in the subreddit""" 568 | subreddit_image = client.subreddit_image(args.subreddit, args.image_id) 569 | data = subreddit_image.__dict__ 570 | generate_output({'subreddit_image': data}) 571 | 572 | 573 | @cli_subparser('gallery') 574 | @cli_arg('tag', help='The name of the tag') 575 | @cli_arg('--sort', default='viral', metavar='<sort>', 576 | choices=['viral', 'top', 'time'], 577 | help='viral | top | time - defaults to %(default)s') 578 | @cli_arg('--page', default=0, metavar='<page>', type=int, 579 | help='The data paging number (defaults to %(default)s)') 580 | @cli_arg('--window', default='week', metavar='<window>', 581 | choices=['day', 'week', 'month', 'year', 'all'], 582 | help='Change the date range of the request if the sort is "top", ' 583 | 'day | week | month | year | all (Defaults to %(default)s)') 584 | @cli_arg('--output-file', default=None, metavar='<output_file>', 585 | help='Save output to a JSON file') 586 | def cmd_gallery_tag(client, args): 587 | """View images for a gallery tag""" 588 | gallery_tag = client.gallery_tag(args.tag, args.sort, args.page, args.window) 589 | data = gallery_tag.__dict__ 590 | data['items'] = [item.__dict__ for item in data['items']] 591 | generate_output({'gallery_tag': data}) 592 | 593 | 594 | @cli_subparser('gallery') 595 | @cli_arg('tag', help='The name of the tag') 596 | @cli_arg('image_id', help='Image ID') 597 | def cmd_gallery_tag_image(client, args): 598 | """View a single image in a gallery tag""" 599 | gallery_tag_image = client.gallery_tag_image(args.tag, args.image_id) 600 | data = gallery_tag_image.__dict__ 601 | generate_output({'gallery_tag_image': data}) 602 | 603 | 604 | @cli_subparser('gallery') 605 | @cli_arg('item_id', help='Gallery item ID') 606 | def cmd_gallery_item_tags(client, args): 607 | """View tags for a gallery item""" 608 | gallery_item_tags = client.gallery_item_tags(args.item_id) 609 | data = [item.__dict__ for item in gallery_item_tags] 610 | generate_output({'gallery_item_tags': data}) 611 | 612 | 613 | @cli_subparser('gallery') 614 | @cli_arg('item_id', help='Gallery item ID') 615 | @cli_arg('tag', help='The name of the tag') 616 | @cli_arg('vote', choices=['up', 'down'], help="'up' or 'down'") 617 | def cmd_gallery_tag_vote(client, args): 618 | """ 619 | Vote for a tag, 'up' or 'down' vote. Send the same value again to undo a vote 620 | """ 621 | gallery_tag_vote = client.gallery_tag_vote(args.item_id, args.tag, args.vote) 622 | generate_output({'gallery_tag_vote': gallery_tag_vote}) 623 | 624 | 625 | @cli_subparser('gallery') 626 | @cli_arg('q', help="Query string (note: if advanced search parameters are set," 627 | "this query string is ignored). This parameter also supports boolean " 628 | "operators (AND, OR, NOT) and indices (tag: user: title: ext: subreddit: " 629 | "album: meme:). An example compound query would be 'title: cats AND dogs " 630 | "ext: gif'") 631 | @cli_arg('--advanced', default=None, help='Advanced Search Query Parameters') 632 | @cli_arg('--sort', default='time', metavar='<sort>', 633 | choices=['viral', 'top', 'time'], 634 | help='viral | top | time - defaults to %(default)s') 635 | @cli_arg('--page', default=0, metavar='<page>', type=int, 636 | help='The data paging number (defaults to %(default)s)') 637 | @cli_arg('--window', default='all', metavar='<window>', 638 | choices=['day', 'week', 'month', 'year', 'all'], 639 | help='Change the date range of the request if the sort is "top", ' 640 | 'day | week | month | year | all (Defaults to %(default)s)') 641 | @cli_arg('--output-file', default=None, metavar='<output_file>', 642 | help='Save output to a JSON file') 643 | def cmd_gallery_search(client, args): 644 | """Search the gallery with a given query string""" 645 | if args.advanced: 646 | config = data_fields(args.advanced, client.allowed_advanced_search_fields) 647 | else: 648 | config = None 649 | gallery_search = client.gallery_search(args.q, config, args.sort, 650 | args.window, args.page) 651 | data = [item.__dict__ for item in gallery_search] 652 | generate_output({'gallery_search': data}, args.output_file) 653 | 654 | 655 | @cli_subparser('gallery') 656 | @cli_arg('--page', default=0, metavar='<page>', type=int, 657 | help='A page of random gallery images, from 0-50. ' 658 | 'Pages are regenerated every hour (defaults to %(default)s)') 659 | @cli_arg('--output-file', default=None, metavar='<output_file>', 660 | help='Save output to a JSON file') 661 | def cmd_gallery_random(client, args): 662 | """View a random set of gallery items""" 663 | gallery_random = client.gallery_random(args.page) 664 | data = [item.__dict__ for item in gallery_random] 665 | generate_output({'gallery_random': data}, args.output_file) 666 | 667 | 668 | @cli_subparser('gallery') 669 | @cli_arg('item_id', help='Gallery item ID') 670 | @cli_arg('title', help='The title of the image') 671 | @cli_arg('--terms', default=0, type=int, metavar='<terms>', 672 | help='If the user has not accepted our terms yet, this endpoint will ' 673 | 'return an error. To by-pass the terms in general simply set this ' 674 | 'value to 1') 675 | def cmd_gallery_publish(client, args): 676 | """Share an Album or Image to the Imgur Gallery""" 677 | publish_to_imgur = client.share_on_imgur(args.item_id, args.title, args.terms) 678 | generate_output({'publish_to_imgur': publish_to_imgur}) 679 | 680 | 681 | @cli_subparser('gallery') 682 | @cli_arg('item_id', help='Gallery item ID') 683 | def cmd_gallery_remove(client, args): 684 | """ 685 | Remove an item from the gallery. You must be logged in as the owner of the 686 | item to do this action 687 | """ 688 | gallery_remove = client.remove_from_gallery(args.item_id) 689 | generate_output({'gallery_remove': gallery_remove}) 690 | 691 | 692 | @cli_subparser('gallery') 693 | @cli_arg('item_id', help='Gallery item ID') 694 | def cmd_gallery_item(client, args): 695 | """View item in a gallery""" 696 | gallery_item = client.gallery_item(args.item_id) 697 | data = gallery_item.__dict__ 698 | generate_output({'gallery_item': data}) 699 | 700 | 701 | @cli_subparser('gallery') 702 | @cli_arg('item_id', help='Gallery item ID') 703 | def cmd_gallery_report(client, args): 704 | """Report an item in the gallery""" 705 | report_gallery_item = client.report_gallery_item(args.item_id) 706 | generate_output({'report_gallery_item': report_gallery_item}) 707 | 708 | 709 | @cli_subparser('gallery') 710 | @cli_arg('item_id', help='Gallery item ID') 711 | @cli_arg('--vote', default='up', metavar='<vote>', choices=['up', 'down'], 712 | help="'up' or 'down'") 713 | def cmd_gallery_item_vote(client, args): 714 | """ 715 | Vote for an item in the gallery, 'up' or 'down' vote. Send the same value 716 | again to undo a vote 717 | """ 718 | gallery_item_vote = client.gallery_item_vote(args.item_id, args.vote) 719 | generate_output({'gallery_item_vote': gallery_item_vote}) 720 | 721 | 722 | @cli_subparser('gallery') 723 | @cli_arg('item_id', help='Gallery item ID') 724 | @cli_arg('--sort', default='best', metavar='<sort>', choices=['best', 'top', 'new'], 725 | help='best | top | new - defaults to %(default)s') 726 | @cli_arg('--output-file', default=None, metavar='<output_file>', 727 | help='Save output to a JSON file') 728 | def cmd_gallery_comments(client, args): 729 | """Get comments on an item in the gallery""" 730 | gallery_comments = client.gallery_item_comments(args.item_id, args.sort) 731 | data = format_comment_tree(gallery_comments) 732 | generate_output({'gallery_comments': data}, args.output_file) 733 | 734 | 735 | @cli_subparser('gallery') 736 | @cli_arg('item_id', help='Gallery item ID') 737 | @cli_arg('comment', help='The text of the comment') 738 | def cmd_gallery_create_comment(client, args): 739 | create_comment = client.gallery_comment(args.item_id, args.comment) 740 | generate_output({'create_comment': create_comment}) 741 | 742 | 743 | @cli_subparser('gallery') 744 | @cli_arg('item_id', help='Gallery item ID') 745 | def cmd_gallery_comment_ids(client, args): 746 | """List all of the IDs for the comments on an item in the gallery""" 747 | gallery_comment_ids = client.gallery_comment_ids(args.item_id) 748 | generate_output({'gallery_comment_ids': gallery_comment_ids}) 749 | 750 | 751 | @cli_subparser('gallery') 752 | @cli_arg('item_id', help='Gallery item ID') 753 | def cmd_gallery_comment_count(client, args): 754 | """The number of comments on an item in the gallery""" 755 | gallery_comment_count = client.gallery_comment_count(args.item_id) 756 | generate_output({'gallery_comment_count': gallery_comment_count}) 757 | 758 | 759 | @cli_subparser('image') 760 | @cli_arg('image_id', help='Image ID') 761 | def cmd_image_id(client, args): 762 | """Get information about an image""" 763 | image = client.get_image(args.image_id) 764 | data = image.__dict__ 765 | generate_output({'image': data}) 766 | 767 | 768 | @cli_subparser('image') 769 | @cli_arg('type', choices=['file', 'url'], 770 | help="The type of the file that's being sent; file, base64 or URL") 771 | @cli_arg('image', help='A binary file, base64 data, or a URL for an image ' 772 | '(up to 10MB)') 773 | @cli_arg('--name', metavar='<name>', help='The name of the file, ' 774 | 'this is automatically detected if uploading a file with a POST and ' 775 | 'multipart / form-data') 776 | @cli_arg('--title', metavar='<title>', help='The title of the image') 777 | @cli_arg('--album', metavar='<album>', 778 | help='The id of the album you want to add the image to') 779 | @cli_arg('--description', metavar='<description>', 780 | help='The description of the image') 781 | def cmd_image_upload(client, args): 782 | """Upload a new image""" 783 | config = data_fields(args, client.allowed_image_fields) 784 | if args.type == 'file': 785 | image = client.upload_from_path(args.image, config) 786 | else: 787 | image = client.upload_from_url(args.image, config) 788 | generate_output({'image': image}) 789 | 790 | 791 | @cli_subparser('image') 792 | @cli_arg('image_id', help='Image ID') 793 | def cmd_image_delete(client, args): 794 | """ 795 | Deletes an image. For an anonymous image, {id} must be the image's deletehash. 796 | If the image belongs to your account then passing the ID of the image is 797 | sufficient 798 | """ 799 | image_to_delete = client.delete_image(args.image_id) 800 | generate_output({'deleted': image_to_delete}) 801 | 802 | 803 | @cli_subparser('image') 804 | @cli_arg('image_id', help='Image ID') 805 | def cmd_image_favorite(client, args): 806 | """ 807 | Favorite an image with a given ID. The user is required to be logged in to 808 | favorite the image 809 | """ 810 | favorite_image = client.favorite_image(args.image_id) 811 | generate_output({'favorite_image': favorite_image}) 812 | 813 | 814 | @cli_subparser('memegen') 815 | @cli_arg('--output-file', default=None, metavar='<output_file>', 816 | help='Save output to a JSON file') 817 | def cmd_memegen_default_memes(client, args): 818 | """Get the list of default memes""" 819 | default_memes = client.default_memes() 820 | data = [item.__dict__ for item in default_memes] 821 | generate_output({'default_memes': args.output_file}) 822 | 823 | 824 | @cli_subparser('notification') 825 | @cli_arg('--new', default=True, metavar='<new>', choices=['true', 'false'], 826 | help='boolean - false for all notifications, true for only non-viewed ' 827 | 'notification (defaults to %(default)s)') 828 | @cli_arg('--output-file', default=None, metavar='<output_file>', 829 | help='Save output to a JSON file') 830 | def cmd_notification_all(client, args): 831 | """Get all notifications for the user that's currently logged in""" 832 | notifications_all = client.get_notifications(args.new) 833 | notifications_all['messages'] = [message.__dict__ for message in 834 | notifications_all['messages']] 835 | formatted_replies = [] 836 | for reply in notifications_all['replies']: 837 | formatted_reply = reply.__dict__ 838 | formatted_reply['content'] = format_comment_tree(formatted_reply['content']) 839 | formatted_replies.append(formatted_reply) 840 | notifications_all['replies'] = formatted_replies 841 | generate_output({'notifications_all': notifications_all}, args.output_file) 842 | 843 | 844 | @cli_subparser('notification') 845 | @cli_arg('notification_id', type=int, help='Notification ID') 846 | def cmd_notification_id(client, args): 847 | """Returns the data about a specific notification""" 848 | notification = client.get_notification(args.notification_id) 849 | notification = notification.__dict__ 850 | if 'comment' in notification['content']: 851 | notification['content'] = format_comment_tree(notification['content']) 852 | generate_output({'notification': notification}) 853 | 854 | 855 | @cli_subparser('notification') 856 | @cli_arg('ids', help='Comma separated list of notification ids to mark as viewed') 857 | def cmd_notification_mark(client, args): 858 | """ 859 | Marks a notification or multiple notifications as viewed, this way it no 860 | longer shows up in the basic notification request 861 | """ 862 | # Converted to list because in current implemented in imgurpython, client method 863 | # expected a comma separated list of ids 864 | ids = args.ids.split(',') 865 | notifications_marked_as_viewed = client.mark_notifications_as_read(args.ids) 866 | generate_output({'notifications_marked_as_viewed': 867 | notifications_marked_as_viewed}) 868 | 869 | 870 | @cli_subparser('auth') 871 | def cmd_auth_url(client, args): 872 | """Retrieve authorization_url""" 873 | authorization_url = client.get_auth_url('pin') 874 | print("Retrieving authorization url...") 875 | print(authorization_url) 876 | print('Go to authorization url, retrieve the PIN and pass it to the subcommand ' 877 | 'set-user-auth. For example:\n\timgur auth set-user-auth <pin>') 878 | 879 | 880 | @cli_subparser('auth') 881 | @cli_arg('pin', help='Pin obtained from authorization url') 882 | def cmd_auth_set_user_auth(client, args): 883 | """To initialize CLI to take actions on behalf of a user""" 884 | credentials = client.authorize(args.pin, 'pin') 885 | access_token = credentials['access_token'] 886 | refresh_token = credentials['refresh_token'] 887 | client.set_user_auth(access_token, refresh_token) 888 | print('Authorization done!\n\tAccess Token: {0}\n\tRefresh Token: {1}' 889 | .format(access_token, refresh_token)) 890 | --------------------------------------------------------------------------------