2024-10-19 19:43:48 -07:00
"""
IRC Parser for the SugarCaneIRC family .
"""
2024-10-19 19:25:37 -07:00
import socket
2024-10-19 19:43:48 -07:00
import ssl as ssl_module
2024-10-19 20:39:58 -07:00
import threading
2024-10-19 21:29:14 -07:00
__version__ = " _TEST_ "
2024-10-19 20:39:58 -07:00
class systemMessage : # System message object
chan = None
def __init__ ( self , content : str , user : str , typ : str , mention : bool , * * kwargs ) :
self . content , self . user , self . type , self . mention = content , user , typ , mention
if " chan " in kwargs :
self . chan = kwargs [ " chan " ]
2024-10-19 19:25:37 -07:00
class message : # Message object
2024-10-19 19:46:42 -07:00
def __init__ ( self , content : str , chan : str , nick : str ) :
2024-10-19 19:25:37 -07:00
self . content = content
2024-10-19 19:46:42 -07:00
self . channel = chan
2024-10-19 19:25:37 -07:00
self . nick = nick
class channel : # Channel object
2024-10-19 19:43:48 -07:00
is_init = False # If the channel's properties are initialized yet
topic = " " # Channel topic
modes = " +nt " # Channel modes
2024-10-19 20:39:58 -07:00
in_channel = True # If the user is in the channel
2024-10-19 19:25:37 -07:00
def __init__ ( self , name : str ) :
self . name = name
def info_set ( self , topic : str , modes : str ) : # Socket will automatically initialize the channel object
self . is_init = True
self . topic , self . modes = topic , modes
class IRCSession : # Actual IRC session
2024-10-19 20:39:58 -07:00
messages = [ ] # Cached messages
raw_text = " " # Cached RAW data
channels = [ ] # Cached channels
2024-10-19 21:29:14 -07:00
connected = False # Connection status
2024-10-19 19:43:48 -07:00
is_ssl = False # Wether the connection uses TLS/SSL
ssl_accept_invalid = False # If SSL is enabled, do not fail to connect if the certificate is invalid.
2024-10-19 21:03:02 -07:00
socket = socket . socket ( ) # Socket
2024-10-19 19:43:48 -07:00
wsocket = None # Wrapped socket (if SSL is enabled)
2024-10-19 20:39:58 -07:00
context = ssl_module . create_default_context ( ) # Context of the SSL module, not to be changed by the client.
2024-10-19 20:57:00 -07:00
def __init__ ( self , address : str = " irc.libera.chat " , port : int = 6697 , nick : str = " sweetAsSugar " , user : str = " sugarCaneIRC " , ssl : bool = True , ssl_igninvalid : bool = False , * * kwargs ) : # Contains the configuration
2024-10-19 21:33:48 -07:00
self . socket = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2024-10-19 21:03:02 -07:00
self . context = ssl_module . create_default_context ( )
self . wsocket = None
2024-10-19 19:43:48 -07:00
self . server , self . port , self . nick , self . user , self . ssl , self . ssl_accept_invalid = address , port , nick , user , ssl , ssl_igninvalid
if ssl :
if ssl_igninvalid :
self . context = ssl_module . _create_unverified_context ( )
2024-10-19 19:45:35 -07:00
self . wsocket = self . context . wrap_socket ( self . socket , server_hostname = address )
2024-10-19 19:25:37 -07:00
def connect ( self ) : # Attempt to connect
2024-10-19 19:27:56 -07:00
print ( " Connecting to " + self . server + " : " + str ( self . port ) + " ... " )
2024-10-19 19:43:48 -07:00
if self . ssl :
self . wsocket . connect ( ( self . server , self . port ) )
else :
self . socket . connect ( ( self . server , self . port ) )
2024-10-20 10:09:47 -07:00
self . connected = True
2024-10-19 21:13:52 -07:00
self . send ( " USER " + self . user + " " + self . user + " " + self . nick + " :SugarCaneIRC user \n " )
self . send ( f " NICK { self . nick } \n " )
def send ( self , content : str ) : # Attempt to send raw data to the socket
2024-10-19 21:29:14 -07:00
if content [ len ( content ) - 1 ] != " \n " :
content + = " \n "
2024-10-19 21:13:52 -07:00
if self . ssl :
self . wsocket . send ( bytes ( content , " UTF-8 " ) )
else :
self . socket . send ( bytes ( content , " UTF-8 " ) )
2024-10-19 21:36:48 -07:00
def quit ( self , message = " ScParseIRC v " + __version__ ) :
2024-10-19 21:42:44 -07:00
self . send ( f " QUIT : { message } \r \n " )
2024-10-20 10:09:47 -07:00
self . connected = False
def join ( self , chan ) :
self . chans + = channel ( chan )
self . send ( f " JOIN { chan } " )
2024-10-19 21:29:14 -07:00
def close ( self ) :
if self . ssl :
self . wsocket . close ( )
else :
self . socket . close ( )
self . connected = False
2024-10-19 20:39:58 -07:00
def get ( self ) : # Attempt to get the raw data and parse it.
# The code is copied from sweeBotIRC btw
2024-10-19 20:58:17 -07:00
if self . ssl :
r = self . wsocket . recv ( 2040 ) . decode ( )
else :
r = self . socket . recv ( 2040 ) . decode ( )
2024-10-19 20:39:58 -07:00
self . raw_text + = r
self . parseall ( )
2024-10-19 20:53:06 -07:00
print ( r )
2024-10-19 20:39:58 -07:00
if r . find ( " PING " ) != - 1 :
2024-10-19 21:29:14 -07:00
self . send (
" PONG " + r . split ( ) [ 1 ] + " \r \n "
2024-10-19 20:39:58 -07:00
)
def parseall ( self ) : # Parse all of the fetched raw data, in a thread.
threading . Thread ( target = self . parse , kwargs = { " content " : self . raw_text } )
2024-10-19 20:53:06 -07:00
def parse ( self , content : str ) :
for i in content . replace ( " \r \n " , " \n " ) . split ( " \n " ) :
pass
2024-10-19 19:25:37 -07:00
def alive ( self ) : # NOT FINISHED: To minimize exceptions, the client can ask the object if the socket connection is still alive.
return False
2024-10-19 19:25:46 -07:00
def whois ( self , nick : str ) : # NOT FINISHED: Try to /whois the user, will return a user() object or None.
2024-10-19 19:25:37 -07:00
return None