2024-12-08 16:54:04 -08:00
#!/usr/bin/python3
2024-12-08 20:06:57 -08:00
__version__ = " 0.0.1-pre-alpha "
2024-12-24 16:50:04 -08:00
print ( f " Codename IRCat v { __version__ } " )
2024-12-08 16:54:04 -08:00
print ( " Welcome! /ᐠ ˵> ⩊ <˵マ " )
2024-12-12 11:39:41 -08:00
import socket , time , threading , traceback , sys , subprocess , yaml , sqlite3 , os , bcrypt
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 = """
____ _ ___ ____ ____ _
/ ___ | ___ __ | | ___ _ __ __ _ _ __ ___ ___ | _ _ | _ \ / ___ | __ _ | | _
| | / _ \ / _ ` | / _ \ ' _ \ / _` | ' _ ` _ \ / _ \ | | | | _ ) | | / _ ` | __ |
| | __ | ( _ ) | ( _ | | __ / | | | ( _ | | | | | | | __ / | | | _ < | | __ | ( _ | | | _
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 = ' '
2024-12-08 19:04:37 -08:00
with open ( sys . argv [ 1 ] , ' r ' ) as file :
data = yaml . safe_load ( file )
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 " )
try : data_path = data [ " data-path " ]
except :
print ( " IRCat requires \" data-path \" in config.yml " )
sys . exit ( 1 )
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. " )
2024-12-08 19:04:37 -08:00
file . close ( )
2024-12-08 20:03:55 -08:00
print ( " Successfully loaded config! " )
2024-12-11 14:50:16 -08:00
class IRCat_DATA_BROKER :
def __init__ ( self ) :
if not os . path . isfile ( data_path ) :
print ( " Creating database file... " )
open ( data_path , " w " ) . write ( " " )
self . conn = sqlite3 . connect ( data_path )
2024-12-12 11:48:41 -08:00
db = self . conn . cursor ( )
2024-12-12 11:52:20 -08:00
print ( " Creating NickServ table... " )
2024-12-13 13:03:06 -08:00
db . execute ( """ CREATE table IF NOT EXISTS nickserv (user varchar(255), modes varchar(255), hash varchar(255), cloak varchar(255)) """ )
2024-12-12 11:52:20 -08:00
print ( " Creating Groups table... " )
db . execute ( """ CREATE table IF NOT EXISTS groups (name varchar(255), owner varchar(255)) """ )
print ( " Creating ChanServ table... " )
db . execute ( """ CREATE table IF NOT EXISTS chanserv (name varchar(255), modes varchar(255), params varchar(255), owner varchar(255), usermodes varchar(255), optimodes varchar(255)) """ )
2024-12-12 11:48:41 -08:00
def nickserv_identify ( self , nick , password : str ) :
db = self . conn . cursor ( )
password = password . encode ( " UTF-8 " )
db . execute ( " SELECT * FROM nickserv WHERE user=?; " , [ nick ] )
2024-12-12 11:49:41 -08:00
if db . fetchall ( ) == [ ] :
2024-12-12 11:48:41 -08:00
return [ " Nickname doesn ' t exist. " ]
2024-12-12 11:39:41 -08:00
2024-12-12 09:56:55 -08:00
config = IRCat_DATA_BROKER ( )
2024-12-08 17:36:59 -08:00
tcp_socket = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
tcp_socket . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
2024-12-26 16:23:58 -08:00
server_address = ( restrict_ip , 6667 )
2024-12-08 16:54:04 -08:00
tcp_socket . bind ( server_address )
tcp_socket . listen ( 1 )
2024-12-11 14:59:32 -08:00
opened = True
2024-12-09 14:35:01 -08:00
reserved = [ " nickserv " , " chanserv " , " gitserv " ] # Reserved nicknames
2024-12-09 12:27:44 -08:00
nickname_list = { } # Stores nicknames and the respective sockets
2024-12-13 13:03:06 -08:00
lower_nicks = { " gitserv " : " GitServ " , " nickserv " : " NickServ " } # Nicknames in lowercase
2024-12-09 12:27:44 -08:00
channels_list = { } # Store channels and their user lists
2024-12-27 20:16:23 -08:00
lower_chans = { } # Channel names in lowercase
2024-12-22 16:54:52 -08:00
property_list = { " GitServ " : { " host " : " IRCatCore " , " username " : " IRCat " , " realname " : " Codename IRCat Integrated services - Updates bot " } , " NickServ " : { " host " : " IRCatCore " , " username " : " IRCat " , " realname " : " Codename IRCat Integrated services - Login bot " , " away " : False } } # Stores properties for active users and channels
2024-12-08 16:54:04 -08:00
print ( " Now listening on port 6667 " )
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 )
connection . sendall ( bytes ( f " PING { server } \r \n " , " UTF-8 " ) )
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
2024-12-27 20:11:35 -08:00
def session ( connection , client , ip ) :
2024-12-15 19:08:18 -08:00
global property_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
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
2024-12-08 16:54:04 -08:00
try :
print ( " Connected to client IP: {} " . format ( client ) )
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Looking for your hostname... \r \n " , " UTF-8 " ) )
2024-12-09 12:41:46 -08:00
try :
hostname = socket . gethostbyaddr ( client [ 0 ] ) [ 0 ]
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Got it! { hostname } \r \n " , " UTF-8 " ) )
except :
hostname = client [ 0 ]
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Oof! Can ' t find your hostname, using IP... \r \n " , " UTF-8 " ) )
2024-12-08 16:54:04 -08:00
while True :
try :
data = connection . recv ( 2048 )
2024-12-09 21:58:05 -08:00
except Exception as ex :
cause = " Read error: " + str ( ex )
break
2024-12-08 16:54:04 -08:00
print ( " Received data: {} " . format ( data ) )
try :
textt = data . decode ( )
2024-12-11 22:32:38 -08:00
for text in textt . replace ( " \r " , " " ) . split ( " \n " ) :
2024-12-09 11:16:30 -08:00
command = text . split ( " " ) [ 0 ] . upper ( )
try :
args = text . split ( " " ) [ 1 : ]
except :
pass
2024-12-12 13:12:10 -08:00
if command == " NICK " and not finished :
2024-12-08 16:54:04 -08:00
pending = text . split ( " " ) [ 1 ]
2024-12-09 15:33:56 -08:00
if pending [ 0 ] == " : " : pending [ 1 : ]
2024-12-09 21:43:11 -08:00
if " ! " in pending or " : " in pending or " # " in pending or " * " in pending :
connection . sendall ( bytes ( f " : { server } 432 * { pending } :Erroneus nickname \r \n " , " UTF-8 " ) )
pending = " * "
elif pending . lower ( ) in lower_nicks or pending in reserved :
2024-12-08 16:54:04 -08:00
connection . sendall ( bytes ( f " : { server } 433 * { pending } :Nickname is already in use. \r \n " , " UTF-8 " ) )
2024-12-09 09:30:20 -08:00
pending = " * "
2024-12-08 16:54:04 -08:00
else :
2024-12-12 08:45:11 -08:00
already_set = True
print ( f " User { pending } set nick " )
2024-12-09 11:07:26 -08:00
elif command == " USER " :
2024-12-08 16:54:04 -08:00
if not ready :
username = text . split ( " " ) [ 1 ]
2024-12-09 13:44:15 -08:00
realname = " " . join ( text . split ( " " ) [ 4 : ] ) [ 1 : ]
2024-12-08 20:06:57 -08:00
ready = True
2024-12-09 13:33:03 -08:00
elif command == " CAP " :
2024-12-09 13:33:26 -08:00
if args [ 0 ] == " LS " :
2024-12-15 05:10:05 -08:00
connection . sendall ( bytes ( f " : { server } CAP * LS :ircat.xyz/foo \r \n " , " UTF-8 " ) )
2024-12-08 20:02:59 -08:00
elif ( ready and already_set ) and not finished :
2024-12-15 19:29:27 -08:00
cleanup_manual ( )
2024-12-11 22:01:28 -08:00
print ( f " User { pending } successfully logged in. " )
2024-12-09 14:35:01 -08:00
nickname_list [ pending ] = connection
2024-12-22 16:54:52 -08:00
property_list [ pending ] = { " host " : hostname , " username " : username , " realname " : realname , " modes " : " iw " , " last_ping " : time . time ( ) , " ping_pending " : False , " away " : False }
2024-12-09 15:57:24 -08:00
lower_nicks [ pending . lower ( ) ] = pending
2024-12-09 16:45:36 -08:00
threading . Thread ( target = pinger , args = [ pending , connection ] ) . start ( )
2024-12-08 20:02:59 -08:00
connection . sendall ( bytes ( f " : { server } 001 { pending } :Welcome to the { displayname } Internet Relay Chat Network { pending } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " : { server } 002 { pending } :Your host is { server } [ { ip } /6667], running version IRCat-v { __version__ } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " : { server } 004 { pending } { server } IRCat- { __version__ } iow ovmsitnlbkq \r \n " , " UTF-8 " ) )
2024-12-26 16:47:12 -08:00
connection . sendall ( bytes ( f " : { server } 005 { pending } CHANMODES=bq,k,l,irmnpst CHANTYPES=# NETWORK= { displayname } :are supported by this server \r \n " , " UTF-8 " ) )
2024-12-15 15:39:48 -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
connection . sendall ( bytes ( f " : { server } 252 { pending } { ops } :IRC Operators online \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " : { server } 253 { pending } 0 :unknown connection(s) \r \n " , " UTF-8 " ) ) # Replace 0 with a variable of not setup clients.
chans = len ( channels_list )
connection . sendall ( bytes ( f " : { server } 254 { pending } { chans } :channels formed \r \n " , " UTF-8 " ) )
cleints = len ( nickname_list )
servers = 1
connection . sendall ( bytes ( f " : { server } 255 { pending } :I have { cleints } clients and { servers } servers \r \n " , " UTF-8 " ) )
# Start the MOTD
if motd_file != None :
motd = open ( motd_file ) . read ( )
connection . sendall ( bytes ( f " : { server } 375 { pending } :- { server } Message of the Day - \r \n " , " UTF-8 " ) )
for i in motd . rstrip ( ) . split ( " \n " ) :
connection . sendall ( bytes ( f " : { server } 376 { pending } :- { i } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " : { server } 372 { pending } :End of /MOTD command \r \n " , " UTF-8 " ) )
# End the MOTD
2024-12-08 20:02:59 -08:00
connection . sendall ( bytes ( f " : { pending } MODE { pending } +iw \r \n " , " UTF-8 " ) )
finished = True
2024-12-09 11:07:26 -08:00
elif command == " PING " :
2024-12-09 21:56:12 -08:00
e = text . split ( " " ) [ 1 ]
2024-12-10 10:25:15 -08:00
print ( " Replying with \" " + str ( [ f " : { server } PONG { server } : { e } \r \n " ] ) + " \" " )
connection . sendall ( bytes ( f " : { server } PONG { server } : { e } \r \n " , " UTF-8 " ) )
2024-12-15 15:39:48 -08:00
elif command == " MOTD " :
if motd_file != None :
motd = open ( motd_file ) . read ( )
connection . sendall ( bytes ( f " : { server } 375 { pending } :- { server } Message of the Day - \r \n " , " UTF-8 " ) )
for i in motd . rstrip ( ) . split ( " \n " ) :
connection . sendall ( bytes ( f " : { server } 376 { pending } :- { i } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " : { server } 372 { pending } :End of /MOTD command \r \n " , " UTF-8 " ) )
2024-12-12 13:12:10 -08:00
elif finished :
2024-12-09 11:07:26 -08:00
if command == " JOIN " :
2024-12-09 12:04:47 -08:00
channels = text . split ( " " ) [ 1 ]
for channelt in channels . split ( " , " ) :
channel = channelt . strip ( )
2024-12-27 20:10:42 -08:00
if channel . lower ( ) in lower_chans :
channel = lower_chans [ channel . lower ( ) ]
2024-12-09 12:04:47 -08:00
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 :
2024-12-08 22:28:45 -08:00
try :
2024-12-09 12:04:47 -08:00
if channel in channels_list :
channels_list [ channel ] . append ( pending )
else :
channels_list [ channel ] = [ pending ]
2024-12-27 20:10:42 -08:00
lower_chans [ channel . lower ( ) ] = channel
2024-12-08 22:28:45 -08:00
except :
2024-12-09 12:04:47 -08:00
connection . sendall ( bytes ( f " : { server } NOTICE * :*** Could not join { channel } \r \n " , " UTF-8 " ) )
print ( channels_list )
for i in channels_list [ channel ] :
try :
2024-12-09 13:26:23 -08:00
nickname_list [ i ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } JOIN { channel } \r \n " , " UTF-8 " ) )
2024-12-09 12:04:47 -08:00
except :
pass
2024-12-09 13:26:23 -08:00
# Code re-used in the NAMES command
2024-12-09 12:04:47 -08:00
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 " ) )
connection . sendall ( bytes ( f " : { server } 366 { pending } { channel } :End of /NAMES list. \r \n " , " UTF-8 " ) )
2024-12-09 12:41:46 -08:00
print ( " Successfully pre-loaded /NAMES list " )
2024-12-09 16:20:40 -08:00
elif command == " PONG " :
e = text . split ( " " ) [ 1 ]
if e == server :
2024-12-09 16:46:15 -08:00
print ( pending + " replied to PING. " )
2024-12-09 16:45:21 -08:00
property_list [ pending ] [ " last_ping " ] = time . time ( )
property_list [ pending ] [ " ping_pending " ] = False
2024-12-12 13:22:59 -08:00
elif command == " NICK " :
2024-12-12 13:12:10 -08:00
if len ( args ) == 0 :
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-13 13:37:52 -08:00
elif text . split ( " " ) [ 1 ] == pending :
pass
2024-12-12 13:12:10 -08:00
else :
pending2 = text . split ( " " ) [ 1 ]
if pending2 [ 0 ] == " : " : pending2 [ 1 : ]
if " ! " in pending2 or " : " in pending2 or " # " in pending2 or " * " in pending2 :
connection . sendall ( bytes ( f " : { server } 432 { pending } { pending2 } :Erroneus nickname \r \n " , " UTF-8 " ) )
2024-12-12 13:19:48 -08:00
elif pending2 . lower ( ) in lower_nicks or pending2 in reserved :
2024-12-12 13:12:10 -08:00
connection . sendall ( bytes ( f " : { server } 433 { pending } { pending2 } :Nickname is already in use. \r \n " , " UTF-8 " ) )
else :
2024-12-12 13:52:18 -08:00
print ( " Sending nickname change... " )
2024-12-12 13:56:22 -08:00
connection . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } NICK { pending2 } \r \n " , " UTF-8 " ) )
2024-12-12 13:12:10 -08:00
# Broadcast the nickname change
done = [ ]
for i , users in channels_list . items ( ) :
2024-12-12 13:15:01 -08:00
if pending in users :
for j in users :
2024-12-12 13:28:45 -08:00
if j != pending and j != pending2 and not j in done :
2024-12-16 09:28:49 -08:00
print ( " Broadcasting on " + j )
2024-12-12 13:15:01 -08:00
nickname_list [ j ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
done . append ( j )
# Replace the nickname
try :
2024-12-12 13:45:48 -08:00
print ( " Changing on " + i )
2024-12-12 13:15:01 -08:00
channels_list [ i ] . remove ( pending )
channels_list [ i ] . append ( pending2 )
except :
print ( traceback . format_exc ( ) )
2024-12-16 09:28:49 -08:00
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... " )
2024-12-09 11:07:26 -08:00
elif command == " PART " :
2024-12-09 18:27:40 -08:00
if len ( args ) == 0 :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
else :
channel = text . split ( " " ) [ 1 ]
for i in channels_list [ channel ] :
try :
nickname_list [ i ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
except :
pass
2024-12-08 20:02:59 -08:00
try :
2024-12-09 18:27:40 -08:00
channels_list [ channel ] . remove ( pending )
2024-12-08 20:02:59 -08:00
except :
2024-12-09 18:27:40 -08:00
print ( traceback . format_exc ( ) )
2024-12-22 16:54:52 -08:00
elif command == " AWAY " :
if len ( args ) == 0 :
property_list [ pending ] [ " away " ] = False
connection . sendall ( bytes ( f " : { server } 305 { pending } :You are no longer marked as being away \r \n " , " UTF-8 " ) )
else :
reasons = " " . join ( args )
if reasons [ 0 ] == " : " : reasons = reasons [ 1 : ]
property_list [ pending ] [ " away " ] = True
property_list [ pending ] [ " reason " ] = reasons
connection . sendall ( bytes ( f " : { server } 306 { pending } :You have been marked as being away \r \n " , " UTF-8 " ) )
2024-12-09 11:07:26 -08:00
elif command == " WHO " :
2024-12-09 18:27:40 -08:00
if len ( args ) == 0 :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -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 " ]
2024-12-22 16:54:52 -08:00
who_away = " G " if property_list [ i ] [ " away " ] else " H "
connection . sendall ( bytes ( f " : { server } 352 { pending } { channel } ~ { who_user } { who_host } { server } { i } { who_away } :0 { who_realname } \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -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 " ]
2024-12-22 16:54:52 -08:00
who_away = " G " if property_list [ channel ] [ " away " ] else " H "
connection . sendall ( bytes ( f " : { server } 352 { pending } * ~ { who_user } { who_host } { server } { channel } { who_away } :0 { who_realname } \r \n " , " UTF-8 " ) )
2024-12-09 12:27:44 -08:00
2024-12-12 16:38:26 -08:00
connection . sendall ( bytes ( f " : { server } 315 { pending } { channel } :End of /WHO list. \r \n " , " UTF-8 " ) )
2024-12-09 14:35:01 -08:00
elif command == " WHOIS " :
2024-12-09 18:27:40 -08:00
if len ( args ) == 0 :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 14:35:01 -08:00
else :
2024-12-09 18:27:40 -08:00
target = text . split ( " " ) [ 1 ]
2024-12-09 18:56:12 -08:00
if target . lower ( ) in lower_nicks :
target = lower_nicks [ target . lower ( ) ]
2024-12-09 18:27:40 -08:00
if target in property_list :
who_user = property_list [ target ] [ " username " ]
who_realname = property_list [ target ] [ " realname " ]
who_host = property_list [ target ] [ " host " ]
2024-12-09 19:31:48 -08:00
try :
who_flags = property_list [ target ] [ " modes " ]
except :
who_flags = None
2024-12-09 18:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 311 { pending } { target } ~ { who_user } { who_host } * : { who_realname } \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
connection . sendall ( bytes ( f " : { server } 312 { pending } { target } { server } : { identifier } \r \n " , " UTF-8 " ) )
2024-12-22 16:54:52 -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 " ]
connection . sendall ( bytes ( f " : { server } 301 { pending } { target } : { who_reason } \r \n " , " UTF-8 " ) )
#connection.sendall(bytes(f":{server} 317 {pending} {target} {time} :seconds idle\r\n","UTF-8")) # I haven't implemented idle time yet.
2024-12-09 19:31:48 -08:00
if who_flags != None and who_flags != " iw " :
connection . sendall ( bytes ( f " : { server } 379 { pending } { target } :Is using modes + { who_flags } \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
connection . sendall ( bytes ( f " : { server } 318 { pending } { target } :End of /WHOIS list \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " : { server } 401 { pending } { target } :No such nick/channel \r \n " , " UTF-8 " ) )
2024-12-09 13:26:23 -08:00
elif command == " NAMES " :
2024-12-09 18:27:40 -08:00
if len ( args ) == 0 :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
else :
channel = text . split ( " " ) [ 1 ]
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 " ) )
connection . sendall ( bytes ( f " : { server } 366 { pending } { channel } :End of /NAMES list. \r \n " , " UTF-8 " ) )
2024-12-09 18:15:10 -08:00
elif command == " NOTICE " :
2024-12-09 18:27:40 -08:00
if len ( args ) > = 2 :
target = text . split ( " " ) [ 1 ]
2024-12-09 18:56:12 -08:00
if target . lower ( ) in lower_nicks :
target = lower_nicks [ target . lower ( ) ]
2024-12-09 18:27:40 -08:00
if target in channels_list :
if pending in channels_list [ target ] :
for i in channels_list [ channel ] :
try :
if i != pending :
nickname_list [ i ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
except :
pass
elif target in nickname_list :
nickname_list [ target ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " : { server } 401 { pending } { target } :No such nick/channel \r \n " , " UTF-8 " ) )
2024-12-08 20:02:59 -08:00
else :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 11:07:26 -08:00
elif command == " QUIT " :
2024-12-08 20:02:59 -08:00
# Parse the quit message.
done = [ ]
2024-12-26 16:15:51 -08:00
if len ( text . split ( " " ) ) == 1 :
msg = " Client Quit "
2024-12-08 20:02:59 -08:00
else :
2024-12-26 16:15:51 -08:00
msg = text . split ( " " ) [ 1 : ]
2024-12-26 16:53:30 -08:00
if msg [ 0 ] [ 0 ] == " : " :
msg [ 0 ] = msg [ 0 ] [ 1 : ]
2024-12-26 16:15:51 -08:00
if len ( msg ) > 0 :
mse = " " . join ( msg )
msg = f " Quit: { mse } "
2024-12-08 20:02:59 -08:00
text = f " QUIT : { msg } "
2024-12-08 20:21:29 -08:00
# Broadcast all users in the joined channels that the person quit.
2024-12-08 20:02:59 -08:00
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-09 13:26:23 -08:00
nickname_list [ j ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
2024-12-08 20:02:59 -08:00
done . append ( j )
# Remove the quitting user from the channel.
2024-12-08 16:54:04 -08:00
try :
2024-12-08 20:02:59 -08:00
channels_list [ i ] . remove ( pending )
2024-12-08 16:54:04 -08:00
except :
2024-12-08 20:02:59 -08:00
print ( traceback . format_exc ( ) )
2024-12-08 20:48:30 -08:00
# Confirm QUIT and close the socket.
2024-12-09 13:53:01 -08:00
try :
2024-12-09 13:48:45 -08:00
connection . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " ERROR :Closing Link: { hostname } ( { msg } ) \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -08:00
finally :
connection . close ( )
safe_quit = True
2024-12-09 17:29:46 -08:00
break
2024-12-09 19:31:48 -08:00
elif command == " MODE " :
2024-12-11 15:12:48 -08:00
target = args [ 0 ]
2024-12-09 19:31:48 -08:00
if len ( args ) == 0 :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 19:31:48 -08:00
elif len ( args ) == 1 :
if args [ 0 ] == pending :
yourmodes = property_list [ pending ] [ " modes " ]
2024-12-11 15:04:03 -08:00
connection . sendall ( bytes ( f " : { server } 221 { pending } + { yourmodes } \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -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 " ]
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 221 { pending } { target } + { modes } { params } \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -08:00
else :
# Default channel mode
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 324 { pending } { target } +n \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -08:00
else :
# Default channel mode
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 324 { pending } { target } +n \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -08:00
else :
if args [ 0 ] [ 0 ] == " # " :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 403 { pending } { target } :No such channel \r \n " , " UTF-8 " ) )
2024-12-09 21:43:11 -08:00
else :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 505 { pending } :Cant change mode for other users \r \n " , " UTF-8 " ) )
2024-12-09 19:31:48 -08:00
2024-12-11 15:28:52 -08:00
elif command == " GITSERV " or ( command == " PRIVMSG " and args [ 0 ] . lower ( ) == " gitserv " ) :
if command == " PRIVMSG " :
args = args [ 1 : ]
2024-12-15 19:10:18 -08:00
if args [ 0 ] [ 0 ] == " : " :
2024-12-28 14:58:26 -08:00
args [ 0 ] = args [ 0 ] [ 1 : ]
2024-12-09 18:27:40 -08:00
if len ( args ) == 0 :
2024-12-09 21:56:12 -08:00
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
elif args [ 0 ] . upper ( ) == " PULL " :
2024-12-09 18:30:43 -08:00
updater = subprocess . run ( [ " git " , " pull " ] , stdout = subprocess . PIPE )
2024-12-09 18:27:40 -08:00
if updater . stdout . decode ( ) . strip ( ) == " Already up to date. " :
2024-12-09 18:57:05 -08:00
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :Codename IRCat is already up-to-date. \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
else :
2024-12-09 18:57:05 -08:00
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :Done, it is recommended to use /RESTART if you ' re an IRC op \r \n " , " UTF-8 " ) )
2024-12-09 18:27:40 -08:00
elif args [ 0 ] . upper ( ) == " VERSION " :
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :Codename IRCat version { __version__ } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :This is Codename IRCat ' s integrated services. \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :GitServ Usage: \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :PULL - Pulls the latest version of Codename IRCat \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " :GitServ!~IRCat@IRCatCore NOTICE { pending } :VERSION - Gets the version number of this service. \r \n " , " UTF-8 " ) )
2024-12-13 13:03:06 -08:00
elif command == " NICKSERV " or ( command == " PRIVMSG " and args [ 0 ] . lower ( ) == " nickserv " ) :
if command == " PRIVMSG " :
args = args [ 1 : ]
2024-12-28 14:58:26 -08:00
if args [ 0 ] [ 0 ] == " : " :
args [ 0 ] = args [ 0 ] [ 1 : ]
2024-12-13 13:03:06 -08:00
if len ( args ) == 0 :
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
elif args [ 0 ] . upper ( ) == " IDENTIFY " :
2024-12-13 13:19:50 -08:00
pass
2024-12-13 13:03:06 -08:00
elif args [ 0 ] . upper ( ) == " VERSION " :
connection . sendall ( bytes ( f " :NickServ!~IRCat@IRCatCore NOTICE { pending } :Codename IRCat version { __version__ } \r \n " , " UTF-8 " ) )
connection . sendall ( bytes ( f " :NickServ!~IRCat@IRCatCore NOTICE { pending } :This is Codename IRCat ' s integrated services. \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " :NickServ!~IRCat@IRCatCore NOTICE { pending } :NickServ Usage: \r \n " , " UTF-8 " ) )
2024-12-13 13:19:50 -08:00
connection . sendall ( bytes ( f " :NickServ!~IRCat@IRCatCore NOTICE { pending } :IDENTIFY - Identifies your nickname \r \n " , " UTF-8 " ) )
2024-12-13 13:03:06 -08:00
connection . sendall ( bytes ( f " :NickServ!~IRCat@IRCatCore NOTICE { pending } :VERSION - Gets the version number of this service. \r \n " , " UTF-8 " ) )
2024-12-11 14:50:16 -08:00
2024-12-11 14:55:35 -08:00
elif command == " RESTART " :
if " o " in property_list [ pending ] [ " modes " ] :
tcp_socket . shutdown ( socket . SHUT_RDWR )
tcp_socket . close ( )
2024-12-11 15:22:20 -08:00
global opened
2024-12-11 14:59:32 -08:00
opened = False
2024-12-11 14:55:35 -08:00
else :
connection . sendall ( bytes ( f " : { server } 481 { pending } :Permission Denied- You ' re not an IRC operator \r \n " , " UTF-8 " ) )
2024-12-11 14:50:16 -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 :
if pending in channels_list [ target ] :
for i in channels_list [ channel ] :
try :
if i != pending :
nickname_list [ i ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
except :
pass
elif target in nickname_list :
nickname_list [ target ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } { text } \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " : { server } 401 { pending } { target } :No such nick/channel \r \n " , " UTF-8 " ) )
else :
connection . sendall ( bytes ( f " : { server } 461 { pending } { command } :Not enough parameters \r \n " , " UTF-8 " ) )
2024-12-09 10:22:43 -08:00
# Ignore empty text
elif text . split ( " " ) [ 0 ] == " " :
pass
else :
# Unknown command
cmd = text . split ( " " ) [ 0 ]
connection . sendall ( bytes ( f " : { server } 421 { pending } { cmd } :Unknown command \r \n " , " UTF-8 " ) )
2024-12-08 21:44:32 -08:00
2024-12-09 14:08:31 -08:00
except Exception as ex :
2024-12-08 16:54:04 -08:00
print ( traceback . format_exc ( ) )
2024-12-09 14:22:31 -08:00
cause = " Read error: " + str ( ex )
2024-12-09 13:53:01 -08:00
break
2024-12-08 16:54:04 -08:00
if not data :
2024-12-09 14:22:31 -08:00
cause = " Remote host closed the connection "
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 :
nickname_list [ j ] . sendall ( bytes ( f " : { pending } !~ { username } @ { hostname } QUIT : { cause } \r \n " , " UTF-8 " ) )
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 :
2024-12-16 09:28:49 -08:00
nickname_list [ k ] . sendall ( f " : { h } !~DISCONNECTED@DISCONNECTED PART { j } :IRCat Cleanup: Found missing connection!! \r \n " )
2024-12-08 16:54:04 -08:00
try :
2024-12-11 14:59:32 -08:00
while opened :
2024-12-12 13:37:45 -08:00
print ( " Waiting for connection... " )
2024-12-08 16:54:04 -08:00
connection , client = tcp_socket . accept ( )
2024-12-27 20:10:42 -08:00
ip_to = restrict_ip
threading . Thread ( target = session , daemon = True , args = [ connection , client , ip_to ] ) . start ( )
2024-12-08 16:54:04 -08:00
except :
print ( " Shutting down... " )
time . sleep ( 2 )
2024-12-08 17:36:59 -08:00
tcp_socket . shutdown ( 1 )
2024-12-08 16:54:04 -08:00
tcp_socket . close ( )