11
11
12
12
from irclib .parser import Message
13
13
14
- from cloudbot .client import Client , client , ClientConnectError
14
+ from cloudbot .client import Client , ClientConnectError , client
15
15
from cloudbot .event import Event , EventType , IrcOutEvent
16
16
from cloudbot .util import async_util
17
17
18
18
logger = logging .getLogger ("cloudbot" )
19
19
20
- irc_nick_re = re .compile (r' [A-Za-z0-9^{\}\[\]\-`_|\\]+' )
20
+ irc_nick_re = re .compile (r" [A-Za-z0-9^{\}\[\]\-`_|\\]+" )
21
21
22
- irc_bad_chars = '' .join ([chr (x ) for x in list (range (0 , 1 )) + list (range (4 , 32 )) + list (range (127 , 160 ))])
23
- irc_clean_re = re .compile ('[{}]' .format (re .escape (irc_bad_chars )))
22
+ irc_bad_chars = "" .join (
23
+ [chr (x ) for x in list (range (0 , 1 )) + list (range (4 , 32 )) + list (range (127 , 160 ))]
24
+ )
25
+ irc_clean_re = re .compile ("[{}]" .format (re .escape (irc_bad_chars )))
24
26
25
27
26
28
def irc_clean (dirty ):
27
- return irc_clean_re .sub ('' , dirty )
29
+ return irc_clean_re .sub ("" , dirty )
28
30
29
31
30
32
irc_command_to_event_type = {
31
33
"PRIVMSG" : EventType .message ,
32
34
"JOIN" : EventType .join ,
33
35
"PART" : EventType .part ,
34
36
"KICK" : EventType .kick ,
35
- "NOTICE" : EventType .notice
37
+ "NOTICE" : EventType .notice ,
36
38
}
37
39
38
40
content_params = {
@@ -57,6 +59,14 @@ def irc_clean(dirty):
57
59
"353" : 2 ,
58
60
"366" : 1 ,
59
61
"324" : 1 ,
62
+ "329" : 1 ,
63
+ "332" : 1 ,
64
+ "333" : 1 ,
65
+ # WHOIS
66
+ "310" : 1 ,
67
+ "311" : 1 ,
68
+ "312" : 1 ,
69
+ "318" : 1 ,
60
70
}
61
71
62
72
target_params = {
@@ -69,13 +79,17 @@ def irc_clean(dirty):
69
79
def decode (bytestring ):
70
80
"""
71
81
Tries to decode a bytestring using multiple encoding formats
82
+
83
+ >>> decode(bytes([0x80, 0xbf, 0x81]) + '\u200b '.encode())
84
+ '\u200b '
72
85
"""
73
- for codec in (' utf-8' , 'iso-8859-1' , ' shift_jis' , ' cp1252' ):
86
+ for codec in (" utf-8" , " shift_jis" , " cp1252" ):
74
87
try :
75
88
return bytestring .decode (codec )
76
89
except UnicodeDecodeError :
77
90
continue
78
- return bytestring .decode ('utf-8' , errors = 'ignore' )
91
+
92
+ return bytestring .decode ("utf-8" , errors = "ignore" )
79
93
80
94
81
95
def _get_param (msg : Message , index_map : Mapping [str , int ]) -> Optional [str ]:
@@ -108,16 +122,16 @@ def __init__(self, bot, _type, name, nick, *, channels=None, config=None):
108
122
super ().__init__ (bot , _type , name , nick , channels = channels , config = config )
109
123
110
124
self .target_nick = nick
111
- conn_config = config [' connection' ]
112
- self .use_ssl = conn_config .get (' ssl' , False )
113
- self ._ignore_cert_errors = conn_config .get (' ignore_cert' , False )
114
- self ._timeout = conn_config .get (' timeout' , 300 )
115
- self .server = conn_config [' server' ]
116
- self .port = conn_config .get (' port' , 6667 )
125
+ conn_config = config [" connection" ]
126
+ self .use_ssl = conn_config .get (" ssl" , False )
127
+ self ._ignore_cert_errors = conn_config .get (" ignore_cert" , False )
128
+ self ._timeout = conn_config .get (" timeout" , 300 )
129
+ self .server = conn_config [" server" ]
130
+ self .port = conn_config .get (" port" , 6667 )
117
131
118
132
local_bind = (
119
- conn_config .get (' bind_addr' , False ),
120
- conn_config .get (' bind_port' , 0 ),
133
+ conn_config .get (" bind_addr" , False ),
134
+ conn_config .get (" bind_port" , 0 ),
121
135
)
122
136
if local_bind [0 ] is False :
123
137
local_bind = False
@@ -135,7 +149,7 @@ def __init__(self, bot, _type, name, nick, *, channels=None, config=None):
135
149
def make_ssl_context (self , conn_config ):
136
150
if self .use_ssl :
137
151
ssl_context = ssl .create_default_context ()
138
- client_cert = conn_config .get (' client_cert' )
152
+ client_cert = conn_config .get (" client_cert" )
139
153
if client_cert :
140
154
path = Path (client_cert )
141
155
if path .exists ():
@@ -177,12 +191,17 @@ async def try_connect(self):
177
191
try :
178
192
await self .connect (self ._timeout )
179
193
except (TimeoutError , asyncio .TimeoutError ):
180
- logger .error ("[%s] Timeout occurred while connecting to %s" , self .name , self .describe_server ())
194
+ logger .error (
195
+ "[%s] Timeout occurred while connecting to %s" ,
196
+ self .name ,
197
+ self .describe_server (),
198
+ )
181
199
except (socket .error , socket .gaierror , OSError , ssl .SSLError ):
182
200
logger .error (
183
201
"[%s] Error occurred while connecting to %s (%s)" ,
184
- self .name , self .describe_server (),
185
- traceback .format_exc ().splitlines ()[- 1 ]
202
+ self .name ,
203
+ self .describe_server (),
204
+ traceback .format_exc ().splitlines ()[- 1 ],
186
205
)
187
206
except Exception as e :
188
207
raise ClientConnectError (self .name , self .describe_server ()) from e
@@ -192,18 +211,18 @@ async def try_connect(self):
192
211
sleep_time = random .randrange (self ._timeout )
193
212
canceller = asyncio .shield (self .cancelled_future )
194
213
try :
195
- await asyncio .wait_for (
196
- canceller , timeout = sleep_time
197
- )
198
- except asyncio .CancelledError :
214
+ await asyncio .wait_for (canceller , timeout = sleep_time )
215
+ except (asyncio .CancelledError , asyncio .TimeoutError ):
199
216
pass
200
217
201
218
async def connect (self , timeout = None ):
202
219
"""
203
220
Connects to the IRC server, or reconnects if already connected.
204
221
"""
205
222
if self ._connecting :
206
- raise ValueError ("Attempted to connect while another connect attempt is happening" )
223
+ raise ValueError (
224
+ "Attempted to connect while another connect attempt is happening"
225
+ )
207
226
208
227
self ._connecting = True
209
228
try :
@@ -226,7 +245,11 @@ async def _connect(self, timeout=None):
226
245
optional_params ["local_addr" ] = self .local_bind
227
246
228
247
coro = self .loop .create_connection (
229
- partial (_IrcProtocol , self ), host = self .server , port = self .port , ssl = self .ssl_context , ** optional_params
248
+ partial (_IrcProtocol , self ),
249
+ host = self .server ,
250
+ port = self .port ,
251
+ ssl = self .ssl_context ,
252
+ ** optional_params ,
230
253
)
231
254
232
255
if timeout is not None :
@@ -235,7 +258,9 @@ async def _connect(self, timeout=None):
235
258
self ._transport , self ._protocol = await coro
236
259
237
260
tasks = [
238
- self .bot .plugin_manager .launch (hook , Event (bot = self .bot , conn = self , hook = hook ))
261
+ self .bot .plugin_manager .launch (
262
+ hook , Event (bot = self .bot , conn = self , hook = hook )
263
+ )
239
264
for hook in self .bot .plugin_manager .connect_hooks
240
265
if not hook .clients or self .type in hook .clients
241
266
]
@@ -421,9 +446,13 @@ async def send(self, line, log=True):
421
446
bot = self .bot , hook = out_sieve , conn = self .conn , irc_raw = line
422
447
)
423
448
424
- ok , new_line = await self .bot .plugin_manager .internal_launch (out_sieve , event )
449
+ ok , new_line = await self .bot .plugin_manager .internal_launch (
450
+ out_sieve , event
451
+ )
425
452
if not ok :
426
- logger .warning ("Error occurred in outgoing sieve, falling back to old behavior" )
453
+ logger .warning (
454
+ "Error occurred in outgoing sieve, falling back to old behavior"
455
+ )
427
456
logger .debug ("Line was: %s" , line )
428
457
filtered = False
429
458
break
@@ -461,7 +490,9 @@ def data_received(self, data):
461
490
except Exception :
462
491
logger .exception (
463
492
"[%s] Error occurred while parsing IRC line '%s' from %s" ,
464
- self .conn .name , line , self .conn .describe_server ()
493
+ self .conn .name ,
494
+ line ,
495
+ self .conn .describe_server (),
465
496
)
466
497
else :
467
498
# handle the message, async
@@ -485,22 +516,20 @@ def parse_line(self, line: str) -> Event:
485
516
content = None
486
517
487
518
# Event type
488
- event_type = irc_command_to_event_type .get (
489
- command , EventType .other
490
- )
519
+ event_type = irc_command_to_event_type .get (command , EventType .other )
491
520
target = _get_param (message , target_params )
492
521
493
522
# Parse for CTCP
494
523
if event_type is EventType .message and content_raw .startswith ("\x01 " ):
495
524
possible_ctcp = content_raw [1 :]
496
- if content_raw .endswith (' \x01 ' ):
525
+ if content_raw .endswith (" \x01 " ):
497
526
possible_ctcp = possible_ctcp [:- 1 ]
498
527
499
- if ' \x01 ' in possible_ctcp :
528
+ if " \x01 " in possible_ctcp :
500
529
logger .debug (
501
530
"[%s] Invalid CTCP message received, "
502
531
"treating it as a mornal message" ,
503
- self .conn .name
532
+ self .conn .name ,
504
533
)
505
534
ctcp_text = None
506
535
else :
@@ -518,6 +547,7 @@ def parse_line(self, line: str) -> Event:
518
547
519
548
# Channel
520
549
channel = _get_param (message , chan_params )
550
+
521
551
prefix = message .prefix
522
552
if prefix is None :
523
553
nick = None
@@ -539,13 +569,29 @@ def parse_line(self, line: str) -> Event:
539
569
# Channel for a PM is the sending user
540
570
if channel == self .conn .nick .lower ():
541
571
channel = nick .lower ()
572
+ else :
573
+ # If the channel isn't set, it's the sending user/server
574
+ channel = nick .lower () if nick else nick
542
575
543
576
# Set up parsed message
544
577
# TODO: Do we really want to send the raw `prefix` and `command_params` here?
545
578
event = Event (
546
- bot = self .bot , conn = self .conn , event_type = event_type , content_raw = content_raw , content = content ,
547
- target = target , channel = channel , nick = nick , user = user , host = host , mask = mask , irc_raw = line ,
548
- irc_prefix = mask , irc_command = command , irc_paramlist = command_params , irc_ctcp_text = ctcp_text
579
+ bot = self .bot ,
580
+ conn = self .conn ,
581
+ event_type = event_type ,
582
+ content_raw = content_raw ,
583
+ content = content ,
584
+ target = target ,
585
+ channel = channel ,
586
+ nick = nick ,
587
+ user = user ,
588
+ host = host ,
589
+ mask = mask ,
590
+ irc_raw = line ,
591
+ irc_prefix = mask ,
592
+ irc_command = command ,
593
+ irc_paramlist = command_params ,
594
+ irc_ctcp_text = ctcp_text ,
549
595
)
550
596
return event
551
597
0 commit comments