2024-12-08 16:54:04 -08:00
#!/usr/bin/python3
2025-01-22 00:25:30 -08:00
__version__ = " 0.0.5 "
2024-12-24 16:50:04 -08:00
print ( f " Codename IRCat v { __version__ } " )
2024-12-08 16:54:04 -08:00
print ( " Welcome! /ᐠ ˵> ⩊ <˵マ " )
2025-02-04 17:03:38 -08:00
import socket , time , ssl , threading , traceback , sys , subprocess , yaml , sqlite3 , os , importlib , datetime
2025-01-21 19:26:35 -08:00
from OpenSSL import SSL
2024-12-08 16:54:04 -08:00
from requests import get
2024-12-08 18:55:43 -08:00
if not len ( sys . argv ) == 2 :
print ( " IRCat requires the following arguments: config.yml " )
2024-12-08 19:04:37 -08:00
sys . exit ( 1 )
server = " 127.0.0.1 "
displayname = " foo "
2024-12-09 15:33:56 -08:00
identifier = " somewhere in the universe "
2024-12-11 12:54:12 -08:00
admin_nick = " admin "
data_path = " "
2024-12-15 15:39:48 -08:00
motd = """
2025-01-21 18:27:58 -08:00
____ _ ___ ____ ____ _
2024-12-15 15:39:48 -08:00
/ ___ | ___ __ | | ___ _ __ __ _ _ __ ___ ___ | _ _ | _ \ / ___ | __ _ | | _
| | / _ \ / _ ` | / _ \ ' _ \ / _` | ' _ ` _ \ / _ \ | | | | _ ) | | / _ ` | __ |
| | __ | ( _ ) | ( _ | | __ / | | | ( _ | | | | | | | __ / | | | _ < | | __ | ( _ | | | _
2024-12-16 09:15:45 -08:00
\____ \___ / \__ , _ | \___ | _ | | _ | \__ , _ | _ | | _ | | _ | \___ | | ___ | _ | \_ \\\\____ \__ , _ | \__ |
2024-12-15 15:39:48 -08:00
https : / / ircat . xyz
2024-12-22 16:54:52 -08:00
This server doesn ' t have a MOTD in its configuration, or is invalid. " " "
2024-12-15 15:39:48 -08:00
motd_file = None
2024-12-15 19:30:22 -08:00
ping_timeout = 255
2024-12-26 16:23:58 -08:00
restrict_ip = ' '
2025-02-03 22:19:58 -08:00
def isalphanumeric ( text : str , channel = False ) :
2025-02-03 22:30:19 -08:00
alphanumericity = list ( " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890+-_[]^ \\ |<>?` " + ( " $ % *,./~ ' \" {} ;#= " if channel else " " ) )
2025-02-03 22:19:58 -08:00
for i in text :
if not i in alphanumericity :
return False
return True
2025-01-25 21:15:19 -08:00
def getident ( hostt : str , clientport : int , ssll : bool ) :
try :
identsender = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2025-01-25 21:25:36 -08:00
identsender . settimeout ( 5 )
2025-01-25 22:31:44 -08:00
responsee = " "
2025-01-25 21:15:19 -08:00
try :
identsender . connect ( ( hostt , 113 ) )
2025-01-25 21:33:22 -08:00
except Exception as ex :
return { " success " : False , " response " : f " Could not connect to your ident server: { ex } " }
2025-01-25 21:15:19 -08:00
serverport = " 6697 " if ssll else " 6667 "
try :
2025-01-25 22:29:11 -08:00
identsender . sendall ( bytes ( f " { clientport } , { serverport } \r \n " , " UTF-8 " ) )
2025-01-25 22:44:24 -08:00
responsee = identsender . recv ( 2048 ) . decode ( ) . replace ( " " , " " ) . replace ( " \r " , " " ) . replace ( " \n " , " " )
2025-01-25 21:15:19 -08:00
print ( responsee )
2025-01-25 21:38:13 -08:00
except Exception as ex :
return { " success " : False , " response " : f " Could not send packets to your server: { ex } " }
2025-01-25 22:44:24 -08:00
if " ERROR:NO-USER " in responsee :
2025-01-25 21:15:19 -08:00
return { " success " : False , " response " : " No user was found by the server. " }
2025-01-25 22:44:24 -08:00
elif " ERROR: " in responsee :
2025-01-26 00:34:16 -08:00
return { " success " : False , " response " : " The ident server had an error. " }
2025-01-25 22:34:47 -08:00
elif responsee == " " :
2025-01-25 21:15:19 -08:00
return { " success " : False , " response " : " The connection was closed. " }
else :
2025-01-25 22:44:24 -08:00
print ( responsee . split ( " , " ) [ 0 ] )
print ( responsee . split ( " , " ) [ 1 ] . split ( " : " ) [ 0 ] )
2025-01-25 22:46:41 -08:00
if responsee . split ( " , " ) [ 0 ] != str ( clientport ) :
2025-01-25 22:44:24 -08:00
return { " success " : False , " response " : " The ident server sent an invalid client port. " }
elif responsee . split ( " , " ) [ 1 ] . split ( " : " ) [ 0 ] != serverport :
return { " success " : False , " response " : " The ident server doesn ' t know what the server port is. " }
else :
return { " success " : True , " response " : responsee . split ( " , " ) [ 1 ] . split ( " : " ) [ 3 ] }
2025-01-25 21:15:19 -08:00
return { " success " : False , " response " : " Unknown error. " }
except :
2025-01-25 21:24:53 -08:00
print ( traceback . format_exc ( ) )
2025-01-25 21:15:19 -08:00
return { " success " : False , " response " : " Unknown error. " }
2025-01-08 22:12:57 -08:00
global mods
2025-01-23 11:30:39 -08:00
mods = { " sql_provider " : None , " command " : [ ] , " allsocket " : [ ] , " identified " : False , " ssl " : False }
2024-12-08 19:04:37 -08:00
with open ( sys . argv [ 1 ] , ' r ' ) as file :
2025-01-28 21:27:34 -08:00
global data2
2024-12-08 19:04:37 -08:00
data = yaml . safe_load ( file )
2025-01-28 21:27:34 -08:00
data2 = data
2024-12-08 19:04:37 -08:00
try : server = data [ " host " ]
except : print ( " using fallback server address " )
try : displayname = data [ " name " ]
except : print ( " using fallback display name " )
2024-12-09 15:39:05 -08:00
try : identifier = data [ " identifier " ]
2024-12-09 15:33:56 -08:00
except : print ( " using fallback identifier " )
2024-12-11 12:54:12 -08:00
try : admin_nick = data [ " admin-nick " ]
except : print ( " using fallback admin nick " )
2024-12-15 15:39:48 -08:00
try : motd = data [ " motd " ]
except : print ( " using fallback MOTD " )
try : motd_file = data [ " motd-file " ]
except : print ( " Not reading MOTD from a file. " )
2024-12-15 19:30:22 -08:00
try : ping_timeout = int ( data [ " ping-timeout " ] )
except : print ( " Using 255 as a ping timeout. " )
2024-12-26 16:23:58 -08:00
try : restrict_ip = data [ " restrict-ip " ]
2024-12-26 16:22:39 -08:00
except : print ( " Listening on all hosts possible. " )
2025-01-02 19:57:09 -08:00
try : ssl_option = bool ( data [ " ssl " ] )
2025-01-02 19:45:42 -08:00
except :
print ( " SSL will be off. " )
2025-01-02 19:57:09 -08:00
ssl_option = False
2025-01-02 19:45:42 -08:00
if ssl_option :
try : ssl_cert = data [ " ssl_cert " ]
except :
print ( " IRCat needs an SSL cert to use SSL! " )
sys . exit ( 1 )
try : ssl_pkey = data [ " ssl_pkey " ]
except :
print ( " IRCat needs an SSL Private Key to use SSL! " )
2025-01-08 21:53:04 -08:00
try : modules = data [ " modules " ]
except :
print ( " IRCat needs at least one module enabled. " )
sys . exit ( 1 )
2024-12-08 19:04:37 -08:00
file . close ( )
2024-12-08 20:03:55 -08:00
print ( " Successfully loaded config! " )
2025-01-08 22:03:18 -08:00
for mod in modules :
i = mod
2025-01-08 21:53:04 -08:00
if not os . path . isabs ( i ) :
2025-01-08 21:59:31 -08:00
i = os . path . dirname ( __file__ ) + " /modules/ " + i
2025-01-08 21:53:04 -08:00
try :
2025-01-08 22:03:18 -08:00
print ( f " Importing module { mod } ... " )
2025-01-08 22:04:42 -08:00
spc = importlib . util . spec_from_file_location ( mod , f " { i } .py " )
temp_module = importlib . util . module_from_spec ( spc )
spc . loader . exec_module ( temp_module )
2025-01-08 22:07:38 -08:00
for j in temp_module . __ircat_requires__ :
if not j in data :
2025-01-08 22:08:06 -08:00
raise Exception ( f " Module { mod } requires { j } in configuration. " )
2025-01-08 21:53:04 -08:00
if temp_module . __ircat_type__ == " sql.provider " :
2025-01-08 22:13:27 -08:00
if mods [ " sql_provider " ] == None :
2025-01-08 22:12:57 -08:00
mods [ " sql_provider " ] = temp_module
2025-01-08 21:53:04 -08:00
else :
2025-01-08 22:03:18 -08:00
raise Exception ( f " Tried to import { mod } as an SQL provider, but something ' s already the SQL provider. " )
2025-01-08 21:53:04 -08:00
elif temp_module . __ircat_type__ == " command " :
2025-01-08 22:12:57 -08:00
mods [ " command " ] . append ( temp_module )
2025-01-10 18:23:13 -08:00
elif temp_module . __ircat_type__ == " allsocket " :
mods [ " allsocket " ] . append ( temp_module )
2025-01-08 21:53:04 -08:00
except :
print ( f " Module { i } failed to load. " )
print ( traceback . format_exc ( ) )
sys . exit ( 1 )
2025-01-11 00:24:24 -08:00
global topic_list
topic_list = { }
2025-01-11 00:37:51 -08:00
channels_list = { } # Store channels and their user lists
2025-01-08 22:12:57 -08:00
if mods [ " sql_provider " ] == None :
2025-01-08 21:53:04 -08:00
print ( " IRCat needs an SQL provider. " )
sys . exit ( 1 )
2025-01-08 22:11:32 -08:00
sqlproviderequires = { }
2025-01-08 22:12:57 -08:00
for i in mods [ " sql_provider " ] . __ircat_requires__ :
2025-01-08 22:15:16 -08:00
sqlproviderequires [ i . replace ( " - " , " _ " ) ] = data [ i ]
2025-01-08 22:12:57 -08:00
config = mods [ " sql_provider " ] . broker ( * * sqlproviderequires )
2025-01-10 18:43:50 -08:00
global socketListeners
socketListeners = [ ]
for i in mods [ ' allsocket ' ] :
requires = { }
for j in i . __ircat_requires__ :
requires [ j . replace ( " - " , " _ " ) ] = data [ j ]
if " sql " in i . __ircat_giveme__ :
requires [ " sql " ] = config
2025-01-11 00:35:40 -08:00
try :
2025-01-11 00:42:22 -08:00
print ( i . __ircat_fakechannels__ )
2025-01-11 00:39:29 -08:00
topic_list = { * * topic_list , * * i . __ircat_fakechannels__ }
2025-01-11 00:46:37 -08:00
for j , v in i . __ircat_fakechannels__ . items ( ) :
2025-01-21 18:57:42 -08:00
channels_list [ j ] = [ " CatServ " ]
2025-01-11 00:37:51 -08:00
except Exception as ex :
print ( str ( ex ) )
2025-01-10 18:43:50 -08:00
socketListeners . append ( i . IRCatModule ( * * requires ) )
commandProviders = [ ]
2025-01-21 19:27:58 -08:00
nickname_list = { } # Stores nicknames and the respective sockets
lower_nicks = { " catserv " : " CatServ " } # Nicknames in lowercase
property_list = { " CatServ " : { " host " : " IRCatCore " , " username " : " Meow " , " realname " : " Updates bot " , " modes " : " iw " , " away " : False } } # Stores properties for active users and channels
2025-01-10 18:43:50 -08:00
for i in mods [ ' command ' ] :
requires = { }
for j in i . __ircat_requires__ :
requires [ j . replace ( " - " , " _ " ) ] = data [ j ]
if " sql " in i . __ircat_giveme__ :
requires [ " sql " ] = config
2025-01-21 18:27:58 -08:00
try :
print ( i . __ircat_fakeusers__ )
property_list = { * * property_list , * * i . __ircat_fakeusers__ }
for j , v in i . __ircat_fakeusers__ . items ( ) :
lower_nicks [ j . lower ( ) ] = j
except Exception as ex :
print ( str ( ex ) )
2025-01-10 18:43:50 -08:00
commandProviders . append ( i . IRCatModule ( * * requires ) )
2025-01-02 19:45:42 -08:00
sockets = { }
sockets_ssl = { }
# Open the specified non-SSL sockets.
for i in restrict_ip . split ( " " ) :
sockets [ i ] = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
sockets [ i ] . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
2025-01-23 07:53:08 -08:00
sockets [ i ] . settimeout ( None )
2025-01-02 19:45:42 -08:00
sockets [ i ] . bind ( ( i , 6667 ) )
sockets [ i ] . listen ( 1 )
if ssl_option :
for i in restrict_ip . split ( " " ) :
sockets_ssl [ i ] = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
sockets_ssl [ i ] . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
2025-01-23 07:53:08 -08:00
sockets_ssl [ i ] . settimeout ( None )
2025-01-02 19:45:42 -08:00
sockets_ssl [ i ] . bind ( ( i , 6697 ) )
sockets_ssl [ i ] . listen ( 1 )
2024-12-11 14:59:32 -08:00
opened = True
2024-12-27 20:16:23 -08:00
lower_chans = { } # Channel names in lowercase
2024-12-09 16:45:21 -08:00
def pinger ( nick , connection ) :
2024-12-10 21:35:50 -08:00
global property_list
2024-12-09 16:45:21 -08:00
while nick in property_list :
2024-12-15 19:29:27 -08:00
if ( time . time ( ) - property_list [ nick ] [ " last_ping " ] ) > 30 and not property_list [ nick ] [ " ping_pending " ] :
2024-12-12 13:35:02 -08:00
if nick in property_list :
print ( " Sent ping message to " + nick )
property_list [ nick ] [ " ping_pending " ] = True
time . sleep ( 0.5 )
2025-01-06 15:38:03 -08:00
try :
connection . sendall ( bytes ( f " PING { server } \r \n " , " UTF-8 " ) )
except Exception as ex :
2025-01-28 18:07:17 -08:00
print ( traceback . format_exc ( ) )
2025-01-06 15:38:03 -08:00
property_list [ nick ] [ " cause " ] = " Send error: " + str ( ex )
print ( " SHUTTING DOWN FOR " + nick )
connection . shutdown ( socket . SHUT_WR )
connection . close ( )
break
2024-12-15 19:29:27 -08:00
elif property_list [ nick ] [ " ping_pending " ] and ( ( time . time ( ) - property_list [ nick ] [ " last_ping " ] ) > ping_timeout ) :
2024-12-12 13:35:02 -08:00
if nick in property_list :
2024-12-15 19:29:27 -08:00
property_list [ nick ] [ " cause " ] = f " Ping timeout: { ping_timeout } seconds "
2024-12-12 13:45:48 -08:00
print ( " SHUTTING DOWN FOR " + nick )
2024-12-12 13:35:02 -08:00
connection . shutdown ( socket . SHUT_WR )
connection . close ( )
break
2025-01-22 19:57:41 -08:00
def session ( connection , client , ip , isssl = False ) :
2024-12-15 19:08:18 -08:00
global property_list
2025-01-13 17:24:21 -08:00
global channels_list
global nickname_list
2024-12-09 09:30:20 -08:00
pending = " * " # The nickname of the client
2024-12-08 20:02:59 -08:00
already_set = False # If the client gave the server a NICK packet
ready = False # If the client gave the server a USER packet
finished = False # If the server gave the client its information, indicating it's ready.
2024-12-09 14:08:31 -08:00
username = " oreo " # Username/ident specified by client
2025-02-04 16:46:55 -08:00
rident = " ~oreo " # Real ident
2024-12-09 14:08:31 -08:00
hostname = " " # Hostname, can be IP or domain
realname = " realname " # Realname specified by client
safe_quit = False # If the client safely exited, or if the server should manually drop the connection
cause = " Unknown " # The cause of the unexpected exit
2025-01-22 23:03:37 -08:00
usesIRCv3 = False
CAPEND = False
2025-01-25 21:15:19 -08:00
clident = None
2025-01-29 20:12:13 -08:00
pendingCommands = " " # list of commands that were executed before verification
2025-01-29 20:17:03 -08:00
unfinished = False
textt = " "
2025-02-03 15:15:09 -08:00
pendingSend = " " # Text that should be sent to the client
2025-02-04 16:40:42 -08:00
IRCv3Features = [ ] # List of Acknowledged IRCv3 features.
def tags ( ) : # Get IRCv3 tags
tags = " "
if " server-time " in IRCv3Features :
tags + = " @time= " + datetime . datetime . now ( datetime . timezone . utc ) . isoformat ( ) [ : - 9 ] + " Z "
2025-02-04 16:46:55 -08:00
# >>> datetime.datetime.now(datetime.timezone.utc).isoformat()
# '2025-02-05T00:15:50.370474+00:00'
# ^^^^^^^^^
# Cutting these off the string
2025-02-04 16:40:42 -08:00
return tags + ( " " if tags != " " else " " )
def tags_diffclient ( nick : str ) : # Get tags of another client
othercap = property_list [ nick ] [ " v3cap " ]
if " server-time " in othercap :
tags + = " @time= " + datetime . datetime . now ( datetime . timezone . utc ) . isoformat ( ) [ : - 9 ] + " Z "
return tags + ( " " if tags != " " else " " )
2024-12-08 16:54:04 -08:00
try :
print ( " Connected to client IP: {} " . format ( client ) )
2025-02-01 13:38:09 -08:00
if isssl :
2025-02-03 22:03:24 -08:00
connection . do_handshake ( )
2025-02-01 13:38:09 -08:00
tlsver = connection . version ( )
print ( f " Got SSL version: { tlsver } " )
2025-02-04 15:28:00 -08:00
connection . settimeout ( None )
2024-12-08 16:54:04 -08:00
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Looking for your hostname... \r \n " , " UTF-8 " ) )
2025-01-25 21:15:19 -08:00
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Checking your ident... \r \n " , " UTF-8 " ) )
2024-12-09 12:41:46 -08:00
try :
hostname = socket . gethostbyaddr ( client [ 0 ] ) [ 0 ]
2025-01-25 21:15:19 -08:00
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Got hostname! { hostname } \r \n " , " UTF-8 " ) )
2024-12-09 12:41:46 -08:00
except :
hostname = client [ 0 ]
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Oof! Can ' t find your hostname, using IP... \r \n " , " UTF-8 " ) )
2025-01-25 21:15:19 -08:00
try :
2025-01-25 21:35:21 -08:00
identQuery = getident ( hostname , client [ 1 ] , isssl )
2025-01-25 21:15:19 -08:00
responseee = identQuery [ " response " ]
2025-01-25 21:23:24 -08:00
print ( identQuery )
2025-01-25 21:26:53 -08:00
if not identQuery [ " success " ] :
2025-01-25 21:15:19 -08:00
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Uhm, Couldn ' t find your ident: { responseee } \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Got ident! { responseee } \r \n " , " UTF-8 " ) )
2025-01-25 21:26:53 -08:00
clident = responseee
rident = responseee
2025-01-25 21:20:27 -08:00
except :
2025-01-25 21:23:24 -08:00
print ( traceback . format_exc ( ) )
2025-01-25 21:20:27 -08:00
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Uhm, Couldn ' t find your ident: Unknown error. \r \n " , " UTF-8 " ) )
2024-12-08 16:54:04 -08:00
while True :
try :
data = connection . recv ( 2048 )
2025-01-06 15:38:03 -08:00
if not data :
cause = " Remote host closed the connection "
break
2025-01-28 19:06:41 -08:00
except ssl . SSLEOFError :
2025-02-04 15:28:00 -08:00
print ( " EOF occurred. " )
2025-01-28 18:09:15 -08:00
pass
except ssl . SSLZeroReturnError :
2025-02-04 15:28:00 -08:00
print ( traceback . format_exc ( ) )
2025-01-28 18:09:15 -08:00
cause = " Remote host closed the connection "
break
2024-12-09 21:58:05 -08:00
except Exception as ex :
2025-02-04 15:28:00 -08:00
print ( traceback . format_exc ( ) )
2024-12-09 21:58:05 -08:00
cause = " Read error: " + str ( ex )
break
2024-12-08 16:54:04 -08:00
print ( " Received data: {} " . format ( data ) )
try :
2025-01-29 20:17:03 -08:00
textt + = data . decode ( )
if textt [ - 1 ] == " \n " :
for text in textt . replace ( " \r " , " " ) . split ( " \n " ) :
2025-01-10 18:50:46 -08:00
for i in socketListeners :
2025-01-29 20:17:03 -08:00
if " onSocket " in dir ( i ) :
2025-02-04 16:50:23 -08:00
i . onSocket ( socket = connection , ip = client [ 0 ] , value = text , cachedNick = pending if pending != " * " else None , validated = finished , v3tag = tags ( ) )
2025-01-29 20:17:03 -08:00
command = text . split ( " " ) [ 0 ] . upper ( )
2025-01-22 00:39:03 -08:00
try :
2025-01-29 20:17:03 -08:00
args = text . split ( " " ) [ 1 : ]
2025-01-22 00:39:03 -08:00
except :
2025-01-29 20:17:03 -08:00
pass
if command == " NICK " and not finished :
pending = text . split ( " " ) [ 1 ]
if pending [ 0 ] == " : " : pending = pending [ 1 : ]
2025-02-03 22:30:19 -08:00
if not isalphanumeric ( pending ) :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 432 * { pending } :Erroneus nickname \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
pending = " * "
elif pending . lower ( ) in lower_nicks :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 433 * { pending } :Nickname is already in use. \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
pending = " * "
else :
already_set = True
print ( f " User { pending } set nick " )
elif command == " USER " :
if not ready :
username = text . split ( " " ) [ 1 ]
realname = " " . join ( text . split ( " " ) [ 4 : ] ) [ 1 : ]
ready = True
elif command == " CAP " :
if args [ 0 ] . upper ( ) == " LS " :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } CAP * LS :server-time \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif args [ 0 ] . upper ( ) == " REQ " :
2025-02-04 16:40:42 -08:00
usesIRCv3 = True # Halt the registration process until CAP END
capabilities = " " . join ( args [ 1 : ] ) [ 1 : ] . split ( " " )
capsuccess = True
for cap in capabilities :
if cap == " server-time " :
IRCv3Features . append ( " server-time " )
else :
capsuccess = False
break
if capsuccess :
2025-02-04 17:02:18 -08:00
connection . sendall ( bytes ( f " : { server } CAP * ACK : { capabilities } " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif args [ 0 ] . upper ( ) == " END " :
CAPEND = True
elif command == " WEBIRC " and not finished :
try :
if args [ 0 ] == data2 [ " webirc_pass " ] :
hostname = args [ 2 ]
client = ( args [ 3 ] , client [ 1 ] )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } NOTICE * :*** WebIRC detected, welcome to IRC! \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
if hostname != client [ 0 ] :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } NOTICE * :*** Got WebIRC hostname! { hostname } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
except :
print ( traceback . format_exc ( ) )
break
elif ( ready and already_set ) and ( CAPEND if usesIRCv3 else True ) and not finished :
cleanup_manual ( )
print ( f " User { pending } successfully logged in. " )
nickname_list [ pending ] = connection
2025-02-04 16:40:42 -08:00
property_list [ pending ] = { " host " : hostname , " username " : clident if clident != None else f " ~ { username } " , " realname " : realname , " modes " : " iw " , " last_ping " : time . time ( ) , " ping_pending " : False , " away " : False , " identified " : False , " ssl " : isssl , " v3cap " : IRCv3Features }
2025-01-29 20:17:03 -08:00
lower_nicks [ pending . lower ( ) ] = pending
for i in socketListeners :
if " onValidate " in dir ( i ) :
2025-02-04 16:46:55 -08:00
i . onValidate ( socket = connection , ip = client [ 0 ] , v3cap = IRCv3Features )
2025-01-29 20:17:03 -08:00
threading . Thread ( target = pinger , args = [ pending , connection ] ) . start ( )
if clident == None :
rident = f " ~ { username } "
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 001 { pending } :Welcome to the { displayname } Internet Relay Chat Network { pending } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 002 { pending } :Your host is { server } [ { ip } /6667], running version IRCat-v { __version__ } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 004 { pending } { server } IRCat- { __version__ } iow msitnR lfovkqb \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 005 { pending } CHANMODES=bq,k,fl,irmnpst CHANTYPES=# NETWORK= { displayname } :are supported by this server \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
# connection.sendall(bytes(f":{server} 251 {pending} :There are {allusers} users and {allinvis} invisible in {servers} servers\r\n", "UTF-8")) Not supported as there isn't multi-server capability (yet)
ops = 0 # Placeholder, will replace with caclulating how much people have +o
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 252 { pending } { ops } :IRC Operators online \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 253 { pending } 0 :unknown connection(s) \r \n " , " UTF-8 " ) ) # Replace 0 with a variable of not setup clients.
2025-01-29 20:17:03 -08:00
chans = len ( channels_list )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 254 { pending } { chans } :channels formed \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
cleints = len ( nickname_list )
servers = 1
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 255 { pending } :I have { cleints } clients and { servers } servers \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
# Start the MOTD
if motd_file != None :
motd = open ( motd_file ) . read ( )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 375 { pending } :- { server } Message of the Day - \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
for i in motd . rstrip ( ) . split ( " \n " ) :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 372 { pending } :- { i } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 376 { pending } :End of /MOTD command \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
# End the MOTD
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } MODE { pending } +iw \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
finished = True
elif command == " PING " :
try :
2025-01-29 19:57:22 -08:00
e = text . split ( " " ) [ 1 ]
2025-01-29 20:17:03 -08:00
e = f " : { e } " if e [ 0 ] != " : " else e
2025-02-03 15:31:07 -08:00
pendingSend + = f " : { server } PONG { server } { e } \r \n "
2025-01-29 20:17:03 -08:00
except :
2025-02-03 15:31:07 -08:00
pendingSend + = f " : { server } PONG { server } \r \n "
2025-01-29 20:17:03 -08:00
elif command == " MOTD " :
if motd_file != None :
motd = open ( motd_file ) . read ( )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 375 { pending } :- { server } Message of the Day - \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
for i in motd . rstrip ( ) . split ( " \n " ) :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 372 { pending } :- { i } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 376 { pending } :End of /MOTD command \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif finished :
pendingCommands + = text
2025-02-03 22:49:04 -08:00
for comd in pendingCommands . replace ( " \r " , " " ) . split ( " \n " ) :
2025-01-29 20:17:03 -08:00
command = comd . split ( " " ) [ 0 ] . upper ( )
args = comd . split ( " " ) [ 1 : ]
text = comd
processedExternally = False
for i in commandProviders :
2025-02-04 16:50:23 -08:00
cmdrun = i . command ( command = command , args = args , nick = pending , ip = client [ 0 ] , user = property_list [ pending ] , connection = connection , v3tag = tags ( ) )
2025-01-29 20:17:03 -08:00
if cmdrun [ " success " ] :
if " identify " in cmdrun :
if cmdrun [ " identify " ] == " logout " :
if " o " in property_list [ pending ] [ " modes " ] :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } MODE { pending } -o \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
if not " i " in property_list [ pending ] [ " modes " ] :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } MODE { pending } +i \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
if not " w " in property_list [ pending ] [ " modes " ] :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } MODE { pending } +w \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
property_list [ pending ] [ " modes " ] = " iw "
property_list [ pending ] [ " identified " ] = False
else :
property_list [ pending ] [ " identified " ] = True
property_list [ pending ] [ " identusername " ] = cmdrun [ " identify " ] [ 0 ]
temp_mode = cmdrun [ " identify " ] [ 1 ]
property_list [ pending ] [ " modes " ] = temp_mode
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } MODE { pending } + { temp_mode } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
processedExternally = True
break
if processedExternally :
2025-01-29 19:57:22 -08:00
pass
2025-01-29 20:17:03 -08:00
elif command == " JOIN " :
channels = text . split ( " " ) [ 1 ]
if channels [ 0 ] == " : " :
channels = channels [ 1 : ]
for channelt in channels . split ( " , " ) :
channel = channelt . strip ( )
2025-02-03 22:30:19 -08:00
if isalphanumeric ( channel , True ) :
if channel . lower ( ) in lower_chans :
channel = lower_chans [ channel . lower ( ) ]
success = True
if channel in channels_list :
if pending in channels_list [ channel ] :
success = False
print ( f " { pending } is already in { channel } , ignoring JOIN request. " )
if success :
2025-01-29 19:57:22 -08:00
try :
2025-02-03 22:30:19 -08:00
if channel in channels_list :
channels_list [ channel ] . append ( pending )
else :
channels_list [ channel ] = [ pending ]
lower_chans [ channel . lower ( ) ] = channel
topic_list [ channel ] = " Topic is not implemented. "
2025-01-29 19:57:22 -08:00
except :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } NOTICE * :*** Could not join { channel } \r \n " , " UTF-8 " ) )
2025-02-03 22:30:19 -08:00
print ( channels_list )
for i in channels_list [ channel ] :
try :
nickname_list [ i ] . sendall ( bytes ( f " : { pending } ! { rident } @ { hostname } JOIN { channel } \r \n " , " UTF-8 " ) )
except :
pass
# Code re-used in the NAMES command
if channel in channels_list :
if pending in channels_list [ channel ] :
users = " " . join ( channels_list [ channel ] )
connection . sendall ( bytes ( f " : { server } 353 { pending } = { channel } : { users } \r \n " , " UTF-8 " ) )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 366 { pending } { channel } :End of /NAMES list. \r \n " , " UTF-8 " ) )
2025-02-03 22:30:19 -08:00
print ( " Successfully pre-loaded /NAMES list " )
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 479 { pending } { channel } :Channel has erroneus characters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " LIST " :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 321 { pending } Channel :Users Name \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
for key , value in topic_list . items ( ) :
usersin = len ( channels_list [ key ] )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 322 { pending } { key } { usersin } : { value } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 323 { pending } :End of /LIST \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " PONG " :
e = text . split ( " " ) [ 1 ]
if e == server or e == f " : { server } " :
print ( pending + " replied to PING. " )
property_list [ pending ] [ " last_ping " ] = time . time ( )
property_list [ pending ] [ " ping_pending " ] = False
elif command == " NICK " :
if len ( args ) == 0 :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif text . split ( " " ) [ 1 ] == pending :
pass
2024-12-09 21:43:11 -08:00
else :
2025-01-29 20:17:03 -08:00
pending2 = text . split ( " " ) [ 1 ]
if pending2 [ 0 ] == " : " : pending2 = pending2 [ 1 : ]
2025-02-03 22:30:19 -08:00
if not isalphanumeric ( pending2 ) :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 432 { pending } { pending2 } :Erroneus nickname \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif pending2 . lower ( ) in lower_nicks :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 433 { pending } { pending2 } :Nickname is already in use. \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
print ( " Sending nickname change... " )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } ! { rident } @ { hostname } NICK { pending2 } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
# Broadcast the nickname change
done = [ ]
for i , users in channels_list . items ( ) :
if pending in users :
for j in users :
if j != pending and j != pending2 and not j in done :
print ( " Broadcasting on " + j )
2025-02-04 16:40:42 -08:00
nickname_list [ j ] . sendall ( bytes ( f " { tags_diffclient ( j ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
done . append ( j )
# Replace the nickname
try :
print ( " Changing on " + i )
channels_list [ i ] . remove ( pending )
channels_list [ i ] . append ( pending2 )
except :
print ( traceback . format_exc ( ) )
print ( " Moving config... " )
property_list [ pending2 ] = property_list . pop ( pending )
nickname_list [ pending2 ] = nickname_list . pop ( pending )
del lower_nicks [ pending . lower ( ) ]
lower_nicks [ pending2 . lower ( ) ] = pending2
print ( " starting pinger... " )
pending = pending2
property_list [ pending2 ] [ " ping_pending " ] = False
property_list [ pending2 ] [ " last_ping " ] = time . time ( )
threading . Thread ( target = pinger , args = [ pending , connection ] ) . start ( )
print ( f " User { pending } set nick " )
print ( " Broadcasting nickname change... " )
elif command == " PART " :
if len ( args ) == 0 :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
channel = text . split ( " " ) [ 1 ]
for i in channels_list [ channel ] :
try :
2025-02-04 16:40:42 -08:00
nickname_list [ i ] . sendall ( bytes ( f " { tags_diffclient ( i ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
except :
pass
2025-01-29 19:57:22 -08:00
try :
2025-01-29 20:17:03 -08:00
channels_list [ channel ] . remove ( pending )
2025-01-29 19:57:22 -08:00
except :
print ( traceback . format_exc ( ) )
2025-01-29 20:17:03 -08:00
elif command == " AWAY " :
if len ( args ) == 0 :
property_list [ pending ] [ " away " ] = False
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 305 { pending } :You are no longer marked as being away \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
reasons = " " . join ( args )
if reasons [ 0 ] == " : " : reasons = reasons [ 1 : ]
property_list [ pending ] [ " away " ] = True
property_list [ pending ] [ " reason " ] = reasons
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 306 { pending } :You have been marked as being away \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " WHO " :
if len ( args ) == 0 :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
channel = text . split ( " " ) [ 1 ]
if channel in channels_list :
for i in channels_list [ channel ] :
who_host = property_list [ i ] [ " host " ]
who_user = property_list [ i ] [ " username " ]
who_realname = property_list [ i ] [ " realname " ]
who_away = " G " if property_list [ i ] [ " away " ] else " H "
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 352 { pending } { channel } { who_user } { who_host } { server } { i } { who_away } :0 { who_realname } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif channel in nickname_list :
who_host = property_list [ channel ] [ " host " ]
who_user = property_list [ channel ] [ " username " ]
who_realname = property_list [ channel ] [ " realname " ]
who_away = " G " if property_list [ channel ] [ " away " ] else " H "
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 352 { pending } * { who_user } { who_host } { server } { channel } { who_away } :0 { who_realname } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 315 { pending } { channel } :End of /WHO list. \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " WHOIS " :
if len ( args ) == 0 :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
target = text . split ( " " ) [ 1 ]
if target . lower ( ) in lower_nicks :
target = lower_nicks [ target . lower ( ) ]
if target in property_list :
who_user = property_list [ target ] [ " username " ]
who_realname = property_list [ target ] [ " realname " ]
who_host = property_list [ target ] [ " host " ]
who_identified = property_list [ target ] [ " identified " ]
who_ssl = property_list [ target ] [ " ssl " ]
if who_identified :
who_identifying = property_list [ target ] [ " identusername " ]
2025-01-29 19:57:22 -08:00
else :
2025-01-29 20:17:03 -08:00
who_identifying = None
try :
who_flags = property_list [ target ] [ " modes " ]
except :
who_flags = None
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 311 { pending } { target } { who_user } { who_host } * : { who_realname } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 312 { pending } { target } { server } : { identifier } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
if " o " in who_flags : connection . sendall ( bytes ( f " : { server } 313 { pending } { target } :is an IRC operator \r \n " , " UTF-8 " ) )
who_away = property_list [ target ] [ " away " ]
if who_away :
who_reason = who_away = property_list [ target ] [ " reason " ]
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 301 { pending } { target } : { who_reason } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
if who_identified :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 330 { pending } { target } { who_identifying } :is logged in as \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
if who_ssl :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 671 { pending } { target } :is using a secure connection \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
#connection.sendall(bytes(f":{server} 317 {pending} {target} {time} :seconds idle\r\n","UTF-8")) # I haven't implemented idle time yet.
if who_flags != None and who_flags != " iw " :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 379 { pending } { target } :Is using modes + { who_flags } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 318 { pending } { target } :End of /WHOIS list \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 401 { pending } { target } :No such nick/channel \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " NAMES " :
if len ( args ) == 0 :
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
else :
channel = text . split ( " " ) [ 1 ]
if channel in channels_list :
if pending in channels_list [ channel ] :
users = " " . join ( channels_list [ channel ] )
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 353 { pending } = { channel } : { users } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } : { server } 366 { pending } { channel } :End of /NAMES list. \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " NOTICE " :
if len ( args ) > = 2 :
target = text . split ( " " ) [ 1 ]
if target . lower ( ) in lower_nicks :
target = lower_nicks [ target . lower ( ) ]
if target in channels_list :
if pending in channels_list [ target ] :
for i in channels_list [ channel ] :
try :
if i != pending :
2025-02-04 16:40:42 -08:00
nickname_list [ i ] . sendall ( bytes ( f " { tags_diffclient ( i ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
except :
pass
elif target in nickname_list :
2025-02-04 16:40:42 -08:00
nickname_list [ target ] . sendall ( bytes ( f " { tags_diffclient ( target ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 19:57:22 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 401 { pending } { target } :No such nick/channel \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " QUIT " :
# Parse the quit message.
done = [ ]
if len ( text . split ( " " ) ) == 1 :
msg = " Client Quit "
else :
msg = text . split ( " " ) [ 1 : ]
if msg [ 0 ] [ 0 ] == " : " :
msg [ 0 ] = msg [ 0 ] [ 1 : ]
if len ( msg ) > 0 :
mse = " " . join ( msg )
msg = f " Quit: { mse } "
text = f " QUIT : { msg } "
# Broadcast all users in the joined channels that the person quit.
for i , users in channels_list . items ( ) :
if pending in users :
for j in users :
if j != pending and not j in done :
2025-02-04 16:40:42 -08:00
nickname_list [ j ] . sendall ( bytes ( f " { tags_diffclient ( j ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
done . append ( j )
# Remove the quitting user from the channel.
try :
channels_list [ i ] . remove ( pending )
except :
print ( traceback . format_exc ( ) )
# Confirm QUIT and close the socket.
try :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
connection . sendall ( bytes ( f " ERROR :Closing Link: { hostname } ( { msg } ) \r \n " , " UTF-8 " ) )
finally :
connection . close ( )
safe_quit = True
break
elif command == " MODE " :
target = args [ 0 ]
if len ( args ) == 0 :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif len ( args ) == 1 :
if args [ 0 ] == pending :
yourmodes = property_list [ pending ] [ " modes " ]
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 221 { pending } + { yourmodes } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif args [ 0 ] in channels_list :
if args [ 0 ] in property_list :
if " modes " in property_list [ args [ 0 ] ] :
# Get the modes + parameters, then print them out.
modes = property_list [ args [ 0 ] ] [ " modes " ]
params = property_list [ args [ 0 ] ] [ " params " ]
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 221 { pending } { target } + { modes } { params } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
# Default channel mode
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 324 { pending } { target } +n \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
# Default channel mode
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 324 { pending } { target } +n \r \n " , " UTF-8 " ) )
2025-01-29 19:57:22 -08:00
else :
2025-01-29 20:17:03 -08:00
if args [ 0 ] [ 0 ] == " # " :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 403 { pending } { target } :No such channel \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 505 { pending } :Cant change mode for other users \r \n " , " UTF-8 " ) )
2024-12-09 19:31:48 -08:00
2025-01-29 20:17:03 -08:00
elif command == " CATSERV " or ( command == " PRIVMSG " and args [ 0 ] . lower ( ) == " catserv " ) :
if command == " PRIVMSG " :
args = args [ 1 : ]
if args [ 0 ] [ 0 ] == " : " :
args [ 0 ] = args [ 0 ] [ 1 : ]
if len ( args ) == 0 :
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
elif args [ 0 ] . upper ( ) == " PULL " :
updater = subprocess . run ( [ " git " , " pull " ] , stdout = subprocess . PIPE )
if updater . stdout . decode ( ) . strip ( ) == " Already up to date. " :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :Codename IRCat is already up-to-date. \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :Done, it is recommended to use /RESTART if you ' re an IRC op \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif args [ 0 ] . upper ( ) == " VERSION " :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :Codename IRCat version { __version__ } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :This is Codename IRCat ' s integrated services. \r \n " , " UTF-8 " ) )
2025-01-29 19:57:22 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :CatServ Usage: \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :PULL - Pulls the latest version of Codename IRCat \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :VERSION - Gets the version number of this service. \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " RESTART " :
if " o " in property_list [ pending ] [ " modes " ] :
global opened
opened = False
2025-01-29 19:57:22 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 481 { pending } :Permission Denied- You ' re not an IRC operator \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " PRIVMSG " :
if len ( args ) > = 2 :
target = text . split ( " " ) [ 1 ]
if target . lower ( ) in lower_nicks :
target = lower_nicks [ target . lower ( ) ]
if target in channels_list :
print ( " Sending to " + target + " Channel " )
if pending in channels_list [ target ] :
print ( channels_list [ target ] )
for i in channels_list [ target ] :
try :
if i != pending :
print ( i )
print ( f " : { pending } ! { rident } @ { hostname } { text } \r \n " )
2025-02-04 16:40:42 -08:00
nickname_list [ i ] . sendall ( bytes ( f " { tags_diffclient ( i ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
print ( i + " Is the current user! " )
except :
print ( traceback . format_exc )
elif target in nickname_list :
2025-02-04 16:40:42 -08:00
nickname_list [ target ] . sendall ( bytes ( f " { tags_diffclient ( target ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 401 { pending } { target } :No such nick/channel \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
else :
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
# Ignore empty text
elif text . split ( " " ) [ 0 ] == " " :
pass
2024-12-11 14:50:16 -08:00
else :
2025-01-29 20:17:03 -08:00
# Unknown command
cmd = text . split ( " " ) [ 0 ]
2025-02-04 16:40:42 -08:00
connection . sendall ( bytes ( f " { tags ( ) } : { server } 421 { pending } { cmd } :Unknown command \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
pendingCommands = " "
else :
pendingCommands + = text
2025-01-29 20:18:25 -08:00
textt = " "
2025-02-03 15:30:07 -08:00
connection . sendall ( bytes ( pendingSend , " UTF-8 " ) )
pendingSend = " "
2024-12-09 14:08:31 -08:00
except Exception as ex :
2024-12-08 16:54:04 -08:00
print ( traceback . format_exc ( ) )
2025-01-10 18:22:14 -08:00
cause = " " + str ( ex )
2024-12-08 16:54:04 -08:00
break
finally :
connection . close ( )
2024-12-15 19:29:27 -08:00
try :
if " cause " in property_list [ pending ] :
cause = property_list [ pending ] [ " cause " ]
if pending != " * " :
del nickname_list [ pending ]
del property_list [ pending ]
del lower_nicks [ pending . lower ( ) ]
if not safe_quit :
done = [ ]
for i , users in channels_list . items ( ) :
if pending in users :
for j in users :
if j != pending and not j in done :
2024-12-16 14:22:14 -08:00
try :
2025-02-04 16:40:42 -08:00
nickname_list [ j ] . sendall ( bytes ( f " { tags_diffclient ( j ) } : { pending } ! { rident } @ { hostname } QUIT : { cause } \r \n " , " UTF-8 " ) )
2024-12-16 14:22:14 -08:00
done . append ( j )
except :
print ( traceback . format_exc ( ) )
2024-12-15 19:29:27 -08:00
# Remove the quitting user from the channel.
try :
channels_list [ i ] . remove ( pending )
except :
print ( traceback . format_exc ( ) )
except :
2024-12-16 14:22:14 -08:00
print ( traceback . format_exc ( ) )
2024-12-15 19:29:27 -08:00
cleanup_manual ( )
def cleanup ( ) :
while True :
time . sleep ( 15 )
2024-12-15 19:39:41 -08:00
cleanup_manual ( )
2024-12-15 19:29:27 -08:00
def cleanup_manual ( ) :
global channels_list
global property_list
print ( " Cleaning up... " )
2024-12-15 19:39:41 -08:00
for j , i in channels_list . items ( ) :
2024-12-15 19:29:27 -08:00
for h in i :
if not h in property_list :
print ( " Found a detached connection: " + h )
i . remove ( h )
for k in channels_list [ j ] :
if k != h and k in nickname_list :
2025-01-25 21:15:19 -08:00
nickname_list [ k ] . sendall ( f " : { h } !DISCONNECTED@DISCONNECTED PART { j } :IRCat Cleanup: Found missing connection!! \r \n " )
2024-12-16 09:28:49 -08:00
2025-01-02 19:45:42 -08:00
def tcp_session ( sock ) :
2025-01-03 17:02:55 -08:00
while True :
2025-01-02 20:01:06 -08:00
try :
while opened :
print ( " Waiting for connection... " )
2025-01-02 19:45:42 -08:00
connection , client = sock . accept ( )
ip_to = restrict_ip
2025-01-26 01:06:47 -08:00
threading . Thread ( target = session , daemon = True , args = [ connection , client , ip_to ] ) . start ( )
2025-01-02 20:01:06 -08:00
except :
print ( " Something went wrong... " )
print ( traceback . format_exc ( ) )
2025-01-26 01:08:44 -08:00
def ssl_session ( sock ) :
2025-01-26 00:46:02 -08:00
while True :
try :
while opened :
2025-01-28 14:52:18 -08:00
context = ssl . SSLContext ( ssl . PROTOCOL_TLS_SERVER )
2025-01-29 19:06:46 -08:00
context . minimum_version = ssl . TLSVersion . TLSv1_2
2025-01-28 14:52:18 -08:00
context . set_ciphers ( ' DEFAULT:@SECLEVEL=0 ' )
2025-01-26 01:00:28 -08:00
context . load_cert_chain ( ssl_cert , keyfile = ssl_pkey )
print ( " Waiting for connection... " )
connection , client = sock . accept ( )
ip_to = restrict_ip
2025-02-03 15:15:09 -08:00
conn = context . wrap_socket ( connection , server_side = True )
threading . Thread ( target = session , daemon = True , args = [ conn , client , ip_to , True ] ) . start ( )
2025-01-26 00:46:02 -08:00
except :
print ( " Something went wrong... " )
print ( traceback . format_exc ( ) )
2025-01-02 19:52:51 -08:00
for ip , i in sockets . items ( ) :
2025-01-02 19:53:39 -08:00
print ( " Now listening on port 6667 with IP " + ip )
2025-01-22 21:11:56 -08:00
threading . Thread ( target = tcp_session , args = [ i ] , daemon = True ) . start ( )
2025-01-02 19:52:13 -08:00
if ssl_option :
2025-01-02 19:52:51 -08:00
for ip , i in sockets_ssl . items ( ) :
2025-01-02 19:53:39 -08:00
print ( " Now listening on SSL port 6697 with IP " + ip )
2025-01-22 21:11:56 -08:00
threading . Thread ( target = ssl_session , args = [ i ] , daemon = True ) . start ( )
while opened :
pass
print ( " Shutting down... " )