1
+ import json
1
2
from typing import TypedDict
2
3
3
4
from aiohttp import ClientConnectorError , ClientSession
6
7
7
8
log = logging .get_logger (__name__ )
8
9
10
+ DEFAULT_PASTEBIN = "https://paste.pythondiscord.com"
9
11
FAILED_REQUEST_ATTEMPTS = 3
10
- MAX_PASTE_SIZE = 128 * 1024 # 128kB
12
+ MAX_PASTE_SIZE = 512 * 1024 # 512kB
11
13
"""The maximum allows size of a paste, in bytes."""
12
14
15
+ # A dict where the keys are paste services and the keys are the lists of lexers that paste service supports.
16
+ _lexers_supported_by_pastebin : dict [str ,list [str ]] = {}
17
+
13
18
14
19
class PasteResponse (TypedDict ):
15
20
"""
@@ -27,6 +32,9 @@ class PasteResponse(TypedDict):
27
32
class PasteUploadError (Exception ):
28
33
"""Raised when an error is encountered uploading to the paste service."""
29
34
35
+ class PasteUnsupportedLexerError (Exception ):
36
+ """Raised when an unsupported lexer is used."""
37
+
30
38
31
39
class PasteTooLongError (Exception ):
32
40
"""Raised when content is too large to upload to the paste service."""
@@ -35,21 +43,21 @@ class PasteTooLongError(Exception):
35
43
async def send_to_paste_service (
36
44
* ,
37
45
contents : str ,
38
- paste_url : str ,
39
46
http_session : ClientSession ,
40
47
file_name : str = "" ,
41
48
lexer : str = "python" ,
49
+ paste_url : str = DEFAULT_PASTEBIN ,
42
50
max_size : int = MAX_PASTE_SIZE ,
43
51
) -> PasteResponse :
44
52
"""
45
53
Upload some contents to the paste service.
46
54
47
55
Args:
48
56
contents: The content to upload to the paste service.
49
- paste_url: The base url to the paste service.
50
57
http_session (aiohttp.ClientSession): The session to use when POSTing the content to the paste service.
51
58
file_name: The name of the file to save to the paste service.
52
59
lexer: The lexer to save the content with.
60
+ paste_url: The base url to the paste service.
53
61
max_size: The max number of bytes to be allowed. Anything larger than :obj:`MAX_PASTE_SIZE` will be rejected.
54
62
55
63
Raises:
@@ -63,6 +71,18 @@ async def send_to_paste_service(
63
71
if max_size > MAX_PASTE_SIZE :
64
72
raise ValueError (f"`max_length` must not be greater than { MAX_PASTE_SIZE } " )
65
73
74
+ if paste_url not in _lexers_supported_by_pastebin :
75
+ try :
76
+ async with http_session .get (f"{ paste_url } /api/v1/lexer" ) as response :
77
+ response_json = await response .json () # Supported lexers are the keys.
78
+ except Exception :
79
+ raise PasteUploadError ("Could not fetch supported lexers from selected paste_url." )
80
+
81
+ _lexers_supported_by_pastebin [paste_url ] = list (response_json )
82
+
83
+ if lexer not in _lexers_supported_by_pastebin [paste_url ]:
84
+ raise PasteUnsupportedLexerError (f"Lexer '{ lexer } ' not supported by pastebin." )
85
+
66
86
contents_size = len (contents .encode ())
67
87
if contents_size > max_size :
68
88
log .info ("Contents too large to send to paste service." )
@@ -79,7 +99,7 @@ async def send_to_paste_service(
79
99
for attempt in range (1 , FAILED_REQUEST_ATTEMPTS + 1 ):
80
100
try :
81
101
async with http_session .post (f"{ paste_url } /api/v1/paste" , json = payload ) as response :
82
- response_json = await response .json ()
102
+ response_text = await response .text ()
83
103
except ClientConnectorError :
84
104
log .warning (
85
105
f"Failed to connect to paste service at url { paste_url } , "
@@ -94,12 +114,17 @@ async def send_to_paste_service(
94
114
continue
95
115
96
116
if response .status == 400 :
117
+ if "Text exceeds size limit" in response_text :
118
+ log .info ("Contents too large to send to paste service after lexing." )
119
+ raise PasteTooLongError (
120
+ f"Contents of size { contents_size } greater than maximum size { max_size } after lexing"
121
+ )
97
122
log .warning (
98
123
f"Paste service returned error { response_json ['message' ]} with status code { response .status } , "
99
124
f"trying again ({ attempt } /{ FAILED_REQUEST_ATTEMPTS } )."
100
125
)
101
126
continue
102
-
127
+ response_json = json . loads ( response_text )
103
128
if response .status == 200 :
104
129
log .info (f"Successfully uploaded contents to { response_json ['link' ]} ." )
105
130
return PasteResponse (link = response_json ["link" ], removal = response_json ["removal" ])
0 commit comments