2024-12-08 16:54:04 -08:00
#!/usr/bin/python3
2025-02-11 23:14:45 -08:00
__version__ = " 0.0.7 "
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
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-02-14 22:45:31 -08:00
def parseOutContent ( text : str ) :
if text [ 0 ] == " : " :
return text [ 1 : ]
else :
return text . split ( " " ) [ 0 ]
2025-01-25 21:15:19 -08:00
def getident ( hostt : str , clientport : int , ssll : bool ) :
try :
2025-02-05 19:29:17 -08:00
print ( hostt )
identsender = socket . socket ( ( socket . AF_INET6 if " : " in hostt else socket . AF_INET ) , socket . SOCK_STREAM )
2025-02-05 12:24:40 -08:00
identsender . settimeout ( 2 )
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
2025-02-14 22:45:31 -08:00
channel_modestore = { } # Channel: {nick: mode}
channel_modestore_identify = { } # Channel: {account: mode}
2025-01-21 19:27:58 -08:00
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 ( " " ) :
2025-02-05 17:27:15 -08:00
if " : " in i :
sockets [ i ] = socket . socket ( socket . AF_INET6 , socket . SOCK_STREAM )
else :
sockets [ i ] = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2025-01-02 19:45:42 -08:00
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 ( " " ) :
2025-02-05 17:27:15 -08:00
if " : " in i :
sockets_ssl [ i ] = socket . socket ( socket . AF_INET6 , socket . SOCK_STREAM )
else :
sockets_ssl [ i ] = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2025-01-02 19:45:42 -08:00
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
2025-02-11 22:06:13 -08:00
#def pinger(nick, connection):
# global property_list
# while nick in property_list:
# if (time.time() - property_list[nick]["last_ping"]) > 30 and not property_list[nick]["ping_pending"]:
# if nick in property_list:
# print("Sent ping message to " + nick)
# property_list[nick]["ping_pending"] = True
# time.sleep(0.5)
# try:
# connection.sendall(bytes(f"PING {server}\r\n","UTF-8"))
# except Exception as ex:
# print(traceback.format_exc())
# property_list[nick]["cause"] = "Send error: " + str(ex)
# print("SHUTTING DOWN FOR " + nick)
# connection.shutdown(socket.SHUT_WR)
# connection.close()
# break
# elif property_list[nick]["ping_pending"] and ((time.time() - property_list[nick]["last_ping"]) > ping_timeout):
# if nick in property_list:
# property_list[nick]["cause"] = f"Ping timeout: {ping_timeout} seconds"
# print("SHUTTING DOWN FOR " + nick)
# connection.shutdown(socket.SHUT_WR)
# connection.close()
# break
2025-01-22 19:57:41 -08:00
def session ( connection , client , ip , isssl = False ) :
2025-01-13 17:24:21 -08:00
global channels_list
2025-02-13 21:43:28 -08:00
global property_list
2025-01-13 17:24:21 -08:00
global nickname_list
2025-02-14 22:45:31 -08:00
global channel_modestore
global channel_modestore_identify
2025-02-14 23:39:03 -08:00
global topic_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-11 22:06:13 -08:00
ping_pending = False
last_ping = time . time ( )
2025-02-04 16:40:42 -08:00
IRCv3Features = [ ] # List of Acknowledged IRCv3 features.
2025-02-05 20:52:42 -08:00
stillRunning = True
2025-02-04 16:40:42 -08:00
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 " " )
2025-02-05 18:59:40 -08:00
def dosend ( content ) :
if content . __class__ . __name__ != " bytes " :
content = bytes ( content , " UTF-8 " )
try :
connection . sendall ( content )
2025-02-11 21:55:09 -08:00
except Exception as ex :
raise ex
2025-02-04 16:40:42 -08:00
def tags_diffclient ( nick : str ) : # Get tags of another client
othercap = property_list [ nick ] [ " v3cap " ]
2025-02-12 20:37:14 -08:00
tags = [ ]
2025-02-04 16:40:42 -08:00
if " server-time " in othercap :
2025-02-12 20:37:14 -08:00
tags . append ( " time= " + datetime . datetime . now ( datetime . timezone . utc ) . isoformat ( ) [ : - 9 ] + " Z " )
if " account-tag " in othercap and property_list [ pending ] [ " identified " ] :
2025-02-13 14:59:43 -08:00
tags . append ( " account= " + property_list [ pending ] [ " identusername " ] )
2025-02-12 20:37:14 -08:00
return ( " @ " if args != [ ] else " " ) + " ; " . join ( tags ) + ( " " if args != [ ] else " " )
2024-12-08 16:54:04 -08:00
try :
print ( " Connected to client IP: {} " . format ( client ) )
2025-02-04 19:12:05 -08:00
#if isssl:
# connection.do_handshake()
# tlsver = connection.version()
# print(f"Got SSL version: {tlsver}")
2025-02-04 15:28:00 -08:00
connection . settimeout ( None )
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } NOTICE * :*** Looking for your hostname... \r \n " , " UTF-8 " ) )
dosend ( 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-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } NOTICE * :*** Got hostname! { hostname } \r \n " , " UTF-8 " ) )
2024-12-09 12:41:46 -08:00
except :
hostname = client [ 0 ]
2025-02-05 18:59:40 -08:00
dosend ( 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-02-05 19:20:57 -08:00
identQuery = getident ( client [ 0 ] , 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-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } NOTICE * :*** Uhm, Couldn ' t find your ident: { responseee } \r \n " , " UTF-8 " ) )
2025-01-25 21:15:19 -08:00
else :
2025-02-05 18:59:40 -08:00
dosend ( 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-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } NOTICE * :*** Uhm, Couldn ' t find your ident: Unknown error. \r \n " , " UTF-8 " ) )
2025-02-05 20:52:42 -08:00
while stillRunning :
2024-12-08 16:54:04 -08:00
try :
2025-02-11 23:14:45 -08:00
connection . settimeout ( 2 )
2024-12-08 16:54:04 -08:00
data = connection . recv ( 2048 )
2025-01-06 15:38:03 -08:00
if not data :
cause = " Remote host closed the connection "
break
2025-02-05 12:24:40 -08:00
except socket . timeout :
print ( " Socket timed out, ticking... " )
data = bytes ( " " , " UTF-8 " )
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 ( )
2025-02-11 22:06:13 -08:00
#if finished and not property_list[pending]["ping_pending"]:
# property_list[pending]["last_ping"] = time.time()
2025-02-11 22:07:57 -08:00
if finished :
2025-02-11 22:06:13 -08:00
if ( time . time ( ) - last_ping ) > 30 and not ping_pending :
print ( f " Sending ping msg to { pending } " )
ping_pending = True
time . sleep ( 0.5 )
dosend ( bytes ( f " PING { server } \r \n " , " UTF-8 " ) )
elif ping_pending and ( time . time ( ) - last_ping ) > ping_timeout :
cause = f " Ping timeout: { ping_timeout } seconds "
print ( f " { pending } timed out. " )
break
2025-02-11 21:58:50 -08:00
if textt != " " and textt [ - 1 ] == " \n " :
2025-01-29 20:17:03 -08:00
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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-12 20:38:24 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } CAP * LS :server-time account-tag \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 " )
2025-02-13 13:49:22 -08:00
elif cap == " account-tag " :
2025-02-12 20:37:14 -08:00
IRCv3Features . append ( " account-tag " )
2025-02-04 16:40:42 -08:00
else :
capsuccess = False
break
2025-02-04 17:10:55 -08:00
capper = " " . join ( capabilities )
2025-02-04 16:40:42 -08:00
if capsuccess :
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } CAP * ACK : { capper } \r \n " , " UTF-8 " ) )
2025-02-04 17:10:55 -08:00
else :
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } CAP * NAK : { capper } \r \n " , " 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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 :
print ( f " User { pending } successfully logged in. " )
nickname_list [ pending ] = connection
2025-02-12 20:23:06 -08:00
property_list [ pending ] = { " host " : hostname , " username " : clident if clident != None else f " ~ { username } " , " realname " : realname , " modes " : " iw " , " away " : False , " identified " : False , " ssl " : isssl , " v3cap " : IRCv3Features , " last_ping " : time . time ( ) , " ping_pending " : False , " pendingSend " : " " }
2025-02-11 22:06:13 -08:00
last_ping = time . time ( )
ping_pending = False
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-02-11 22:06:13 -08:00
#threading.Thread(target=pinger, args=[pending, connection]).start()
2025-01-29 20:17:03 -08:00
if clident == None :
rident = f " ~ { username } "
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 001 { pending } :Welcome to the { displayname } Internet Relay Chat Network { pending } \r \n " , " UTF-8 " ) )
2025-02-12 20:21:36 -08:00
actport = " 6697 " if isssl else " 6667 "
dosend ( bytes ( f " { tags ( ) } : { server } 002 { pending } :Your host is { server } [ { ip } / { actport } ], running version IRCat-v { __version__ } \r \n " , " UTF-8 " ) )
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 004 { pending } { server } IRCat- { __version__ } iow msitnR lfovkqb \r \n " , " UTF-8 " ) )
dosend ( bytes ( f " { tags ( ) } : { server } 005 { pending } CHANMODES=bq,k,fl,irmnpst CHANTYPES=# NETWORK= { displayname } :are supported by this server \r \n " , " UTF-8 " ) )
2025-02-12 20:18:18 -08:00
# dosend(bytes(f":{server} 251 {pending} :There are {allusers} users and {allinvis} invisible in {servers} servers\r\n", "UTF-8"))
2025-02-12 20:20:21 -08:00
ops = 0
2025-02-12 20:22:00 -08:00
for k , v in property_list . items ( ) :
2025-02-12 20:18:18 -08:00
if " o " in v [ " modes " ] :
ops + = 1
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 252 { pending } { ops } :IRC Operators online \r \n " , " UTF-8 " ) )
2025-02-12 20:20:21 -08:00
# dosend(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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 372 { pending } :- { i } \r \n " , " UTF-8 " ) )
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-04 17:13:16 -08:00
pendingSend + = f " { tags ( ) } : { server } PONG { server } { e } \r \n "
2025-01-29 20:17:03 -08:00
except :
2025-02-04 17:13:16 -08:00
pendingSend + = f " { tags ( ) } : { 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 372 { pending } :- { i } \r \n " , " UTF-8 " ) )
dosend ( 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-02-14 22:45:31 -08:00
if " identify " in cmdrun :
if cmdrun [ " identify " ] == " logout " :
if " o " in property_list [ pending ] [ " modes " ] :
dosend ( bytes ( f " { tags ( ) } : { pending } MODE { pending } -o \r \n " , " UTF-8 " ) )
if not " i " in property_list [ pending ] [ " modes " ] :
dosend ( bytes ( f " { tags ( ) } : { pending } MODE { pending } +i \r \n " , " UTF-8 " ) )
if not " w " in property_list [ pending ] [ " modes " ] :
dosend ( bytes ( f " { tags ( ) } : { pending } MODE { pending } +w \r \n " , " UTF-8 " ) )
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
dosend ( bytes ( f " { tags ( ) } : { pending } MODE { pending } + { temp_mode } \r \n " , " UTF-8 " ) )
2025-02-14 23:52:14 -08:00
if " initchan " in cmdrun :
2025-02-14 23:23:41 -08:00
print ( cmdrun [ " initchan " ] )
2025-02-14 23:51:54 -08:00
for chran in cmdrun [ " initchan " ] :
if chran [ " name " ] not in channels_list :
channels_list [ chran [ " name " ] ] = [ ]
topic_list [ chran [ " name " ] ] = chran [ " topic " ]
lower_chans [ chran [ " name " ] . lower ( ) ] = chran [ " name " ]
channel_modestore_identify [ chran [ " name " ] ] = chran [ " automodes " ]
channel_modestore [ chran [ " name " ] ] = { }
2025-02-15 00:03:04 -08:00
if cmdrun [ " success " ] == " skip " :
pass
elif cmdrun [ " success " ] :
processedExternally = True
break
2025-01-29 20:17:03 -08:00
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 ) :
2025-02-06 15:38:52 -08:00
if channel [ 0 ] == " # " :
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-02-03 22:30:19 -08:00
try :
2025-02-06 15:38:52 -08:00
if channel in channels_list :
channels_list [ channel ] . append ( pending )
else :
channels_list [ channel ] = [ pending ]
lower_chans [ channel . lower ( ) ] = channel
2025-02-14 22:45:31 -08:00
topic_list [ channel ] = " "
channel_modestore [ channel ] = { pending : " o " }
dosend ( bytes ( f " : { server } MODE { channel } +o { pending } \r \n " , " UTF-8 " ) )
2025-02-03 22:30:19 -08:00
except :
2025-02-06 15:38:52 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } NOTICE * :*** Could not join { channel } \r \n " , " UTF-8 " ) )
print ( channels_list )
2025-02-15 00:03:47 -08:00
if property_list [ pending ] [ " identified " ] and channel in channel_modestore_identify :
2025-02-15 00:01:58 -08:00
print ( property_list [ pending ] [ " identusername " ] )
2025-02-15 00:00:42 -08:00
if property_list [ pending ] [ " identusername " ] in channel_modestore_identify [ channel ] :
print ( f " { pending } has an identify mode, setting mode " + channel_modestore_identify [ channel ] [ property_list [ pending ] [ " identusername " ] ] )
channel_modestore [ channel ] [ pending ] = channel_modestore_identify [ channel ] [ property_list [ pending ] [ " identusername " ] ]
2025-02-15 00:31:50 -08:00
mo = channel_modestore [ channel ] [ pending ]
pendingSend + = f " : { server } MODE { channel } + { mo } { pending } \r \n "
2025-02-06 15:38:52 -08:00
for i in channels_list [ channel ] :
try :
2025-02-13 21:46:41 -08:00
if i != pending :
property_list [ i ] [ " pendingSend " ] + = f " : { pending } ! { rident } @ { hostname } JOIN { channel } \r \n "
2025-02-14 22:45:31 -08:00
if pending in channel_modestore [ channel ] and channel_modestore [ channel ] [ pending ] != " " :
mo = channel_modestore [ channel ] [ pending ]
property_list [ i ] [ " pendingSend " ] + = f " : { server } MODE { channel } + { mo } { pending } \r \n "
2025-02-06 15:38:52 -08:00
except :
pass
2025-02-13 21:47:57 -08:00
dosend ( bytes ( f " : { pending } ! { rident } @ { hostname } JOIN { channel } \r \n " , " UTF-8 " ) )
2025-02-14 23:19:22 -08:00
users = " "
for i in channels_list [ channel ] :
who_mode = " " if not i in channel_modestore [ channel ] else ( " @ " if channel_modestore [ channel ] [ i ] == " o " else ( " + " if channel_modestore [ channel ] [ i ] == " v " else " " ) )
users + = f " { who_mode } { i } "
dosend ( bytes ( f " { tags ( ) } : { server } 353 { pending } = { channel } : { users } \r \n " , " UTF-8 " ) )
dosend ( bytes ( f " { tags ( ) } : { server } 366 { pending } { channel } :End of /NAMES list. \r \n " , " UTF-8 " ) )
2025-02-14 23:40:41 -08:00
if topic_list [ channel ] != " " :
2025-02-14 23:19:22 -08:00
tpc = topic_list [ channel ]
2025-02-14 23:28:26 -08:00
print ( tpc )
2025-02-14 23:19:22 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 332 { pending } { channel } : { tpc } \r \n " , " UTF-8 " ) )
else :
dosend ( bytes ( f " { tags ( ) } : { server } 331 { pending } { channel } :No topic is set \r \n " , " UTF-8 " ) )
2025-02-06 15:38:52 -08:00
else :
dosend ( bytes ( f " { tags ( ) } : { server } 479 { pending } { channel } :Channel name needs to start with # \r \n " , " UTF-8 " ) )
2025-02-03 22:30:19 -08:00
else :
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 322 { pending } { key } { usersin } : { value } \r \n " , " UTF-8 " ) )
dosend ( bytes ( f " { tags ( ) } : { server } 323 { pending } :End of /LIST \r \n " , " UTF-8 " ) )
2025-02-14 22:45:31 -08:00
elif command == " TOPIC " :
2025-02-14 23:22:58 -08:00
channel = args [ 0 ]
2025-02-14 22:45:31 -08:00
if channel in channels_list :
if pending in channels_list [ channel ] :
if len ( args ) > 1 :
if " o " in channel_modestore [ channel ] [ pending ] :
content = parseOutContent ( " " . join ( text . split ( " " ) [ 2 : ] ) )
for i in channels_list [ channel ] :
try :
property_list [ i ] [ " pendingSend " ] + = f " : { pending } ! { rident } @ { hostname } TOPIC { channel } : { content } \r \n "
except :
pass
else :
dosend ( bytes ( f " { tags ( ) } : { server } 482 { pending } { channel } :You ' re not channel operator \r \n " , " UTF-8 " ) )
else :
2025-02-14 23:40:41 -08:00
if topic_list [ channel ] != " " :
2025-02-14 22:45:31 -08:00
tpc = topic_list [ channel ]
dosend ( bytes ( f " { tags ( ) } : { server } 332 { pending } { channel } : { tpc } \r \n " , " UTF-8 " ) )
else :
dosend ( bytes ( f " { tags ( ) } : { server } 331 { pending } { channel } :No topic is set \r \n " , " UTF-8 " ) )
else :
dosend ( bytes ( f " { tags ( ) } : { server } 441 { pending } { channel } :You ' re not on that channel \r \n " , " UTF-8 " ) )
else :
dosend ( bytes ( f " { tags ( ) } : { server } 441 { pending } { channel } :You ' re not on that channel \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. " )
2025-02-11 22:06:13 -08:00
#property_list[pending]["last_ping"] = time.time()
#property_list[pending]["ping_pending"] = False
last_ping = time . time ( )
ping_pending = False
2025-01-29 20:17:03 -08:00
elif command == " NICK " :
if len ( args ) == 0 :
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-13 21:42:10 -08:00
property_list [ j ] [ " pendingSend " ] + = f " { tags_diffclient ( j ) } : { pending } ! { rident } @ { hostname } { text } \r \n "
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
2025-02-11 22:06:13 -08:00
#print("starting pinger...")
2025-01-29 20:17:03 -08:00
pending = pending2
2025-02-11 22:06:13 -08:00
#property_list[pending2]["ping_pending"] = False
#property_list[pending2]["last_ping"] = time.time()
#threading.Thread(target=pinger, args=[pending, connection]).start()
2025-01-29 20:17:03 -08:00
print ( f " User { pending } set nick " )
print ( " Broadcasting nickname change... " )
2025-02-25 17:18:53 -08:00
elif command == " ISON " :
if args [ 0 ] [ 0 ] == " : " :
args [ 0 ] = args [ 0 ] [ 1 : ]
onlines = [ ]
for someuser in args :
if someuser . lower ( ) in lower_nicks :
someuser = lower_nicks [ someuser . lower ( ) ]
if someuser in nickname_list :
onlines . append ( someuser )
onlines = " " . join ( onlines )
dosend ( bytes ( f " { tags ( ) } : { server } 303 { pending } : { onlines } \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif command == " PART " :
if len ( args ) == 0 :
2025-02-05 18:59:40 -08:00
dosend ( 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-13 21:42:10 -08:00
property_list [ i ] [ " pendingSend " ] + = f " { tags_diffclient ( i ) } : { pending } ! { rident } @ { hostname } { text } \r \n "
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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-14 22:45:31 -08:00
who_mode = " " if not i in channel_modestore [ channel ] else ( " @ " if channel_modestore [ channel ] [ i ] == " o " else ( " + " if channel_modestore [ channel ] [ i ] == " v " else " " ) )
dosend ( bytes ( f " { tags ( ) } : { server } 352 { pending } { channel } { who_user } { who_host } { server } { i } { who_away } { who_mode } :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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 311 { pending } { target } { who_user } { who_host } * : { who_realname } \r \n " , " UTF-8 " ) )
dosend ( bytes ( f " { tags ( ) } : { server } 312 { pending } { target } { server } : { identifier } \r \n " , " UTF-8 " ) )
if " o " in who_flags : dosend ( bytes ( f " : { server } 313 { pending } { target } :is an IRC operator \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
who_away = property_list [ target ] [ " away " ]
if who_away :
who_reason = who_away = property_list [ target ] [ " reason " ]
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 671 { pending } { target } :is using a secure connection \r \n " , " UTF-8 " ) )
#dosend(bytes(f":{server} 317 {pending} {target} {time} :seconds idle\r\n","UTF-8")) # I haven't implemented idle time yet.
2025-01-29 20:17:03 -08:00
if who_flags != None and who_flags != " iw " :
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 379 { pending } { target } :Is using modes + { who_flags } \r \n " , " UTF-8 " ) )
dosend ( 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-05 18:59:40 -08:00
dosend ( 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 :
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { 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 :
if pending in channels_list [ channel ] :
2025-02-14 22:45:31 -08:00
users = " "
for i in channels_list [ channel ] :
who_mode = " " if not i in channel_modestore [ channel ] else ( " @ " if channel_modestore [ channel ] [ i ] == " o " else ( " + " if channel_modestore [ channel ] [ i ] == " v " else " " ) )
users + = f " { who_mode } { i } "
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { server } 353 { pending } = { channel } : { users } \r \n " , " UTF-8 " ) )
dosend ( 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 :
2025-02-14 22:45:31 -08:00
content = praseOutContent ( " " . join ( text . split ( " " ) [ 2 : ] ) )
2025-01-29 20:17:03 -08:00
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-14 22:45:31 -08:00
property_list [ i ] [ " pendingSend " ] + = f " { tags_diffclient ( i ) } : { pending } ! { rident } @ { hostname } NOTICE { target } : { content } \r \n "
2025-01-29 20:17:03 -08:00
except :
pass
elif target in nickname_list :
2025-02-14 22:45:31 -08:00
property_list [ target ] [ " pendingSend " ] + = f " { tags_diffclient ( target ) } : { pending } ! { rident } @ { hostname } NOTICE { target } : { content } \r \n "
2025-01-29 19:57:22 -08:00
else :
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-13 21:42:10 -08:00
property_list [ j ] [ " pendingSend " ] + = f " { tags_diffclient ( j ) } : { pending } ! { rident } @ { hostname } { text } \r \n "
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.
2025-02-05 20:52:42 -08:00
stillRunning = False
2025-01-29 20:17:03 -08:00
try :
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } : { pending } ! { rident } @ { hostname } { text } \r \n " , " UTF-8 " ) )
dosend ( bytes ( f " ERROR :Closing Link: { hostname } ( { msg } ) \r \n " , " UTF-8 " ) )
2025-02-05 20:52:42 -08:00
except :
2025-01-29 20:17:03 -08:00
connection . close ( )
safe_quit = True
break
elif command == " MODE " :
target = args [ 0 ]
if len ( args ) == 0 :
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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 :
2025-02-05 18:59:40 -08:00
dosend ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2025-01-29 20:17:03 -08:00
elif args [ 0 ] . upper ( ) == " PULL " :
updater = subprocess . run ( [ " git " , " pull " ] , stdout = subprocess . PIPE )
if updater . stdout . decode ( ) . strip ( ) == " Already up to date. " :
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :Codename IRCat version { __version__ } \r \n " , " UTF-8 " ) )
dosend ( 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-05 18:59:40 -08:00
dosend ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :CatServ Usage: \r \n " , " UTF-8 " ) )
dosend ( bytes ( f " { tags ( ) } :CatServ!Meow@IRCatCore NOTICE { pending } :PULL - Pulls the latest version of Codename IRCat \r \n " , " UTF-8 " ) )
dosend ( 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-05 18:59:40 -08:00
dosend ( 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 :
2025-02-14 22:45:31 -08:00
content = parseOutContent ( " " . join ( text . split ( " " ) [ 2 : ] ) )
2025-01-29 20:17:03 -08:00
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 )
2025-02-14 22:45:31 -08:00
print ( f " : { pending } ! { rident } @ { hostname } PRIVMSG { target } : { content } \r \n " )
2025-02-15 18:40:57 -08:00
property_list [ i ] [ " pendingSend " ] + = f " { tags_diffclient ( i ) } : { pending } ! { rident } @ { hostname } PRIVMSG { target } : { content } \r \n "
2025-01-29 20:17:03 -08:00
else :
print ( i + " Is the current user! " )
except :
2025-02-04 17:10:55 -08:00
print ( traceback . format_exc ( ) )
2025-01-29 20:17:03 -08:00
elif target in nickname_list :
2025-02-14 22:45:31 -08:00
property_list [ target ] [ " pendingSend " ] + = f " { tags_diffclient ( target ) } : { pending } ! { rident } @ { hostname } PRIVMSG { target } : { content } \r \n "
2025-01-29 20:17:03 -08:00
else :
2025-02-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-05 18:59:40 -08:00
dosend ( 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-13 21:45:16 -08:00
if finished and property_list [ pending ] [ " pendingSend " ] != " " :
2025-02-13 21:51:15 -08:00
print ( str ( property_list [ pending ] [ " pendingSend " ] ) )
dosend ( bytes ( str ( property_list [ pending ] [ " pendingSend " ] ) , " UTF-8 " ) )
2025-02-13 21:34:48 -08:00
property_list [ pending ] [ " pendingSend " ] = " "
2025-02-13 21:45:16 -08:00
if pendingSend != " " :
dosend ( bytes ( pendingSend , " UTF-8 " ) )
2025-02-03 15:30:07 -08:00
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-13 21:51:15 -08:00
property_list [ j ] [ " pendingSend " ] + = f " : { pending } ! { rident } @ { hostname } QUIT : { cause } \r \n "
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 ( ) )
2025-02-12 20:18:18 -08:00
def tcp_session ( sock , ip_to ) :
2025-02-20 11:37:42 -08:00
while opened :
2025-01-02 20:01:06 -08:00
try :
2025-02-20 11:37:42 -08:00
print ( " Waiting for connection... " )
connection , client = sock . accept ( )
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-02-12 20:18:18 -08:00
def ssl_session ( sock , ip_to ) :
2025-02-20 11:37:42 -08:00
while opened :
2025-01-26 00:46:02 -08:00
try :
2025-02-20 11:37:42 -08:00
print ( " Waiting for SSL connection... " )
connection , client = sock . accept ( )
ctx = ssl . SSLContext ( ssl . PROTOCOL_TLS_SERVER )
ctx . load_cert_chain ( ssl_cert , keyfile = ssl_pkey )
conn = ctx . wrap_socket ( connection , server_side = True )
conn . do_handshake ( )
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-02-12 20:18:18 -08:00
threading . Thread ( target = tcp_session , args = [ i , ip ] , 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-02-12 20:18:18 -08:00
threading . Thread ( target = ssl_session , args = [ i , ip ] , daemon = True ) . start ( )
2025-01-22 21:11:56 -08:00
while opened :
pass
2025-02-13 13:49:22 -08:00
print ( " Shutting down... " )