├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── pastebin.py ├── requirements-development.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | dist: 2 | python setup.py sdist bdist_wheel 3 | 4 | lint: 5 | autoflake --in-place --recursive --remove-unused-variables --remove-all-unused-imports . 6 | isort -rc . 7 | black -l 120 . 8 | 9 | upload: dist 10 | twine upload dist/* 11 | 12 | .PHONY: dist lint upload 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pastebin-cli 2 | 3 | Command-line interface to paste code/text to pastebin.com, written in pure 4 | Python without any external dependency. If your operating system has an old 5 | Python version: this software works from Python 3.5 to 3.8. 6 | 7 | > Note: the command-line works fine but still needs some API features, like 8 | > listing user pastes. 9 | 10 | 11 | ## Installation 12 | 13 | You can either install it using `pip`: 14 | 15 | ```shell 16 | pip install pastebin-cli 17 | ``` 18 | 19 | or download `pastebin.py` executable and put in your `$PATH` (you can rename it 20 | to `pastebin`). 21 | 22 | 23 | ## Usage 24 | 25 | ```shell 26 | $ pastebin --help 27 | usage: pastebin [-h] [--log-level {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}] 28 | [--api-key API_KEY] 29 | {paste,get} ... 30 | 31 | optional arguments: 32 | -h, --help show this help message and exit 33 | --log-level {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET} 34 | --api-key API_KEY 35 | 36 | Subcommands: 37 | {paste,get} 38 | paste Paste a file 39 | get Get a specific paste 40 | ``` 41 | 42 | ```shell 43 | $ ./pastebin paste --help 44 | usage: pastebin paste [-h] [--expires-in {N,10M,1H,1D,1W,2W,1M,6M,1Y}] 45 | [--privacy {public,unlisted,private}] 46 | [--file-format FILE_FORMAT] 47 | filename 48 | 49 | positional arguments: 50 | filename 51 | 52 | optional arguments: 53 | -h, --help show this help message and exit 54 | --expires-in {N,10M,1H,1D,1W,2W,1M,6M,1Y} 55 | --privacy {public,unlisted,private} 56 | --file-format FILE_FORMAT 57 | ``` 58 | -------------------------------------------------------------------------------- /pastebin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2020 Álvaro Justen 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Lesser General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Lesser General Public License for more details. 13 | 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this program. If not, see . 16 | 17 | import argparse 18 | import logging 19 | import mimetypes 20 | import os 21 | import sys 22 | from pathlib import Path 23 | from urllib.parse import urlencode, urljoin, urlparse 24 | from urllib.request import Request, urlopen 25 | 26 | EXPIRES_IN_CHOICES = "N 10M 1H 1D 1W 2W 1M 6M 1Y".split() 27 | PRIVACY_CHOICES = {"public": "0", "unlisted": "1", "private": "2"} 28 | LOG_LEVEL_CHOICES = "CRITICAL ERROR WARNING INFO DEBUG NOTSET".split() 29 | 30 | 31 | def get_logger(level): 32 | logger = logging.getLogger(__name__) 33 | logger.setLevel(level) 34 | 35 | handler = logging.StreamHandler(sys.stdout) 36 | handler.setLevel(level) 37 | formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 38 | handler.setFormatter(formatter) 39 | logger.addHandler(handler) 40 | 41 | return logger 42 | 43 | 44 | def error(message, exit_code): 45 | print("ERROR: {message}".format(message=message), file=sys.stderr) 46 | exit(exit_code) 47 | 48 | 49 | class Pastebin: 50 | base_url = "https://pastebin.com" 51 | 52 | def make_url(self, path): 53 | return urljoin(self.base_url, path) 54 | 55 | def set_logger(self, logger): 56 | self.logger = logger 57 | 58 | def define_file_format(self, filename): 59 | extension = filename.name.rsplit(".", maxsplit=1)[-1] 60 | file_format = mimetypes.types_map[".{extension}".format(extension=extension)] 61 | file_format = file_format.split("/")[-1] 62 | if file_format.startswith("x-"): 63 | file_format = file_format[2:] 64 | return file_format 65 | 66 | def paste(self, args): 67 | filename = Path(args.filename) 68 | if not filename.exists(): 69 | error("file '{filename}' not found.".format(filename=args.filename), 1) 70 | with open(filename) as fobj: 71 | paste_contents = fobj.read() 72 | 73 | url = self.make_url("/api/api_post.php") 74 | post_data = { 75 | "api_dev_key": args.api_key, 76 | "api_option": "paste", 77 | "api_paste_code": paste_contents, 78 | "api_paste_expire_date": args.expires_in, 79 | "api_paste_format": args.file_format or self.define_file_format(filename), 80 | "api_paste_name": filename.name, 81 | "api_paste_private": PRIVACY_CHOICES[args.privacy], 82 | "api_user_key": "", 83 | } 84 | self.logger.debug("POST to {url}: {post_data}".format(url=url, post_data=post_data)) 85 | post_body = urlencode(post_data).encode("utf-8") 86 | request = Request(url, data=post_body, method="POST") 87 | response = urlopen(request) 88 | print(response.read().decode("utf-8")) 89 | 90 | def get(self, args): 91 | # TODO: implement get private paste (for authenticated user) 92 | key = args.paste_key 93 | if key.startswith("https://") or key.startswith("http://"): 94 | key = urlparse(key).path[1:] 95 | url = self.make_url("/raw/{key}".format(key=key)) 96 | response = urlopen(url) 97 | print(response.read().decode("utf-8")) 98 | 99 | 100 | def main(): 101 | api = Pastebin() 102 | 103 | parser = argparse.ArgumentParser() 104 | parser.add_argument( 105 | "--log-level", default=os.environ.get("LOG_LEVEL", "INFO"), choices=LOG_LEVEL_CHOICES, 106 | ) 107 | parser.add_argument("--api-key", default=os.environ.get("PASTEBIN_API_KEY")) 108 | subparsers = parser.add_subparsers(title="Subcommands") 109 | 110 | paste_parser = subparsers.add_parser("paste", help="Paste a file") 111 | paste_parser.add_argument("--expires-in", default="N", choices=EXPIRES_IN_CHOICES) 112 | paste_parser.add_argument("--privacy", default="private", choices=PRIVACY_CHOICES.keys()) 113 | paste_parser.add_argument("--file-format") 114 | paste_parser.add_argument("filename") 115 | paste_parser.set_defaults(func=api.paste) 116 | 117 | get_parser = subparsers.add_parser("get", help="Get a specific paste") 118 | get_parser.add_argument("paste_key") 119 | get_parser.set_defaults(func=api.get) 120 | 121 | args = parser.parse_args() 122 | if not hasattr(args, "func"): # No parameter passed 123 | parser.print_help() 124 | else: 125 | api.set_logger(get_logger(args.log_level)) 126 | args.func(args) 127 | 128 | 129 | if __name__ == "__main__": 130 | main() 131 | -------------------------------------------------------------------------------- /requirements-development.txt: -------------------------------------------------------------------------------- 1 | autoflake 2 | black 3 | isort 4 | setuptools 5 | twine 6 | wheel 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fobj: 4 | long_description = fobj.read() 5 | 6 | 7 | setuptools.setup( 8 | name="pastebin-cli", 9 | version="0.1.0", 10 | author="Álvaro Justen", 11 | author_email="alvarojusten@gmail.com", 12 | description="Command-line interface for pastebin.com API", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://github.com/turicas/pastebin-cli", 16 | packages=setuptools.find_packages(), 17 | py_modules=["pastebin"], 18 | install_requires=[], 19 | entry_points={"console_scripts": ["pastebin = pastebin:main"]}, 20 | classifiers=["Programming Language :: Python :: 3",], 21 | python_requires=">=3.5", 22 | ) 23 | --------------------------------------------------------------------------------