├── .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 |
--------------------------------------------------------------------------------