1212# <https://creativecommons.org/publicdomain/zero/1.0/>.
1313
1414# to run this script:
15- # pip3 install --update girc pyyaml docopt
15+ # pip3 install --upgrade girc pyyaml docopt
1616# cd _data
17- # python3 checknetworks.py
17+ # python3 checknetworks.py run su_networks.yml
1818
1919"""checknetworks.py - tests IRCv3 network support file.
2020Usage:
2424Options:
2525 <supportfile> YAML network list [default: su_networks.yml]."""
2626from docopt import docopt
27- import girc
2827import yaml
28+ import os
29+ import ssl
30+ import socket
31+ import traceback
32+ from datetime import datetime
33+ from girc .ircreactor .envelope import RFC1459Message
34+ from girc .ircreactor .events import EventManager
35+
36+ # settings
37+ nick = 'ircv3-tst'
38+ username = 'ircv3'
39+ realname = 'IRCv3 cap/isupport Tester'
40+
41+
42+ class IRCv3Connection :
43+ """Connects to an IRC server at the given address."""
44+ def __init__ (self , hostname , nick , username , realname , ipv6 = False , debug = False ):
45+ self .debug = debug
46+ self ._data = b''
47+
48+ if ipv6 :
49+ family = socket .AF_INET6
50+ else :
51+ family = socket .AF_INET
52+
53+ self .sock = socket .socket (family , socket .SOCK_STREAM )
54+ context = ssl .create_default_context ()
55+ context .check_hostname = False
56+ context .verify_mode = ssl .CERT_NONE
57+ self .sock = context .wrap_socket (self .sock , server_hostname = hostname )
58+ self .sock .connect ((hostname , 6697 ))
59+
60+ # identify
61+ self .send ('CAP LS 302' )
62+ self .send ('NICK {}' .format (nick ))
63+ self .send ('USER {} 0 * :{}' .format (username , realname ))
64+ self .send ('CAP END' )
65+
66+ # events setup
67+ self ._events = EventManager ()
68+ self ._events .register ('irc message 005' , self .on_005 )
69+ self ._events .register ('irc message cap' , self .on_cap )
70+ self ._events .register ('irc message ping' , self .on_ping )
71+ self ._events .register ('irc message 376' , self .on_connected )
72+ self ._events .register ('irc message 422' , self .on_connected )
73+
74+ # basic data
75+ self .caps = {}
76+ self .isupport = {}
77+
78+ def loop (self ):
79+ """Start connection loop."""
80+ try :
81+ while True :
82+ line = self .recv ()
83+ if line .strip () == '' :
84+ continue
85+ m = RFC1459Message .from_message (line )
86+ if m .verb == 'ERROR' :
87+ break
88+ except :
89+ trace = traceback .format_exc ()
90+ print (trace )
91+
92+ def recv (self , bytes = 4096 ):
93+ """Receive any info from the IRC server."""
94+ # recv existing lines before receiving new ones
95+ if b'\n ' not in self ._data :
96+ self ._data += self .sock .recv (bytes )
97+ if b'\n ' in self ._data :
98+ raw_bytes , self ._data = self ._data .split (b'\n ' , maxsplit = 1 )
99+ line = raw_bytes .decode ('utf-8' , 'ignore' ).strip ('\r \n ' )
100+ if line .strip () == '' :
101+ return ''
102+ if self .debug :
103+ print ('{} <- {}' .format (datetime .today ().strftime ('%H:%M:%S' ), line ))
104+ m = RFC1459Message .from_message (line )
105+ self ._events .dispatch ('irc message {}' .format (m .verb .lower ()), m )
106+ return line
107+ return ''
108+
109+ def send (self , line ):
110+ """Send the given line to the IRC server."""
111+ self .send_raw ('{}\r \n ' .format (line ))
112+
113+ def send_raw (self , line ):
114+ """Send the given line to the IRC server."""
115+ if self .debug :
116+ print ('{} -> {}' .format (datetime .today ().strftime ('%H:%M:%S' ), line .strip ('\r \n ' )))
117+ self .sock .send (line .encode ('utf-8' , 'ignore' ))
118+
119+ def on_ping (self , msg ):
120+ self .send ('PONG :{}' .format (msg .params [- 1 ]))
121+
122+ def on_cap (self , msg ):
123+ if msg .params [1 ].upper () != 'LS' :
124+ return
125+ for cap in msg .params [- 1 ].split ():
126+ value = ''
127+ if '=' in cap :
128+ cap , value = cap .split ('=' , maxsplit = 1 )
129+ self .caps [cap ] = value
130+
131+ def on_005 (self , msg ):
132+ for name in msg .params [1 :- 1 ]:
133+ value = ''
134+ if '=' in name :
135+ name , value = name .split ('=' , maxsplit = 1 )
136+ self .isupport [name ] = value
137+
138+ def on_connected (self , msg ):
139+ self .send ('QUIT' )
29140
30141
31142def check_net (versions , info ):
32143 print ("Connecting to:" , info ['name' ])
33144 hostname = info ['net-address' ]['display' ]
34145
35- reactor = girc .Reactor ()
36-
37146 supported = {}
38147
39- # register events
40- @reactor .handler ('in' , 'endofmotd' )
41- @reactor .handler ('in' , 'nomotd' )
42- def handle_endofmotd (event ):
43- # get caps
44- supported ['caps' ] = []
45- for name in event ['server' ].capabilities .available :
46- supported ['caps' ].append (name )
47-
48- # get features
49- supported ['features' ] = []
50- for name in event ['server' ].features .available :
51- supported ['features' ].append (name )
52-
53- event ['server' ].quit ()
54-
55- # without TLS then with TLS
56- server = reactor .create_server (info ['name' ] + ' noTLS' )
57- server .set_user_info ('ircv3-tst' , user = 'ircv3' , real = 'IRCv3 tester' )
58- server .connect (hostname , 6667 , ssl = False )
148+ irc = IRCv3Connection (hostname , nick , username , realname , debug = False )
149+ irc .loop ()
59150
60- reactor .run_forever ()
151+ supported ['features' ] = irc .isupport .keys ()
152+ supported ['caps' ] = irc .caps .keys ()
61153
62154 # generate cap lists
63155 all_caps = []
@@ -84,7 +176,7 @@ def handle_endofmotd(event):
84176 print (' *!* + ' , info ['name' ], 'now also supports' , cap )
85177
86178if __name__ == '__main__' :
87- arguments = docopt (__doc__ , version = girc . __version__ )
179+ arguments = docopt (__doc__ , version = '0.0.1' )
88180
89181 if arguments ['run' ]:
90182 print ("NOTE: The output of this script can be incorrect due to nets only exposing certain caps when on/not on TLS, and other implementation and network-specific factors." )
0 commit comments