8 Commits

Author SHA1 Message Date
adator
8cac3316fb V6.1.5 i forgot 2025-03-10 20:47:53 +01:00
adator
befe452df8 V6.1.1 Updates 2024-12-08 23:02:36 +01:00
adator
cb042a5411 V6.1.0 update the help command 2024-12-08 12:52:36 +01:00
adator
a3edf48120 Update on_version method 2024-11-22 23:26:39 +01:00
adator
f7664c9874 V6.0.4 2024-11-18 23:49:45 +01:00
adator
d37c152160 Update to version 6.0.3 2024-11-17 21:09:14 +01:00
adator
b81f502b95 V6.0.2 2024-11-15 22:14:11 +01:00
adator
44da01945c V6.0.1 2024-11-11 23:38:05 +01:00
21 changed files with 2104 additions and 750 deletions

View File

@@ -194,14 +194,19 @@ class Base:
file_hanlder = logging.FileHandler(f'logs{self.Config.OS_SEP}defender.log',encoding='UTF-8') file_hanlder = logging.FileHandler(f'logs{self.Config.OS_SEP}defender.log',encoding='UTF-8')
file_hanlder.setLevel(self.Config.DEBUG_LEVEL) file_hanlder.setLevel(self.Config.DEBUG_LEVEL)
stdout_handler = logging.StreamHandler()
stdout_handler.setLevel(50)
# Define log format # Define log format
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
# Apply log format # Apply log format
file_hanlder.setFormatter(formatter) file_hanlder.setFormatter(formatter)
stdout_handler.setFormatter(formatter)
# Add handler to logs # Add handler to logs
self.logs.addHandler(file_hanlder) self.logs.addHandler(file_hanlder)
self.logs.addHandler(stdout_handler)
# Apply the filter # Apply the filter
self.logs.addFilter(self.replace_filter) self.logs.addFilter(self.replace_filter)
@@ -656,10 +661,25 @@ class Base:
) )
''' '''
table_core_client = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CLIENT} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
createdOn TEXT,
account TEXT,
nickname TEXT,
hostname TEXT,
vhost TEXT,
realname TEXT,
email TEXT,
password TEXT,
level INTEGER
)
'''
self.db_execute_query(table_core_log) self.db_execute_query(table_core_log)
self.db_execute_query(table_core_log_command) self.db_execute_query(table_core_log_command)
self.db_execute_query(table_core_module) self.db_execute_query(table_core_module)
self.db_execute_query(table_core_admin) self.db_execute_query(table_core_admin)
self.db_execute_query(table_core_client)
self.db_execute_query(table_core_channel) self.db_execute_query(table_core_channel)
self.db_execute_query(table_core_config) self.db_execute_query(table_core_config)
@@ -738,7 +758,7 @@ class Base:
except TypeError: except TypeError:
return None return None
def is_valid_ip(self, ip_to_control:str) -> bool: def is_valid_ip(self, ip_to_control: str) -> bool:
try: try:
if ip_to_control in self.Config.WHITELISTED_IP: if ip_to_control in self.Config.WHITELISTED_IP:
@@ -749,6 +769,26 @@ class Base:
except ValueError: except ValueError:
return False return False
def is_valid_email(self, email_to_control: str) -> bool:
"""Check if the email is valid
Args:
email_to_control (str): email to control
Returns:
bool: True is the email is correct
"""
try:
pattern = '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
if re.match(pattern, email_to_control):
return True
else:
return False
except Exception as err:
self.logs.error(f'General Error: {err}')
return False
def decode_ip(self, ip_b64encoded: str) -> Union[str, None]: def decode_ip(self, ip_b64encoded: str) -> Union[str, None]:
binary_ip = b64decode(ip_b64encoded) binary_ip = b64decode(ip_b64encoded)
@@ -802,6 +842,27 @@ class Base:
# Vider le dictionnaire de fonction # Vider le dictionnaire de fonction
self.periodic_func.clear() self.periodic_func.clear()
def execute_dynamic_method(self, obj: object, method_name: str, params: list) -> None:
"""#### Ajouter les méthodes a éxecuter dans un dictionnaire
Les methodes seront exécuter par heartbeat.
Args:
obj (object): Une instance de la classe qui va etre executer
method_name (str): Le nom de la méthode a executer
params (list): les parametres a faire passer
Returns:
None: aucun retour attendu
"""
self.periodic_func[len(self.periodic_func) + 1] = {
'object': obj,
'method_name': method_name,
'param': params
}
self.logs.debug(f'Method to execute : {str(self.periodic_func)}')
return None
def clean_uid(self, uid:str) -> Union[str, None]: def clean_uid(self, uid:str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / : """Clean UID by removing @ / % / + / ~ / * / :

View File

@@ -37,6 +37,9 @@ class Admin:
result = False result = False
if not self.is_exist(uid):
return result
for record in self.UID_ADMIN_DB: for record in self.UID_ADMIN_DB:
if record.uid == uid: if record.uid == uid:
# If the admin exist, update and do not go further # If the admin exist, update and do not go further
@@ -46,7 +49,7 @@ class Admin:
return result return result
if not result: if not result:
self.Logs.critical(f'The new nickname {newNickname} was not updated, uid = {uid}') self.Logs.critical(f'Admin: The new nickname {newNickname} was not updated, uid = {uid}')
return result return result
@@ -124,4 +127,18 @@ class Admin:
if record.uid == uidornickname: if record.uid == uidornickname:
nickname = record.nickname nickname = record.nickname
self.Logs.debug(f'The value {uidornickname} -- {nickname}') self.Logs.debug(f'The value {uidornickname} -- {nickname}')
return nickname return nickname
def is_exist(self, uidornickname: str) -> bool:
"""Check if this uid or nickname is logged in as an admin
Args:
uidornickname (str): The UID or the Nickname
Returns:
bool: True if the Nickname or UID is an admin
"""
if self.get_Admin(uidornickname) is None:
return False
else:
return True

View File

@@ -2,6 +2,8 @@ from re import findall
from typing import Union, Literal, TYPE_CHECKING from typing import Union, Literal, TYPE_CHECKING
from dataclasses import asdict from dataclasses import asdict
from core.classes import user
if TYPE_CHECKING: if TYPE_CHECKING:
from core.definition import MChannel from core.definition import MChannel
from core.base import Base from core.base import Base
@@ -31,6 +33,10 @@ class Channel:
result = False result = False
exist = False exist = False
if not self.Is_Channel(newChan.name):
self.Logs.error(f"The channel {newChan.name} is not valid, channel must start with #")
return False
for record in self.UID_CHANNEL_DB: for record in self.UID_CHANNEL_DB:
if record.name.lower() == newChan.name.lower(): if record.name.lower() == newChan.name.lower():
# If the channel exist, update the user list and do not go further # If the channel exist, update the user list and do not go further
@@ -84,6 +90,7 @@ class Channel:
if self.Base.clean_uid(userid) == self.Base.clean_uid(uid): if self.Base.clean_uid(userid) == self.Base.clean_uid(uid):
chanObj.uids.remove(userid) chanObj.uids.remove(userid)
result = True result = True
self.Logs.debug(f"The UID: {userid} has been removed from the {channel_name}")
self.clean_channel() self.clean_channel()
@@ -129,6 +136,29 @@ class Channel:
except Exception as err: except Exception as err:
self.Logs.error(f'{err}') self.Logs.error(f'{err}')
def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool:
"""Check if a user is present in the channel
Args:
channel_name (str): The channel to check
uid (str): The UID
Returns:
bool: True if the user is present in the channel
"""
user_found = False
chan = self.get_Channel(channel_name=channel_name)
if chan is None:
return user_found
clean_uid = self.Base.clean_uid(uid=uid)
for chan_uid in chan.uids:
if self.Base.clean_uid(chan_uid) == clean_uid:
user_found = True
break
return user_found
def clean_channel(self) -> None: def clean_channel(self) -> None:
"""Remove Channels if empty """Remove Channels if empty
""" """

243
core/classes/client.py Normal file
View File

@@ -0,0 +1,243 @@
from re import sub
from typing import Union, TYPE_CHECKING
from dataclasses import asdict
if TYPE_CHECKING:
from core.base import Base
from core.definition import MClient
class Client:
CLIENT_DB: list['MClient'] = []
def __init__(self, baseObj: 'Base') -> None:
self.Logs = baseObj.logs
self.Base = baseObj
return None
def insert(self, newUser: 'MClient') -> bool:
"""Insert a new User object
Args:
newUser (UserModel): New userModel object
Returns:
bool: True if inserted
"""
userObj = self.get_Client(newUser.uid)
if not userObj is None:
# User already created return False
return False
self.CLIENT_DB.append(newUser)
return True
def update_nickname(self, uid: str, newNickname: str) -> bool:
"""Update the nickname starting from the UID
Args:
uid (str): UID of the user
newNickname (str): New nickname
Returns:
bool: True if updated
"""
userObj = self.get_Client(uidornickname=uid)
if userObj is None:
return False
userObj.nickname = newNickname
return True
def update_mode(self, uidornickname: str, modes: str) -> bool:
"""Updating user mode
Args:
uidornickname (str): The UID or Nickname of the user
modes (str): new modes to update
Returns:
bool: True if user mode has been updaed
"""
response = True
userObj = self.get_Client(uidornickname=uidornickname)
if userObj is None:
return False
action = modes[0]
new_modes = modes[1:]
existing_umodes = userObj.umodes
umodes = userObj.umodes
if action == '+':
for nm in new_modes:
if nm not in existing_umodes:
umodes += nm
elif action == '-':
for nm in new_modes:
if nm in existing_umodes:
umodes = umodes.replace(nm, '')
else:
return False
liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}"
return response
def delete(self, uid: str) -> bool:
"""Delete the User starting from the UID
Args:
uid (str): UID of the user
Returns:
bool: True if deleted
"""
userObj = self.get_Client(uidornickname=uid)
if userObj is None:
return False
self.CLIENT_DB.remove(userObj)
return True
def get_Client(self, uidornickname: str) -> Union['MClient', None]:
"""Get The Client Object model
Args:
uidornickname (str): UID or Nickname
Returns:
UserModel|None: The UserModel Object | None
"""
User = None
for record in self.CLIENT_DB:
if record.uid == uidornickname:
User = record
elif record.nickname == uidornickname:
User = record
return User
def get_uid(self, uidornickname:str) -> Union[str, None]:
"""Get the UID of the user starting from the UID or the Nickname
Args:
uidornickname (str): UID or Nickname
Returns:
str|None: Return the UID
"""
userObj = self.get_Client(uidornickname=uidornickname)
if userObj is None:
return None
return userObj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
"""Get the Nickname starting from UID or the nickname
Args:
uidornickname (str): UID or Nickname of the user
Returns:
str|None: the nickname
"""
userObj = self.get_Client(uidornickname=uidornickname)
if userObj is None:
return None
return userObj.nickname
def get_Client_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
"""Transform User Object to a dictionary
Args:
uidornickname (str): The UID or The nickname
Returns:
Union[dict[str, any], None]: User Object as a dictionary or None
"""
userObj = self.get_Client(uidornickname=uidornickname)
if userObj is None:
return None
return asdict(userObj)
def is_exist(self, uidornikname: str) -> bool:
"""Check if the UID or the nickname exist in the USER DB
Args:
uidornickname (str): The UID or the NICKNAME
Returns:
bool: True if exist
"""
userObj = self.get_Client(uidornickname=uidornikname)
if userObj is None:
return False
return True
def db_is_account_exist(self, account: str) -> bool:
"""Check if the account exist in the database
Args:
account (str): The account to check
Returns:
bool: True if exist
"""
table_client = self.Base.Config.TABLE_CLIENT
account_to_check = {'account': account.lower()}
account_to_check_query = self.Base.db_execute_query(f"""
SELECT id FROM {table_client} WHERE LOWER(account) = :account
""", account_to_check)
account_to_check_result = account_to_check_query.fetchone()
if account_to_check_result:
self.Logs.debug(f"Account ({account}) already exist")
return True
return False
def clean_uid(self, uid: str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
uid (str): The UID to clean
Returns:
str: Clean UID without any sign
"""
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = sub(pattern, '', uid)
if not parsed_UID:
return None
return parsed_UID

View File

@@ -401,7 +401,9 @@ class Inspircd:
uid = str(serverMsg[1]).lstrip(':') uid = str(serverMsg[1]).lstrip(':')
newnickname = serverMsg[3] newnickname = serverMsg[3]
self.__Irc.User.update(uid, newnickname) self.__Irc.User.update_nickname(uid, newnickname)
self.__Irc.Client.update_nickname(uid, newnickname)
self.__Irc.Admin.update_nickname(uid, newnickname)
return None return None

View File

@@ -1,8 +1,7 @@
from re import match, findall from re import match, findall, search
from datetime import datetime from datetime import datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Union
from ssl import SSLEOFError, SSLError from ssl import SSLEOFError, SSLError
from dataclasses import dataclass
if TYPE_CHECKING: if TYPE_CHECKING:
from core.irc import Irc from core.irc import Irc
@@ -17,6 +16,12 @@ class Unrealircd6:
self.__Base = ircInstance.Base self.__Base = ircInstance.Base
self.__Settings = ircInstance.Base.Settings self.__Settings = ircInstance.Base.Settings
self.known_protocol = ['SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT',
'EOS', 'PRIVMSG', 'MODE', 'UMODE2',
'VERSION', 'REPUTATION', 'SVS2MODE',
'SLOG', 'NICK', 'PART', 'PONG'
]
self.__Base.logs.info(f"** Loading protocol [{__name__}]") self.__Base.logs.info(f"** Loading protocol [{__name__}]")
def send2socket(self, message: str, print_log: bool = True) -> None: def send2socket(self, message: str, print_log: bool = True) -> None:
@@ -104,6 +109,40 @@ class Unrealircd6:
except Exception as err: except Exception as err:
self.__Base.logs.error(f"General Error: {err}") self.__Base.logs.error(f"General Error: {err}")
def parse_server_msg(self, server_msg: list[str]) -> Union[str, None]:
"""Parse the server message and return the command
Args:
server_msg (list[str]): The Original server message >>
Returns:
Union[str, None]: Return the command protocol name
"""
protocol_exception = ['PING', 'SERVER', 'PROTOCTL']
increment = 0
server_msg_copy = server_msg.copy()
first_index = 0
second_index = 0
for index, element in enumerate(server_msg_copy):
# Handle the protocol exceptions ex. ping, server ....
if element in protocol_exception and index == 0:
return element
if element.startswith(':'):
increment += 1
first_index = index + 1 if increment == 1 else first_index
second_index = index if increment == 2 else second_index
second_index = len(server_msg_copy) if second_index == 0 else second_index
parsed_msg = server_msg_copy[first_index:second_index]
for cmd in parsed_msg:
if cmd in self.known_protocol:
return cmd
return None
def link(self): def link(self):
"""Créer le link et envoyer les informations nécessaires pour la """Créer le link et envoyer les informations nécessaires pour la
connexion au serveur. connexion au serveur.
@@ -159,7 +198,7 @@ class Unrealircd6:
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}") self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}")
userObj = self.__Irc.User.get_User(self.__Config.SERVICE_NICKNAME) userObj = self.__Irc.User.get_User(self.__Config.SERVICE_NICKNAME)
self.__Irc.User.update(userObj.uid, newnickname) self.__Irc.User.update_nickname(userObj.uid, newnickname)
return None return None
def squit(self, server_id: str, server_link: str, reason: str) -> None: def squit(self, server_id: str, server_link: str, reason: str) -> None:
@@ -206,6 +245,33 @@ class Unrealircd6:
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID])) self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID]))
return None return None
def send_svs_nick(self, oldnickname: str, newnickname: str) -> None:
unixtime = self.__Base.get_unixtime()
self.send2socket(f':{self.__Config.SERVEUR_ID} SVSNICK {oldnickname} {newnickname} {unixtime}')
user_obj = self.__Irc.User.get_User(oldnickname)
self.__Irc.User.update_nickname(user_obj.uid, newnickname)
return None
def send_sjoin(self, uid_to_join: str, channel: str) -> None:
"""UID will join a channel with pre defined umodes
Args:
channel (str): Channel to join
"""
if not self.__Irc.Channel.Is_Channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
return None
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} {self.__Config.SERVICE_UMODES} :{uid_to_join}")
self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {self.__Config.SERVICE_UMODES} {uid_to_join}")
# Add defender to the channel uids list
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[uid_to_join]))
return None
def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None:
"""_summary_ """_summary_
@@ -287,7 +353,26 @@ class Unrealircd6:
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Base.logs.error(f"{__name__} - General Error: {err}")
def send_quit(self, uid: str, reason: str, print_log: True) -> None: def send_svs2mode(self, service_uid: str, nickname: str, user_mode: str) -> None:
try:
user_obj = self.__Irc.User.get_User(uidornickname=nickname)
service_uid = service_uid
if user_obj is None:
# User not exist: leave
return None
self.send2socket(f':{service_uid} SVS2MODE {nickname} {user_mode}')
# Update new mode
self.__Irc.User.update_mode(user_obj.uid, user_mode)
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def send_quit(self, uid: str, reason: str, print_log: bool = True) -> None:
"""Send quit message """Send quit message
- Delete uid from User object - Delete uid from User object
- Delete uid from Clone object - Delete uid from Clone object
@@ -381,6 +466,18 @@ class Unrealircd6:
# Add defender to the channel uids list # Add defender to the channel uids list
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid])) self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid]))
# Set the automode to the user
if 'r' not in userObj.umodes and 'o' not in userObj.umodes:
return None
db_data: dict[str, str] = {"nickname": userObj.nickname, "channel": channel}
db_query = self.__Base.db_execute_query("SELECT id, mode FROM command_automode WHERE nickname = :nickname AND channel = :channel", db_data)
db_result = db_query.fetchone()
if db_result is not None:
id, mode = db_result
self.send2socket(f":{self.__Config.SERVICE_ID} MODE {channel} {mode} {userObj.nickname}")
return None return None
def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
@@ -408,6 +505,79 @@ class Unrealircd6:
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid) self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
return None return None
def send_mode_chan(self, channel_name: str, channel_mode: str) -> None:
channel = self.__Irc.Channel.Is_Channel(channelToCheck=channel_name)
if not channel:
self.__Base.logs.error(f'The channel [{channel_name}] is not correct')
return None
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} MODE {channel_name} {channel_mode}")
return None
def send_topic_chan(self, channel_name: str, topic_msg: str) -> None:
"""Set a channel topic
Args:
channel_name (str): Channel name starting with #
topic_msg (str): The message of the topic
"""
if self.__Irc.Channel.Is_Channel(channel_name) is None:
self.__Base.logs.error(f"The channel {channel_name} is not valid")
return None
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} TOPIC {channel_name} :{topic_msg}")
return None
def send_raw(self, raw_command: str) -> None:
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} {raw_command}")
return None
#####################
# HANDLE EVENTS #
#####################
def on_svs2mode(self, serverMsg: list[str]) -> None:
"""Handle svs2mode coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
uid_user_to_edit = serverMsg[2]
umode = serverMsg[3]
userObj = self.__Irc.User.get_User(uid_user_to_edit)
if userObj is None:
return None
if self.__Irc.User.update_mode(userObj.uid, umode):
return None
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_mode(self, serverMsg: list[str]) -> None:
"""Handle mode coming from a server
Args:
serverMsg (list[str]): Original server message
"""
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6...', ':001', 'MODE', '#a', '+nt', '1723207536']
#['@unrealircd.org/userhost=adator@localhost;...', ':001LQ0L0C', 'MODE', '#services', '-l']
return None
def on_umode2(self, serverMsg: list[str]) -> None: def on_umode2(self, serverMsg: list[str]) -> None:
"""Handle umode2 coming from a server """Handle umode2 coming from a server
@@ -451,6 +621,7 @@ class Unrealircd6:
self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit) self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit)
self.__Irc.User.delete(uid_who_quit) self.__Irc.User.delete(uid_who_quit)
self.__Irc.Client.delete(uid_who_quit)
self.__Irc.Reputation.delete(uid_who_quit) self.__Irc.Reputation.delete(uid_who_quit)
self.__Irc.Clone.delete(uid_who_quit) self.__Irc.Clone.delete(uid_who_quit)
@@ -490,12 +661,32 @@ class Unrealircd6:
serverMsg (list[str]): Original server message serverMsg (list[str]): Original server message
""" """
# ['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1728815798', 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1730662755', 'EXTSWHOIS'] # ['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1728815798', 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1730662755', 'EXTSWHOIS']
if len(serverMsg) > 5: user_modes: str = None
if '=' in serverMsg[5]: prefix: str = None
serveur_hosting_id = str(serverMsg[5]).split('=') host_server_id: str = None
self.__Config.HSID = serveur_hosting_id[1]
if 'USERMODES=' in serverMsg[2]: for msg in serverMsg:
self.__Settings.USER_MODES = list(serverMsg[2].split('=')[1]) pattern = None
if msg.startswith('PREFIX='):
pattern = r'^PREFIX=\((.*)\).*$'
find_match = match(pattern, msg)
prefix = find_match.group(1) if find_match else None
if find_match:
prefix = find_match.group(1)
elif msg.startswith('USERMODES='):
pattern = r'^USERMODES=(.*)$'
find_match = match(pattern, msg)
user_modes = find_match.group(1) if find_match else None
elif msg.startswith('SID='):
host_server_id = msg.split('=')[1]
if user_modes is None or prefix is None or host_server_id is None:
return None
self.__Config.HSID = host_server_id
self.__Settings.PROTOCTL_USER_MODES = list(user_modes)
self.__Settings.PROTOCTL_PREFIX = list(prefix)
return None return None
@@ -512,7 +703,9 @@ class Unrealircd6:
uid = str(serverMsg[1]).lstrip(':') uid = str(serverMsg[1]).lstrip(':')
newnickname = serverMsg[3] newnickname = serverMsg[3]
self.__Irc.User.update(uid, newnickname) self.__Irc.User.update_nickname(uid, newnickname)
self.__Irc.Client.update_nickname(uid, newnickname)
self.__Irc.Admin.update_nickname(uid, newnickname)
return None return None
@@ -534,8 +727,11 @@ class Unrealircd6:
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB', # ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7', # '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users'] # '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
# [':00B', 'SJOIN', '1731872579', '#services', '+', ':00BAAAAAB']
serverMsg_copy = serverMsg.copy() serverMsg_copy = serverMsg.copy()
serverMsg_copy.pop(0) if serverMsg_copy[0].startswith('@'):
serverMsg_copy.pop(0)
channel = str(serverMsg_copy[3]).lower() channel = str(serverMsg_copy[3]).lower()
len_cmd = len(serverMsg_copy) len_cmd = len(serverMsg_copy)
list_users:list = [] list_users:list = []
@@ -581,7 +777,7 @@ class Unrealircd6:
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z', # ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris'] # ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
uid = str(serverMsg[1]).lstrip(':') uid = str(serverMsg[1]).replace(':', '')
channel = str(serverMsg[3]).lower() channel = str(serverMsg[3]).lower()
self.__Irc.Channel.delete_user_from_channel(channel, uid) self.__Irc.Channel.delete_user_from_channel(channel, uid)
@@ -592,6 +788,105 @@ class Unrealircd6:
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_eos(self, serverMsg: list[str]) -> None:
"""Handle EOS coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# [':001', 'EOS']
server_msg_copy = serverMsg.copy()
hsid = str(server_msg_copy[0]).replace(':','')
if hsid == self.__Config.HSID:
if self.__Config.DEFENDER_INIT == 1:
current_version = self.__Config.CURRENT_VERSION
latest_version = self.__Config.LATEST_VERSION
if self.__Base.check_for_new_version(False):
version = f'{current_version} >>> {latest_version}'
else:
version = f'{current_version}'
print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.__Config.SERVEUR_IP} ")
print(f"# PORT : {self.__Config.SERVEUR_PORT} ")
print(f"# SSL : {self.__Config.SERVEUR_SSL} ")
print(f"# SSL VER : {self.__Config.SSL_VERSION} ")
print(f"# NICKNAME : {self.__Config.SERVICE_NICKNAME} ")
print(f"# CHANNEL : {self.__Config.SERVICE_CHANLOG} ")
print(f"# VERSION : {version} ")
print(f"################################################")
self.__Base.logs.info(f"################### DEFENDER ###################")
self.__Base.logs.info(f"# SERVICE CONNECTE ")
self.__Base.logs.info(f"# SERVEUR : {self.__Config.SERVEUR_IP} ")
self.__Base.logs.info(f"# PORT : {self.__Config.SERVEUR_PORT} ")
self.__Base.logs.info(f"# SSL : {self.__Config.SERVEUR_SSL} ")
self.__Base.logs.info(f"# SSL VER : {self.__Config.SSL_VERSION} ")
self.__Base.logs.info(f"# NICKNAME : {self.__Config.SERVICE_NICKNAME} ")
self.__Base.logs.info(f"# CHANNEL : {self.__Config.SERVICE_CHANLOG} ")
self.__Base.logs.info(f"# VERSION : {version} ")
self.__Base.logs.info(f"################################################")
if self.__Base.check_for_new_version(False):
self.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f" New Version available {version}",
channel=self.__Config.SERVICE_CHANLOG
)
# Initialisation terminé aprés le premier PING
self.send_priv_msg(
nick_from=self.__Config.SERVICE_NICKNAME,
msg=f"[{self.__Config.COLORS.green}INFORMATION{self.__Config.COLORS.nogc}] >> Defender is ready",
channel=self.__Config.SERVICE_CHANLOG
)
self.__Config.DEFENDER_INIT = 0
# Send EOF to other modules
for classe_name, classe_object in self.__Irc.loaded_classes.items():
classe_object.cmd(server_msg_copy)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Key Error: {ie}")
except KeyError as ke:
self.__Base.logs.error(f"{__name__} - Key Error: {ke}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_reputation(self, serverMsg: list[str]) -> None:
"""Handle REPUTATION coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# :001 REPUTATION 127.0.0.1 118
server_msg_copy = serverMsg.copy()
self.__Irc.first_connexion_ip = server_msg_copy[2]
self.__Irc.first_score = 0
if str(server_msg_copy[3]).find('*') != -1:
# If * available, it means that an ircop changed the repurtation score
# means also that the user exist will try to update all users with same IP
self.__Irc.first_score = int(str(server_msg_copy[3]).replace('*',''))
for user in self.__Irc.User.UID_DB:
if user.remote_ip == self.__Irc.first_connexion_ip:
user.score_connexion = self.__Irc.first_score
else:
self.__Irc.first_score = int(server_msg_copy[3])
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'Index Error {__name__}: {ie}')
except ValueError as ve:
self.__Irc.first_score = 0
self.Logs.error(f'Value Error {__name__}: {ve}')
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_uid(self, serverMsg: list[str]) -> None: def on_uid(self, serverMsg: list[str]) -> None:
"""Handle uid message coming from the server """Handle uid message coming from the server
@@ -606,31 +901,25 @@ class Unrealircd6:
isWebirc = True if 'webirc' in serverMsg[0] else False isWebirc = True if 'webirc' in serverMsg[0] else False
isWebsocket = True if 'websocket' in serverMsg[0] else False isWebsocket = True if 'websocket' in serverMsg[0] else False
uid = str(serverMsg[8])
nickname = str(serverMsg[3]) nickname = str(serverMsg[3])
hopcount = str(serverMsg[4])
timestamp = str(serverMsg[5])
username = str(serverMsg[6]) username = str(serverMsg[6])
hostname = str(serverMsg[7]) hostname = str(serverMsg[7])
uid = str(serverMsg[8])
servicestamp = str(serverMsg[9])
umodes = str(serverMsg[10]) umodes = str(serverMsg[10])
vhost = str(serverMsg[11]) vhost = str(serverMsg[11])
cloacked_host = str(serverMsg[12])
if not 'S' in umodes: remote_ip = self.__Base.decode_ip(str(serverMsg[13])) if 'S' in umodes else '127.0.0.1'
remote_ip = self.__Base.decode_ip(str(serverMsg[13]))
else:
remote_ip = '127.0.0.1'
# extract realname
realname = ' '.join(serverMsg[14:]).lstrip(':') realname = ' '.join(serverMsg[14:]).lstrip(':')
# Extract Geoip information # Extract Geoip information
pattern = r'^.*geoip=cc=(\S{2}).*$' pattern = r'^.*geoip=cc=(\S{2}).*$'
geoip_match = match(pattern, serverMsg[0]) geoip_match = match(pattern, serverMsg[0])
geoip = geoip_match.group(1) if geoip_match else None
if geoip_match: score_connexion = self.__Irc.first_score
geoip = geoip_match.group(1)
else:
geoip = None
score_connexion = 0
self.__Irc.User.insert( self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser( self.__Irc.Loader.Definition.MUser(
@@ -641,6 +930,10 @@ class Unrealircd6:
hostname=hostname, hostname=hostname,
umodes=umodes, umodes=umodes,
vhost=vhost, vhost=vhost,
hopcount=hopcount,
timestamp=timestamp,
servicestamp=servicestamp,
cloacked_host=cloacked_host,
isWebirc=isWebirc, isWebirc=isWebirc,
isWebsocket=isWebsocket, isWebsocket=isWebsocket,
remote_ip=remote_ip, remote_ip=remote_ip,
@@ -655,6 +948,103 @@ class Unrealircd6:
except Exception as err: except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}") self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_privmsg(self, serverMsg: list[str]) -> None:
"""Handle PRIVMSG message coming from the server
Args:
serverMsg (list[str]): Original server message
"""
try:
srv_msg = serverMsg.copy()
cmd = serverMsg.copy()
# Supprimer la premiere valeur si MTAGS activé
if cmd[0].startswith('@'):
cmd.pop(0)
# Hide auth logs
if len(cmd) == 7:
if cmd[2] == 'PRIVMSG' and cmd[4] == ':auth':
data_copy = cmd.copy()
data_copy[6] = '**********'
self.__Base.logs.debug(f">> {data_copy}")
else:
self.__Base.logs.debug(f">> {cmd}")
else:
self.__Base.logs.debug(f">> {cmd}")
get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.__Irc.User.get_nickname(get_uid_or_nickname)
dnickname = self.__Config.SERVICE_NICKNAME
pattern = fr'(:\{self.__Config.SERVICE_PREFIX})(.*)$'
hcmds = search(pattern, ' '.join(cmd)) # va matcher avec tout les caractéres aprés le .
if hcmds: # Commande qui commencent par le point
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
arg.remove(f':{self.__Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.__Irc.module_commands_list:
self.__Base.logs.debug(f"This command {arg[0]} is not available")
self.send_notice(
nick_from=self.__Config.SERVICE_NICKNAME,
nick_to=user_trigger,
msg=f"This command [{self.__Config.COLORS.bold}{arg[0]}{self.__Config.COLORS.bold}] is not available"
)
return None
cmd_to_send = convert_to_string.replace(':','')
self.__Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = str(cmd[2]).lower() if self.__Irc.Channel.Is_Channel(cmd[2]) else None
self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd)
if cmd[2] == self.__Config.SERVICE_ID or cmd[2] == self.__Settings.NICKSERV_UID:
pattern = fr'^:.*?:(.*)$'
hcmds = search(pattern, ' '.join(cmd))
if hcmds: # par /msg defender [commande]
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
# Réponse a un CTCP VERSION
if arg[0] == '\x01VERSION\x01':
self.on_version(srv_msg)
return False
# Réponse a un TIME
if arg[0] == '\x01TIME\x01':
self.on_time(srv_msg)
return False
# Réponse a un PING
if arg[0] == '\x01PING':
self.on_ping(srv_msg)
return False
if not arg[0].lower() in self.__Irc.module_commands_list:
self.__Base.logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available")
return False
cmd_to_send = convert_to_string.replace(':','')
self.__Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = None
if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.__Irc.Channel.Is_Channel(arg[1]) else None
self.__Irc.hcmds(user_trigger, fromchannel, arg, cmd)
return None
except KeyError as ke:
self.__Base.logs.error(f"Key Error: {ke}")
except AttributeError as ae:
self.__Base.logs.error(f"Attribute Error: {ae}")
except Exception as err:
self.__Base.logs.error(f"General Error: {err} - {srv_msg}")
def on_server_ping(self, serverMsg: list[str]) -> None: def on_server_ping(self, serverMsg: list[str]) -> None:
"""Send a PONG message to the server """Send a PONG message to the server
@@ -662,7 +1052,7 @@ class Unrealircd6:
serverMsg (list[str]): List of str coming from the server serverMsg (list[str]): List of str coming from the server
""" """
try: try:
#
pong = str(serverMsg[1]).replace(':','') pong = str(serverMsg[1]).replace(':','')
self.send2socket(f"PONG :{pong}", print_log=False) self.send2socket(f"PONG :{pong}", print_log=False)
@@ -754,7 +1144,7 @@ class Unrealircd6:
def on_version_msg(self, serverMsg: list[str]) -> None: def on_version_msg(self, serverMsg: list[str]) -> None:
"""Handle version coming from the server """Handle version coming from the server
\n ex. /version Defender
Args: Args:
serverMsg (list[str]): Original message from the server serverMsg (list[str]): Original message from the server
""" """
@@ -776,7 +1166,7 @@ class Unrealircd6:
response_005 = ' | '.join(modules) response_005 = ' | '.join(modules)
self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server') self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server')
response_005 = ''.join(self.__Settings.USER_MODES) response_005 = ''.join(self.__Settings.PROTOCTL_USER_MODES)
self.send2socket(f":{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server") self.send2socket(f":{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server")
return None return None

View File

@@ -3,12 +3,15 @@ from socket import socket
class Settings: class Settings:
RUNNING_TIMERS: list[Timer] = [] RUNNING_TIMERS: list[Timer] = []
RUNNING_THREADS: list[Thread] = [] RUNNING_THREADS: list[Thread] = []
RUNNING_SOCKETS: list[socket] = [] RUNNING_SOCKETS: list[socket] = []
PERIODIC_FUNC: dict[object] = {} PERIODIC_FUNC: dict[object] = {}
LOCK: RLock = RLock() LOCK: RLock = RLock()
CONSOLE: bool = False CONSOLE: bool = False
USER_MODES: list[str] = [] PROTOCTL_USER_MODES: list[str] = []
PROTOCTL_PREFIX: list[str] = []
NICKSERV_UID: str = None

View File

@@ -37,7 +37,7 @@ class User:
return True return True
def update(self, uid: str, newNickname: str) -> bool: def update_nickname(self, uid: str, newNickname: str) -> bool:
"""Update the nickname starting from the UID """Update the nickname starting from the UID
Args: Args:
@@ -92,7 +92,7 @@ class User:
return False return False
liste_umodes = list(umodes) liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.USER_MODES if x in liste_umodes] final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste) final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}" userObj.umodes = f"+{final_umodes}"

View File

@@ -3,6 +3,28 @@ from dataclasses import dataclass, field
from typing import Literal from typing import Literal
from os import sep from os import sep
@dataclass
class MClient:
"""Model Client for registred nickname"""
account: str = None
uid: str = None
nickname: str = None
username: str = None
realname: str = None
hostname: str = None
umodes: str = None
vhost: str = None
hopcount: str = None
timestamp: str = None
servicestamp: str = None
cloacked_host: str = None
isWebirc: bool = False
isWebsocket: bool = False
remote_ip: str = None
score_connexion: int = 0
geoip: str = None
connexion_datetime: datetime = field(default=datetime.now())
@dataclass @dataclass
class MUser: class MUser:
"""Model User""" """Model User"""
@@ -14,6 +36,10 @@ class MUser:
hostname: str = None hostname: str = None
umodes: str = None umodes: str = None
vhost: str = None vhost: str = None
hopcount: str = None
timestamp: str = None
servicestamp: str = None
cloacked_host: str = None
isWebirc: bool = False isWebirc: bool = False
isWebsocket: bool = False isWebsocket: bool = False
remote_ip: str = None remote_ip: str = None
@@ -32,6 +58,10 @@ class MAdmin:
hostname: str = None hostname: str = None
umodes: str = None umodes: str = None
vhost: str = None vhost: str = None
hopcount: str = None
timestamp: str = None
servicestamp: str = None
cloacked_host: str = None
isWebirc: bool = False isWebirc: bool = False
isWebsocket: bool = False isWebsocket: bool = False
remote_ip: str = None remote_ip: str = None
@@ -83,6 +113,7 @@ class ColorModel:
yellow: str = "\x0306" yellow: str = "\x0306"
bold: str = "\x02" bold: str = "\x02"
nogc: str = "\x03" nogc: str = "\x03"
underline: str = "\x1F"
@dataclass @dataclass
class MConfig: class MConfig:
@@ -214,8 +245,11 @@ class MConfig:
LOGGING_NAME: str = "defender" LOGGING_NAME: str = "defender"
"""The name of the Logging instance""" """The name of the Logging instance"""
TABLE_CLIENT: str = "core_client"
"""Core Client table"""
TABLE_ADMIN: str = "core_admin" TABLE_ADMIN: str = "core_admin"
"""Admin table""" """Core Admin table"""
TABLE_COMMAND: str = "core_command" TABLE_COMMAND: str = "core_command"
"""Core command table""" """Core command table"""

View File

@@ -61,6 +61,9 @@ class Irc:
# Use Admin Instance # Use Admin Instance
self.Admin = self.Loader.Admin self.Admin = self.Loader.Admin
# Use Client Instance
self.Client = self.Loader.Client
# Use Channel Instance # Use Channel Instance
self.Channel = self.Loader.Channel self.Channel = self.Loader.Channel
@@ -73,27 +76,54 @@ class Irc:
self.autolimit_started: bool = False self.autolimit_started: bool = False
"""This variable is to make sure the thread is not running""" """This variable is to make sure the thread is not running"""
self.first_score: int = 100 # define first reputation score to 0
self.first_score: int = 0
# Define first IP connexion
self.first_connexion_ip: str = None
# Define the dict that will contain all loaded modules
self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés
# Global full module commands that contains level, module name, commands and description
self.module_commands: dict[int, dict[str, dict[str, str]]] = {}
# Global command list contains only the commands
self.module_commands_list: list[str] = []
self.build_command(0, 'core', 'help', 'This provide the help')
self.build_command(0, 'core', 'auth', 'Login to the IRC Service')
self.build_command(0, 'core', 'copyright', 'Give some information about the IRC Service')
self.build_command(0, 'core', 'uptime', 'Give you since when the service is connected')
self.build_command(0, 'core', 'firstauth', 'First authentication of the Service')
self.build_command(0, 'core', 'register', f'Register your nickname /msg {self.Config.SERVICE_NICKNAME} REGISTER <password> <email>')
# self.build_command(0, 'core', 'identify', f'Identify yourself with your password /msg {self.Config.SERVICE_NICKNAME} IDENTIFY <account> <password>')
self.build_command(0, 'core', 'logout', 'Reverse the effect of the identify command')
self.build_command(1, 'core', 'load', 'Load an existing module')
self.build_command(1, 'core', 'unload', 'Unload a module')
self.build_command(1, 'core', 'reload', 'Reload a module')
self.build_command(1, 'core', 'deauth', 'Deauth from the irc service')
self.build_command(1, 'core', 'checkversion', 'Check the version of the irc service')
self.build_command(2, 'core', 'show_modules', 'Display a list of loaded modules')
self.build_command(2, 'core', 'show_timers', 'Display active timers')
self.build_command(2, 'core', 'show_threads', 'Display active threads in the system')
self.build_command(2, 'core', 'show_channels', 'Display a list of active channels')
self.build_command(2, 'core', 'show_users', 'Display a list of connected users')
self.build_command(2, 'core', 'show_clients', 'Display a list of connected clients')
self.build_command(2, 'core', 'show_admins', 'Display a list of administrators')
self.build_command(2, 'core', 'show_configuration', 'Display the current configuration settings')
self.build_command(3, 'core', 'quit', 'Disconnect the bot or user from the server.')
self.build_command(3, 'core', 'restart', 'Restart the bot or service.')
self.build_command(3, 'core', 'addaccess', 'Add a user or entity to an access list with specific permissions.')
self.build_command(3, 'core', 'editaccess', 'Modify permissions for an existing user or entity in the access list.')
self.build_command(3, 'core', 'delaccess', 'Remove a user or entity from the access list.')
self.build_command(4, 'core', 'rehash', 'Reload the configuration file without restarting')
self.build_command(4, 'core', 'raw', 'Send a raw command directly to the IRC server')
# Define the IrcSocket object
self.IrcSocket:Union[socket.socket, SSLSocket] = None self.IrcSocket:Union[socket.socket, SSLSocket] = None
# Liste des commandes internes du bot
self.commands_level = {
0: ['help', 'auth', 'copyright', 'uptime', 'firstauth'],
1: ['load','reload','unload', 'deauth', 'checkversion'],
2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins', 'show_configuration'],
3: ['quit', 'restart','addaccess','editaccess', 'delaccess'],
4: ['rehash']
}
# l'ensemble des commandes.
self.commands = []
for level, commands in self.commands_level.items():
for command in self.commands_level[level]:
self.commands.append(command)
self.__create_table() self.__create_table()
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, )) self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
@@ -107,6 +137,7 @@ class Irc:
ircInstance (Irc): Instance of Irc object. ircInstance (Irc): Instance of Irc object.
""" """
try: try:
self.init_service_user()
self.__create_socket() self.__create_socket()
self.__connect_to_irc(ircInstance) self.__connect_to_irc(ircInstance)
except AssertionError as ae: except AssertionError as ae:
@@ -290,7 +321,7 @@ class Irc:
except AssertionError as ae: except AssertionError as ae:
self.Logs.error(f"Assertion error : {ae}") self.Logs.error(f"Assertion error : {ae}")
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
# This is only to reference the method # This is only to reference the method
return None return None
@@ -298,6 +329,71 @@ class Irc:
# FIN CONNEXION IRC # # FIN CONNEXION IRC #
############################################## ##############################################
def build_command(self, level: int, module_name: str, command_name: str, command_description: str) -> None:
"""This method build the commands variable
Args:
level (int): The Level of the command
module_name (str): The module name
command_name (str): The command name
command_description (str): The description of the command
"""
self.module_commands.setdefault(level, {}).setdefault(module_name, {}).update({command_name: command_description})
self.module_commands_list.append(command_name)
return None
def generate_help_menu(self, nickname: str) -> None:
# Check if the nickname is an admin
admin_obj = self.Admin.get_Admin(nickname)
dnickname = self.Config.SERVICE_NICKNAME
color_bold = self.Config.COLORS.bold
color_nogc = self.Config.COLORS.nogc
color_blue = self.Config.COLORS.blue
color_black = self.Config.COLORS.black
color_underline = self.Config.COLORS.underline
current_level = 0
count = 0
if admin_obj is not None:
current_level = admin_obj.level
self.Protocol.send_notice(nick_from=dnickname,nick_to=nickname, msg=f" ***************** LISTE DES COMMANDES *****************")
for level, modules in self.module_commands.items():
if level > current_level:
break
if count > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=nickname, msg=" ")
self.Protocol.send_notice(nick_from=dnickname, nick_to=nickname, msg=f"{color_blue}{color_bold}Level {level}:{color_nogc}")
for module_name, commands in modules.items():
self.Protocol.send_notice(nick_from=dnickname, nick_to=nickname, msg=f"{color_black} {color_underline}Module: {module_name}{color_nogc}")
for command, description in commands.items():
self.Protocol.send_notice(nick_from=dnickname, nick_to=nickname, msg=f" {command:<20}: {description}")
count += 1
self.Protocol.send_notice(nick_from=dnickname,nick_to=nickname,msg=f" ***************** FIN DES COMMANDES *****************")
return None
def is_cmd_allowed(self, nickname: str, command_name: str) -> bool:
admin_obj = self.Admin.get_Admin(nickname)
current_level = 0
if admin_obj is not None:
current_level = admin_obj.level
for level, modules in self.module_commands.items():
for module_name, commands in modules.items():
for command, description in commands.items():
if command.lower() == command_name.lower() and level <= current_level:
return True
return False
def __create_table(self): def __create_table(self):
"""## Create core tables """## Create core tables
""" """
@@ -497,12 +593,6 @@ class Irc:
if class_name in self.loaded_classes: if class_name in self.loaded_classes:
self.loaded_classes[class_name].unload() self.loaded_classes[class_name].unload()
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name] del self.loaded_classes[class_name]
# Supprimer le module de la base de données # Supprimer le module de la base de données
@@ -526,20 +616,13 @@ class Irc:
if 'mods.' + module_name in sys.modules: if 'mods.' + module_name in sys.modules:
self.Logs.info('Unload the module ...') self.Logs.info('Unload the module ...')
self.loaded_classes[class_name].unload() self.loaded_classes[class_name].unload(reloading=True)
self.Logs.info('Module Already Loaded ... reloading the module ...') self.Logs.info('Module Already Loaded ... reloading the module ...')
the_module = sys.modules['mods.' + module_name] the_module = sys.modules['mods.' + module_name]
importlib.reload(the_module) importlib.reload(the_module)
# Supprimer la class déja instancier # Supprimer la class déja instancier
if class_name in self.loaded_classes: if class_name in self.loaded_classes:
# Supprimer les commandes déclarer dans la classe
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name] del self.loaded_classes[class_name]
my_class = getattr(the_module, class_name, None) my_class = getattr(the_module, class_name, None)
@@ -673,31 +756,6 @@ class Irc:
self.Logs.info(response) self.Logs.info(response)
return response return response
def is_cmd_allowed(self, nickname:str, cmd:str) -> bool:
# Vérifier si le user est identifié et si il a les droits
is_command_allowed = False
uid = self.User.get_uid(nickname)
get_admin = self.Admin.get_Admin(uid)
if not get_admin is None:
admin_level = get_admin.level
for ref_level, ref_commands in self.commands_level.items():
# print(f"LevelNo: {ref_level} - {ref_commands} - {admin_level}")
if ref_level <= int(admin_level):
# print(f"LevelNo: {ref_level} - {ref_commands}")
if cmd in ref_commands:
is_command_allowed = True
else:
for ref_level, ref_commands in self.commands_level.items():
if ref_level == 0:
# print(f"LevelNo: {ref_level} - {ref_commands}")
if cmd in ref_commands:
is_command_allowed = True
return is_command_allowed
def logs(self, log_msg:str) -> None: def logs(self, log_msg:str) -> None:
"""Log to database if you want """Log to database if you want
@@ -731,10 +789,8 @@ class Irc:
""" """
try: try:
original_response: list[str] = data.copy() original_response: list[str] = data.copy()
interm_response: list[str] = data.copy() interm_response: list[str] = data.copy()
"""This the original without first value""" """This the original without first value"""
interm_response.pop(0) interm_response.pop(0)
if len(original_response) == 0 or len(original_response) == 1: if len(original_response) == 0 or len(original_response) == 1:
@@ -751,179 +807,22 @@ class Irc:
else: else:
self.Logs.debug(f">> {original_response}") self.Logs.debug(f">> {original_response}")
match original_response[0]: parsed_protocol = self.Protocol.parse_server_msg(original_response.copy())
match parsed_protocol:
case 'PING': case 'PING':
# Sending PONG response to the serveur self.Protocol.on_server_ping(serverMsg=original_response)
self.Protocol.on_server_ping(original_response) self.Logs.debug(f"** handle {parsed_protocol}")
return None return None
case 'PROTOCTL':
#['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1702138935',
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST
self.Protocol.on_protoctl(serverMsg=original_response)
return None
case _:
pass
if len(original_response) < 2:
return False
match original_response[1]:
case 'PING':
# Sending PONG response to the serveur
self.Protocol.on_server_ping(original_response)
return None
case 'SLOG':
# self.Base.scan_ports(cmd[7])
# if self.Config.ABUSEIPDB == 1:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
pass
case 'VERSION':
self.Protocol.on_version_msg(original_response)
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response)
case 'REPUTATION':
# :001 REPUTATION 127.0.0.1 118
try:
self.first_connexion_ip = original_response[2]
self.first_score = 0
if str(original_response[3]).find('*') != -1:
# If * available, it means that an ircop changed the repurtation score
# means also that the user exist will try to update all users with same IP
self.first_score = int(str(original_response[3]).replace('*',''))
for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score
else:
self.first_score = int(original_response[3])
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'{ie}')
except ValueError as ve:
self.first_score = 0
self.Logs.error(f'Impossible to convert first_score: {ve}')
case '320':
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
pass
case '318':
#:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
pass
case 'MD':
# [':001', 'MD', 'client', '001CG0TG7', 'webirc', ':2']
pass
case 'EOS':
hsid = str(original_response[0]).replace(':','')
if hsid == self.Config.HSID:
if self.Config.DEFENDER_INIT == 1:
current_version = self.Config.CURRENT_VERSION
latest_version = self.Config.LATEST_VERSION
if self.Base.check_for_new_version(False):
version = f'{current_version} >>> {latest_version}'
else:
version = f'{current_version}'
print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
print(f"# PORT : {self.Config.SERVEUR_PORT} ")
print(f"# SSL : {self.Config.SERVEUR_SSL} ")
print(f"# SSL VER : {self.Config.SSL_VERSION} ")
print(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
print(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
print(f"# VERSION : {version} ")
print(f"################################################")
self.Logs.info(f"################### DEFENDER ###################")
self.Logs.info(f"# SERVICE CONNECTE ")
self.Logs.info(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
self.Logs.info(f"# PORT : {self.Config.SERVEUR_PORT} ")
self.Logs.info(f"# SSL : {self.Config.SERVEUR_SSL} ")
self.Logs.info(f"# SSL VER : {self.Config.SSL_VERSION} ")
self.Logs.info(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
self.Logs.info(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
self.Logs.info(f"# VERSION : {version} ")
self.Logs.info(f"################################################")
if self.Base.check_for_new_version(False):
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f" New Version available {version}",
channel=self.Config.SERVICE_CHANLOG
)
# Initialisation terminé aprés le premier PING
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.green}INFORMATION{self.Config.COLORS.nogc}] >> Defender is ready",
channel=self.Config.SERVICE_CHANLOG
)
self.Config.DEFENDER_INIT = 0
# Send EOF to other modules
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
# Stop here When EOS
return None
case _:
pass
if len(original_response) < 3:
return False
match original_response[2]:
case 'VERSION':
self.Protocol.on_version_msg(original_response)
case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response)
case 'PONG':
# ['@msgid=aTNJhp17kcPboF5diQqkUL;time=2023-12-28T20:35:58.411Z', ':irc.deb.biz.st', 'PONG', 'irc.deb.biz.st', ':Dev-PyDefender']
self.Base.execute_periodic_action()
case 'NICK':
self.Protocol.on_nick(original_response)
case 'MODE':
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6+Z4494xWUg;time=2024-08-09T12:45:36.651Z',
# ':001', 'MODE', '#a', '+nt', '1723207536']
# [':adator_', 'UMODE2', '-i']
pass
case 'SJOIN': case 'SJOIN':
self.Protocol.on_sjoin(serverMsg=original_response) self.Protocol.on_sjoin(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PART': case 'EOS':
self.Protocol.on_eos(serverMsg=original_response)
self.Protocol.on_part(serverMsg=original_response) self.Logs.debug(f"** handle {parsed_protocol}")
case 'UID': case 'UID':
try: try:
@@ -932,99 +831,83 @@ class Irc:
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response) classe_object.cmd(original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
except Exception as err: except Exception as err:
self.Logs.error(f'General Error: {err}') self.Logs.error(f'General Error: {err}')
case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PROTOCTL':
self.Protocol.on_protoctl(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SVS2MODE':
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
self.Protocol.on_svs2mode(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PART':
self.Protocol.on_part(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'VERSION':
self.Protocol.on_version_msg(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'NICK':
self.Protocol.on_nick(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'REPUTATION':
self.Protocol.on_reputation(serverMsg=original_response)
self.Logs.debug(f"** handle {parsed_protocol}")
case 'SLOG': # TODO
self.Logs.debug(f"** handle {parsed_protocol}")
case 'MD': # TODO
self.Logs.debug(f"** handle {parsed_protocol}")
case 'PRIVMSG': case 'PRIVMSG':
try: self.Protocol.on_privmsg(serverMsg=original_response)
# Supprimer la premiere valeur self.Logs.debug(f"** handle {parsed_protocol}")
cmd = interm_response.copy()
get_uid_or_nickname = str(cmd[0].replace(':','')) case 'PONG': # TODO
user_trigger = self.User.get_nickname(get_uid_or_nickname) self.Logs.debug(f"** handle {parsed_protocol}")
dnickname = self.Config.SERVICE_NICKNAME
if len(cmd) == 6: case 'MODE': # TODO
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace(self.Config.SERVICE_PREFIX,'') == ':auth': #['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6...', ':001', 'MODE', '#a', '+nt', '1723207536']
cmd_copy = cmd.copy() #['@unrealircd.org/userhost=adator@localhost;...', ':001LQ0L0C', 'MODE', '#services', '-l']
cmd_copy[5] = '**********' self.Logs.debug(f"** handle {parsed_protocol}")
self.Logs.info(f'>> {cmd_copy}')
else:
self.Logs.info(f'>> {cmd}')
else:
self.Logs.info(f'>> {cmd}')
pattern = fr'(:\{self.Config.SERVICE_PREFIX})(.*)$' case '320': # TODO
hcmds = re.search(pattern, ' '.join(cmd)) # va matcher avec tout les caractéres aprés le . #:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
self.Logs.debug(f"** handle {parsed_protocol}")
if hcmds: # Commande qui commencent par le point case '318': # TODO
liste_des_commandes = list(hcmds.groups()) #:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
convert_to_string = ' '.join(liste_des_commandes) self.Logs.debug(f"** handle {parsed_protocol}")
arg = convert_to_string.split()
arg.remove(f':{self.Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.commands:
self.Logs.debug(f"This command {arg[0]} is not available")
self.Protocol.send_notice(
nick_from=self.Config.SERVICE_NICKNAME,
nick_to=user_trigger,
msg=f"This command [{self.Config.COLORS.bold}{arg[0]}{self.Config.COLORS.bold}] is not available"
)
return None
cmd_to_send = convert_to_string.replace(':','') case None:
self.Base.log_cmd(user_trigger, cmd_to_send) self.Logs.debug(f"** TO BE HANDLE {original_response}")
fromchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(cmd[2]) else None if len(original_response) > 2:
self._hcmds(user_trigger, fromchannel, arg, cmd) if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées
if cmd[2] == self.Config.SERVICE_ID: for classe_name, classe_object in self.loaded_classes.items():
pattern = fr'^:.*?:(.*)$' classe_object.cmd(original_response)
hcmds = re.search(pattern, ' '.join(cmd))
if hcmds: # par /msg defender [commande]
liste_des_commandes = list(hcmds.groups())
convert_to_string = ' '.join(liste_des_commandes)
arg = convert_to_string.split()
# Réponse a un CTCP VERSION
if arg[0] == '\x01VERSION\x01':
self.Protocol.on_version(original_response)
return False
# Réponse a un TIME
if arg[0] == '\x01TIME\x01':
self.Protocol.on_time(original_response)
return False
# Réponse a un PING
if arg[0] == '\x01PING':
self.Protocol.on_ping(original_response)
return False
if not arg[0].lower() in self.commands:
self.Logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available")
return False
cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = None
if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.Channel.Is_Channel(arg[1]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
except IndexError as io:
self.Logs.error(f'{io}')
case _:
pass
if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
except IndexError as ie: except IndexError as ie:
self.Logs.error(f"{ie} / {original_response} / length {str(len(original_response))}") self.Logs.error(f"{ie} / {original_response} / length {str(len(original_response))}")
@@ -1032,8 +915,8 @@ class Irc:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
self.Logs.error(f"General Error: {traceback.format_exc()}") self.Logs.error(f"General Error: {traceback.format_exc()}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_ """Create
Args: Args:
user (str): The user who sent the query user (str): The user who sent the query
@@ -1064,7 +947,7 @@ class Irc:
# Envoyer la commande aux classes dynamiquement chargées # Envoyer la commande aux classes dynamiquement chargées
if command != 'notallowed': if command != 'notallowed':
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object._hcmds(user, channel, cmd, fullcmd) classe_object.hcmds(user, channel, cmd, fullcmd)
match command: match command:
@@ -1184,6 +1067,10 @@ class Irc:
case 'auth': case 'auth':
# ['auth', 'adator', 'password'] # ['auth', 'adator', 'password']
if len(cmd) != 3:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [password]")
return None
current_command = cmd[0] current_command = cmd[0]
user_to_log = self.User.get_nickname(cmd[1]) user_to_log = self.User.get_nickname(cmd[1])
password = cmd[2] password = cmd[2]
@@ -1241,25 +1128,30 @@ class Irc:
case 'editaccess': case 'editaccess':
# .editaccess [USER] [PASSWORD] [LEVEL] # .editaccess [USER] [PASSWORD] [LEVEL]
try: try:
user_to_edit = cmd[1] if len(cmd) < 3:
user_new_level = int(cmd[3]) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Right command : /msg {dnickname} editaccess [nickname] [NEWPASSWORD] [NEWLEVEL]")
user_password = self.Base.crypt_password(cmd[2]) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"level: from 1 to 4")
if len(cmd) < 4 or len(cmd) > 4:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{self.Config.SERVICE_PREFIX}editaccess [USER] [NEWPASSWORD] [NEWLEVEL]")
return None return None
user_to_edit = cmd[1]
user_password = self.Base.crypt_password(cmd[2])
get_admin = self.Admin.get_Admin(fromuser) get_admin = self.Admin.get_Admin(fromuser)
if get_admin is None: if get_admin is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This user {fromuser} has no Admin access") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"This user {fromuser} has no Admin access")
return None return None
current_user = self.User.get_nickname(fromuser) current_user = self.User.get_nickname(fromuser)
current_uid = self.User.get_uid(fromuser) current_uid = self.User.get_uid(fromuser)
current_user_level = get_admin.level current_user_level = get_admin.level
user_new_level = int(cmd[3]) if len(cmd) == 4 else get_admin.level
if current_user == fromuser:
user_new_level = get_admin.level
if user_new_level > 5: if user_new_level > 5:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" Maximum authorized level is 5") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Maximum authorized level is 5")
return None return None
# Rechercher le user dans la base de données. # Rechercher le user dans la base de données.
@@ -1271,9 +1163,9 @@ class Irc:
if not isUserExist is None: if not isUserExist is None:
if current_user_level < int(isUserExist[1]): if current_user_level < int(isUserExist[1]):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You are not allowed to edit this access") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You are not allowed to edit this access")
return None return None
if current_user_level == int(isUserExist[1]) and current_user != user_to_edit: if current_user_level == int(isUserExist[1]) and current_user != user_to_edit:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You can't edit access of a user with same level") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" You can't edit access of a user with same level")
return None return None
@@ -1343,34 +1235,7 @@ class Irc:
case 'help': case 'help':
count_level_definition = 0 self.generate_help_menu(nickname=fromuser)
get_admin = self.Admin.get_Admin(uid)
if not get_admin is None:
user_level = get_admin.level
else:
user_level = 0
self.Protocol.send_notice(nick_from=dnickname,nick_to=fromuser,msg=f" ***************** LISTE DES COMMANDES *****************")
self.Protocol.send_notice(nick_from=dnickname,nick_to=fromuser,msg=f" ")
for levDef in self.commands_level:
if int(user_level) >= int(count_level_definition):
self.Protocol.send_notice(nick_from=dnickname,nick_to=fromuser,
msg=f" ***************** {self.Config.COLORS.nogc}[ {self.Config.COLORS.green}LEVEL {str(levDef)} {self.Config.COLORS.nogc}] *****************"
)
batch = 7
for i in range(0, len(self.commands_level[count_level_definition]), batch):
groupe = self.commands_level[count_level_definition][i:i + batch] # Extraire le groupe
batch_commands = ' | '.join(groupe)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {batch_commands}")
self.Protocol.send_notice(nick_from=dnickname,nick_to=fromuser,msg=f" ")
count_level_definition += 1
self.Protocol.send_notice(nick_from=dnickname,nick_to=fromuser,msg=f" ***************** FIN DES COMMANDES *****************")
case 'load': case 'load':
try: try:
@@ -1413,6 +1278,9 @@ class Irc:
self.Base.shutdown() self.Base.shutdown()
self.Base.execute_periodic_action() self.Base.execute_periodic_action()
for chan_name in self.Channel.UID_CHANNEL_DB:
self.Protocol.send_mode_chan(chan_name.name, '-l')
self.Protocol.send_notice( self.Protocol.send_notice(
nick_from=dnickname, nick_from=dnickname,
nick_to=fromuser, nick_to=fromuser,
@@ -1464,13 +1332,15 @@ class Irc:
current_version = self.Config.CURRENT_VERSION current_version = self.Config.CURRENT_VERSION
latest_version = self.Config.LATEST_VERSION latest_version = self.Config.LATEST_VERSION
mods = ["core.config", "core.base", "core.classes.protocols.unreal6", "core.classes.protocol"] mods = ["core.definition", "core.config", "core.base", "core.classes.protocols.unreal6", "core.classes.protocol"]
mod_unreal6 = sys.modules['core.classes.protocols.unreal6'] mod_unreal6 = sys.modules['core.classes.protocols.unreal6']
mod_protocol = sys.modules['core.classes.protocol'] mod_protocol = sys.modules['core.classes.protocol']
mod_base = sys.modules['core.base'] mod_base = sys.modules['core.base']
mod_config = sys.modules['core.classes.config'] mod_config = sys.modules['core.classes.config']
mod_definition = sys.modules['core.definition']
importlib.reload(mod_definition)
importlib.reload(mod_config) importlib.reload(mod_config)
self.Config = self.Loader.ConfModule.Configuration().ConfigObject self.Config = self.Loader.ConfModule.Configuration().ConfigObject
self.Config.HSID = hsid self.Config.HSID = hsid
@@ -1485,7 +1355,7 @@ class Irc:
config_dict: dict = self.Config.__dict__ config_dict: dict = self.Config.__dict__
for key, value in conf_bkp_dict.items(): for key, value in conf_bkp_dict.items():
if config_dict[key] != value: if config_dict[key] != value and key != 'COLORS':
self.Protocol.send_priv_msg( self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME, nick_from=self.Config.SERVICE_NICKNAME,
msg=f'[{key}]: {value} ==> {config_dict[key]}', msg=f'[{key}]: {value} ==> {config_dict[key]}',
@@ -1598,6 +1468,16 @@ class Irc:
msg=f"UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - isWebSocket: {db_user.isWebsocket} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}" msg=f"UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - isWebSocket: {db_user.isWebsocket} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}"
) )
case 'show_clients':
count_users = len(self.Client.CLIENT_DB)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Total Connected Clients: {count_users}")
for db_client in self.Client.CLIENT_DB:
self.Protocol.send_notice(
nick_from=dnickname,
nick_to=fromuser,
msg=f"UID : {db_client.uid} - isWebirc: {db_client.isWebirc} - isWebSocket: {db_client.isWebsocket} - Nickname: {db_client.nickname} - Account: {db_client.account} - Connection: {db_client.connexion_datetime}"
)
case 'show_admins': case 'show_admins':
for db_admin in self.Admin.UID_ADMIN_DB: for db_admin in self.Admin.UID_ADMIN_DB:
@@ -1615,7 +1495,7 @@ class Irc:
self.Protocol.send_notice( self.Protocol.send_notice(
nick_from=dnickname, nick_from=dnickname,
nick_to=fromuser, nick_to=fromuser,
msg=f'{key} > {value}' msg=f'{key} = {value}'
) )
case 'uptime': case 'uptime':
@@ -1640,5 +1520,9 @@ class Irc:
(fromuser, ) (fromuser, )
) )
case 'raw':
raw_command = ' '.join(cmd[1:])
self.Protocol.send_raw(raw_command)
case _: case _:
pass pass

View File

@@ -1,5 +1,4 @@
from core.classes import user, admin, channel, clone, reputation, settings from core.classes import user, admin, client, channel, clone, reputation, settings
import utils
import core.definition as df import core.definition as df
import core.base as baseModule import core.base as baseModule
import core.classes.config as confModule import core.classes.config as confModule
@@ -15,8 +14,6 @@ class Loader:
self.BaseModule: baseModule = baseModule self.BaseModule: baseModule = baseModule
self.Utils: utils
# Load Classes # Load Classes
self.Settings: settings = settings.Settings() self.Settings: settings = settings.Settings()
@@ -26,6 +23,8 @@ class Loader:
self.User: user.User = user.User(self.Base) self.User: user.User = user.User(self.Base)
self.Client: client.Client = client.Client(self.Base)
self.Admin: admin.Admin = admin.Admin(self.Base) self.Admin: admin.Admin = admin.Admin(self.Base)
self.Channel: channel.Channel = channel.Channel(self.Base) self.Channel: channel.Channel = channel.Channel(self.Base)

View File

@@ -1,30 +1,21 @@
from core import installation from core import installation
############################################# #############################################
# @Version : 1 # # @Version : 6 #
# Requierements : # # Requierements : #
# Python3.10 or higher # # Python3.10 or higher #
# SQLAlchemy, requests, psutil # # SQLAlchemy, requests, psutil #
# UnrealIRCD 6.2.2 or higher # # UnrealIRCD 6.2.2 or higher #
############################################# #############################################
#########################
# LANCEMENT DE DEFENDER #
#########################
# 1. Chargement de la configuration
# 2. Chargement de l'ensemble des classes
# 3.
#
try: try:
installation.Install() installation.Install()
from core.loader import Loader from core.loader import Loader
from core.irc import Irc from core.irc import Irc
loader = Loader() # loader = Loader()
ircInstance = Irc(loader) ircInstance = Irc(Loader())
ircInstance.init_irc(ircInstance) ircInstance.init_irc(ircInstance)
except AssertionError as ae: except AssertionError as ae:

View File

@@ -1,7 +1,5 @@
from dataclasses import dataclass, fields, field from dataclasses import dataclass
import copy
import random, faker, time, logging import random, faker, time, logging
from datetime import datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -45,9 +43,7 @@ class Clone():
self.Definition = ircInstance.Loader.Definition self.Definition = ircInstance.Loader.Definition
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
1: ['clone']
}
# Init the module (Mandatory) # Init the module (Mandatory)
self.__init_module() self.__init_module()
@@ -57,9 +53,6 @@ class Clone():
def __init_module(self) -> None: def __init_module(self) -> None:
# Enrigstrer les nouvelles commandes dans le code
self.__set_commands(self.commands_level)
# Créer les tables necessaire a votre module (ce n'es pas obligatoire) # Créer les tables necessaire a votre module (ce n'es pas obligatoire)
self.__create_tables() self.__create_tables()
@@ -79,20 +72,6 @@ class Clone():
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts") self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}") self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas. """Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -132,7 +111,7 @@ class Clone():
except TypeError as te: except TypeError as te:
self.Logs.critical(te) self.Logs.critical(te)
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
"""Cette methode sera executée a chaque désactivation ou """Cette methode sera executée a chaque désactivation ou
rechargement de module rechargement de module
""" """
@@ -155,7 +134,7 @@ class Clone():
vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP' vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
return vhost return vhost
def generate_clones(self, group: str = 'Default') -> None: def generate_clones(self, group: str = 'Default', auto_remote_ip: bool = False) -> None:
try: try:
fakeEN = self.fakeEN fakeEN = self.fakeEN
@@ -188,7 +167,7 @@ class Clone():
department = fakeFR.department_name() department = fakeFR.department_name()
realname = f'{age} {gender} {department}' realname = f'{age} {gender} {department}'
decoded_ip = fakeEN.ipv4_private() decoded_ip = fakeEN.ipv4_private() if auto_remote_ip else '127.0.0.1'
hostname = fakeEN.hostname() hostname = fakeEN.hostname()
vhost = self.generate_vhost() vhost = self.generate_vhost()
@@ -231,10 +210,10 @@ class Clone():
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
def thread_connect_clones(self, number_of_clones:int , group: str, interval: float = 0.2) -> None: def thread_connect_clones(self, number_of_clones:int , group: str = 'Default', auto_remote_ip: bool = False, interval: float = 0.2) -> None:
for i in range(0, number_of_clones): for i in range(0, number_of_clones):
self.generate_clones(group=group) self.generate_clones(group=group, auto_remote_ip=auto_remote_ip)
for clone in self.Clone.UID_CLONE_DB: for clone in self.Clone.UID_CLONE_DB:
@@ -306,7 +285,7 @@ class Clone():
except Exception as err: except Exception as err:
self.Logs.error(f'General Error: {err}') self.Logs.error(f'General Error: {err}')
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
try: try:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
@@ -331,15 +310,15 @@ class Clone():
case 'connect': case 'connect':
try: try:
# clone connect 5 Group 3 # clone connect 5 GroupName 3
self.stop = False self.stop = False
number_of_clones = int(cmd[2]) number_of_clones = int(cmd[2])
group = str(cmd[3]).lower() group = str(cmd[3]).lower()
connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.5 connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.2
self.Base.create_thread( self.Base.create_thread(
func=self.thread_connect_clones, func=self.thread_connect_clones,
func_args=(number_of_clones, group, connection_interval) func_args=(number_of_clones, group, False, connection_interval)
) )
except Exception as err: except Exception as err:

View File

@@ -1,10 +1,12 @@
from typing import TYPE_CHECKING from typing import Union, TYPE_CHECKING
from dataclasses import dataclass, fields from dataclasses import dataclass
if TYPE_CHECKING: if TYPE_CHECKING:
from core.irc import Irc from core.irc import Irc
from core.definition import MUser
from sqlalchemy import CursorResult, Row, Sequence
class Command(): class Command:
@dataclass @dataclass
class ModConfModel: class ModConfModel:
@@ -20,6 +22,9 @@ class Command():
# Add Irc Object to the module (Mandatory) # Add Irc Object to the module (Mandatory)
self.Irc = ircInstance self.Irc = ircInstance
# Add Loader Object to the module (Mandatory)
self.Loader = ircInstance.Loader
# Add Protocol object to the module (Mandatory) # Add Protocol object to the module (Mandatory)
self.Protocol = ircInstance.Protocol self.Protocol = ircInstance.Protocol
@@ -35,21 +40,62 @@ class Command():
# Add User object to the module (Mandatory) # Add User object to the module (Mandatory)
self.User = ircInstance.User self.User = ircInstance.User
# Add Client object to the module (Mandatory)
self.Client = ircInstance.Client
# Add Channel object to the module (Mandatory) # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
# Create module commands (Mandatory) self.Irc.build_command(1, self.module_name, 'join', 'Join a channel')
self.commands_level = { self.Irc.build_command(1, self.module_name, 'assign', 'Assign a user to a role or task')
1: ['join', 'part','owner', 'deowner', 'protect', 'deprotect', 'op', self.Irc.build_command(1, self.module_name, 'part', 'Leave a channel')
'deop', 'halfop', 'dehalfop', 'voice','devoice', 'topic'], self.Irc.build_command(1, self.module_name, 'unassign', 'Remove a user from a role or task')
2: ['opall', 'deopall', 'devoiceall', 'voiceall', 'ban', self.Irc.build_command(1, self.module_name, 'owner', 'Give channel ownership to a user')
'unban','kick', 'kickban', 'umode', 'mode', 'get_mode', 'svsjoin', 'svspart', 'svsnick', self.Irc.build_command(1, self.module_name, 'deowner', 'Remove channel ownership from a user')
'wallops', 'globops','gnotice','whois', 'names', 'invite', 'inviteme', self.Irc.build_command(1, self.module_name, 'protect', 'Protect a user from being kicked')
'sajoin', 'sapart', self.Irc.build_command(1, self.module_name, 'deprotect', 'Remove protection from a user')
'kill', 'gline', 'ungline', 'kline', 'unkline', 'shun', 'unshun', self.Irc.build_command(1, self.module_name, 'op', 'Grant operator privileges to a user')
'glinelist', 'shunlist', 'klinelist'], self.Irc.build_command(1, self.module_name, 'deop', 'Remove operator privileges from a user')
3: ['map'] self.Irc.build_command(1, self.module_name, 'halfop', 'Grant half-operator privileges to a user')
} self.Irc.build_command(1, self.module_name, 'dehalfop', 'Remove half-operator privileges from a user')
self.Irc.build_command(1, self.module_name, 'voice', 'Grant voice privileges to a user')
self.Irc.build_command(1, self.module_name, 'devoice', 'Remove voice privileges from a user')
self.Irc.build_command(1, self.module_name, 'topic', 'Change the topic of a channel')
self.Irc.build_command(2, self.module_name, 'opall', 'Grant operator privileges to all users')
self.Irc.build_command(2, self.module_name, 'deopall', 'Remove operator privileges from all users')
self.Irc.build_command(2, self.module_name, 'devoiceall', 'Remove voice privileges from all users')
self.Irc.build_command(2, self.module_name, 'voiceall', 'Grant voice privileges to all users')
self.Irc.build_command(2, self.module_name, 'ban', 'Ban a user from a channel')
self.Irc.build_command(2, self.module_name, 'automode', 'Automatically set user modes upon join')
self.Irc.build_command(2, self.module_name, 'unban', 'Remove a ban from a user')
self.Irc.build_command(2, self.module_name, 'kick', 'Kick a user from a channel')
self.Irc.build_command(2, self.module_name, 'kickban', 'Kick and ban a user from a channel')
self.Irc.build_command(2, self.module_name, 'umode', 'Set user mode')
self.Irc.build_command(2, self.module_name, 'mode', 'Set channel mode')
self.Irc.build_command(2, self.module_name, 'get_mode', 'Retrieve current channel mode')
self.Irc.build_command(2, self.module_name, 'svsjoin', 'Force a user to join a channel')
self.Irc.build_command(2, self.module_name, 'svspart', 'Force a user to leave a channel')
self.Irc.build_command(2, self.module_name, 'svsnick', 'Force a user to change their nickname')
self.Irc.build_command(2, self.module_name, 'wallops', 'Send a message to all operators')
self.Irc.build_command(2, self.module_name, 'globops', 'Send a global operator message')
self.Irc.build_command(2, self.module_name, 'gnotice', 'Send a global notice')
self.Irc.build_command(2, self.module_name, 'whois', 'Get information about a user')
self.Irc.build_command(2, self.module_name, 'names', 'List users in a channel')
self.Irc.build_command(2, self.module_name, 'invite', 'Invite a user to a channel')
self.Irc.build_command(2, self.module_name, 'inviteme', 'Invite yourself to a channel')
self.Irc.build_command(2, self.module_name, 'sajoin', 'Force yourself into a channel')
self.Irc.build_command(2, self.module_name, 'sapart', 'Force yourself to leave a channel')
self.Irc.build_command(2, self.module_name, 'kill', 'Disconnect a user from the server')
self.Irc.build_command(2, self.module_name, 'gline', 'Ban a user from the entire server')
self.Irc.build_command(2, self.module_name, 'ungline', 'Remove a global server ban')
self.Irc.build_command(2, self.module_name, 'kline', 'Ban a user based on their hostname')
self.Irc.build_command(2, self.module_name, 'unkline', 'Remove a K-line ban')
self.Irc.build_command(2, self.module_name, 'shun', 'Prevent a user from sending messages')
self.Irc.build_command(2, self.module_name, 'unshun', 'Remove a shun from a user')
self.Irc.build_command(2, self.module_name, 'glinelist', 'List all global bans')
self.Irc.build_command(2, self.module_name, 'shunlist', 'List all shunned users')
self.Irc.build_command(2, self.module_name, 'klinelist', 'List all K-line bans')
self.Irc.build_command(3, self.module_name, 'map', 'Show the server network map')
# Init the module # Init the module
self.__init_module() self.__init_module()
@@ -59,9 +105,6 @@ class Command():
def __init_module(self) -> None: def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables (Mandatory) # Create you own tables (Mandatory)
self.__create_tables() self.__create_tables()
@@ -74,20 +117,6 @@ class Command():
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas. """Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -98,14 +127,17 @@ class Command():
None: Aucun retour n'es attendu None: Aucun retour n'es attendu
""" """
table_logs = '''CREATE TABLE IF NOT EXISTS test_logs ( table_automode = '''CREATE TABLE IF NOT EXISTS command_automode (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT, created_on TEXT,
server_msg TEXT updated_on TEXT,
nickname TEXT,
channel TEXT,
mode TEXT
) )
''' '''
# self.Base.db_execute_query(table_logs) self.Base.db_execute_query(table_automode)
return None return None
def __load_module_configuration(self) -> None: def __load_module_configuration(self) -> None:
@@ -132,111 +164,242 @@ class Command():
""" """
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
return None return None
def cmd(self, data: list) -> None: def cmd(self, data: list[str]) -> None:
try:
service_id = self.Config.SERVICE_ID dnickname = self.Config.SERVICE_NICKNAME
dnickname = self.Config.SERVICE_NICKNAME red = self.Config.COLORS.red
dchanlog = self.Config.SERVICE_CHANLOG green = self.Config.COLORS.green
red = self.Config.COLORS.red bold = self.Config.COLORS.bold
green = self.Config.COLORS.green nogc = self.Config.COLORS.nogc
bold = self.Config.COLORS.bold cmd = list(data).copy()
nogc = self.Config.COLORS.nogc
cmd = list(data).copy()
if len(cmd) < 2: if len(cmd) < 2:
return None return None
match cmd[1]: match cmd[1]:
# [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel'] # [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel']
case '403' | '401': case '403' | '401':
try: try:
message = ' '.join(cmd[3:]) message = ' '.join(cmd[3:])
self.Protocol.send_notice( self.Protocol.send_notice(
nick_from=dnickname, nick_from=dnickname,
nick_to=self.user_to_notice, nick_to=self.user_to_notice,
msg=f"[{red}ERROR MSG{nogc}] {message}" msg=f"[{red}ERROR MSG{nogc}] {message}"
) )
self.Logs.error(f"{cmd[1]} - {message}") self.Logs.error(f"{cmd[1]} - {message}")
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case '006' | '018': case '006' | '018':
try: try:
# [':irc.deb.biz.st', '006', 'Dev-PyDefender', ':`-services.deb.biz.st', '------', '|', 'Users:', '9', '(47.37%)', '[00B]'] # [':irc.deb.biz.st', '006', 'Dev-PyDefender', ':`-services.deb.biz.st', '------', '|', 'Users:', '9', '(47.37%)', '[00B]']
# [':irc.deb.biz.st', '018', 'Dev-PyDefender', ':4', 'servers', 'and', '19', 'users,', 'average', '4.75', 'users', 'per', 'server'] # [':irc.deb.biz.st', '018', 'Dev-PyDefender', ':4', 'servers', 'and', '19', 'users,', 'average', '4.75', 'users', 'per', 'server']
message = ' '.join(cmd[3:]) message = ' '.join(cmd[3:])
self.Protocol.send_notice( self.Protocol.send_notice(
nick_from=dnickname, nick_from=dnickname,
nick_to=self.user_to_notice, nick_to=self.user_to_notice,
msg=f"[{green}SERVER MSG{nogc}] {message}" msg=f"[{green}SERVER MSG{nogc}] {message}"
) )
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case '219': case '219':
try: try:
# [':irc.deb.biz.st', '219', 'Dev-PyDefender', 's', ':End', 'of', '/STATS', 'report'] # [':irc.deb.biz.st', '219', 'Dev-PyDefender', 's', ':End', 'of', '/STATS', 'report']
if not self.show_219: if not self.show_219:
# If there is a result in 223 then stop here # If there is a result in 223 then stop here
self.show_219 = True self.show_219 = True
return None return None
type_of_stats = str(cmd[3]) type_of_stats = str(cmd[3])
match type_of_stats: match type_of_stats:
case 's': case 's':
self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No shun") self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No shun")
case 'G': case 'G':
self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No gline") self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No gline")
case 'k': case 'k':
self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No kline") self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, msg="No kline")
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case '223': case '223':
try: try:
# [':irc.deb.biz.st', '223', 'Dev-PyDefender', 'G', '*@162.142.125.217', '67624', '18776', 'irc.deb.biz.st', ':Proxy/Drone', 'detected.', 'Check', 'https://dronebl.org/lookup?ip=162.142.125.217', 'for', 'details.'] # [':irc.deb.biz.st', '223', 'Dev-PyDefender', 'G', '*@162.142.125.217', '67624', '18776', 'irc.deb.biz.st', ':Proxy/Drone', 'detected.', 'Check', 'https://dronebl.org/lookup?ip=162.142.125.217', 'for', 'details.']
self.show_219 = False self.show_219 = False
host = str(cmd[4]) host = str(cmd[4])
author = str(cmd[7]) author = str(cmd[7])
reason = ' '.join(cmd[8:]) reason = ' '.join(cmd[8:])
self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice, self.Protocol.send_notice(nick_from=dnickname,nick_to=self.user_to_notice,
msg=f"{bold}Author{nogc}: {author} - {bold}Host{nogc}: {host} - {bold}Reason{nogc}: {reason}" msg=f"{bold}Author{nogc}: {author} - {bold}Host{nogc}: {host} - {bold}Reason{nogc}: {reason}"
) )
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case _: case _:
pass pass
return None if len(cmd) < 3:
return None
def _hcmds(self, user: str, channel: any, cmd: list, fullcmd: list = []) -> None: match cmd[2]:
case 'SJOIN':
# ['@msgid=yldTlbwAGbzCGUcCIHi3ku;time=2024-11-11T17:56:24.297Z', ':001', 'SJOIN', '1728815963', '#znc', ':001LQ0L0C']
# Check if the user has an automode
try:
if len(cmd) < 6:
return None
user_uid = self.User.clean_uid(cmd[5])
userObj: MUser = self.User.get_User(user_uid)
channel_name = cmd[4] if self.Channel.Is_Channel(cmd[4]) else None
client_obj = self.Client.get_Client(user_uid)
nickname = userObj.nickname if userObj is not None else None
if client_obj is not None:
nickname = client_obj.account
if userObj is None:
return None
if 'r' not in userObj.umodes and 'o' not in userObj.umodes and not self.Client.is_exist(userObj.uid):
return None
db_data: dict[str, str] = {"nickname": nickname, "channel": channel_name}
db_query = self.Base.db_execute_query("SELECT id, mode FROM command_automode WHERE nickname = :nickname AND channel = :channel", db_data)
db_result = db_query.fetchone()
if db_result is not None:
id, mode = db_result
self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {channel_name} {mode} {userObj.nickname}")
except KeyError as ke:
self.Logs.error(f"Key Error: {err}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
def hcmds(self, uidornickname: str, channel_name: Union[str, None], cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG dchanlog = self.Config.SERVICE_CHANLOG
self.user_to_notice = user self.user_to_notice = uidornickname
fromuser = user fromuser = self.User.get_nickname(uidornickname)
fromchannel = channel fromchannel = channel_name
match command: match command:
case "automode":
# automode set nickname [+/-mode] #channel
# automode set adator +o #channel
try:
option: str = str(cmd[1]).lower()
match option:
case 'set':
allowed_modes: list[str] = self.Base.Settings.PROTOCTL_PREFIX # ['q','a','o','h','v']
if len(cmd) < 5:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AutoModes available: {' / '.join(allowed_modes)}")
return None
# userObj: MUser = self.User.get_User(str(cmd[2]))
nickname = str(cmd[2])
mode = str(cmd[3])
chan: str = str(cmd[4]).lower() if self.Channel.Is_Channel(cmd[4]) else None
sign = mode[0] if mode.startswith( ('+', '-')) else None
clean_mode = mode[1:] if len(mode) > 0 else None
if sign is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You must provide the flag mode + or -")
return None
if clean_mode not in allowed_modes:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}")
return None
if chan is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You should use one of those modes {' / '.join(allowed_modes)}")
return None
db_data: dict[str, str] = {"nickname": nickname, "channel": chan}
db_query = self.Base.db_execute_query(query="SELECT id FROM command_automode WHERE nickname = :nickname and channel = :channel", params=db_data)
db_result = db_query.fetchone()
if db_result is not None:
if sign == '+':
db_data = {"updated_on": self.Base.get_datetime(), "nickname": nickname, "channel": chan, "mode": mode}
db_result = self.Base.db_execute_query(query="UPDATE command_automode SET mode = :mode, updated_on = :updated_on WHERE nickname = :nickname and channel = :channel",
params=db_data)
if db_result.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} edited for {nickname} in {chan}")
elif sign == '-':
db_data = {"nickname": nickname, "channel": chan, "mode": f"+{clean_mode}"}
db_result = self.Base.db_execute_query(query="DELETE FROM command_automode WHERE nickname = :nickname and channel = :channel and mode = :mode",
params=db_data)
if db_result.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} deleted for {nickname} in {chan}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The mode [{mode}] has not been found for {nickname} in channel {chan}")
return None
# Instert a new automode
if sign == '+':
db_data = {"created_on": self.Base.get_datetime(), "updated_on": self.Base.get_datetime(), "nickname": nickname, "channel": chan, "mode": mode}
db_query = self.Base.db_execute_query(
query="INSERT INTO command_automode (created_on, updated_on, nickname, channel, mode) VALUES (:created_on, :updated_on, :nickname, :channel, :mode)",
params=db_data
)
if db_query.rowcount > 0:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Automode {mode} applied to {nickname} in {chan}")
if self.Channel.is_user_present_in_channel(chan, self.User.get_uid(nickname)):
self.Protocol.send2socket(f":{service_id} MODE {chan} {mode} {nickname}")
else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"AUTOMODE {mode} cannot be added to {nickname} in {chan} because it doesn't exist")
case 'list':
db_query: CursorResult = self.Base.db_execute_query("SELECT nickname, channel, mode FROM command_automode")
db_results: Sequence[Row] = db_query.fetchall()
if not db_results:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
msg="There is no automode to display.")
for db_result in db_results:
db_nickname, db_channel, db_mode = db_result
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,
msg=f"Nickname: {db_nickname} | Channel: {db_channel} | Mode: {db_mode}")
case _:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(allowed_modes)}")
except IndexError:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} SET [nickname] [+/-mode] [#channel]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} LIST")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"[AUTOMODES AVAILABLE] are {' / '.join(self.Base.Settings.PROTOCTL_PREFIX)}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
case 'deopall': case 'deopall':
try: try:
@@ -647,7 +810,7 @@ class Command():
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'join': case 'join' | 'assign':
try: try:
sent_channel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None sent_channel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None
@@ -665,7 +828,7 @@ class Command():
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'part': case 'part' | 'unassign':
try: try:
sent_channel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None sent_channel = str(cmd[1]) if self.Channel.Is_Channel(cmd[1]) else None
@@ -679,7 +842,7 @@ class Command():
self.Protocol.send_part_chan(uidornickname=dnickname, channel=sent_channel) self.Protocol.send_part_chan(uidornickname=dnickname, channel=sent_channel)
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} LEFT {sent_channel}") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} LEFT {sent_channel}")
self.Channel.db_query_channel('del', self.module_name, sent_channel) self.Channel.db_query_channel('del', self.module_name, sent_channel)
except IndexError as ie: except IndexError as ie:
@@ -689,23 +852,23 @@ class Command():
case 'topic': case 'topic':
try: try:
if len(cmd) == 1: # TOPIC <#CHANNEL> <THE TOPIC DESCRIPTION>
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") if len(cmd) < 2:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} <#channel> THE_TOPIC_MESSAGE")
return None return None
chan = str(cmd[1]) chan = str(cmd[1])
if not self.Channel.Is_Channel(chan): if not self.Channel.Is_Channel(chan):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The channel must start with #") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE")
return None return None
topic_msg = ' '.join(cmd[2:]).strip() topic_msg: str = ' '.join(cmd[2:]).strip()
if topic_msg: if topic_msg:
self.Protocol.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}') self.Protocol.send_topic_chan(channel_name=chan, topic_msg=topic_msg)
else: else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the topic") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the topic")
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -723,7 +886,7 @@ class Command():
if wallops_msg: if wallops_msg:
self.Protocol.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})') self.Protocol.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})')
else: else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the wallops message") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the wallops message")
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -741,7 +904,7 @@ class Command():
if globops_msg: if globops_msg:
self.Protocol.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})') self.Protocol.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})')
else: else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the globops message") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the globops message")
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -759,7 +922,7 @@ class Command():
if gnotice_msg: if gnotice_msg:
self.Protocol.send_notice(nick_from=dnickname, nick_to='$*.*', msg=f"[{self.Config.COLORS.red}GLOBAL NOTICE{self.Config.COLORS.nogc}] {gnotice_msg}") self.Protocol.send_notice(nick_from=dnickname, nick_to='$*.*', msg=f"[{self.Config.COLORS.red}GLOBAL NOTICE{self.Config.COLORS.nogc}] {gnotice_msg}")
else: else:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You need to specify the global notice message") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="You need to specify the global notice message")
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
@@ -776,7 +939,7 @@ class Command():
nickname = str(cmd[1]) nickname = str(cmd[1])
if self.User.get_nickname(nickname) is None: if self.User.get_nickname(nickname) is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nickname not found !") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nickname not found !")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME")
return None return None
@@ -796,7 +959,7 @@ class Command():
chan = str(cmd[1]) chan = str(cmd[1])
if not self.Channel.Is_Channel(chan): if not self.Channel.Is_Channel(chan):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The channel must start with #") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #channel") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} #channel")
return None return None
@@ -817,12 +980,12 @@ class Command():
chan = str(cmd[2]) chan = str(cmd[2])
if not self.Channel.Is_Channel(chan): if not self.Channel.Is_Channel(chan):
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"The channel must start with #") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="The channel must start with #")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL")
return None return None
if self.User.get_nickname(nickname) is None: if self.User.get_nickname(nickname) is None:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Nickname not found !") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nickname not found !")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {str(cmd[0]).upper()} NICKNAME #CHANNEL")
return None return None
@@ -906,8 +1069,10 @@ class Command():
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'get_mode': case 'get_mode':
try:
self.Protocol.send2socket(f'MODE {channel}') self.Protocol.send2socket(f'MODE {fromchannel}')
except Exception as err:
self.Logs.error(f"General Error {err}")
case 'svsjoin': case 'svsjoin':
try: try:
@@ -927,7 +1092,7 @@ class Command():
case 'svspart': case 'svspart':
try: try:
# .svspart nickname #channel # svspart nickname #channel
nickname = str(cmd[1]) nickname = str(cmd[1])
channel = str(cmd[2]) channel = str(cmd[2])
if len(cmd) != 3: if len(cmd) != 3:
@@ -961,18 +1126,19 @@ class Command():
case 'sapart': case 'sapart':
try: try:
# .sapart nickname #channel # .sapart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) < 3: if len(cmd) < 3:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel")
return None return None
nickname = str(cmd[1])
channel = str(cmd[2])
self.Protocol.send_sapart(nick_to_sapart=nickname, channel_name=channel) self.Protocol.send_sapart(nick_to_sapart=nickname, channel_name=channel)
except KeyError as ke: except KeyError as ke:
self.Logs.error(ke) self.Logs.error(ke)
except Exception as err: except Exception as err:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel")
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.error(f'Unknown Error: {str(err)}')
case 'svsnick': case 'svsnick':
try: try:
@@ -1191,4 +1357,4 @@ class Command():
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case _: case _:
pass pass

View File

@@ -4,10 +4,9 @@ import time
import re import re
import psutil import psutil
import requests import requests
from dataclasses import dataclass, fields, field from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from typing import Union, TYPE_CHECKING from typing import Union, TYPE_CHECKING
from sys import exit
import core.definition as df import core.definition as df
# Le module crée devra réspecter quelques conditions # Le module crée devra réspecter quelques conditions
@@ -19,7 +18,7 @@ import core.definition as df
# 4 . Créer vos tables, en utilisant toujours le nom des votre classe en minuscule ==> defender_votre-table # 4 . Créer vos tables, en utilisant toujours le nom des votre classe en minuscule ==> defender_votre-table
# 3. Methode suivantes: # 3. Methode suivantes:
# cmd(self, data:list) # cmd(self, data:list)
# _hcmds(self, user:str, cmd: list) # hcmds(self, user:str, cmd: list)
# unload(self) # unload(self)
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -82,11 +81,16 @@ class Defender():
self.Reputation = ircInstance.Reputation self.Reputation = ircInstance.Reputation
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access')
0: ['code'], self.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
1: ['info', 'autolimit'], self.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'sentinel'] self.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
} self.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
self.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
self.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
self.Irc.build_command(3, self.module_name, 'timer', 'Set or manage timers')
self.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
self.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
# Init the module (Mandatory) # Init the module (Mandatory)
self.__init_module() self.__init_module()
@@ -96,9 +100,6 @@ class Defender():
def __init_module(self) -> None: def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables if needed (Mandatory) # Create you own tables if needed (Mandatory)
self.__create_tables() self.__create_tables()
@@ -141,7 +142,9 @@ class Defender():
self.Base.create_thread(func=self.thread_local_scan) self.Base.create_thread(func=self.thread_local_scan)
self.Base.create_thread(func=self.thread_psutil_scan) self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer) self.Base.create_thread(func=self.thread_reputation_timer)
self.Base.create_thread(func=self.thread_autolimit)
if self.ModConfig.autolimit == 1:
self.Base.create_thread(func=self.thread_autolimit)
if self.ModConfig.reputation == 1: if self.ModConfig.reputation == 1:
self.Protocol.sjoin(self.Config.SALON_JAIL) self.Protocol.sjoin(self.Config.SALON_JAIL)
@@ -149,20 +152,6 @@ class Defender():
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas. """Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -173,31 +162,15 @@ class Defender():
None: Aucun retour n'es attendu None: Aucun retour n'es attendu
""" """
table_channel = '''CREATE TABLE IF NOT EXISTS def_channels ( # table_autoop = '''CREATE TABLE IF NOT EXISTS defender_autoop (
id INTEGER PRIMARY KEY AUTOINCREMENT, # id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT, # datetime TEXT,
channel TEXT # nickname TEXT,
) # channel TEXT
''' # )
# '''
table_config = '''CREATE TABLE IF NOT EXISTS def_config ( # self.Base.db_execute_query(table_autoop)
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
parameter TEXT,
value TEXT
)
'''
table_trusted = '''CREATE TABLE IF NOT EXISTS def_trusted (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
user TEXT,
host TEXT,
vhost TEXT
)
'''
# self.Base.db_execute_query(table_channel)
# self.Base.db_execute_query(table_config) # self.Base.db_execute_query(table_config)
# self.Base.db_execute_query(table_trusted) # self.Base.db_execute_query(table_trusted)
return None return None
@@ -227,7 +200,7 @@ class Defender():
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
"""Cette methode sera executée a chaque désactivation ou """Cette methode sera executée a chaque désactivation ou
rechargement de module rechargement de module
""" """
@@ -255,7 +228,7 @@ class Defender():
exec_query = self.Base.db_execute_query(query, {"user": nickname}) exec_query = self.Base.db_execute_query(query, {"user": nickname})
response = exec_query.fetchone() response = exec_query.fetchone()
if not response is None: if response is not None:
q_insert = "INSERT INTO def_trusted (datetime, user, host, vhost) VALUES (?, ?, ?, ?)" q_insert = "INSERT INTO def_trusted (datetime, user, host, vhost) VALUES (?, ?, ?, ?)"
mes_donnees = {'datetime': self.Base.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'} mes_donnees = {'datetime': self.Base.get_datetime(), 'user': nickname, 'host': '*', 'vhost': '*'}
exec_query = self.Base.db_execute_query(q_insert, mes_donnees) exec_query = self.Base.db_execute_query(q_insert, mes_donnees)
@@ -301,7 +274,8 @@ class Defender():
# Convertir la date enregistrée dans UID_DB en un objet {datetime} # Convertir la date enregistrée dans UID_DB en un objet {datetime}
connected_time_string = get_user.connexion_datetime connected_time_string = get_user.connexion_datetime
if type(connected_time_string) == datetime:
if isinstance(connected_time_string, datetime):
connected_time = connected_time_string connected_time = connected_time_string
else: else:
connected_time = datetime.strptime(connected_time_string, "%Y-%m-%d %H:%M:%S.%f") connected_time = datetime.strptime(connected_time_string, "%Y-%m-%d %H:%M:%S.%f")
@@ -386,7 +360,6 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG dchanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc nogc = self.Config.COLORS.nogc
salon_jail = self.Config.SALON_JAIL salon_jail = self.Config.SALON_JAIL
@@ -480,7 +453,7 @@ class Defender():
unixtime = self.Base.get_unixtime() unixtime = self.Base.get_unixtime()
get_diff_secondes = 0 get_diff_secondes = 0
if not get_detected_uid in self.flood_system: if get_detected_uid not in self.flood_system:
self.flood_system[get_detected_uid] = { self.flood_system[get_detected_uid] = {
'nbr_msg': 0, 'nbr_msg': 0,
'first_msg_time': unixtime 'first_msg_time': unixtime
@@ -694,7 +667,7 @@ class Defender():
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
if not 'data' in decodedResponse: if 'data' not in decodedResponse:
return None return None
result = { result = {
@@ -707,7 +680,6 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc nogc = self.Config.COLORS.nogc
# pseudo!ident@host # pseudo!ident@host
@@ -780,7 +752,6 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc nogc = self.Config.COLORS.nogc
url = f'https://freeipapi.com/api/json/{remote_ip}' url = f'https://freeipapi.com/api/json/{remote_ip}'
@@ -797,7 +768,7 @@ class Defender():
status_code = response.status_code status_code = response.status_code
if status_code == 429: if status_code == 429:
self.Logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.') self.Logs.warning('Too Many Requests - The rate limit for the API has been exceeded.')
return None return None
elif status_code != 200: elif status_code != 200:
self.Logs.warning(f'status code = {str(status_code)}') self.Logs.warning(f'status code = {str(status_code)}')
@@ -873,10 +844,9 @@ class Defender():
service_id = self.Config.SERVICE_ID service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.COLORS.red color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
nogc = self.Config.COLORS.nogc nogc = self.Config.COLORS.nogc
url = f"https://developers18334.cloudfilt.com/" url = "https://developers18334.cloudfilt.com/"
data = { data = {
'ip': remote_ip, 'ip': remote_ip,
@@ -941,7 +911,7 @@ class Defender():
def thread_autolimit(self) -> None: def thread_autolimit(self) -> None:
if self.ModConfig.autolimit == 0: if self.ModConfig.autolimit == 0:
self.Logs.debug(f"autolimit deactivated ... canceling the thread") self.Logs.debug("autolimit deactivated ... canceling the thread")
return None return None
while self.Irc.autolimit_started: while self.Irc.autolimit_started:
@@ -958,7 +928,7 @@ class Defender():
while self.autolimit_isRunning: while self.autolimit_isRunning:
if self.ModConfig.autolimit == 0: if self.ModConfig.autolimit == 0:
self.Logs.debug(f"autolimit deactivated ... stopping the current thread") self.Logs.debug("autolimit deactivated ... stopping the current thread")
break break
for chan in self.Channel.UID_CHANNEL_DB: for chan in self.Channel.UID_CHANNEL_DB:
@@ -967,14 +937,14 @@ class Defender():
self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + self.ModConfig.autolimit_amount}") self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + self.ModConfig.autolimit_amount}")
chan_copy["uids_count"] = len(chan.uids) chan_copy["uids_count"] = len(chan.uids)
if not chan.name in chan_list: if chan.name not in chan_list:
chan_list.append(chan.name) chan_list.append(chan.name)
chanObj_copy.append({"name": chan.name, "uids_count": 0}) chanObj_copy.append({"name": chan.name, "uids_count": 0})
# Verifier si un salon a été vidé # Verifier si un salon a été vidé
current_chan_in_list = [d.name for d in self.Channel.UID_CHANNEL_DB] current_chan_in_list = [d.name for d in self.Channel.UID_CHANNEL_DB]
for c in chan_list: for c in chan_list:
if not c in current_chan_in_list: if c not in current_chan_in_list:
chan_list.remove(c) chan_list.remove(c)
# Si c'est la premiere execution # Si c'est la premiere execution
@@ -1008,7 +978,7 @@ class Defender():
match cmd[1]: match cmd[1]:
case 'REPUTATION': case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118 # :001 REPUTATION 8.8.8.8 118
try: try:
self.reputation_first_connexion['ip'] = cmd[2] self.reputation_first_connexion['ip'] = cmd[2]
self.reputation_first_connexion['score'] = cmd[3] self.reputation_first_connexion['score'] = cmd[3]
@@ -1030,11 +1000,18 @@ class Defender():
case 'MODE': case 'MODE':
# ['...', ':001XSCU0Q', 'MODE', '#jail', '+b', '~security-group:unknown-users'] # ['...', ':001XSCU0Q', 'MODE', '#jail', '+b', '~security-group:unknown-users']
# ['@unrealircd.org/...', ':001C0MF01', 'MODE', '#services', '+l', '1']
channel = str(cmd[3]) channel = str(cmd[3])
mode = str(cmd[4]) mode = str(cmd[4])
group_to_check = str(cmd[5:]) group_to_check = str(cmd[5:])
group_to_unban = '~security-group:unknown-users' group_to_unban = '~security-group:unknown-users'
if self.ModConfig.autolimit == 1:
if mode == '+l' or mode == '-l':
chan = self.Channel.get_Channel(channel)
self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + self.ModConfig.autolimit_amount}")
if self.Config.SALON_JAIL == channel: if self.Config.SALON_JAIL == channel:
if mode == '+b' and group_to_unban in group_to_check: if mode == '+b' and group_to_unban in group_to_check:
self.Protocol.send2socket(f":{service_id} MODE {self.Config.SALON_JAIL} -b ~security-group:unknown-users") self.Protocol.send2socket(f":{service_id} MODE {self.Config.SALON_JAIL} -b ~security-group:unknown-users")
@@ -1059,12 +1036,12 @@ class Defender():
_User = self.User.get_User(str(cmd[7])) _User = self.User.get_User(str(cmd[7]))
# If user is not service or IrcOp then scan them # If user is not service or IrcOp then scan them
if not re.match(fr'^.*[S|o?].*$', _User.umodes): if not re.match(r'^.*[S|o?].*$', _User.umodes):
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and _User.remote_ip not in self.Config.WHITELISTED_IP else None
if _User is None: if _User is None:
self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why') self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why')
@@ -1075,9 +1052,9 @@ class Defender():
if self.Config.DEFENDER_INIT == 0: if self.Config.DEFENDER_INIT == 0:
# Si le user n'es pas un service ni un IrcOP # Si le user n'es pas un service ni un IrcOP
if not re.match(fr'^.*[S|o?].*$', _User.umodes): if not re.match(r'^.*[S|o?].*$', _User.umodes):
if reputation_flag == 1 and _User.score_connexion <= reputation_seuil: if reputation_flag == 1 and _User.score_connexion <= reputation_seuil:
currentDateTime = self.Base.get_datetime() # currentDateTime = self.Base.get_datetime()
self.Reputation.insert( self.Reputation.insert(
self.Loader.Definition.MReputation( self.Loader.Definition.MReputation(
**_User.__dict__, **_User.__dict__,
@@ -1107,7 +1084,7 @@ class Defender():
self.Protocol.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users") self.Protocol.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users")
self.Protocol.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") self.Protocol.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
if not get_reputation is None: if get_reputation is not None:
isWebirc = get_reputation.isWebirc isWebirc = get_reputation.isWebirc
if not isWebirc: if not isWebirc:
@@ -1187,7 +1164,7 @@ class Defender():
get_user_reputation = self.Reputation.get_Reputation(final_UID) get_user_reputation = self.Reputation.get_Reputation(final_UID)
if not get_user_reputation is None: if get_user_reputation is not None:
final_nickname = get_user_reputation.nickname final_nickname = get_user_reputation.nickname
for chan in self.Channel.UID_CHANNEL_DB: for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_salon and ban_all_chan == 1: if chan.name != jail_salon and ban_all_chan == 1:
@@ -1201,7 +1178,7 @@ class Defender():
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
fromuser = user fromuser = user
@@ -1231,12 +1208,12 @@ class Defender():
case 'show_reputation': case 'show_reputation':
if not self.Reputation.UID_REPUTATION_DB: if not self.Reputation.UID_REPUTATION_DB:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" No one is suspected") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="No one is suspected")
for suspect in self.Reputation.UID_REPUTATION_DB: for suspect in self.Reputation.UID_REPUTATION_DB:
self.Protocol.send_notice(nick_from=dnickname, self.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser, nick_to=fromuser,
msg=f" Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score_connexion} | Secret code: {suspect.secret_code} | Connected on: {suspect.connected_datetime}") msg=f" Uid: {suspect.uid} | Nickname: {suspect.nickname} | Reputation: {suspect.score_connexion} | Secret code: {suspect.secret_code} | Connected on: {suspect.connexion_datetime}")
case 'code': case 'code':
try: try:
@@ -1299,6 +1276,10 @@ class Defender():
try: try:
# autolimit on # autolimit on
# autolimit set [amount] [interval] # autolimit set [amount] [interval]
if len(cmd) < 2:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]")
return None
arg = str(cmd[1]).lower() arg = str(cmd[1]).lower()
@@ -1332,12 +1313,12 @@ class Defender():
) )
case _: case _:
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON", nickname=fromuser) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON")
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]", nickname=fromuser) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]")
except Exception as err: except Exception as err:
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON", nickname=fromuser) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ON")
self.Protocol.send_notice(nick_from=dnickname, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]", nickname=fromuser) self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} SET [AMOUNT] [INTERVAL]")
self.Logs.error(f"Value Error -> {err}") self.Logs.error(f"Value Error -> {err}")
case 'reputation': case 'reputation':
@@ -1522,7 +1503,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.warning(f'{ve}') self.Logs.warning(f'{ve}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" La valeur devrait etre un entier >= 0") self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" La valeur devrait etre un entier >= 0")
case 'proxy_scan': case 'proxy_scan':
@@ -1729,14 +1710,17 @@ class Defender():
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_after_release ==> {self.ModConfig.reputation_score_after_release}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_after_release ==> {self.ModConfig.reputation_score_after_release}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_ban_all_chan ==> {self.ModConfig.reputation_ban_all_chan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_ban_all_chan ==> {self.ModConfig.reputation_ban_all_chan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_timer ==> {self.ModConfig.reputation_timer}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' reputation_timer ==> {self.ModConfig.reputation_timer}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [Proxy_scan]') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' [Proxy_scan]')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.local_scan == 1 else color_red}local_scan{nogc} ==> {self.ModConfig.local_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.local_scan == 1 else color_red}local_scan{nogc} ==> {self.ModConfig.local_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.psutil_scan == 1 else color_red}psutil_scan{nogc} ==> {self.ModConfig.psutil_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.psutil_scan == 1 else color_red}psutil_scan{nogc} ==> {self.ModConfig.psutil_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.abuseipdb_scan == 1 else color_red}abuseipdb_scan{nogc} ==> {self.ModConfig.abuseipdb_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.abuseipdb_scan == 1 else color_red}abuseipdb_scan{nogc} ==> {self.ModConfig.abuseipdb_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.freeipapi_scan == 1 else color_red}freeipapi_scan{nogc} ==> {self.ModConfig.freeipapi_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.freeipapi_scan == 1 else color_red}freeipapi_scan{nogc} ==> {self.ModConfig.freeipapi_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.cloudfilt_scan == 1 else color_red}cloudfilt_scan{nogc} ==> {self.ModConfig.cloudfilt_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.cloudfilt_scan == 1 else color_red}cloudfilt_scan{nogc} ==> {self.ModConfig.cloudfilt_scan}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit{nogc}] ==> {self.ModConfig.autolimit}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit Amount{nogc} ==> {self.ModConfig.autolimit_amount}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit Interval{nogc} ==> {self.ModConfig.autolimit_interval}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.flood == 1 else color_red}Flood{nogc}] ==> {self.ModConfig.flood}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.flood == 1 else color_red}Flood{nogc}] ==> {self.ModConfig.flood}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_action ==> Coming soon') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' flood_action ==> Coming soon')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_message ==> {self.ModConfig.flood_message}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_message ==> {self.ModConfig.flood_message}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_time ==> {self.ModConfig.flood_time}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_time ==> {self.ModConfig.flood_time}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_timer ==> {self.ModConfig.flood_timer}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_timer ==> {self.ModConfig.flood_timer}')
@@ -1748,7 +1732,7 @@ class Defender():
nickoruid = cmd[1] nickoruid = cmd[1]
UserObject = self.User.get_User(nickoruid) UserObject = self.User.get_User(nickoruid)
if not UserObject is None: if UserObject is not None:
channels: list = [] channels: list = []
for chan in self.Channel.UID_CHANNEL_DB: for chan in self.Channel.UID_CHANNEL_DB:
for uid_in_chan in chan.uids: for uid_in_chan in chan.uids:
@@ -1784,10 +1768,10 @@ class Defender():
if activation == 'on': if activation == 'on':
for chan in self.Channel.UID_CHANNEL_DB: for chan in self.Channel.UID_CHANNEL_DB:
if not chan.name in channel_to_dont_quit: if chan.name not in channel_to_dont_quit:
self.Protocol.send_join_chan(uidornickname=dnickname, channel=chan.name) self.Protocol.send_join_chan(uidornickname=dnickname, channel=chan.name)
if activation == 'off': if activation == 'off':
for chan in self.Channel.UID_CHANNEL_DB: for chan in self.Channel.UID_CHANNEL_DB:
if not chan.name in channel_to_dont_quit: if chan.name not in channel_to_dont_quit:
self.Protocol.part(uidornickname=dnickname, channel=chan.name) self.Protocol.part(uidornickname=dnickname, channel=chan.name)
self.join_saved_channels() self.join_saved_channels()

View File

@@ -42,9 +42,8 @@ class Jsonrpc():
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]')
1: ['jsonrpc', 'jruser'] self.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC')
}
# Init the module # Init the module
self.__init_module() self.__init_module()
@@ -54,9 +53,8 @@ class Jsonrpc():
def __init_module(self) -> None: def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
logging.getLogger('websockets').setLevel(logging.WARNING) logging.getLogger('websockets').setLevel(logging.WARNING)
logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL)
# Create you own tables (Mandatory) # Create you own tables (Mandatory)
# self.__create_tables() # self.__create_tables()
@@ -65,13 +63,6 @@ class Jsonrpc():
self.__load_module_configuration() self.__load_module_configuration()
# End of mandatory methods you can start your customization # # End of mandatory methods you can start your customization #
# self.UnrealIrcdRpcLive: Live = Live(
# req_method='unixsocket',
# path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
# callback_object_instance=self,
# callback_method_name='callback_sent_to_irc'
# )
self.UnrealIrcdRpcLive: Live = Live( self.UnrealIrcdRpcLive: Live = Live(
req_method='websocket', req_method='websocket',
url=self.Config.JSONRPC_URL, url=self.Config.JSONRPC_URL,
@@ -109,20 +100,6 @@ class Jsonrpc():
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas. """Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -205,7 +182,7 @@ class Jsonrpc():
""" """
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
if self.UnrealIrcdRpcLive.Error.code != -1: if self.UnrealIrcdRpcLive.Error.code != -1:
self.UnrealIrcdRpcLive.unsubscribe() self.UnrealIrcdRpcLive.unsubscribe()
return None return None
@@ -214,7 +191,7 @@ class Jsonrpc():
return None return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
@@ -235,6 +212,11 @@ class Jsonrpc():
match option: match option:
case 'on': case 'on':
# for logger_name, logger in logging.root.manager.loggerDict.items():
# if isinstance(logger, logging.Logger):
# self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{logger_name} - {logger.level}")
for thread in self.Base.running_threads: for thread in self.Base.running_threads:
if thread.getName() == 'thread_start_jsonrpc': if thread.getName() == 'thread_start_jsonrpc':
if thread.is_alive(): if thread.is_alive():

367
mods/mod_nickserv.py Normal file
View File

@@ -0,0 +1,367 @@
import socket
import json
import time
import re
import psutil
import requests
from dataclasses import dataclass
from datetime import datetime
from typing import Union, TYPE_CHECKING
from core.classes import user
import core.definition as df
if TYPE_CHECKING:
from core.irc import Irc
class Nickserv():
@dataclass
class ModConfModel:
active: bool = False
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Loader Object to the module (Mandatory)
self.Loader = ircInstance.Loader
# Add server protocol Object to the module (Mandatory)
self.Protocol = ircInstance.Protocol
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
# Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Client object to the module (Mandatory)
self.Client = ircInstance.Client
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
# Add Reputation object to the module (Optional)
self.Reputation = ircInstance.Reputation
# Check if NickServ already exist
# for user_obj in self.User.UID_DB:
# if user_obj.nickname.lower() == 'nickserv':
# self.Logs.warning(f"The NickServ service already exist, impossible to load 2 NickServ services")
# return None
# Create module commands (Mandatory)
self.Irc.build_command(0, self.module_name, 'register', f'Register your nickname /msg NickServ REGISTER <password> <email>')
self.Irc.build_command(0, self.module_name, 'identify', f'Identify yourself with your password /msg NickServ IDENTIFY <account> <password>')
self.Irc.build_command(0, self.module_name, 'logout', 'Reverse the effect of the identify command')
# Init the module (Mandatory)
self.__init_module()
# Log the module
self.Logs.debug(f'-- Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Create you own tables if needed (Mandatory)
self.__create_tables()
# Load module configuration (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
self.build_nickserv_information()
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
Args:
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
Returns:
None: Aucun retour n'es attendu
"""
# table_autoop = '''CREATE TABLE IF NOT EXISTS defender_autoop (
# id INTEGER PRIMARY KEY AUTOINCREMENT,
# datetime TEXT,
# nickname TEXT,
# channel TEXT
# )
# '''
# self.Base.db_execute_query(table_autoop)
# self.Base.db_execute_query(table_config)
# self.Base.db_execute_query(table_trusted)
return None
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Variable qui va contenir les options de configuration du module Defender
self.ModConfig = self.ModConfModel(active=True)
# Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str):
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def build_nickserv_information(self) -> None:
self.NICKSERV_NICKNAME = 'NickServ'
if self.User.is_exist(self.NICKSERV_NICKNAME):
# If NickServ already exist just return None
self.NICKSERV_UID = self.User.get_uid(self.NICKSERV_NICKNAME)
self.Irc.Settings.NICKSERV_UID = self.NICKSERV_UID
return None
self.NICKSERV_UID = self.Config.SERVEUR_ID + self.Base.get_random(6)
nickserv_uid = self.NICKSERV_UID
self.Irc.Settings.NICKSERV_UID = self.NICKSERV_UID
self.Protocol.send_uid(
nickname=self.NICKSERV_NICKNAME, username='NickServ', hostname='nickserv.deb.biz.st', uid=nickserv_uid, umodes='+ioqBS',
vhost='nickserv.deb.biz.st', remote_ip='127.0.0.1', realname='Nick Service'
)
new_user_obj = self.Loader.Definition.MUser(uid=nickserv_uid, nickname=self.NICKSERV_NICKNAME, username='NickServ', realname='Nick Service',
hostname='nickserv.deb.biz.st', umodes='+ioqBS', vhost='nickserv.deb.biz.st', remote_ip='127.0.0.1')
self.User.insert(new_user_obj)
self.Protocol.send_sjoin(nickserv_uid, self.Config.SERVICE_CHANLOG)
return None
def timer_force_change_nickname(self, nickname: str) -> None:
client_obj = self.Client.get_Client(nickname)
if client_obj is None:
self.Protocol.send_svs_nick(nickname, self.Base.get_random(8))
return None
def unload(self, reloading: bool = False) -> None:
"""Cette methode sera executée a chaque désactivation ou
rechargement de module
"""
if reloading:
pass
else:
# NickServ will be disconnected
self.Protocol.send_quit(self.NICKSERV_UID, f'Stopping {self.module_name} module')
del self.NICKSERV_NICKNAME
del self.NICKSERV_UID
return None
def cmd(self, data: list[str]) -> None:
try:
service_id = self.Config.SERVICE_ID # Defender serveur id
original_serv_response = list(data).copy()
parsed_protocol = self.Protocol.parse_server_msg(data.copy())
match parsed_protocol:
case 'UID':
try:
# ['@...', ':001', 'UID', 'adator', '0', '1733865747', '...', '192.168.1.10', '00118YU01', '0', '+iotwxz', 'netadmin.irc.dev.local', '745A32E5.421A4718.F33AB65A.IP', 'wKgBCg==', ':...']
get_uid = str(original_serv_response[8])
user_obj = self.User.get_User(get_uid)
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=user_obj.nickname,
msg='NickServ is here to protect your nickname'
)
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=user_obj.nickname,
msg='To register a nickname tape /msg NickServ register password email'
)
if self.Client.db_is_account_exist(user_obj.nickname):
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=user_obj.nickname, msg='The nickname is protected please login to keep it')
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=user_obj.nickname, msg=f'/msg {self.NICKSERV_NICKNAME} IDENTIFY <ACCOUNT> <PASSWORD>')
self.Base.create_timer(60, self.timer_force_change_nickname, (user_obj.nickname, ))
except IndexError as ie:
self.Logs.error(f'cmd reputation: index error: {ie}')
case None:
self.Logs.debug(f"** TO BE HANDLE {original_serv_response} {__name__}")
except KeyError as ke:
self.Logs.error(f"{ke} / {original_serv_response} / length {str(len(original_serv_response))}")
except IndexError as ie:
self.Logs.error(f"{ie} / {original_serv_response} / length {str(len(original_serv_response))}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
def hcmds(self, user: str, channel: str, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
fromuser = user
fromchannel = channel if self.Channel.Is_Channel(channel) else None
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
dchanlog = self.Config.SERVICE_CHANLOG # Defender chan log
dumodes = self.Config.SERVICE_UMODES # Les modes de Defender
service_id = self.Config.SERVICE_ID # Defender serveur id
match command:
case 'register':
# Register PASSWORD EMAIL
try:
if len(cmd) < 3:
self.Protocol.send_notice(
nick_from=self.NICKSERV_NICKNAME,
nick_to=fromuser,
msg=f'/msg {self.NICKSERV_NICKNAME} {command.upper()} <PASSWORD> <EMAIL>'
)
return None
password = cmd[1]
email = cmd[2]
if not self.Base.is_valid_email(email_to_control=email):
self.Protocol.send_notice(
nick_from=self.NICKSERV_NICKNAME,
nick_to=fromuser,
msg='The email is not valid. You must provide a valid email address (first.name@email.extension)'
)
return None
user_obj = self.User.get_User(fromuser)
if user_obj is None:
self.Logs.error(f"Nickname ({fromuser}) doesn't exist, it is impossible to register this nickname")
return None
# If the account already exist.
if self.Client.db_is_account_exist(fromuser):
self.Protocol.send_notice(
nick_from=self.NICKSERV_NICKNAME,
nick_to=fromuser,
msg=f"Your account already exist, please try to login instead /msg {self.NICKSERV_NICKNAME} IDENTIFY <account> <password>"
)
return None
# If the account doesn't exist then insert into database
data_to_record = {
'createdOn': self.Base.get_datetime(), 'account': fromuser,
'nickname': user_obj.nickname, 'hostname': user_obj.hostname, 'vhost': user_obj.vhost, 'realname': user_obj.realname, 'email': email,
'password': self.Base.crypt_password(password=password), 'level': 0
}
insert_to_db = self.Base.db_execute_query(f"""
INSERT INTO {self.Config.TABLE_CLIENT}
(createdOn, account, nickname, hostname, vhost, realname, email, password, level)
VALUES
(:createdOn, :account, :nickname, :hostname, :vhost, :realname, :email, :password, :level)
""", data_to_record)
if insert_to_db.rowcount > 0:
self.Protocol.send_notice(
nick_from=self.NICKSERV_NICKNAME,
nick_to=fromuser,
msg=f"You have register your nickname successfully"
)
return None
except ValueError as ve:
self.Logs.error(f"Value Error : {ve}")
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f" {self.Config.SERVICE_PREFIX}{command.upper()} <PASSWORD> <EMAIL>")
case 'identify':
try:
# IDENTIFY <ACCOUNT> <PASSWORD>
if len(cmd) < 3:
self.Protocol.send_notice(
nick_from=dnickname,
nick_to=fromuser,
msg=f'/msg {self.NICKSERV_NICKNAME} {command.upper()} <ACCOUNT> <PASSWORD>'
)
return None
account = str(cmd[1]) # account
encrypted_password = self.Base.crypt_password(cmd[2])
user_obj = self.User.get_User(fromuser)
client_obj = self.Client.get_Client(user_obj.uid)
if client_obj is not None:
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"You are already logged in")
return None
db_query = f"SELECT account FROM {self.Config.TABLE_CLIENT} WHERE account = :account AND password = :password"
db_param = {'account': account, 'password': encrypted_password}
exec_query = self.Base.db_execute_query(
db_query,
db_param
)
result_query = exec_query.fetchone()
if result_query:
account = result_query[0]
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"You are now logged in")
user_obj_dict = self.User.get_User_AsDict(fromuser)
client = self.Loader.Definition.MClient(
account=account,
**user_obj_dict
)
self.Client.insert(client)
self.Protocol.send_svs2mode(service_uid=self.NICKSERV_UID, nickname=fromuser, user_mode='+r')
else:
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"Wrong password or account")
except TypeError as te:
self.Logs.error(f"Type Error -> {te}")
except ValueError as ve:
self.Logs.error(f"Value Error -> {ve}")
case 'logout':
try:
# LOGOUT <account>
if len(cmd) < 2:
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"/msg {self.NICKSERV_NICKNAME} {command.upper()} <ACCOUNT>")
return None
user_obj = self.User.get_User(fromuser)
if user_obj is None:
self.Logs.error(f"The User [{fromuser}] is not available in the database")
return None
client_obj = self.Client.get_Client(user_obj.uid)
if client_obj is None:
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg="Nothing to logout. please login first")
return None
self.Protocol.send_svs2mode(service_uid=self.NICKSERV_UID, nickname=fromuser, user_mode='-r')
self.Client.delete(user_obj.uid)
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"You have been logged out successfully")
except ValueError as ve:
self.Logs.error(f"Value Error: {ve}")
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"/msg {self.NICKSERV_NICKNAME} {command.upper()} <account>")
except Exception as err:
self.Logs.error(f"General Error: {err}")
self.Protocol.send_notice(nick_from=self.NICKSERV_NICKNAME, nick_to=fromuser, msg=f"/msg {self.NICKSERV_NICKNAME} {command.upper()} <account>")

View File

@@ -46,12 +46,11 @@ class Test():
self.Reputation = ircInstance.Reputation self.Reputation = ircInstance.Reputation
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command')
0: ['test-command'], self.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command')
1: ['test_level_1'], self.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command')
2: ['test_level_2'], self.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command')
3: ['test_level_3']
}
# Init the module # Init the module
self.__init_module() self.__init_module()
@@ -61,9 +60,6 @@ class Test():
def __init_module(self) -> None: def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables (Mandatory) # Create you own tables (Mandatory)
self.__create_tables() self.__create_tables()
@@ -73,20 +69,6 @@ class Test():
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas. """Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -131,7 +113,7 @@ class Test():
""" """
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
return None return None
@@ -147,7 +129,7 @@ class Test():
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower() command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME

View File

@@ -56,9 +56,7 @@ class Votekick():
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module')
0: ['vote']
}
# Init the module # Init the module
self.__init_module() self.__init_module()
@@ -70,27 +68,11 @@ class Votekick():
# Add admin object to retrieve admin users # Add admin object to retrieve admin users
self.Admin = self.Irc.Admin self.Admin = self.Irc.Admin
self.__set_commands(self.commands_level)
self.__create_tables() self.__create_tables()
self.join_saved_channels() self.join_saved_channels()
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None: def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas. """Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -119,7 +101,7 @@ class Votekick():
self.Base.db_execute_query(table_vote) self.Base.db_execute_query(table_vote)
return None return None
def unload(self) -> None: def unload(self, reloading: bool = False) -> None:
try: try:
for chan in self.VOTE_CHANNEL_DB: for chan in self.VOTE_CHANNEL_DB:
self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name) self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)
@@ -274,7 +256,7 @@ class Votekick():
except Exception as err: except Exception as err:
self.Logs.error(f"General Error: {err}") self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
# cmd is the command starting from the user command # cmd is the command starting from the user command
# full cmd is sending the entire server response # full cmd is sending the entire server response

258
mods/mod_weather.py Normal file
View File

@@ -0,0 +1,258 @@
from urllib import request
import requests
from dataclasses import dataclass
from datetime import datetime
from typing import Union, TYPE_CHECKING
from core.classes import user
import core.definition as df
if TYPE_CHECKING:
from core.irc import Irc
class Weather():
@dataclass
class ModConfModel:
active: bool = False
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Loader Object to the module (Mandatory)
self.Loader = ircInstance.Loader
# Add server protocol Object to the module (Mandatory)
self.Protocol = ircInstance.Protocol
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
# Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Client object to the module (Mandatory)
self.Client = ircInstance.Client
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
# Add Reputation object to the module (Optional)
self.Reputation = ircInstance.Reputation
# Create module commands (Mandatory)
self.Irc.build_command(0, self.module_name, 'meteo', 'Get the meteo of a current city')
# Init the module (Mandatory)
self.__init_module()
# Log the module
self.Logs.debug(f'-- Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Create you own tables if needed (Mandatory)
self.__create_tables()
# Load module configuration (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
self.from_user = None
self.from_channel = None
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
Args:
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
Returns:
None: Aucun retour n'es attendu
"""
# table_autoop = '''CREATE TABLE IF NOT EXISTS defender_autoop (
# id INTEGER PRIMARY KEY AUTOINCREMENT,
# datetime TEXT,
# nickname TEXT,
# channel TEXT
# )
# '''
# self.Base.db_execute_query(table_autoop)
# self.Base.db_execute_query(table_config)
# self.Base.db_execute_query(table_trusted)
return None
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Variable qui va contenir les options de configuration du module Defender
self.ModConfig = self.ModConfModel(active=True)
# Sync the configuration with core configuration (Mandatory)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str):
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def get_geo_information(self, city: str) -> tuple[str, str, float, float]:
"""_summary_
Args:
city (str): The city you want to get
Returns:
tuple[str, str, float, float]: Country Code, City Name Latitude, Longitude
"""
api_key = 'fd36a3f3715c93f6770a13f5a34ae1e3'
geo_api_url = "http://api.openweathermap.org/geo/1.0/direct"
response = requests.get(
url=geo_api_url,
params={'q': city, 'limit': 1, 'appid': api_key}
)
geo_data = response.json()
if not geo_data:
return (None, None, 0, 0)
country_code = geo_data[0]['country']
city_name = geo_data[0]['name']
latitude: float = geo_data[0]['lat']
longitude: float = geo_data[0]['lon']
return (country_code, city_name, latitude, longitude)
def get_meteo_information(self, city: str, to_nickname: str) -> None:
api_key = 'fd36a3f3715c93f6770a13f5a34ae1e3'
meteo_api_url = "https://api.openweathermap.org/data/2.5/weather" #?lat={lat}&lon={lon}&appid={API key}
country_code, city_name, latitude, longitude = self.get_geo_information(city=city)
response_meteo = requests.get(
url=meteo_api_url,
params={'lat': latitude, 'lon': longitude, 'appid': api_key, 'units': "metric"}
)
meteo_data = response_meteo.json()
if not meteo_data or city_name is None:
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=to_nickname,
msg=f"Impossible to find the meteo for [{city}]")
return None
temp_cur = meteo_data['main']['temp']
temp_min = meteo_data['main']['temp_min']
temp_max = meteo_data['main']['temp_max']
if self.from_channel is None:
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=to_nickname,
msg=f"Weather of {city_name} located in {country_code} is:")
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=to_nickname,
msg=f"Current temperature: {temp_cur} °C")
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=to_nickname,
msg=f"Minimum temperature: {temp_min} °C")
self.Protocol.send_notice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=to_nickname,
msg=f"Maximum temperature: {temp_max} °C")
else:
self.Protocol.send_priv_msg(nick_from=self.Config.SERVICE_NICKNAME, channel=self.from_channel,
msg=f"Weather of {city_name} located in {country_code} is:")
self.Protocol.send_priv_msg(nick_from=self.Config.SERVICE_NICKNAME, channel=self.from_channel,
msg=f"Current temperature: {temp_cur} °C")
self.Protocol.send_priv_msg(nick_from=self.Config.SERVICE_NICKNAME, channel=self.from_channel,
msg=f"Minimum temperature: {temp_min} °C")
self.Protocol.send_priv_msg(nick_from=self.Config.SERVICE_NICKNAME, channel=self.from_channel,
msg=f"Maximum temperature: {temp_max} °C")
return None
def unload(self, reloading: bool = False) -> None:
"""Cette methode sera executée a chaque désactivation ou
rechargement de module
"""
if reloading:
pass
else:
self.Protocol.send_quit(self.NICKSERV_UID, f'Stopping {self.module_name} module')
return None
def cmd(self, data: list[str]) -> None:
try:
service_id = self.Config.SERVICE_ID # Defender serveur id
original_serv_response = list(data).copy()
parsed_protocol = self.Protocol.parse_server_msg(data.copy())
match parsed_protocol:
case 'UID':
try:
pass
except IndexError as ie:
self.Logs.error(f'cmd reputation: index error: {ie}')
case None:
self.Logs.debug(f"** TO BE HANDLE {original_serv_response} {__name__}")
except KeyError as ke:
self.Logs.error(f"{ke} / {original_serv_response} / length {str(len(original_serv_response))}")
except IndexError as ie:
self.Logs.error(f"{ie} / {original_serv_response} / length {str(len(original_serv_response))}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
def hcmds(self, user: str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
fromuser = user
fromchannel = channel if self.Channel.Is_Channel(channel) else None
self.from_channel = fromchannel
self.from_user = fromuser
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
dchanlog = self.Config.SERVICE_CHANLOG # Defender chan log
dumodes = self.Config.SERVICE_UMODES # Les modes de Defender
service_id = self.Config.SERVICE_ID # Defender serveur id
match command:
case 'meteo':
try:
# meteo <CITY>
if len(cmd) < 2:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {self.Config.SERVICE_NICKNAME} {command.upper()} METEO <City name>")
return None
city = str(' '.join(cmd[1:]))
to_nickname = fromuser
self.Base.create_thread(self.get_meteo_information, (city, to_nickname))
except TypeError as te:
self.Logs.error(f"Type Error -> {te}")
except ValueError as ve:
self.Logs.error(f"Value Error -> {ve}")

View File

@@ -1,9 +1,9 @@
{ {
"version": "6.0.0", "version": "6.1.5",
"requests": "2.32.3", "requests": "2.32.3",
"psutil": "6.0.0", "psutil": "6.0.0",
"unrealircd_rpc_py": "1.0.6", "unrealircd_rpc_py": "1.0.7",
"sqlalchemy": "2.0.35", "sqlalchemy": "2.0.35",
"faker": "30.1.0" "faker": "30.1.0"
} }