/
hastebin
executable file
·178 lines (147 loc) · 4.99 KB
/
hastebin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env python3
# Project: hastebin
# Author: Kevin Morris <kevr@0cost.org>
# Description: A tool which uploads data from stdin to hastebin.com
# Copyright (C) 2017 Kevin Morris <kevr@0cost.org>
# Copyright (C) 2020 Denis Zheleztsov <difrex@lessmore.pw>
import argparse
import json
import os
import sys
from select import select
from subprocess import Popen
import requests
url = os.environ.get("HASTEBIN_SERVER_URL", "https://hastebin.com")
timeout = int(os.environ.get("HASTEBIN_SERVER_TIMEOUT", 5))
github_token = os.environ.get("HASTEBIN_TOKEN", "")
def has_data(fd):
"""Immediately timed out select poll"""
return select([fd], [], [], 0.0) == ([fd], [], [])
def quit(code, msg):
"""Print message to the STDERR and returns exit code"""
sys.stderr.write(msg + "\n")
return code
def get_paste(id, token):
paste_url = f"{url}/raw/{id}"
try:
response = requests.get(
paste_url,
headers={"Authorization": f"Bearer {token}"},
timeout=timeout,
)
except requests.exceptions.ReadTimeout:
return 1, "error: http request timed out"
except requests.exceptions.ConnectTimeout:
return 1, "error: http connection timed out"
if response.status_code != requests.codes.ok:
return 3, f"error: invalid http status code {response.status_code}"
return 0, response.content.decode().rstrip()
def post_paste(text, token):
post_url = f"{url}/documents"
try:
response = requests.post(
post_url,
headers={
"Content-Type": "text/plain",
"Accept": "application/json",
"Authorization": f"Bearer {token}",
},
data=text,
timeout=timeout,
)
except requests.exceptions.ReadTimeout:
return 1, "error: http request timed out"
except requests.exceptions.ConnectTimeout:
return 1, "error: http connection timed out"
if response.status_code != requests.codes.ok:
return 3, f"error: invalid http status code {response.status_code}"
return 0, response.content.decode().rstrip()
def main():
help_description = "Upload text from stdin to HASTEBIN_SERVER_URL. "
help_description += "If [id] is provided,\nthe corresponding paste is "
help_description += "fetched and displayed instead."
footer = """environment variables:
HASTEBIN_CLIPPER (default: 'xclip -sel primary')
HASTEBIN_SERVER_URL (default: 'https://hastebin.com')
HASTEBIN_SERVER_TIMEOUT (default: 5)
"""
parser = argparse.ArgumentParser(
description=help_description,
epilog=footer,
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
"--clip-command",
"-cc",
metavar="clip_command",
default=os.environ.get("HASTEBIN_CLIPPER", "xclip -sel primary"),
help="clipboard command (default: HASTEBIN_CLIPPER)",
)
parser.add_argument(
"--clipboard",
"-c",
metavar="clipboard",
action="store_const",
const=True,
default=False,
help="pipe stdout to --clip-command",
)
parser.add_argument(
"--token",
"-t",
metavar="token",
default=github_token,
help="valid github api token",
)
parser.add_argument(
"id",
nargs="?",
help="when provided, fetches and displays a hastebin paste",
)
args = parser.parse_args()
if not args.token:
return quit(
1,
"error: interacting with hastebin api requires a valid --token,"
" see https://www.toptal.com/developers/hastebin/documentation",
)
if args.id is not None:
http = False
if args.id[:4] == "http":
if args.id[: len(url)] != url:
return quit(1, f"error: URLs must begin with '{url}'")
http = True
paste_id = args.id.split("/")[-1] if http else args.id
if not paste_id:
return quit(1, "error: no id provided")
return_code, response = get_paste(paste_id, args.token)
if return_code:
return quit(return_code, response)
print(response)
else:
if not has_data(sys.stdin):
return quit(1, "error: no data given via stdin")
try:
stdin = sys.stdin.read()
except UnicodeDecodeError:
return quit(2, "error: an error occured reading stdin")
return_code, response = post_paste(stdin.encode("utf-8"), args.token)
if return_code:
return quit(return_code, response)
data = json.loads(response)
paste_url = f'{url}/{data["key"]}'
print(paste_url)
if args.clipboard:
proc = Popen(
[
"/bin/sh",
"-c",
f'echo -n "{paste_url}" | {args.clip_command}',
]
)
proc.wait()
return 0
# main execution
if __name__ == "__main__":
e = main()
exit(e)