#!/usr/bin/env python
"""
NAME
hb - Uploads data to a hastebin server
SYNOPSYS
hb [OPTION]... [FILE]...
DESCRIPTION
When invoked without any file name, hb will reads from standard
input. If the caller supplies the file names, each of them will be
uploaded separately. Upon completion, hb will print to the standard
output the URL for each upload.
-h, --help
Displays help and exit
CONFIGURATION
By default, hb will upload to http://localhost:7777. If you
want to upload to a different server, there are three ways to
configure it.
The first is to create a configuration file in the same directory
of the script and name it `hb.json`. In a multiple-user
environment, this configuration is meant to be global to all
the users.
The second is to create a per-user configuration file in
`~/.hb.json`.
Below is an example of such configuration file:
{
"HASTE_SERVER": "http://my-hastebin.com"
}
The third is to use an environment variable. Here is an example
for the bash shell:
export HASTE_SERVER=http://my-hastebin.com
Below is the order of look-up to find the server, with the
environment variable having the highest precedence:
1. The environment variable
2. The global configuration file
3. The per-user configuration file
4. If all failed, use http://localhost:7777
LOG
hb keeps a log of all the URLs in ~/.hb.log
AUTHOR
Written by Hai Vu (haivu2004 at gmail dot com)
"""
import collections
import json
import logging
import os
import pathlib
import sys
import urllib.request
logging.basicConfig(level=os.getenv('LOGLEVEL', 'WARN'))
LOGGER = logging.getLogger(__name__)
CONFIG = {
'HASTE_SERVER': 'http://localhost:7777'
}
def read_config_file(path):
"""
Reads the user configuration from `path` and returns a dictionary.
If the configuration file is not found, returns an empty
dictionary.
"""
try:
with open(path, encoding='utf-8') as file_handle:
config = json.load(file_handle)
except FileNotFoundError:
config = dict()
return config
def get_server_url():
"""
Returns the server URL in the following search order. See the
document above for search order.
"""
user_config = read_config_file(pathlib.Path('~/.hb.json').expanduser())
global_config_path = pathlib.Path(__file__).with_name('hb.json')
global_config = read_config_file(global_config_path)
all_configs = collections.ChainMap(
os.environ,
user_config,
global_config,
CONFIG)
server_url = all_configs['HASTE_SERVER']
if server_url.endswith('/'):
server_url = server_url.rstrip('/')
LOGGER.debug('Server URL: %s', server_url)
return server_url
def truncate(text):
"""
given some text, truncate it to a certain size for display and
replace all the new lines with spaces
"""
return text[:40].replace('\n', ' ')
def upload(url, data):
"""
Upload the data to the hastebin server. The `url` parameter indicates the
server's URL, e.g. 'https://hastebin.com'. The `data` parameter can either
be a string, a bytes stream, or a file-like object serving raw bytes. Upon
completion, the function returns a dictionary in which the key `key` will
provide the token for locating the paste, e.g. `{"key": "xyz"}`. The caller
can combine the server url with this key to create the full URL for
accessing the paste, e.g. https://hastebin.com/xyz.
"""
url = f'{url}/documents'
if isinstance(data, str):
data = data.encode('utf-8')
request = urllib.request.Request(url, data)
with urllib.request.urlopen(request) as response:
output = json.load(response)
return output
def upload_wrapper(url, filename=None):
"""
Uploads a file and perform other administrative tasks
"""
if filename is None:
data = sys.stdin.read()
preview = truncate(data)
else:
data = open(filename, 'rb')
preview = truncate(data.read(40).decode('utf-8'))
data.seek(0)
output = upload(url, data)
output_url = f'{url}/{output["key"]}'
log_file = pathlib.Path('~/.hb.log').expanduser()
with open(log_file, 'a', encoding='utf-8') as file_handle:
if filename is None:
filename = 'stdin'
output = f'{filename} - {output_url} - {preview}'
print(output)
file_handle.write(output)
file_handle.write('\n')
def show_usage():
"""
Show the usage
"""
print(__doc__)
def main():
""" Entry """
server_url = get_server_url()
if len(sys.argv) > 1:
if sys.argv[1] in {'-h', '--help'}:
show_usage()
return
for filename in sys.argv[1:]:
upload_wrapper(server_url, filename)
else:
upload_wrapper(server_url)
if __name__ == '__main__':
main()
Comments