First Version 6

This commit is contained in:
adator
2024-11-01 23:52:22 +01:00
parent 860e265979
commit 9d9ede0e80
27 changed files with 3331 additions and 2432 deletions

View File

@@ -1,506 +0,0 @@
from dataclasses import dataclass, field
from datetime import datetime
from typing import Union
from core.base import Base
class User:
@dataclass
class UserModel:
uid: str
nickname: str
username: str
realname: str
hostname: str
umodes: str
vhost: str
isWebirc: bool
isWebsocket: bool
remote_ip: str
score_connexion: int
geoip: str = None
connexion_datetime: datetime = field(default=datetime.now())
UID_DB: list[UserModel] = []
def __init__(self, Base: Base) -> None:
self.log = Base.logs
pass
def insert(self, newUser: UserModel) -> bool:
"""Insert a new User object
Args:
newUser (UserModel): New userModel object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_DB:
if record.uid == newUser.uid:
# If the user exist then return False and do not go further
exist = True
self.log.debug(f'{record.uid} already exist')
return result
if not exist:
self.UID_DB.append(newUser)
result = True
self.log.debug(f'New User Created: ({newUser})')
if not result:
self.log.critical(f'The User Object was not inserted {newUser}')
return result
def update(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
"""
result = False
for record in self.UID_DB:
if record.uid == uid:
# If the user exist then update and return True and do not go further
record.nickname = newNickname
result = True
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
if not result:
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
return result
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
"""
result = False
for record in self.UID_DB:
if record.uid == uid:
# If the user exist then remove and return True and do not go further
self.UID_DB.remove(record)
result = True
self.log.debug(f'UID ({record.uid}) has been deleted')
return result
if not result:
self.log.critical(f'The UID {uid} was not deleted')
return result
def get_User(self, uidornickname: str) -> Union[UserModel, None]:
"""Get The User Object model
Args:
uidornickname (str): UID or Nickname
Returns:
UserModel|None: The UserModel Object | None
"""
User = None
for record in self.UID_DB:
if record.uid == uidornickname:
User = record
elif record.nickname == uidornickname:
User = record
self.log.debug(f'Search {uidornickname} -- result = {User}')
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
"""
uid = None
for record in self.UID_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
self.log.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
return 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
"""
nickname = None
for record in self.UID_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
self.log.debug(f'The value to check {uidornickname} -> {nickname}')
return nickname
class Admin:
@dataclass
class AdminModel:
uid: str
nickname: str
username: str
hostname: str
umodes: str
vhost: str
level: int
connexion_datetime: datetime = field(default=datetime.now())
UID_ADMIN_DB: list[AdminModel] = []
def __init__(self, Base: Base) -> None:
self.log = Base.logs
pass
def insert(self, newAdmin: AdminModel) -> bool:
result = False
exist = False
for record in self.UID_ADMIN_DB:
if record.uid == newAdmin.uid:
# If the admin exist then return False and do not go further
exist = True
self.log.debug(f'{record.uid} already exist')
return result
if not exist:
self.UID_ADMIN_DB.append(newAdmin)
result = True
self.log.debug(f'UID ({newAdmin.uid}) has been created')
if not result:
self.log.critical(f'The User Object was not inserted {newAdmin}')
return result
def update(self, uid: str, newNickname: str) -> bool:
result = False
for record in self.UID_ADMIN_DB:
if record.uid == uid:
# If the admin exist, update and do not go further
record.nickname = newNickname
result = True
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
if not result:
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
return result
def delete(self, uid: str) -> bool:
result = False
for record in self.UID_ADMIN_DB:
if record.uid == uid:
# If the admin exist, delete and do not go further
self.UID_ADMIN_DB.remove(record)
result = True
self.log.debug(f'UID ({record.uid}) has been created')
return result
if not result:
self.log.critical(f'The UID {uid} was not deleted')
return result
def get_Admin(self, uidornickname: str) -> Union[AdminModel, None]:
Admin = None
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
Admin = record
elif record.nickname == uidornickname:
Admin = record
self.log.debug(f'Search {uidornickname} -- result = {Admin}')
return Admin
def get_uid(self, uidornickname:str) -> Union[str, None]:
uid = None
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
self.log.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
return uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
nickname = None
for record in self.UID_ADMIN_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
self.log.debug(f'The value {uidornickname} -- {nickname}')
return nickname
class Channel:
@dataclass
class ChannelModel:
name: str
"""### Channel name
It include the #"""
uids: list
"""### List of UID available in the channel
including their modes ~ @ % + *
Returns:
list: The list of UID's including theirs modes
"""
UID_CHANNEL_DB: list[ChannelModel] = []
"""List that contains all the Channels objects (ChannelModel)
"""
def __init__(self, Base: Base) -> None:
self.log = Base.logs
self.Base = Base
pass
def insert(self, newChan: ChannelModel) -> bool:
"""This method will insert a new channel and if the channel exist it will update the user list (uids)
Args:
newChan (ChannelModel): The channel model object
Returns:
bool: True if new channel, False if channel exist (However UID could be updated)
"""
result = False
exist = False
for record in self.UID_CHANNEL_DB:
if record.name == newChan.name:
# If the channel exist, update the user list and do not go further
exist = True
self.log.debug(f'{record.name} already exist')
for user in newChan.uids:
record.uids.append(user)
# Supprimer les doublons
del_duplicates = list(set(record.uids))
record.uids = del_duplicates
self.log.debug(f'Updating a new UID to the channel {record}')
return result
if not exist:
# If the channel don't exist, then create it
self.UID_CHANNEL_DB.append(newChan)
result = True
self.log.debug(f'New Channel Created: ({newChan})')
if not result:
self.log.critical(f'The Channel Object was not inserted {newChan}')
return result
def delete(self, name: str) -> bool:
result = False
for record in self.UID_CHANNEL_DB:
if record.name == name:
# If the channel exist, then remove it and return True.
# As soon as the channel found, return True and stop the loop
self.UID_CHANNEL_DB.remove(record)
result = True
self.log.debug(f'Channel ({record.name}) has been created')
return result
if not result:
self.log.critical(f'The Channel {name} was not deleted')
return result
def delete_user_from_channel(self, chan_name: str, uid:str) -> bool:
try:
result = False
for record in self.UID_CHANNEL_DB:
if record.name == chan_name:
for user_id in record.uids:
if self.Base.clean_uid(user_id) == uid:
record.uids.remove(user_id)
self.log.debug(f'The UID {uid} has been removed, here is the new object: {record}')
result = True
for record in self.UID_CHANNEL_DB:
if not record.uids:
self.UID_CHANNEL_DB.remove(record)
self.log.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
return result
except ValueError as ve:
self.log.error(f'{ve}')
def delete_user_from_all_channel(self, uid:str) -> bool:
try:
result = False
for record in self.UID_CHANNEL_DB:
for user_id in record.uids:
if self.Base.clean_uid(user_id) == self.Base.clean_uid(uid):
record.uids.remove(user_id)
self.log.debug(f'The UID {uid} has been removed, here is the new object: {record}')
result = True
for record in self.UID_CHANNEL_DB:
if not record.uids:
self.UID_CHANNEL_DB.remove(record)
self.log.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
return result
except ValueError as ve:
self.log.error(f'{ve}')
def get_Channel(self, name: str) -> Union[ChannelModel, None]:
Channel = None
for record in self.UID_CHANNEL_DB:
if record.name == name:
Channel = record
self.log.debug(f'Search {name} -- result = {Channel}')
return Channel
class Clones:
@dataclass
class CloneModel:
alive: bool
nickname: str
username: str
realname: str
channels: list
vhost: str = None
init: bool = True
connected: bool = False
UID_CLONE_DB: list[CloneModel] = []
def __init__(self, Base: Base) -> None:
self.log = Base.logs
def insert(self, newCloneObject: CloneModel) -> bool:
"""Create new Clone object
Args:
newCloneObject (CloneModel): New CloneModel object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_CLONE_DB:
if record.nickname == newCloneObject.nickname:
# If the user exist then return False and do not go further
exist = True
self.log.debug(f'{record.nickname} already exist')
return result
if not exist:
self.UID_CLONE_DB.append(newCloneObject)
result = True
self.log.debug(f'New Clone Object Created: ({newCloneObject})')
if not result:
self.log.critical(f'The Clone Object was not inserted {newCloneObject}')
return result
def delete(self, nickname: str) -> bool:
"""Delete the Clone Object starting from the nickname
Args:
nickname (str): nickname of the clone
Returns:
bool: True if deleted
"""
result = False
for record in self.UID_CLONE_DB:
if record.nickname == nickname:
# If the user exist then remove and return True and do not go further
self.UID_CLONE_DB.remove(record)
result = True
self.log.debug(f'The clone ({record.nickname}) has been deleted')
return result
if not result:
self.log.critical(f'The UID {nickname} was not deleted')
return result
def exists(self, nickname: str) -> bool:
"""Check if the nickname exist
Args:
nickname (str): Nickname of the clone
Returns:
bool: True if the nickname exist
"""
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
response = True
return response
def kill(self, nickname:str) -> bool:
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
cloneObject.alive = False # Kill the clone
response = True
return response

View File

@@ -13,27 +13,39 @@ import ast
import requests
from dataclasses import fields
from typing import Union, Literal
from typing import Union, Literal, TYPE_CHECKING
from base64 import b64decode, b64encode
from datetime import datetime, timedelta, timezone
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
from core.loadConf import ConfigDataModel
from core.definition import MConfig
if TYPE_CHECKING:
from core.classes.settings import Settings
class Base:
def __init__(self, Config: ConfigDataModel) -> None:
def __init__(self, Config: MConfig, settings: 'Settings') -> None:
self.Config = Config # Assigner l'objet de configuration
self.Settings: Settings = settings
self.init_log_system() # Demarrer le systeme de log
self.check_for_new_version(True) # Verifier si une nouvelle version est disponible
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
self.running_sockets: list[socket.socket] = [] # Les sockets ouvert
self.periodic_func:dict[object] = {} # Liste des fonctions en attentes
# Liste des timers en cours
self.running_timers:list[threading.Timer] = self.Settings.RUNNING_TIMERS
self.lock = threading.RLock() # Création du lock
# Liste des threads en cours
self.running_threads:list[threading.Thread] = self.Settings.RUNNING_THREADS
# Les sockets ouvert
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
# Liste des fonctions en attentes
self.periodic_func:dict[object] = self.Settings.PERIODIC_FUNC
# Création du lock
self.lock = self.Settings.LOCK
self.install: bool = False # Initialisation de la variable d'installation
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
@@ -48,16 +60,15 @@ class Base:
with open(version_filename, 'r') as version_data:
current_version:dict[str, str] = json.load(version_data)
self.Config.current_version = current_version['version']
self.Config.CURRENT_VERSION = current_version['version']
return None
def __get_latest_defender_version(self) -> None:
try:
self.logs.debug(f'Looking for a new version available on Github')
# print(f'===> Looking for a new version available on Github')
token = ''
json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json'
json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json'
headers = {
'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
@@ -71,7 +82,7 @@ class Base:
response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json()
# self.LATEST_DEFENDER_VERSION = json_response["version"]
self.Config.latest_version = json_response['version']
self.Config.LATEST_VERSION = json_response['version']
return None
except requests.HTTPError as err:
@@ -99,8 +110,8 @@ class Base:
self.__get_latest_defender_version()
isNewVersion = False
latest_version = self.Config.latest_version
current_version = self.Config.current_version
latest_version = self.Config.LATEST_VERSION
current_version = self.Config.CURRENT_VERSION
curr_major , curr_minor, curr_patch = current_version.split('.')
last_major, last_minor, last_patch = latest_version.split('.')
@@ -120,6 +131,10 @@ class Base:
return isNewVersion
except ValueError as ve:
self.logs.error(f'Impossible to convert in version number : {ve}')
except AttributeError as atterr:
self.logs.error(f'Attribute Error: {atterr}')
except Exception as err:
self.logs.error(f'General Error: {err}')
def get_unixtime(self) -> int:
"""
@@ -159,7 +174,7 @@ class Base:
Returns:
None: Aucun retour
"""
sql_insert = f"INSERT INTO {self.Config.table_log} (datetime, server_msg) VALUES (:datetime, :server_msg)"
sql_insert = f"INSERT INTO {self.Config.TABLE_LOG} (datetime, server_msg) VALUES (:datetime, :server_msg)"
mes_donnees = {'datetime': str(self.get_datetime()),'server_msg': f'{log_message}'}
self.db_execute_query(sql_insert, mes_donnees)
@@ -167,21 +182,34 @@ class Base:
def init_log_system(self) -> None:
# Create folder if not available
logs_directory = f'logs{os.sep}'
logs_directory = f'logs{self.Config.OS_SEP}'
if not os.path.exists(f'{logs_directory}'):
os.makedirs(logs_directory)
# Init logs object
self.logs = logging
self.logs.basicConfig(level=self.Config.DEBUG_LEVEL,
filename='logs/defender.log',
filename=f'logs{self.Config.OS_SEP}defender.log',
encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
logger = logging.getLogger()
logger.addFilter(self.replace_filter)
self.logs.info('#################### STARTING DEFENDER ####################')
return None
def replace_filter(self, record: logging.LogRecord) -> bool:
response = True
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
response = False if "PING" in record.getMessage() else True
response = False if f":{self.Config.SERVICE_PREFIX}auth" in record.getMessage() else True
return response # Retourne True pour permettre l'affichage du message
def log_cmd(self, user_cmd:str, cmd:str) -> None:
"""Enregistre les commandes envoyées par les utilisateurs
@@ -195,7 +223,7 @@ class Base:
cmd_list[2] = '*******'
cmd = ' '.join(cmd_list)
insert_cmd_query = f"INSERT INTO {self.Config.table_commande} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_COMMAND} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
self.db_execute_query(insert_cmd_query, mes_donnees)
@@ -210,7 +238,7 @@ class Base:
Returns:
bool: True si le module existe déja dans la base de données sinon False
"""
query = f"SELECT id FROM {self.Config.table_module} WHERE module_name = :module_name"
query = f"SELECT id FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name"
mes_donnes = {'module_name': module_name}
results = self.db_execute_query(query, mes_donnes)
@@ -228,7 +256,7 @@ class Base:
if not self.db_isModuleExist(module_name):
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
insert_cmd_query = f"INSERT INTO {self.Config.table_module} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
insert_cmd_query = f"INSERT INTO {self.Config.TABLE_MODULE} (datetime, user, module_name, isdefault) VALUES (:datetime, :user, :module_name, :isdefault)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault}
self.db_execute_query(insert_cmd_query, mes_donnees)
else:
@@ -243,7 +271,7 @@ class Base:
user_cmd (str): le user qui a rechargé le module
module_name (str): le module a rechargé
"""
update_cmd_query = f"UPDATE {self.Config.table_module} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
update_cmd_query = f"UPDATE {self.Config.TABLE_MODULE} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name}
self.db_execute_query(update_cmd_query, mes_donnees)
@@ -255,7 +283,7 @@ class Base:
Args:
cmd (str): le module a supprimer
"""
insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name"
insert_cmd_query = f"DELETE FROM {self.Config.TABLE_MODULE} WHERE module_name = :module_name"
mes_donnees = {'module_name': module_name}
self.db_execute_query(insert_cmd_query, mes_donnees)
@@ -278,7 +306,7 @@ class Base:
try:
response = True
current_date = self.get_datetime()
core_table = self.Config.table_config
core_table = self.Config.TABLE_CONFIG
# Add local parameters to DB
for field in fields(dataclassObj):
@@ -342,7 +370,7 @@ class Base:
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = self.Config.table_config
core_table = self.Config.TABLE_CONFIG
# Check if the param exist
if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
@@ -374,62 +402,62 @@ class Base:
return True
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
"""You can add a channel or delete a channel.
# def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
# """You can add a channel or delete a channel.
Args:
action (Literal['add','del']): Action on the database
module_name (str): The module name (mod_test)
channel_name (str): The channel name (With #)
# Args:
# action (Literal['add','del']): Action on the database
# module_name (str): The module name (mod_test)
# channel_name (str): The channel name (With #)
Returns:
bool: True if action done
"""
try:
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
core_table = 'core_channel'
# Returns:
# bool: True if action done
# """
# try:
# channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
# core_table = self.Config.TABLE_CHANNEL
if not channel_name:
self.logs.warn(f'The channel [{channel_name}] is not correct')
return False
# if not channel_name:
# self.logs.warning(f'The channel [{channel_name}] is not correct')
# return False
match action:
# match action:
case 'add':
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
isChannelExist = response.fetchone()
# case 'add':
# mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
# response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
# isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
if insert.rowcount:
self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
return True
else:
return False
pass
# if isChannelExist is None:
# mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
# insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
# if insert.rowcount:
# self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
# return True
# else:
# return False
# pass
case 'del':
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
# case 'del':
# mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
# response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
if response.rowcount > 0:
self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
return True
else:
# if response.rowcount > 0:
# self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
# return True
# else:
return False
# return False
case _:
return False
# case _:
# return False
except Exception as err:
self.logs.error(err)
# except Exception as err:
# self.logs.error(err)
def db_create_first_admin(self) -> None:
user = self.db_execute_query(f"SELECT id FROM {self.Config.table_admin}")
user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")
if not user.fetchall():
admin = self.Config.OWNER
password = self.crypt_password(self.Config.PASSWORD)
@@ -442,7 +470,7 @@ class Base:
'level': 5
}
self.db_execute_query(f"""
INSERT INTO {self.Config.table_admin}
INSERT INTO {self.Config.TABLE_ADMIN}
(createdOn, user, password, hostname, vhost, level)
VALUES
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
@@ -489,6 +517,25 @@ class Base:
except AssertionError as ae:
self.logs.error(f'{ae}')
def thread_count(self, thread_name: str) -> int:
"""This method return the number of existing threads
currently running or not running
Args:
thread_name (str): The name of the thread
Returns:
int: Number of threads
"""
with self.lock:
count = 0
for thr in self.running_threads:
if thread_name == thr.getName():
count += 1
return count
def garbage_collector_timer(self) -> None:
"""Methode qui supprime les timers qui ont finis leurs job
"""
@@ -511,6 +558,7 @@ class Base:
try:
for thread in self.running_threads:
if thread.getName() != 'heartbeat':
# print(thread.getName(), thread.is_alive(), sep=' / ')
if not thread.is_alive():
self.running_threads.remove(thread)
self.logs.info(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
@@ -564,8 +612,8 @@ class Base:
def db_init(self) -> tuple[Engine, Connection]:
db_directory = self.Config.db_path
full_path_db = self.Config.db_path + self.Config.db_name
db_directory = self.Config.DB_PATH
full_path_db = self.Config.DB_PATH + self.Config.DB_NAME
if not os.path.exists(db_directory):
self.install = True
@@ -578,14 +626,14 @@ class Base:
def __create_db(self) -> None:
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_log} (
table_core_log = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_LOG} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
server_msg TEXT
)
'''
table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_config} (
table_core_config = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CONFIG} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
module_name TEXT,
@@ -594,7 +642,7 @@ class Base:
)
'''
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_commande} (
table_core_log_command = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_COMMAND} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
user TEXT,
@@ -602,7 +650,7 @@ class Base:
)
'''
table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_module} (
table_core_module = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_MODULE} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
user TEXT,
@@ -611,7 +659,7 @@ class Base:
)
'''
table_core_channel = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_channel} (
table_core_channel = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CHANNEL} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
module_name TEXT,
@@ -619,7 +667,7 @@ class Base:
)
'''
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_admin} (
table_core_admin = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_ADMIN} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
createdOn TEXT,
user TEXT,
@@ -717,16 +765,19 @@ class Base:
self.logs.critical(f'This remote ip is not valid : {ve}')
return None
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
# binary_ip = b64encode()
# try:
# decoded_ip = ipaddress.ip_address(binary_ip)
binary_ip = socket.inet_aton(remote_ip_address)
try:
encoded_ip = b64encode(binary_ip).decode()
# return decoded_ip.exploded
# except ValueError as ve:
# self.logs.critical(f'This remote ip is not valid : {ve}')
# return None
return encoded_ip
except ValueError as ve:
self.logs.critical(f'This remote ip is not valid : {ve}')
return None
except Exception as err:
self.logs.critical(f'General Error: {err}')
return None
def get_random(self, lenght:int) -> str:
"""
@@ -756,7 +807,7 @@ class Base:
# Vider le dictionnaire de fonction
self.periodic_func.clear()
def clean_uid(self, uid:str) -> str:
def clean_uid(self, uid:str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
@@ -765,34 +816,13 @@ class Base:
Returns:
str: Clean UID without any sign
"""
try:
if uid is None:
return None
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', uid)
return parsed_UID
def Is_Channel(self, channelToCheck: str) -> bool:
"""Check if the string has the # caractere and return True if this is a channel
Args:
channelToCheck (str): The string to test if it is a channel or not
Returns:
bool: True if the string is a channel / False if this is not a channel
"""
try:
if channelToCheck is None:
return False
pattern = fr'^#'
isChannel = re.findall(pattern, channelToCheck)
if not isChannel:
return False
else:
return True
except TypeError as te:
self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
except Exception as err:
self.logs.error(f'Error Not defined: {err}')
self.logs.error(f'Type Error: {te}')

127
core/classes/admin.py Normal file
View File

@@ -0,0 +1,127 @@
from typing import Union
import core.definition as df
from core.base import Base
class Admin:
UID_ADMIN_DB: list[df.MAdmin] = []
def __init__(self, baseObj: Base) -> None:
self.Logs = baseObj.logs
pass
def insert(self, newAdmin: df.MAdmin) -> bool:
result = False
exist = False
for record in self.UID_ADMIN_DB:
if record.uid == newAdmin.uid:
# If the admin exist then return False and do not go further
exist = True
self.Logs.debug(f'{record.uid} already exist')
return result
if not exist:
self.UID_ADMIN_DB.append(newAdmin)
result = True
self.Logs.debug(f'UID ({newAdmin.uid}) has been created')
if not result:
self.Logs.critical(f'The User Object was not inserted {newAdmin}')
return result
def update_nickname(self, uid: str, newNickname: str) -> bool:
result = False
for record in self.UID_ADMIN_DB:
if record.uid == uid:
# If the admin exist, update and do not go further
record.nickname = newNickname
result = True
self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
if not result:
self.Logs.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
return result
def update_level(self, nickname: str, newLevel: int) -> bool:
result = False
for record in self.UID_ADMIN_DB:
if record.nickname == nickname:
# If the admin exist, update and do not go further
record.level = newLevel
result = True
self.Logs.debug(f'Admin ({record.nickname}) has been updated with new level {newLevel}')
return result
if not result:
self.Logs.critical(f'The new level {newLevel} was not updated, nickname = {nickname}')
return result
def delete(self, uidornickname: str) -> bool:
result = False
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
# If the admin exist, delete and do not go further
self.UID_ADMIN_DB.remove(record)
result = True
self.Logs.debug(f'UID ({record.uid}) has been deleted')
return result
if record.nickname == uidornickname:
# If the admin exist, delete and do not go further
self.UID_ADMIN_DB.remove(record)
result = True
self.Logs.debug(f'nickname ({record.nickname}) has been deleted')
return result
if not result:
self.Logs.critical(f'The UID {uidornickname} was not deleted')
return result
def get_Admin(self, uidornickname: str) -> Union[df.MAdmin, None]:
Admin = None
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
Admin = record
elif record.nickname == uidornickname:
Admin = record
#self.Logs.debug(f'Search {uidornickname} -- result = {Admin}')
return Admin
def get_uid(self, uidornickname:str) -> Union[str, None]:
uid = None
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
return uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
nickname = None
for record in self.UID_ADMIN_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
self.Logs.debug(f'The value {uidornickname} -- {nickname}')
return nickname

225
core/classes/channel.py Normal file
View File

@@ -0,0 +1,225 @@
from re import findall
from typing import Union, Literal, TYPE_CHECKING
from dataclasses import asdict
if TYPE_CHECKING:
from core.definition import MChannel
from core.base import Base
class Channel:
UID_CHANNEL_DB: list['MChannel'] = []
"""List that contains all the Channels objects (ChannelModel)
"""
def __init__(self, baseObj: 'Base') -> None:
self.Logs = baseObj.logs
self.Base = baseObj
return None
def insert(self, newChan: 'MChannel') -> bool:
"""This method will insert a new channel and if the channel exist it will update the user list (uids)
Args:
newChan (ChannelModel): The channel model object
Returns:
bool: True if new channel, False if channel exist (However UID could be updated)
"""
result = False
exist = False
for record in self.UID_CHANNEL_DB:
if record.name.lower() == newChan.name.lower():
# If the channel exist, update the user list and do not go further
exist = True
# self.Logs.debug(f'{record.name} already exist')
for user in newChan.uids:
record.uids.append(user)
# Supprimer les doublons
del_duplicates = list(set(record.uids))
record.uids = del_duplicates
# self.Logs.debug(f'Updating a new UID to the channel {record}')
return result
if not exist:
# If the channel don't exist, then create it
newChan.name = newChan.name.lower()
self.UID_CHANNEL_DB.append(newChan)
result = True
# self.Logs.debug(f'New Channel Created: ({newChan})')
if not result:
self.Logs.critical(f'The Channel Object was not inserted {newChan}')
self.clean_channel()
return result
def delete(self, name: str) -> bool:
result = False
for record in self.UID_CHANNEL_DB:
if record.name == name:
# If the channel exist, then remove it and return True.
# As soon as the channel found, return True and stop the loop
self.UID_CHANNEL_DB.remove(record)
result = True
# self.Logs.debug(f'Channel ({record.name}) has been created')
return result
if not result:
self.Logs.critical(f'The Channel {name} was not deleted')
return result
def delete_user_from_channel(self, chan_name: str, uid:str) -> bool:
try:
result = False
for record in self.UID_CHANNEL_DB:
if record.name == chan_name:
for user_id in record.uids:
if self.Base.clean_uid(user_id) == uid:
record.uids.remove(user_id)
# self.Logs.debug(f'The UID {uid} has been removed, here is the new object: {record}')
result = True
self.clean_channel()
return result
except ValueError as ve:
self.Logs.error(f'{ve}')
def delete_user_from_all_channel(self, uid:str) -> bool:
try:
result = False
for record in self.UID_CHANNEL_DB:
for user_id in record.uids:
if self.Base.clean_uid(user_id) == self.Base.clean_uid(uid):
record.uids.remove(user_id)
# self.Logs.debug(f'The UID {uid} has been removed, here is the new object: {record}')
result = True
self.clean_channel()
return result
except ValueError as ve:
self.Logs.error(f'{ve}')
def clean_channel(self) -> None:
"""Remove Channels if empty
"""
try:
for record in self.UID_CHANNEL_DB:
if not record.uids:
self.UID_CHANNEL_DB.remove(record)
# self.Logs.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
return None
except Exception as err:
self.Logs.error(f'{err}')
def get_Channel(self, name: str) -> Union['MChannel', None]:
Channel = None
for record in self.UID_CHANNEL_DB:
if record.name == name:
Channel = record
# self.Logs.debug(f'Search {name} -- result = {Channel}')
return Channel
def get_Channel_AsDict(self, chan_name: str) -> Union[dict[str, any], None]:
chanObj = self.get_Channel(chan_name=chan_name)
if not chanObj is None:
chan_as_dict = asdict(chanObj)
return chan_as_dict
else:
return None
def Is_Channel(self, channelToCheck: str) -> bool:
"""Check if the string has the # caractere and return True if this is a channel
Args:
channelToCheck (str): The string to test if it is a channel or not
Returns:
bool: True if the string is a channel / False if this is not a channel
"""
try:
if channelToCheck is None:
return False
pattern = fr'^#'
isChannel = findall(pattern, channelToCheck)
if not isChannel:
return False
else:
return True
except TypeError as te:
self.logs.error(f'TypeError: [{channelToCheck}] - {te}')
except Exception as err:
self.logs.error(f'Error Not defined: {err}')
def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
"""You can add a channel or delete a channel.
Args:
action (Literal['add','del']): Action on the database
module_name (str): The module name (mod_test)
channel_name (str): The channel name (With #)
Returns:
bool: True if action done
"""
try:
channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
core_table = self.Base.Config.TABLE_CHANNEL
if not channel_name:
self.Logs.warning(f'The channel [{channel_name}] is not correct')
return False
match action:
case 'add':
mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
response = self.Base.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
insert = self.Base.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
if insert.rowcount:
self.Logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
return True
else:
return False
case 'del':
mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
response = self.Base.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
if response.rowcount > 0:
self.Logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
return True
else:
return False
case _:
return False
except Exception as err:
self.Logs.error(err)

162
core/classes/clone.py Normal file
View File

@@ -0,0 +1,162 @@
from dataclasses import asdict
from core.definition import MClone
from typing import Union
from core.base import Base
class Clone:
UID_CLONE_DB: list[MClone] = []
def __init__(self, baseObj: Base) -> None:
self.Logs = baseObj.logs
return None
def insert(self, newCloneObject: MClone) -> bool:
"""Create new Clone object
Args:
newCloneObject (CloneModel): New CloneModel object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_CLONE_DB:
if record.nickname == newCloneObject.nickname:
# If the user exist then return False and do not go further
exist = True
self.Logs.warning(f'Nickname {record.nickname} already exist')
return result
if record.uid == newCloneObject.uid:
exist = True
self.Logs.warning(f'UID: {record.uid} already exist')
return result
if not exist:
self.UID_CLONE_DB.append(newCloneObject)
result = True
# self.Logs.debug(f'New Clone Object Created: ({newCloneObject})')
if not result:
self.Logs.critical(f'The Clone Object was not inserted {newCloneObject}')
return result
def delete(self, uidornickname: str) -> bool:
"""Delete the Clone Object starting from the nickname or the UID
Args:
uidornickname (str): UID or nickname of the clone
Returns:
bool: True if deleted
"""
result = False
for record in self.UID_CLONE_DB:
if record.nickname == uidornickname or record.uid == uidornickname:
# If the user exist then remove and return True and do not go further
self.UID_CLONE_DB.remove(record)
result = True
# self.Logs.debug(f'The clone ({record.nickname}) has been deleted')
return result
if not result:
self.Logs.critical(f'The UID or Nickname {uidornickname} was not deleted')
return result
def exists(self, nickname: str) -> bool:
"""Check if the nickname exist
Args:
nickname (str): Nickname of the clone
Returns:
bool: True if the nickname exist
"""
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
response = True
return response
def uid_exists(self, uid: str) -> bool:
"""Check if the nickname exist
Args:
uid (str): uid of the clone
Returns:
bool: True if the nickname exist
"""
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.uid == uid:
response = True
return response
def get_Clone(self, uidornickname: str) -> Union[MClone, None]:
"""Get MClone object or None
Args:
uidornickname (str): The UID or the Nickname
Returns:
Union[MClone, None]: Return MClone object or None
"""
for clone in self.UID_CLONE_DB:
if clone.uid == uidornickname:
return clone
if clone.nickname == uidornickname:
return clone
def get_uid(self, uidornickname: str) -> Union[str, None]:
"""Get the UID of the clone starting from the UID or the Nickname
Args:
uidornickname (str): UID or Nickname
Returns:
str|None: Return the UID
"""
uid = None
for record in self.UID_CLONE_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
# if not uid is None:
# self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
return uid
def get_Clone_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
cloneObj = self.get_Clone(uidornickname=uidornickname)
if not cloneObj is None:
cloneObj_as_dict = asdict(cloneObj)
return cloneObj_as_dict
else:
return None
def kill(self, nickname:str) -> bool:
response = False
for cloneObject in self.UID_CLONE_DB:
if cloneObject.nickname == nickname:
cloneObject.alive = False # Kill the clone
response = True
return response

57
core/classes/config.py Normal file
View File

@@ -0,0 +1,57 @@
from json import load
from sys import exit
from os import sep
from typing import Union
from core.definition import MConfig
class Configuration:
def __init__(self) -> None:
self.ConfigObject: MConfig = self.__load_service_configuration()
return None
def __load_json_service_configuration(self):
try:
conf_filename = f'config{sep}configuration.json'
with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = load(configuration_data)
return configuration
except FileNotFoundError as fe:
print(f'FileNotFound: {fe}')
print('Configuration file not found please create config/configuration.json')
exit(0)
except KeyError as ke:
print(f'Key Error: {ke}')
print('The key must be defined in core/configuration.json')
def __load_service_configuration(self) -> MConfig:
try:
import_config = self.__load_json_service_configuration()
Model_keys = MConfig().__dict__
model_key_list: list = []
json_config_key_list: list = []
for key in Model_keys:
model_key_list.append(key)
for key in import_config:
json_config_key_list.append(key)
for json_conf in json_config_key_list:
if not json_conf in model_key_list:
import_config.pop(json_conf, None)
print(f"\!/ The key {json_conf} is not expected, it has been removed from the system ! please remove it from configuration.json file \!/")
ConfigObject: MConfig = MConfig(
**import_config
)
return ConfigObject
except TypeError as te:
print(te)

15
core/classes/protocol.py Normal file
View File

@@ -0,0 +1,15 @@
from typing import Literal, TYPE_CHECKING
from .protocols.unreal6 import Unrealircd6
if TYPE_CHECKING:
from core.irc import Irc
class Protocol:
def __init__(self, protocol: Literal['unreal6'], ircInstance: 'Irc'):
self.Protocol = None
if protocol == 'unreal6':
self.Protocol: Unrealircd6 = Unrealircd6(ircInstance)
else:
self.Protocol = None

View File

@@ -0,0 +1,336 @@
from typing import TYPE_CHECKING
from ssl import SSLEOFError, SSLError
if TYPE_CHECKING:
from core.irc import Irc
class Unrealircd6:
def __init__(self, ircInstance: 'Irc'):
self.__Irc = ircInstance
self.__Config = ircInstance.Config
self.__Base = ircInstance.Base
def send2socket(self, message: str, print_log: bool = True) -> None:
"""Envoit les commandes à envoyer au serveur.
Args:
string (Str): contient la commande à envoyer au serveur.
"""
try:
with self.__Base.lock:
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0]))
if print_log:
self.__Base.logs.debug(f'<< {message}')
except UnicodeDecodeError:
self.__Base.logs.error(f'Decode Error try iso-8859-1 - message: {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0],'replace'))
except UnicodeEncodeError:
self.__Base.logs.error(f'Encode Error try iso-8859-1 - message: {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0],'replace'))
except AssertionError as ae:
self.__Base.logs.warning(f'Assertion Error {ae} - message: {message}')
except SSLEOFError as soe:
self.__Base.logs.error(f"SSLEOFError: {soe} - {message}")
except SSLError as se:
self.__Base.logs.error(f"SSLError: {se} - {message}")
except OSError as oe:
self.__Base.logs.error(f"OSError: {oe} - {message}")
except AttributeError as ae:
self.__Base.logs.critical(f"Attribute Error: {ae}")
def sendPrivMsg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None):
"""Sending PRIVMSG to a channel or to a nickname by batches
could be either channel or nickname not both together
Args:
msg (str): The message to send
nick_from (str): The sender nickname
channel (str, optional): The receiver channel. Defaults to None.
nick_to (str, optional): The reciever nickname. Defaults to None.
"""
try:
batch_size = self.__Config.BATCH_SIZE
User_from = self.__Irc.User.get_User(nick_from)
User_to = self.__Irc.User.get_User(nick_to) if nick_to is None else None
if User_from is None:
self.__Base.logs.error(f"The sender nickname [{nick_from}] do not exist")
return None
if not channel is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{User_from.uid} PRIVMSG {channel} :{batch}")
if not nick_to is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{nick_from} PRIVMSG {User_to.uid} :{batch}")
except Exception as err:
self.__Base.logs.error(f"General Error: {err}")
def sendNotice(self, nick_from: str, nick_to: str, msg: str) -> None:
"""Sending NOTICE by batches
Args:
msg (str): The message to send to the server
nick_from (str): The sender Nickname
nick_to (str): The reciever nickname
"""
try:
batch_size = self.__Config.BATCH_SIZE
User_from = self.__Irc.User.get_User(nick_from)
User_to = self.__Irc.User.get_User(nick_to)
if User_from is None or User_to is None:
self.__Base.logs.error(f"The sender [{nick_from}] or the Reciever [{nick_to}] do not exist")
return None
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{User_from.uid} NOTICE {User_to.uid} :{batch}")
except Exception as err:
self.__Base.logs.error(f"General Error: {err}")
def link(self):
"""Créer le link et envoyer les informations nécessaires pour la
connexion au serveur.
"""
nickname = self.__Config.SERVICE_NICKNAME
username = self.__Config.SERVICE_USERNAME
realname = self.__Config.SERVICE_REALNAME
chan = self.__Config.SERVICE_CHANLOG
info = self.__Config.SERVICE_INFO
smodes = self.__Config.SERVICE_SMODES
cmodes = self.__Config.SERVICE_CMODES
umodes = self.__Config.SERVICE_UMODES
host = self.__Config.SERVICE_HOST
service_name = self.__Config.SERVICE_NAME
password = self.__Config.SERVEUR_PASSWORD
link = self.__Config.SERVEUR_LINK
server_id = self.__Config.SERVEUR_ID
service_id = self.__Config.SERVICE_ID
version = self.__Config.CURRENT_VERSION
unixtime = self.__Base.get_unixtime()
self.send2socket(f":{server_id} PASS :{password}")
self.send2socket(f":{server_id} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS")
# self.__Irc.send2socket(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS")
self.send2socket(f":{server_id} PROTOCTL EAUTH={link},,,{service_name}-v{version}")
self.send2socket(f":{server_id} PROTOCTL SID={server_id}")
self.send2socket(f":{server_id} SERVER {link} 1 :{info}")
self.send2socket(f":{server_id} {nickname} :Reserved for services")
#self.__Irc.send2socket(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}")
self.send2socket(f":{server_id} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}")
# self.__Irc.send2socket(f":{server_id} SJOIN {unixtime} {chan} + :{service_id}")
self.sjoin(chan)
self.send2socket(f":{server_id} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services")
self.send2socket(f":{service_id} MODE {chan} {cmodes}")
self.send2socket(f":{service_id} MODE {chan} {umodes} {service_id}")
self.__Base.logs.debug(f'>> {__name__} Link information sent to the server')
def gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + G {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
return None
def set_nick(self, newnickname: str) -> None:
self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}")
return None
def squit(self, server_id: str, server_link: str, reason: str) -> None:
self.send2socket(f":{server_id} SQUIT {server_link} :{reason}")
return None
def ungline(self, nickname:str, hostname: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - G {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
return None
def kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None:
# TKL + k user host set_by expire_timestamp set_at_timestamp :reason
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL + k {nickname} {hostname} {set_by} {expire_timestamp} {set_at_timestamp} :{reason}")
return None
def sjoin(self, channel: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}")
# Add defender to the channel uids list
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID]))
return None
def join(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
"""Joining a channel
Args:
uidornickname (str): UID or nickname that need to join
channel (str): channel to join
password (str, optional): The password of the channel to join. Default to None
print_log (bool, optional): Write logs. Defaults to True.
"""
userObj = self.__Irc.User.get_User(uidornickname)
passwordChannel = password if not password is None else ''
if userObj is None:
return None
if not self.__Irc.Channel.Is_Channel(channel):
return None
self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log)
# Add defender to the channel uids list
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid]))
return None
def part(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
"""Part from a channel
Args:
uidornickname (str): UID or nickname that need to join
channel (str): channel to join
print_log (bool, optional): Write logs. Defaults to True.
"""
userObj = self.__Irc.User.get_User(uidornickname)
if userObj is None:
return None
if not self.__Irc.Channel.Is_Channel(channel):
return None
self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log)
# Add defender to the channel uids list
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
return None
def unkline(self, nickname:str, hostname: str) -> None:
self.send2socket(f":{self.__Config.SERVEUR_ID} TKL - K {nickname} {hostname} {self.__Config.SERVICE_NICKNAME}")
return None
def on_server_ping(self, serverMsg: list[str]) -> None:
"""Send a PONG message to the server
Args:
serverMsg (list[str]): List of str coming from the server
"""
try:
pong = str(serverMsg[1]).replace(':','')
self.send2socket(f"PONG :{pong}", print_log=False)
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_version(self, serverMsg: list[str]) -> None:
"""Sending Server Version to the server
Args:
serverMsg (list[str]): List of str coming from the server
"""
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01VERSION\x01']
# Réponse a un CTCP VERSION
try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '')
if nickname is None:
return None
if arg == '\x01VERSION\x01':
self.send2socket(f':{dnickname} NOTICE {nickname} :\x01VERSION Service {self.__Config.SERVICE_NICKNAME} V{self.__Config.CURRENT_VERSION}\x01')
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_time(self, serverMsg: list[str]) -> None:
"""Sending TIME answer to a requestor
Args:
serverMsg (list[str]): List of str coming from the server
"""
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':00BAAAAAI', 'PRIVMSG', '12ZAAAAAB', ':\x01TIME\x01']
# Réponse a un CTCP VERSION
try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '')
current_datetime = self.__Base.get_datetime()
if nickname is None:
return None
if arg == '\x01TIME\x01':
self.send2socket(f':{dnickname} NOTICE {nickname} :\x01TIME {current_datetime}\x01')
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_ping(self, serverMsg: list[str]) -> None:
"""Sending a PING answer to requestor
Args:
serverMsg (list[str]): List of str coming from the server
"""
# ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':001INC60B', 'PRIVMSG', '12ZAAAAAB', ':\x01PING', '762382207\x01']
# Réponse a un CTCP VERSION
try:
nickname = self.__Irc.User.get_nickname(self.__Base.clean_uid(serverMsg[1]))
dnickname = self.__Config.SERVICE_NICKNAME
arg = serverMsg[4].replace(':', '')
if nickname is None:
return None
if arg == '\x01PING':
recieved_unixtime = int(serverMsg[5].replace('\x01',''))
current_unixtime = self.__Base.get_unixtime()
ping_response = current_unixtime - recieved_unixtime
# self.__Irc.send2socket(f':{dnickname} NOTICE {nickname} :\x01PING {ping_response} secs\x01')
self.sendNotice(
nick_from=dnickname,
nick_to=nickname,
msg=f"\x01PING {ping_response} secs\x01"
)
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_version_msg(self, serverMsg: list[str]) -> None:
# ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender']
getUser = self.__Irc.User.get_User(self.__Irc.User.clean_uid(serverMsg[1]))
if getUser is None:
return None
self.send2socket(f'{self.__Config.SERVEUR_ID} 351 {getUser.nickname} {self.__Config.CURRENT_VERSION} {self.__Config.SERVICE_NAME} *:')

175
core/classes/reputation.py Normal file
View File

@@ -0,0 +1,175 @@
from typing import Union
from core.definition import MReputation
from core.base import Base
class Reputation:
UID_REPUTATION_DB: list[MReputation] = []
def __init__(self, baseObj: Base) -> None:
self.Logs = baseObj.logs
self.MReputation: MReputation = MReputation
return None
def insert(self, newReputationUser: MReputation) -> bool:
"""Insert a new Reputation User object
Args:
newReputationUser (MReputation): New Reputation Model object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_REPUTATION_DB:
if record.uid == newReputationUser.uid:
# If the user exist then return False and do not go further
exist = True
self.Logs.debug(f'{record.uid} already exist')
return result
if not exist:
self.UID_REPUTATION_DB.append(newReputationUser)
result = True
self.Logs.debug(f'New Reputation User Captured: ({newReputationUser})')
if not result:
self.Logs.critical(f'The Reputation User Object was not inserted {newReputationUser}')
return result
def update(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
"""
result = False
for record in self.UID_REPUTATION_DB:
if record.uid == uid:
# If the user exist then update and return True and do not go further
record.nickname = newNickname
result = True
self.Logs.debug(f'Reputation UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
if not result:
self.Logs.critical(f'Reputation new nickname {newNickname} was not updated, uid = {uid}')
return result
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
"""
result = False
for record in self.UID_REPUTATION_DB:
if record.uid == uid:
# If the user exist then remove and return True and do not go further
self.UID_REPUTATION_DB.remove(record)
result = True
self.Logs.debug(f'UID ({record.uid}) has been deleted')
return result
if not result:
self.Logs.critical(f'The UID {uid} was not deleted')
return result
def get_Reputation(self, uidornickname: str) -> Union[MReputation, None]:
"""Get The User Object model
Args:
uidornickname (str): UID or Nickname
Returns:
UserModel|None: The UserModel Object | None
"""
User = None
for record in self.UID_REPUTATION_DB:
if record.uid == uidornickname:
User = record
elif record.nickname == uidornickname:
User = record
if not User is None:
self.Logs.debug(f'Reputation found for {uidornickname} -> {User}')
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
"""
uid = None
for record in self.UID_REPUTATION_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
if not uid is None:
self.Logs.debug(f'Reputation UID found for {uidornickname} -> {uid}')
return 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
"""
nickname = None
for record in self.UID_REPUTATION_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
if not nickname is None:
self.Logs.debug(f'Reputation nickname found for {uidornickname} -> {nickname}')
return nickname
def is_exist(self, uidornickname: str) -> bool:
"""Check if the UID or the nickname exist in the reputation DB
Args:
uidornickname (str): The UID or the NICKNAME
Returns:
bool: True if exist
"""
found = False
for record in self.UID_REPUTATION_DB:
if record.uid == uidornickname:
found = True
if record.nickname == uidornickname:
found = True
return found

10
core/classes/settings.py Normal file
View File

@@ -0,0 +1,10 @@
from threading import Timer, Thread, RLock
from socket import socket
class Settings:
RUNNING_TIMERS: list[Timer] = []
RUNNING_THREADS: list[Thread] = []
RUNNING_SOCKETS: list[socket] = []
PERIODIC_FUNC: dict[object] = {}
LOCK: RLock = RLock()

180
core/classes/user.py Normal file
View File

@@ -0,0 +1,180 @@
import re
from typing import Union, TYPE_CHECKING
from dataclasses import asdict
if TYPE_CHECKING:
from core.base import Base
from core.definition import MUser
class User:
UID_DB: list['MUser'] = []
def __init__(self, baseObj: 'Base') -> None:
self.Logs = baseObj.logs
self.Base = baseObj
return None
def insert(self, newUser: 'MUser') -> bool:
"""Insert a new User object
Args:
newUser (UserModel): New userModel object
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_DB:
if record.uid == newUser.uid:
# If the user exist then return False and do not go further
exist = True
self.Logs.debug(f'{record.uid} already exist')
return result
if not exist:
self.UID_DB.append(newUser)
result = True
# self.Logs.debug(f'New User Created: ({newUser})')
if not result:
self.Logs.critical(f'The User Object was not inserted {newUser}')
return result
def update(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
"""
result = False
for record in self.UID_DB:
if record.uid == uid:
# If the user exist then update and return True and do not go further
record.nickname = newNickname
result = True
# self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
if not result:
self.Logs.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
return result
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
"""
result = False
for record in self.UID_DB:
if record.uid == uid:
# If the user exist then remove and return True and do not go further
self.UID_DB.remove(record)
result = True
# self.Logs.debug(f'UID ({record.uid}) has been deleted')
return result
if not result:
self.Logs.critical(f'The UID {uid} was not deleted')
return result
def get_User(self, uidornickname: str) -> Union['MUser', None]:
"""Get The User Object model
Args:
uidornickname (str): UID or Nickname
Returns:
UserModel|None: The UserModel Object | None
"""
User = None
for record in self.UID_DB:
if record.uid == uidornickname:
User = record
elif record.nickname == uidornickname:
User = record
# self.Logs.debug(f'Search {uidornickname} -- result = {User}')
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
"""
uid = None
for record in self.UID_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
# if not uid is None:
# self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
return 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
"""
nickname = None
for record in self.UID_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
# self.Logs.debug(f'The value to check {uidornickname} -> {nickname}')
return nickname
def get_User_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
userObj = self.get_User(uidornickname=uidornickname)
if not userObj is None:
user_as_dict = asdict(userObj)
return user_as_dict
else:
return None
def clean_uid(self, uid: str) -> str:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
uid (str): The UID to clean
Returns:
str: Clean UID without any sign
"""
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', uid)
return parsed_UID

View File

@@ -1,274 +0,0 @@
import socket
import ssl
import traceback
from ssl import SSLSocket
from typing import Union
from core.loadConf import Config
from core.Model import Clones
from core.base import Base
class Connection:
def __init__(self, server_port: int, nickname: str, username: str, realname: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
self.Config = Config().ConfigObject
self.Base = Base(self.Config)
self.IrcSocket: Union[socket.socket, SSLSocket] = None
self.nickname = nickname
self.username = username
self.realname = realname
self.clone_chanlog = self.Config.CLONE_CHANNEL
self.clone_log_exempt = self.Config.CLONE_LOG_HOST_EXEMPT
self.channels:list[str] = channels
self.CHARSET = ['utf-8', 'iso-8859-1']
self.Clones = CloneObject
self.signal: bool = True
for clone in self.Clones.UID_CLONE_DB:
if clone.nickname == nickname:
self.currentCloneObject = clone
self.create_socket(self.Config.SERVEUR_IP, self.Config.SERVEUR_HOSTNAME, server_port, ssl)
self.send_connection_information_to_server(self.IrcSocket)
self.connect()
def create_socket(self, server_ip: str, server_hostname: str, server_port: int, ssl: bool = False) -> bool:
try:
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
connexion_information = (server_ip, server_port)
if ssl:
# Créer un object ssl
ssl_context = self.__ssl_context()
ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=server_hostname)
ssl_connexion.connect(connexion_information)
self.IrcSocket:SSLSocket = ssl_connexion
self.SSL_VERSION = self.IrcSocket.version()
self.Base.logs.debug(f'> Connexion en mode SSL : Version = {self.SSL_VERSION}')
else:
soc.connect(connexion_information)
self.IrcSocket:socket.socket = soc
self.Base.logs.debug(f'> Connexion en mode normal')
return True
except ssl.SSLEOFError as soe:
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
return False
except ssl.SSLError as se:
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
return False
except OSError as oe:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
return False
except AttributeError as ae:
self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}")
return False
def send2socket(self, send_message:str, disconnect: bool = False) -> None:
"""Envoit les commandes à envoyer au serveur.
Args:
string (Str): contient la commande à envoyer au serveur.
"""
try:
with self.Base.lock:
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>>: {send_message}')
except UnicodeDecodeError:
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[1],'replace'))
except UnicodeEncodeError:
self.Base.logs.error(f'Encode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[1],'replace'))
except AssertionError as ae:
self.Base.logs.warning(f'Assertion Error {ae} - message: {send_message}')
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError: {soe} - {send_message}")
except ssl.SSLError as se:
self.Base.logs.error(f"SSLError: {se} - {send_message}")
except OSError as oe:
self.Base.logs.error(f"OSError: {oe} - {send_message}")
def send_connection_information_to_server(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la
connexion au serveur.
Args:
writer (StreamWriter): permet l'envoi des informations au serveur.
"""
try:
nickname = self.nickname
username = self.username
realname = self.realname
# Envoyer un message d'identification
writer.send(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8'))
writer.send(f"USER {username} {username} {username} :{realname}\r\n".encode('utf-8'))
writer.send(f"NICK {nickname}\r\n".encode('utf-8'))
self.Base.logs.debug('Link information sent to the server')
return None
except AttributeError as ae:
self.Base.logs.critical(f'{ae}')
def connect(self):
try:
while self.signal:
try:
# 4072 max what the socket can grab
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
data_in_bytes = self.IrcSocket.recv(buffer_size)
data = data_in_bytes.splitlines(True)
count_bytes = len(data_in_bytes)
while count_bytes > 4070:
# If the received message is > 4070 then loop and add the value to the variable
new_data = self.IrcSocket.recv(buffer_size)
data_in_bytes += new_data
count_bytes = len(new_data)
data = data_in_bytes.splitlines(True)
if not data:
# If no data then quit the loop
break
self.parser(data)
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
self.signal = False
except ssl.SSLError as se:
self.Base.logs.error(f"SSLError __connect_to_irc: {se} - {data}")
self.signal = False
except OSError as oe:
self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}")
self.signal = False
self.IrcSocket.shutdown(socket.SHUT_WR)
self.IrcSocket.shutdown(socket.SHUT_RD)
self.currentCloneObject.init = False
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> Clone Disconnected ...")
except AssertionError as ae:
self.Base.logs.error(f'Assertion error : {ae}')
except ValueError as ve:
self.Base.logs.error(f'Value Error : {ve}')
except ssl.SSLEOFError as soe:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte:
self.Base.logs.critical(f"{atte}")
self.Base.logs.critical(f"{traceback.format_exc()}")
except Exception as e:
self.Base.logs.error(f"Exception: {e}")
def parser(self, cmd:list[bytes]):
try:
for data in cmd:
response = data.decode(self.CHARSET[0]).split()
current_clone_nickname = self.currentCloneObject.nickname
# print(response)
match response[0]:
case 'PING':
pong = str(response[1]).replace(':','')
self.send2socket(f"PONG :{pong}")
return None
case 'ERROR':
error_value = str(response[1]).replace(':','')
if error_value == 'Closing':
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...")
self.currentCloneObject.connected = False
else:
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...")
# self.signal = False
match response[1]:
case '376':
# End of MOTD
self.currentCloneObject.connected = True
self.currentCloneObject.init = False
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '422':
# Missing MOTD
self.currentCloneObject.connected = True
self.currentCloneObject.init = False
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '433':
# Nickname already in use
self.currentCloneObject.connected = False
self.currentCloneObject.init = False
self.send2socket(f'QUIT :Thanks and goodbye')
self.Base.logs.warning(f"Nickname {self.currentCloneObject.nickname} already in use >> Clone should be disconnected")
return None
case 'PRIVMSG':
self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Response: {response}')
self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Alive: {self.currentCloneObject.alive}')
fullname = str(response[0]).replace(':', '')
nickname = fullname.split('!')[0].replace(':','')
if response[2] == current_clone_nickname and nickname != self.Config.SERVICE_NICKNAME:
message = []
for i in range(3, len(response)):
message.append(response[i])
final_message = ' '.join(message)
exampt = False
for log_exception in self.clone_log_exempt:
if log_exception in fullname:
exampt = True
if not exampt:
self.send2socket(f"PRIVMSG {self.clone_chanlog} :{fullname} => {final_message[1:]}")
if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','')
if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye')
if command == 'JOIN':
channel_to_join = str(response[4])
self.send2socket(f"JOIN {channel_to_join}")
if command == 'SAY':
clone_channel = str(response[4])
message = []
for i in range(5, len(response)):
message.append(response[i])
final_message = ' '.join(message)
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
except UnicodeEncodeError:
for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split()
except UnicodeDecodeError:
for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split()
except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}")
def __ssl_context(self) -> ssl.SSLContext:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
self.Base.logs.debug(f'SSLContext initiated with verified mode {ctx.verify_mode}')
return ctx

View File

@@ -1,274 +0,0 @@
from dataclasses import dataclass, field
from datetime import datetime
from typing import Union
class User:
@dataclass
class UserDataClass:
uid: str
nickname: str
username: str
hostname: str
umodes: str
vhost: str
isWebirc: bool
connexion_datetime: datetime = field(default=datetime.now())
UID_DB:list[UserDataClass] = []
def __init__(self) -> None:
pass
def insert(self, user: UserDataClass) -> bool:
"""Insert new user
Args:
user (UserDataClass): The User dataclass
Returns:
bool: True if the record has been created
"""
exists = False
inserted = False
for record in self.UID_DB:
if record.uid == user.uid:
exists = True
print(f'{user.uid} already exist')
if not exists:
self.UID_DB.append(user)
print(f'New record with uid: {user.uid}')
inserted = True
return inserted
def update(self, uid: str, newnickname: str) -> bool:
"""Updating a single record with a new nickname
Args:
uid (str): the uid of the user
newnickname (str): the new nickname
Returns:
bool: True if the record has been updated
"""
status = False
for user in self.UID_DB:
if user.uid == uid:
user.nickname = newnickname
status = True
print(f'Updating record with uid: {uid}')
return status
def delete(self, uid: str) -> bool:
"""Delete a user based on his uid
Args:
uid (str): The UID of the user
Returns:
bool: True if the record has been deleted
"""
status = False
for user in self.UID_DB:
if user.uid == uid:
self.UID_DB.remove(user)
status = True
print(f'Removing record with uid: {uid}')
return status
def isexist(self, uidornickname:str) -> bool:
"""do the UID or Nickname exist ?
Args:
uidornickname (str): The UID or the Nickname
Returns:
bool: True if exist or False if don't exist
"""
result = False
for record in self.UID_DB:
if record.uid == uidornickname:
result = True
if record.nickname == uidornickname:
result = True
return result
def get_User(self, uidornickname) -> Union[UserDataClass, None]:
UserObject = None
for record in self.UID_DB:
if record.uid == uidornickname:
UserObject = record
elif record.nickname == uidornickname:
UserObject = record
return UserObject
def get_uid(self, uidornickname:str) -> Union[str, None]:
uid = None
for record in self.UID_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
return uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
nickname = None
for record in self.UID_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
return nickname
class Admin:
@dataclass
class AdminDataClass:
uid: str
nickname: str
username: str
hostname: str
umodes: str
vhost: str
level: int
connexion_datetime: datetime = field(default=datetime.now())
UID_ADMIN_DB:list[AdminDataClass] = []
def __init__(self) -> None:
pass
def insert(self, admin: AdminDataClass) -> bool:
"""Insert new user
Args:
user (UserDataClass): The User dataclass
Returns:
bool: True if the record has been created
"""
exists = False
inserted = False
for record in self.UID_ADMIN_DB:
if record.uid == admin.uid:
exists = True
print(f'{admin.uid} already exist')
if not exists:
self.UID_ADMIN_DB.append(admin)
print(f'New record with uid: {admin.uid}')
inserted = True
return inserted
def update(self, uid: str, newnickname: str) -> bool:
"""Updating a single record with a new nickname
Args:
uid (str): the uid of the user
newnickname (str): the new nickname
Returns:
bool: True if the record has been updated
"""
status = False
for admin in self.UID_ADMIN_DB:
if admin.uid == uid:
admin.nickname = newnickname
status = True
print(f'Updating record with uid: {uid}')
return status
def delete(self, uid: str) -> bool:
"""Delete a user based on his uid
Args:
uid (str): The UID of the user
Returns:
bool: True if the record has been deleted
"""
status = False
for admin in self.UID_ADMIN_DB:
if admin.uid == uid:
self.UID_ADMIN_DB.remove(admin)
status = True
print(f'Removing record with uid: {uid}')
return status
def isexist(self, uidornickname:str) -> bool:
"""do the UID or Nickname exist ?
Args:
uidornickname (str): The UID or the Nickname
Returns:
bool: True if exist or False if don't exist
"""
result = False
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
result = True
if record.nickname == uidornickname:
result = True
return result
def get_Admin(self, uidornickname) -> Union[AdminDataClass, None]:
AdminObject = None
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
AdminObject = record
elif record.nickname == uidornickname:
AdminObject = record
return AdminObject
def get_uid(self, uidornickname:str) -> Union[str, None]:
uid = None
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
return uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
nickname = None
for record in self.UID_ADMIN_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
return nickname
def get_level(self, uidornickname:str) -> int:
level = 0
for record in self.UID_ADMIN_DB:
if record.uid == uidornickname:
level = record.level
if record.nickname == uidornickname:
level = record.level
return level

291
core/definition.py Normal file
View File

@@ -0,0 +1,291 @@
from datetime import datetime
from dataclasses import dataclass, field
from typing import Literal
from os import sep
@dataclass
class MUser:
"""Model User"""
uid: str = None
nickname: str = None
username: str = None
realname: str = None
hostname: str = None
umodes: str = None
vhost: 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
class MAdmin:
"""Model Admin"""
uid: str = None
nickname: str = None
username: str = None
realname: str = None
hostname: str = None
umodes: str = None
vhost: 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())
level: int = 0
@dataclass
class MReputation:
"""Model Reputation"""
uid: str = None
nickname: str = None
username: str = None
realname: str = None
hostname: str = None
umodes: str = None
vhost: 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())
secret_code: str = None
@dataclass
class MChannel:
"""Model Channel"""
name: str = None
"""### Channel name
It include the #"""
uids: list[str] = field(default_factory=list[str])
"""### List of UID available in the channel
including their modes ~ @ % + *
Returns:
list: The list of UID's including theirs modes
"""
@dataclass
class ColorModel:
white: str = "\x0300"
black: str = "\x0301"
blue: str = "\x0302"
green: str = "\x0303"
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
@dataclass
class MConfig:
"""Model Configuration"""
SERVEUR_IP: str = "127.0.0.1"
"""Server public IP (could be 127.0.0.1 localhost)"""
SERVEUR_HOSTNAME: str = "your.host.name"
"""IRC Server Hostname (your.hostname.extension)"""
SERVEUR_LINK: str = "your.link.url"
"""The link hostname (should be the same as your unrealircd link block)"""
SERVEUR_PORT: int = 6697
"""Server port as configured in your unrealircd link block"""
SERVEUR_PASSWORD: str = "YOUR.STRONG.PASSWORD"
"""Your link password"""
SERVEUR_ID: str = "Z01"
"""Service identification could be Z01 should be unique"""
SERVEUR_SSL: bool = True
"""Activate SSL connexion"""
SERVEUR_PROTOCOL: str = "unreal6"
"""Which server are you going to use. (default: unreal6)"""
SERVEUR_CHARSET: list[str] = field(default_factory=list[str])
"""0: utf-8 | 1: iso-8859-1"""
SERVICE_NAME: str = "Defender"
"""Service name (Ex. Defender)"""
SERVICE_NICKNAME: str = "Defender"
"""Nickname of the service (Ex. Defender)"""
SERVICE_REALNAME: str = "Defender IRC Service"
"""Realname of the service"""
SERVICE_USERNAME: str = "Security"
"""Username of the service"""
SERVICE_HOST: str = "Your.Service.Hostname"
"""The service hostname"""
SERVICE_INFO: str = "Defender IRC Service"
"""Swhois of the service"""
SERVICE_CHANLOG: str = "#services"
"""The channel used by the service (ex. #services)"""
SERVICE_SMODES: str = "+ioqBS"
"""The service mode (ex. +ioqBS)"""
SERVICE_CMODES: str = "ntsO"
"""The mode of the log channel (ex. ntsO)"""
SERVICE_UMODES: str = "o"
"""The mode of the service when joining chanlog (ex. o, the service will be operator in the chanlog)"""
SERVICE_PREFIX: str = "!"
"""The default prefix to communicate with the service"""
SERVICE_ID: str = field(init=False)
"""The service unique ID"""
OWNER: str = "admin"
"""The nickname of the admin of the service"""
PASSWORD: str = "password"
"""The password of the admin of the service"""
JSONRPC_URL: str = None
"""The RPC url, if local https://127.0.0.1:PORT/api should be fine"""
JSONRPC_PATH_TO_SOCKET_FILE: str = None
"""The full path of the socket file (/PATH/TO/YOUR/UNREALIRCD/SOCKET/FILE.socket)"""
JSONRPC_METHOD: str = None
"""3 methods are available; requests/socket/unixsocket"""
JSONRPC_USER: str = None
"""The RPC User defined in your unrealircd.conf"""
JSONRPC_PASSWORD: str = None
"""The RPC Password defined in your unrealircd.conf"""
SALON_JAIL: str = "#jail"
"""The JAIL channel (ex. #jail)"""
SALON_JAIL_MODES: str = "sS"
"""The jail channel modes (ex. sS)"""
SALON_LIBERER: str = "#welcome"
"""Channel where the nickname will be released"""
CLONE_CHANNEL: str = "clones"
"""Channel where clones are hosted and will log PRIVMSG"""
CLONE_CMODES: str = "+nts"
"""Clone channel modes (ex. +nts)"""
CLONE_LOG_HOST_EXEMPT: list[str] = field(default_factory=list[str])
"""Hosts that clones will not log"""
CLONE_CHANNEL_PASSWORD: str = "clone_Password_1234"
"""Clone password channel"""
API_TIMEOUT: int = 60
"""Default api timeout in second. (default: 60)"""
PORTS_TO_SCAN: list[int] = field(default_factory=list[int])
"""List of ports to scan available for proxy_scan in the mod_defender module"""
WHITELISTED_IP: list[str] = field(default_factory=list[str])
"""List of remote IP to don't scan"""
GLINE_DURATION: str = "30"
"""Gline duration"""
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50] = 20
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50. (default: 20)"""
TABLE_ADMIN: str = "core_admin"
"""Admin table"""
TABLE_COMMAND: str = "core_command"
"""Core command table"""
TABLE_LOG: str = "core_log"
"""Core log table"""
TABLE_MODULE: str = "core_module"
"""Core module table"""
TABLE_CONFIG: str = "core_config"
"""Core configuration table"""
TABLE_CHANNEL: str = "core_channel"
"""Core channel table"""
CURRENT_VERSION: str = None
"""Current version of Defender"""
LATEST_VERSION: str = None
"""The Latest version fetched from github"""
DB_NAME: str = "defender"
"""The database name"""
DB_PATH: str = f"db{sep}"
"""The database path"""
COLORS: ColorModel = field(default_factory=ColorModel)
"""Available colors in Defender"""
BATCH_SIZE: int = 400
"""The batch size used for privmsg and notice"""
DEFENDER_CONNEXION_DATETIME: datetime = field(default=datetime.now())
"""First Connexion datetime of the service"""
DEFENDER_INIT: int = 1
"""Init flag. When Defender is ready, this variable will be set to 0. (default: 1)"""
DEFENDER_RESTART: int = 0
"""Restart flag. When Defender should restart this variable should be set to 1 (default: 0)"""
DEFENDER_HEARTBEAT: bool = True
"""Activate the hearbeat pulse (default: True)"""
DEFENDER_HEARTBEAT_FREQUENCY: int = 2
"""Frequency in seconds between every pulse (default: 30 seconds)"""
OS_SEP: str = sep
"""The OS Separator. (default: os.sep)"""
HSID: str = None
"""Host Server ID. The Server ID of the server who is hosting Defender. (Default: None)"""
SSL_VERSION: str = None
"""If SSL is used. This variable will be filled out by the system. (Default: None)"""
def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID: str = f"{self.SERVEUR_ID}AAAAAB"
"""The service ID which is SERVEUR_ID and AAAAAB"""
self.SERVEUR_CHARSET: list = ["utf-8", "iso-8859-1"]
"""0: utf-8 | 1: iso-8859-1"""
@dataclass
class MClone:
"""Model Clone"""
connected: bool = False
uid: str = None
nickname: str = None
username: str = None
realname: str = None
channels: list = field(default_factory=list)
vhost: str = None
hostname: str = 'localhost'
remote_ip: str = '127.0.0.1'
group: str = 'Default'

View File

@@ -37,6 +37,7 @@ class Install:
self.set_configuration()
if self.skip_install:
self.install_dependencies()
self.check_packages_version()
return None
@@ -84,7 +85,7 @@ class Install:
# If the Python version is not good then Exit
exit("/!\\ Python version error /!\\")
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')):
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'config', 'configuration.json')):
# If configuration file do not exist
exit("/!\\ Configuration file (core/configuration.json) doesn't exist! please create it /!\\")

File diff suppressed because it is too large Load Diff

View File

@@ -1,260 +0,0 @@
import sys
import json
from os import sep
from typing import Union, Literal
from dataclasses import dataclass, field
##########################################
# CONFIGURATION FILE #
##########################################
@dataclass
class ColorModel:
white: str = "\x0300"
black: str = "\x0301"
blue: str = "\x0302"
green: str = "\x0303"
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
@dataclass
class ConfigDataModel:
SERVEUR_IP: str
"""Server public IP (could be 127.0.0.1 localhost)"""
SERVEUR_HOSTNAME: str
"""IRC Server Hostname (your.hostname.extension)"""
SERVEUR_LINK: str
"""The link hostname (should be the same as your unrealircd link block)"""
SERVEUR_PORT: int
"""Server port as configured in your unrealircd link block"""
SERVEUR_PASSWORD: str
"""Your link password"""
SERVEUR_ID: str
"""Service identification could be Z01 should be unique"""
SERVEUR_SSL: bool
"""Activate SSL connexion"""
SERVICE_NAME: str
"""Service name (Ex. Defender)"""
SERVICE_NICKNAME: str
"""Nickname of the service (Ex. Defender)"""
SERVICE_REALNAME: str
"""Realname of the service"""
SERVICE_USERNAME: str
"""Username of the service"""
SERVICE_HOST: str
"""The service hostname"""
SERVICE_INFO: str
"""Swhois of the service"""
SERVICE_CHANLOG: str
"""The channel used by the service (ex. #services)"""
SERVICE_SMODES: str
"""The service mode (ex. +ioqBS)"""
SERVICE_CMODES: str
"""The mode of the log channel (ex. ntsO)"""
SERVICE_UMODES: str
"""The mode of the service when joining chanlog (ex. o, the service will be operator in the chanlog)"""
SERVICE_PREFIX: str
"""The default prefix to communicate with the service"""
SERVICE_ID: str = field(init=False)
"""The service unique ID"""
OWNER: str
"""The nickname of the admin of the service"""
PASSWORD: str
"""The password of the admin of the service"""
JSONRPC_URL: str
"""The RPC url, if local https://127.0.0.1:PORT/api should be fine"""
JSONRPC_PATH_TO_SOCKET_FILE: str
"""The full path of the socket file (/PATH/TO/YOUR/UNREALIRCD/SOCKET/FILE.socket)"""
JSONRPC_METHOD: str
"""3 methods are available; requests/socket/unixsocket"""
JSONRPC_USER: str
"""The RPC User defined in your unrealircd.conf"""
JSONRPC_PASSWORD: str
"""The RPC Password defined in your unrealircd.conf"""
SALON_JAIL: str
"""The JAIL channel (ex. #jail)"""
SALON_JAIL_MODES: str
"""The jail channel modes (ex. sS)"""
SALON_LIBERER: str
"""Channel where the nickname will be released"""
CLONE_CHANNEL: str
"""Channel where clones are hosted and will log PRIVMSG"""
CLONE_CMODES: str
"""Clone channel modes"""
CLONE_LOG_HOST_EXEMPT: list[str]
"""Hosts that clones will not log"""
CLONE_CHANNEL_PASSWORD: str
"""Clone password channel"""
API_TIMEOUT: int
"""Default api timeout in second"""
PORTS_TO_SCAN: list
"""List of ports to scan available for proxy_scan in the mod_defender module"""
WHITELISTED_IP: list
"""List of remote IP to don't scan"""
GLINE_DURATION: str
"""Gline duration"""
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50]
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
table_admin: str
"""Admin table"""
table_commande: str
"""Core command table"""
table_log: str
"""Core log table"""
table_module: str
"""Core module table"""
table_config: str
"""Core configuration table"""
table_channel: str
"""Core channel table"""
current_version: str
"""Current version of Defender"""
latest_version: str
"""The Latest version fetched from github"""
db_name: str
"""The database name"""
db_path: str
"""The database path"""
COLORS: ColorModel = field(default_factory=ColorModel)
"""Available colors in Defender"""
BATCH_SIZE: int = 400
"""The batch size used for privmsg and notice"""
def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
"""The service ID which is SERVEUR_ID and AAAAAB"""
class Config:
def __init__(self):
self.ConfigObject: ConfigDataModel = self.__load_service_configuration()
return None
def __load_json_service_configuration(self):
try:
conf_filename = f'core{sep}configuration.json'
with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
return configuration
except FileNotFoundError as fe:
print(f'FileNotFound: {fe}')
print('Configuration file not found please create core/configuration.json')
sys.exit(0)
except KeyError as ke:
print(f'Key Error: {ke}')
print('The key must be defined in core/configuration.json')
def __load_service_configuration(self) -> ConfigDataModel:
import_config = self.__load_json_service_configuration()
ConfigObject: ConfigDataModel = ConfigDataModel(
SERVEUR_IP=import_config["SERVEUR_IP"] if "SERVEUR_IP" in import_config else '127.0.0.1',
SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"] if "SERVEUR_HOSTNAME" in import_config else None,
SERVEUR_LINK=import_config["SERVEUR_LINK"] if "SERVEUR_LINK" in import_config else None,
SERVEUR_PORT=import_config["SERVEUR_PORT"] if "SERVEUR_PORT" in import_config else 6667,
SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"] if "SERVEUR_PASSWORD" in import_config else None,
SERVEUR_ID=import_config["SERVEUR_ID"] if "SERVEUR_ID" in import_config else '19Z',
SERVEUR_SSL=import_config["SERVEUR_SSL"] if "SERVEUR_SSL" in import_config else False,
SERVICE_NAME=import_config["SERVICE_NAME"] if "SERVICE_NAME" in import_config else 'Defender',
SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"] if "SERVICE_NICKNAME" in import_config else 'Defender',
SERVICE_REALNAME=import_config["SERVICE_REALNAME"] if "SERVICE_REALNAME" in import_config else 'Defender Security',
SERVICE_USERNAME=import_config["SERVICE_USERNAME"] if "SERVICE_USERNAME" in import_config else 'IRCSecurity',
SERVICE_HOST=import_config["SERVICE_HOST"] if "SERVICE_HOST" in import_config else 'defender.local.network',
SERVICE_INFO=import_config["SERVICE_INFO"] if "SERVICE_INFO" in import_config else 'Defender Network IRC Service',
SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"] if "SERVICE_CHANLOG" in import_config else '#services',
SERVICE_SMODES=import_config["SERVICE_SMODES"] if "SERVICE_SMODES" in import_config else '+ioqBS',
SERVICE_CMODES=import_config["SERVICE_CMODES"] if "SERVICE_CMODES" in import_config else 'ntsOP',
SERVICE_UMODES=import_config["SERVICE_UMODES"] if "SERVICE_UMODES" in import_config else 'o',
SERVICE_PREFIX=import_config["SERVICE_PREFIX"] if "SERVICE_PREFIX" in import_config else '!',
OWNER=import_config["OWNER"] if "OWNER" in import_config else 'admin',
PASSWORD=import_config["PASSWORD"] if "PASSWORD" in import_config else 'admin',
JSONRPC_METHOD=import_config["JSONRPC_METHOD"] if "JSONRPC_METHOD" in import_config else 'socket',
JSONRPC_URL=import_config["JSONRPC_URL"] if "JSONRPC_URL" in import_config else None,
JSONRPC_PATH_TO_SOCKET_FILE=import_config["JSONRPC_PATH_TO_SOCKET_FILE"] if "JSONRPC_PATH_TO_SOCKET_FILE" in import_config else None,
JSONRPC_USER=import_config["JSONRPC_USER"] if "JSONRPC_USER" in import_config else None,
JSONRPC_PASSWORD=import_config["JSONRPC_PASSWORD"] if "JSONRPC_PASSWORD" in import_config else None,
SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail',
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS',
SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome',
CLONE_CHANNEL=import_config["CLONE_CHANNEL"] if "CLONE_CHANNEL" in import_config else '#clones',
CLONE_CMODES=import_config["CLONE_CMODES"] if "CLONE_CMODES" in import_config else '+nts',
CLONE_LOG_HOST_EXEMPT=import_config["CLONE_LOG_HOST_EXEMPT"] if "CLONE_LOG_HOST_EXEMPT" in import_config else [],
CLONE_CHANNEL_PASSWORD=import_config["CLONE_CHANNEL_PASSWORD"] if "CLONE_CHANNEL_PASSWORD" in import_config else "clone_Password_1234",
API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2,
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [],
WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'],
GLINE_DURATION=import_config["GLINE_DURATION"] if "GLINE_DURATION" in import_config else '30',
DEBUG_LEVEL=import_config["DEBUG_LEVEL"] if "DEBUG_LEVEL" in import_config else 20,
table_admin='core_admin',
table_commande='core_command',
table_log='core_log',
table_module='core_module',
table_config='core_config',
table_channel='core_channel',
current_version='',
latest_version='',
db_name='defender',
db_path=f'db{sep}'
)
return ConfigObject

32
core/loader.py Normal file
View File

@@ -0,0 +1,32 @@
from core.classes import user, admin, channel, clone, reputation, settings
import core.definition as df
import core.base as baseModule
import core.classes.config as confModule
class Loader:
def __init__(self):
# Load Modules
self.Definition: df = df
self.ConfModule: confModule = confModule
self.BaseModule: baseModule = baseModule
# Load Classes
self.Settings: settings = settings.Settings()
self.Config: df.MConfig = self.ConfModule.Configuration().ConfigObject
self.Base: baseModule.Base = self.BaseModule.Base(self.Config, self.Settings)
self.User: user.User = user.User(self.Base)
self.Admin: admin.Admin = admin.Admin(self.Base)
self.Channel: channel.Channel = channel.Channel(self.Base)
self.Clone: clone.Clone = clone.Clone(self.Base)
self.Reputation: reputation.Reputation = reputation.Reputation(self.Base)

View File

@@ -12,12 +12,19 @@ from core import installation
# LANCEMENT DE DEFENDER #
#########################
# 1. Chargement de la configuration
# 2. Chargement de l'ensemble des classes
# 3.
#
try:
installation.Install()
from core.loader import Loader
from core.irc import Irc
ircInstance = Irc()
loader = Loader()
ircInstance = Irc(loader)
ircInstance.init_irc(ircInstance)
except AssertionError as ae:

View File

@@ -1,9 +1,11 @@
from dataclasses import dataclass, fields, field
import copy
import random, faker, time, logging
from datetime import datetime
from typing import Union
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.irc import Irc
from core.connection import Connection
class Clone():
@@ -11,7 +13,7 @@ class Clone():
class ModConfModel:
clone_nicknames: list[str]
def __init__(self, ircInstance:Irc) -> None:
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
@@ -19,6 +21,9 @@ class Clone():
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Irc Protocol Object to the module (Mandatory)
self.Protocol = ircInstance.Protocol
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
@@ -34,7 +39,10 @@ class Clone():
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
self.Clone = ircInstance.Clones
# Add clone object to the module (Optionnal)
self.Clone = ircInstance.Clone
self.Definition = ircInstance.Loader.Definition
# Créer les nouvelles commandes du module
self.commands_level = {
@@ -55,14 +63,22 @@ class Clone():
# Créer les tables necessaire a votre module (ce n'es pas obligatoire)
self.__create_tables()
self.CloneCopy = [self.Definition.MClone()]
self.stop = False
logging.getLogger('faker').setLevel(logging.CRITICAL)
self.fakeEN = faker.Faker('en_GB')
self.fakeFR = faker.Faker('fr_FR')
# Load module configuration (Mandatory)
self.__load_module_configuration()
self.Base.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} JOIN {self.Config.CLONE_CHANNEL}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
self.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Protocol.join(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
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}")
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
@@ -96,7 +112,7 @@ class Clone():
)
'''
self.Base.db_execute_query(table_channel)
# self.Base.db_execute_query(table_channel)
return None
@@ -122,153 +138,175 @@ class Clone():
rechargement de module
"""
# kill all clones before unload
for clone in self.ModConfig.clone_nicknames:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
self.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
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.part(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
self.Base.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -k {self.Config.CLONE_CHANNEL_PASSWORD}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {self.Config.CLONE_CHANNEL}")
return None
def thread_clone_clean_up(self, wait: float):
def generate_vhost(self) -> str:
activated = True
while activated:
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
if not clone.connected and clone.alive and not clone.init:
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
if self.Clone.delete(clone_nickname):
self.Logs.debug(f'<<{clone_nickname}>> object has been deleted')
del clone_to_kill
# If no more clones then stop this thread
if not self.Clone.UID_CLONE_DB:
break
time.sleep(wait)
def thread_change_hostname(self):
fake = faker.Faker('en_GB')
for clone in self.Clone.UID_CLONE_DB:
if not clone.vhost is None:
continue
fake = self.fakeEN
rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
found = False
vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
return vhost
while not found:
if clone.connected:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}')
found = True
clone.vhost = rand_ip
break
if not clone in self.Clone.UID_CLONE_DB:
found = True
break
def thread_create_clones_with_interval(self, number_of_clones:int, channels: list, connection_interval: float):
for i in range(number_of_clones):
nickname, username, realname = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, realname, channels, 6697, True)
)
time.sleep(connection_interval)
self.Base.create_thread(
self.thread_change_hostname
)
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
self.Base.create_thread(self.thread_clone_clean_up, (5, ), run_once=True)
def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None:
Connection(server_port=server_port, nickname=nickname, username=username, realname=realname, channels=channels, CloneObject=self.Clone, ssl=ssl)
return None
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Clones start to join {channel_name} with {wait} secondes frequency')
if clone_name is None:
for clone in self.Clone.UID_CLONE_DB:
if not channel_name in clone.channels:
time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
else:
for clone in self.Clone.UID_CLONE_DB:
if clone_name == clone.nickname:
if not channel_name in clone.channels:
time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
def generate_names(self) -> tuple[str, str, str]:
def generate_clones(self, group: str = 'Default') -> None:
try:
logging.getLogger('faker').setLevel(logging.CRITICAL)
fake = faker.Faker('en_GB')
# nickname = fake.first_name()
# username = fake.last_name()
# :5TC UID Q 1 1727473203 qb4u localhost 5TC00000Q * Sdiop network/service/test * AAAAAAAAAAAAAAAAAAAAAQ== :qbot4u
fakeEN = self.fakeEN
fakeFR = self.fakeFR
unixtime = self.Base.get_unixtime()
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
generate_uid = fakeEN.random_sample(chaine, 6)
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
# Generate Username
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
new_username = fake.random_sample(chaine, 9)
new_username = fakeEN.random_sample(chaine, 9)
username = ''.join(new_username)
# Create realname XX F|M Department
gender = fake.random_choices(['F','M'], 1)
gender = fakeEN.random_choices(['F','M'], 1)
gender = ''.join(gender)
if gender == 'F':
nickname = fake.first_name_female()
nickname = fakeEN.first_name_female()
elif gender == 'M':
nickname = fake.first_name_male()
nickname = fakeEN.first_name_male()
else:
nickname = fake.first_name()
nickname = fakeEN.first_name()
age = random.randint(20, 60)
fake_fr = faker.Faker(['fr_FR', 'en_GB'])
department = fake_fr.department_name()
department = fakeFR.department_name()
realname = f'{age} {gender} {department}'
if self.Clone.exists(nickname=nickname):
ip = self.Base.encode_ip(fakeEN.ipv4_private())
vhost = self.generate_vhost()
checkNickname = self.Clone.exists(nickname=nickname)
checkUid = self.Clone.uid_exists(uid=uid)
while checkNickname:
caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize)
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
)
else:
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
checkNickname = self.Clone.exists(nickname=nickname)
while checkUid:
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
generate_uid = fakeEN.random_sample(chaine, 6)
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
checkUid = self.Clone.uid_exists(uid=uid)
clone = self.Definition.MClone(
connected=False,
nickname=nickname,
username=username,
realname=realname,
uid=uid,
remote_ip=ip,
vhost=vhost,
group=group,
channels=[]
)
return (nickname, username, realname)
self.Clone.insert(clone)
self.CloneCopy.append(clone)
return None
except AttributeError as ae:
self.Logs.error(f'Attribute Error : {ae}')
except Exception as err:
self.Logs.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:
for i in range(0, number_of_clones):
self.generate_clones(group=group)
for clone in self.Clone.UID_CLONE_DB:
if self.stop:
print(f"Stop creating clones ...")
self.stop = False
break
if not clone.connected:
cloneObj_asdict = self.Clone.get_Clone_AsDict(clone.uid)
for key in ['connected','group','channels']:
cloneObj_asdict.pop(key, None)
self.User.insert(
self.Definition.MUser(**cloneObj_asdict)
)
self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} UID {clone.nickname} 1 {self.Base.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * +ixwz {clone.vhost} * {clone.remote_ip} :{clone.realname}", False)
self.Protocol.join(uidornickname=clone.uid, channel=self.Config.CLONE_CHANNEL, password=self.Config.CLONE_CHANNEL_PASSWORD, print_log=False)
self.Irc.Channel.insert(self.Irc.Loader.Definition.MChannel(name=self.Config.CLONE_CHANNEL, uids=[clone.uid]))
time.sleep(interval)
clone.connected = True
def thread_kill_clones(self, fromuser: str) -> None:
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
self.Protocol.send2socket(f':{clone.uid} QUIT :Goood bye', False)
clone_to_kill.append(clone.uid)
for clone_uid in clone_to_kill:
self.Irc.Channel.delete_user_from_all_channel(clone_uid)
self.Clone.delete(clone_uid)
self.User.delete(clone_uid)
del clone_to_kill
self.clean_clones(fromuser)
return None
def clean_clones(self, fromuser: str) -> None:
connected = 0
for c in self.CloneCopy:
if c.connected:
connected += 1
self.Protocol.sendNotice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=fromuser, msg=f"Clean in progress | Total Clones in memory {len(self.CloneCopy) - 1} - {connected} / {len(self.CloneCopy) - 1} Connected ...")
clone_to_kill: list[str] = []
# clean from Channels
for clone in self.CloneCopy:
self.Irc.Channel.delete_user_from_all_channel(clone.uid)
# clean from users
for clone in self.CloneCopy:
self.Protocol.send2socket(f':{clone.uid} QUIT :Goood bye', False)
clone_to_kill.append(clone.uid)
# clean original clone object
for clone_uid in clone_to_kill:
self.User.delete(clone_uid)
self.Clone.delete(clone_uid)
self.CloneCopy = [self.Definition.MClone()]
self.Protocol.sendNotice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=fromuser,
msg="Clone cleaning done!")
return None
def cmd(self, data:list) -> None:
try:
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
@@ -280,6 +318,36 @@ class Clone():
case 'REPUTATION':
pass
if len(cmd) < 3:
return None
match cmd[2]:
case 'PRIVMSG':
# print(cmd)
uid_sender = self.User.clean_uid(cmd[1])
senderObj = self.User.get_User(uid_sender)
if senderObj.hostname in self.Config.CLONE_LOG_HOST_EXEMPT:
return None
if not senderObj is None:
senderMsg = ' '.join(cmd[4:])
getClone = self.Clone.get_Clone(cmd[3])
if getClone is None:
return None
if getClone.uid != self.Config.SERVICE_ID:
final_message = f"{senderObj.nickname}!{senderObj.username}@{senderObj.hostname} > {senderMsg.lstrip(':')}"
self.Protocol.sendPrivMsg(
nick_from=senderObj.uid,
msg=final_message,
channel=self.Config.CLONE_CHANNEL
)
except Exception as err:
self.Base.logs.error(f'General Error: {err}')
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
try:
@@ -293,10 +361,11 @@ class Clone():
case 'clone':
if len(cmd) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6 2.5')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | nickname] #channel")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | nickname] #channel")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
option = str(cmd[1]).lower()
@@ -304,46 +373,47 @@ class Clone():
case 'connect':
try:
# clone connect 5
# clone connect 5 Group 3
self.stop = False
number_of_clones = int(cmd[2])
connection_interval = int(cmd[3]) if len(cmd) == 4 else 0.5
group = str(cmd[3]).lower()
connection_interval = int(cmd[4]) if len(cmd) == 5 else 0.5
self.Base.create_thread(
self.thread_create_clones_with_interval,
(number_of_clones, [], connection_interval)
func=self.thread_connect_clones,
func_args=(number_of_clones, group, connection_interval)
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect] [Connection Interval]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6 2.5')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group]")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"Exemple /msg {dnickname} clone connect 6 Ambiance")
case 'kill':
try:
# clone kill [all | nickname]
self.stop = True
clone_name = str(cmd[2])
clone_to_kill: list[str] = []
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
self.Clone.delete(clone_nickname)
del clone_to_kill
self.Base.create_thread(func=self.thread_kill_clones, func_args=(fromuser, ))
else:
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL')
self.Clone.kill(clone_name)
self.Protocol.send2socket(f':{clone_name} QUIT :Goood bye')
clone_uid = self.Clone.get_uid(clone_name)
if not clone_uid is None:
self.Irc.Channel.delete_user_from_all_channel(clone_uid)
self.Clone.delete(clone_name)
self.User.delete(clone_uid)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill all")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill clone_nickname")
case 'join':
try:
@@ -352,21 +422,53 @@ class Clone():
clone_channel_to_join = str(cmd[3])
if clone_name.lower() == 'all':
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2))
for clone in self.Clone.UID_CLONE_DB:
self.Protocol.join(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False)
self.Irc.Channel.insert(self.Irc.Loader.Definition.MChannel(name=clone_channel_to_join, uids=[clone.uid]))
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
if self.Clone.exists(clone_name):
if not self.Clone.get_uid(clone_name) is None:
self.Protocol.join(uidornickname=clone_name, channel=clone_channel_to_join, print_log=False)
self.Irc.Channel.insert(self.Irc.Loader.Definition.MChannel(name=clone_channel_to_join, uids=[self.Clone.get_uid(clone_name)]))
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join all #channel")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join clone_nickname #channel")
case 'part':
try:
# clone part [all | nickname] #channel
clone_name = str(cmd[2])
clone_channel_to_part = str(cmd[3])
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Protocol.part(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False)
self.Irc.Channel.delete_user_from_channel(clone_channel_to_part, clone.uid)
else:
if self.Clone.exists(clone_name):
clone_uid = self.Clone.get_uid(clone_name)
if not clone_uid is None:
self.Protocol.part(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False)
self.Irc.Channel.delete_user_from_channel(clone_channel_to_part, clone_uid)
except Exception as err:
self.Logs.error(f'{err}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part all #channel")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part clone_nickname #channel")
case 'list':
try:
clone_count = len(self.Clone.UID_CLONE_DB)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Number of connected clones: {clone_count}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f">> Number of connected clones: {clone_count}")
for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | Init: {clone_name.init} | Live: {clone_name.alive} | Connected: {clone_name.connected}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser,
msg=f">> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | UID: {clone_name.uid} | Group: {clone_name.group} | Connected: {clone_name.connected}")
except Exception as err:
self.Logs.error(f'{err}')
@@ -374,30 +476,36 @@ class Clone():
try:
# clone say clone_nickname #channel message
clone_name = str(cmd[2])
clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None
clone_channel = str(cmd[3]) if self.Channel.Is_Channel(str(cmd[3])) else None
message = []
for i in range(4, len(cmd)):
message.append(cmd[i])
final_message = ' '.join(message)
final_message = ' '.join(cmd[4:])
if clone_channel is None or not self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
self.Protocol.sendNotice(
nick_from=dnickname,
nick_to=fromuser,
msg=f"/msg {dnickname} clone say [clone_nickname] #channel message"
)
return None
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
self.Protocol.sendPrivMsg(nick_from=clone_name, msg=final_message, channel=clone_channel)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
self.Protocol.sendNotice(
nick_from=dnickname,
nick_to=fromuser,
msg=f"/msg {dnickname} clone say [clone_nickname] #channel message"
)
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | nickname] #channel")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | nickname] #channel")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
except IndexError as ie:
self.Logs.error(f'Index Error: {ie}')
except Exception as err:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,11 @@
import logging
from typing import TYPE_CHECKING
from dataclasses import dataclass
from core.irc import Irc
from unrealircd_rpc_py.Live import Live
from unrealircd_rpc_py.Loader import Loader
if TYPE_CHECKING:
from core.irc import Irc
class Jsonrpc():
@@ -12,7 +15,7 @@ class Jsonrpc():
"""
jsonrpc: int = 0
def __init__(self, ircInstance:Irc) -> None:
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
@@ -20,6 +23,9 @@ class Jsonrpc():
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Protocol to the module (Mandatory)
self.Protocol = ircInstance.Protocol
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
@@ -50,6 +56,7 @@ class Jsonrpc():
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
logging.getLogger('websockets').setLevel(logging.WARNING)
# Create you own tables (Mandatory)
# self.__create_tables()
@@ -71,8 +78,7 @@ class Jsonrpc():
username=self.Config.JSONRPC_USER,
password=self.Config.JSONRPC_PASSWORD,
callback_object_instance=self,
callback_method_name='callback_sent_to_irc',
debug_level=10
callback_method_name='callback_sent_to_irc'
)
self.Rpc: Loader = Loader(
@@ -85,10 +91,18 @@ class Jsonrpc():
self.subscribed = False
if self.Rpc.Error.code != 0:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}", self.Config.SERVICE_CHANLOG)
self.Protocol.sendPrivMsg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}",
channel=self.Config.SERVICE_CHANLOG
)
if self.UnrealIrcdRpcLive.Error.code != 0:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG)
self.Protocol.sendPrivMsg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}",
channel=self.Config.SERVICE_CHANLOG
)
if self.ModConfig.jsonrpc == 1:
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
@@ -139,7 +153,10 @@ class Jsonrpc():
red = self.Config.COLORS.red
if json_response.result == True:
self.Irc.sendPrivMsg(msg=f"[{bold}{green}JSONRPC{nogc}{bold}] Event activated", channel=dchanlog)
self.Protocol.sendPrivMsg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{bold}{green}JSONRPC{nogc}{bold}] Event activated",
channel=dchanlog)
return None
level = json_response.result.level
@@ -150,7 +167,7 @@ class Jsonrpc():
build_msg = f"{green}{log_source}{nogc}: [{bold}{level}{bold}] {subsystem}.{event_id} - {msg}"
self.Irc.sendPrivMsg(msg=build_msg, channel=dchanlog)
self.Protocol.sendPrivMsg(nick_from=dnickname, msg=build_msg, channel=dchanlog)
def thread_start_jsonrpc(self):
@@ -158,7 +175,11 @@ class Jsonrpc():
self.UnrealIrcdRpcLive.subscribe(["all"])
self.subscribed = True
else:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG)
self.Protocol.sendPrivMsg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}",
channel=self.Config.SERVICE_CHANLOG
)
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
@@ -208,8 +229,8 @@ class Jsonrpc():
option = str(cmd[1]).lower()
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc on')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc off')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc on')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off')
match option:
@@ -217,9 +238,17 @@ class Jsonrpc():
for thread in self.Base.running_threads:
if thread.getName() == 'thread_start_jsonrpc':
if thread.is_alive():
self.Irc.sendPrivMsg(f"Thread {thread.getName()} is running", dchannel)
self.Protocol.sendPrivMsg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"Thread {thread.getName()} is running",
channel=dchannel
)
else:
self.Irc.sendPrivMsg(f"Thread {thread.getName()} is not running, wait untill the process will be cleaned up", dchannel)
self.Protocol.sendPrivMsg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"Thread {thread.getName()} is not running, wait untill the process will be cleaned up",
channel=dchannel
)
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
self.__update_configuration('jsonrpc', 1)
@@ -236,7 +265,7 @@ class Jsonrpc():
option = str(cmd[1]).lower()
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jruser get nickname')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname')
match option:
@@ -250,37 +279,37 @@ class Jsonrpc():
UserInfo = rpc.User.get(uid_to_get)
if rpc.Error.code != 0:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{rpc.Error.message}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'{rpc.Error.message}')
return None
chan_list = []
for chan in UserInfo.user.channels:
chan_list.append(chan.name)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.user.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.user.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :MODES : {UserInfo.user.modes}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {chan_list}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.user.security_groups}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.user.reputation}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'UID : {UserInfo.id}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'NICKNAME : {UserInfo.name}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'USERNAME : {UserInfo.user.username}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'REALNAME : {UserInfo.user.realname}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'MODES : {UserInfo.user.modes}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'CHANNELS : {chan_list}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'SECURITY GROUP : {UserInfo.user.security_groups}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'REPUTATION : {UserInfo.user.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IP : {UserInfo.ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :COUNTRY CODE : {UserInfo.geoip.country_code}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASN : {UserInfo.geoip.asn}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASNAME : {UserInfo.geoip.asname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLOAKED HOST : {UserInfo.user.cloakedhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :HOSTNAME : {UserInfo.hostname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :VHOST : {UserInfo.user.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLIENT PORT : {UserInfo.client_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SERVER PORT : {UserInfo.server_port}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'IP : {UserInfo.ip}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'COUNTRY CODE : {UserInfo.geoip.country_code}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'ASN : {UserInfo.geoip.asn}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'ASNAME : {UserInfo.geoip.asname}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'CLOAKED HOST : {UserInfo.user.cloakedhost}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'HOSTNAME : {UserInfo.hostname}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'VHOST : {UserInfo.user.vhost}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'CLIENT PORT : {UserInfo.client_port}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'SERVER PORT : {UserInfo.server_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CERTFP : {UserInfo.tls.certfp}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CIPHER : {UserInfo.tls.cipher}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'CERTFP : {UserInfo.tls.certfp}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'CIPHER : {UserInfo.tls.cipher}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IDLE SINCE : {UserInfo.idle_since}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CONNECTED SINCE : {UserInfo.connected_since}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'IDLE SINCE : {UserInfo.idle_since}')
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f'CONNECTED SINCE : {UserInfo.connected_since}')
except IndexError as ie:
self.Logs.error(ie)
@@ -290,11 +319,11 @@ class Jsonrpc():
self.Base.create_thread(self.thread_ask_ia, ('',))
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...")
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...")
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f" This is a notice to the sender ...")
self.Protocol.sendPrivMsg(nick_from=dnickname, msg="This is private message to the sender ...", nick_to=fromuser)
if not fromchannel is None:
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...")
self.Protocol.sendPrivMsg(nick_from=dnickname, msg="This is channel message to the sender ...", channel=fromchannel)
# How to update your module configuration
self.__update_configuration('param_exemple2', 7)

View File

@@ -1,4 +1,7 @@
from typing import TYPE_CHECKING
from dataclasses import dataclass, fields
if TYPE_CHECKING:
from core.irc import Irc
class Test():
@@ -10,7 +13,7 @@ class Test():
param_exemple1: str
param_exemple2: int
def __init__(self, ircInstance:Irc) -> None:
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
@@ -18,6 +21,12 @@ class Test():
# 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
@@ -33,6 +42,9 @@ class Test():
# 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.commands_level = {
0: ['test-command'],

View File

@@ -1,7 +1,10 @@
from core.irc import Irc
from typing import TYPE_CHECKING
import re
from dataclasses import dataclass, field
if TYPE_CHECKING:
from core.irc import Irc
# Activer le systeme sur un salon (activate #salon)
# Le service devra se connecter au salon
# Le service devra se mettre en op
@@ -23,7 +26,7 @@ class Votekick():
VOTE_CHANNEL_DB:list[VoteChannelModel] = []
def __init__(self, ircInstance:Irc) -> None:
def __init__(self, ircInstance: 'Irc') -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
@@ -195,7 +198,7 @@ class Votekick():
def join_saved_channels(self) -> None:
param = {'module_name': self.module_name}
result = self.Base.db_execute_query(f"SELECT id, channel_name FROM {self.Config.table_channel} WHERE module_name = :module_name", param)
result = self.Base.db_execute_query(f"SELECT id, channel_name FROM {self.Config.TABLE_CHANNEL} WHERE module_name = :module_name", param)
channels = result.fetchall()
unixtime = self.Base.get_unixtime()
@@ -285,7 +288,7 @@ class Votekick():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
sentchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(str(cmd[2]).lower()) else None
if sentchannel is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
@@ -299,7 +302,7 @@ class Votekick():
)
)
self.Base.db_query_channel('add', self.module_name, sentchannel)
self.Channel.db_query_channel('add', self.module_name, sentchannel)
self.Irc.send2socket(f":{dnickname} JOIN {sentchannel}")
self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
@@ -316,7 +319,7 @@ class Votekick():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
sentchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(str(cmd[2]).lower()) else None
if sentchannel is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
@@ -326,7 +329,7 @@ class Votekick():
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == sentchannel:
self.VOTE_CHANNEL_DB.remove(chan)
self.Base.db_query_channel('del', self.module_name, chan.channel_name)
self.Channel.db_query_channel('del', self.module_name, chan.channel_name)
self.Logs.debug(f"The Channel {sentchannel} has been deactivated from the vote system")
except Exception as err:

View File

@@ -1,5 +1,5 @@
{
"version": "5.4.0",
"version": "6.0.0",
"requests": "2.32.3",
"psutil": "6.0.0",