37 Commits

Author SHA1 Message Date
adator
be36c56ceb Merge pull request #73 from adator85/update/protocol
Update/protocol
2025-07-20 19:44:49 +02:00
adator
ac332e6802 Update version number 2025-07-20 19:42:59 +02:00
adator
6063ceba35 Update Protocol Connection 2025-07-20 19:41:10 +02:00
adator
2204c4fdf8 Merge pull request #72 from adator85/V6.X.X
V6.1.1 Updates
2024-12-08 23:03:01 +01:00
adator
befe452df8 V6.1.1 Updates 2024-12-08 23:02:36 +01:00
adator
b98a20ad45 Merge pull request #71 from adator85/V6.X.X
V6.1.0 update the help command
2024-12-08 12:56:18 +01:00
adator
cb042a5411 V6.1.0 update the help command 2024-12-08 12:52:36 +01:00
adator
dc63df08cf Merge pull request #70 from adator85/V6.X.X
Update on_version method
2024-11-22 23:27:15 +01:00
adator
a3edf48120 Update on_version method 2024-11-22 23:26:39 +01:00
adator
a3a61c332f Merge pull request #69 from adator85/V6.X.X
V6.0.4
2024-11-18 23:50:25 +01:00
adator
f7664c9874 V6.0.4 2024-11-18 23:49:45 +01:00
adator
eeaacddbf2 Merge pull request #68 from adator85/V6.X.X
Update to version 6.0.3
2024-11-17 21:09:51 +01:00
adator
d37c152160 Update to version 6.0.3 2024-11-17 21:09:14 +01:00
adator
39412fc1c0 Merge pull request #67 from adator85/V6.X.X
V6.0.2
2024-11-15 22:14:55 +01:00
adator
b81f502b95 V6.0.2 2024-11-15 22:14:11 +01:00
adator
e148659d00 Merge pull request #66 from adator85/V6.X.X
V6.0.1
2024-11-11 23:38:26 +01:00
adator
44da01945c V6.0.1 2024-11-11 23:38:05 +01:00
adator
71a7d29b08 Merge pull request #65 from adator85/V6.X.X
Update the version 6
2024-11-11 15:39:20 +01:00
adator
bd9713006a Update the version 6 2024-11-11 15:38:38 +01:00
adator
71170baf1a Merge pull request #64 from adator85/V6.X.X
Fix clone reply
2024-11-07 23:43:02 +01:00
adator
4825775b73 Fix clone reply 2024-11-07 23:42:11 +01:00
adator
1b20435b83 Merge pull request #63 from adator85/V6.X.X
fix main file name
2024-11-07 23:11:26 +01:00
adator
96ebf0511b fix main file name 2024-11-07 23:10:18 +01:00
adator
008dacfde6 Merge pull request #62 from adator85/V6.X.X
Disable console
2024-11-07 23:03:47 +01:00
adator
91a2eafd82 Disable console 2024-11-07 23:03:21 +01:00
adator
5347c45579 Merge pull request #61 from adator85/V6.X.X
V6.0.0
2024-11-07 22:50:18 +01:00
adator
a93d69214e Activate installation 2024-11-07 22:49:41 +01:00
adator
63130fbc06 Merge pull request #60 from adator85/V6.X.X
V6.0.0
2024-11-07 22:46:20 +01:00
adator
b8cd2f244b First version 6.0.0 2024-11-07 22:45:25 +01:00
adator
395dec47be Connexion to inspircd done ... 2024-11-07 00:51:09 +01:00
adator
709e8d4419 Could be the first version 6-rc 2024-11-06 01:15:11 +01:00
adator
e07b047b6a Update all Protocol calls 2024-11-03 18:49:04 +01:00
adator
cbae3dce96 many updates 2024-11-02 23:22:36 +01:00
adator
9d9ede0e80 First Version 6 2024-11-01 23:52:22 +01:00
adator
860e265979 Latest release for version 5 2024-11-01 14:05:26 +01:00
adator
b27b503d78 Merge pull request #59 from adator85/V5.X.X
V5.3.9
2024-10-13 22:22:00 +02:00
adator
f7c80d190e V5.3.9 2024-10-13 22:21:01 +02:00
30 changed files with 6212 additions and 3402 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')
self.logs.debug(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:
@@ -89,18 +100,18 @@ class Base:
bool: True if there is a new version available
"""
try:
self.logs.debug(f'Checking for a new service version')
self.logs.debug(f'-- Checking for a new service version')
# Assigner la version actuelle de Defender
self.__set_current_defender_version()
# Récuperer la dernier version disponible dans github
if online:
self.logs.debug(f'Retrieve the latest version from Github')
self.logs.debug(f'-- Retrieve the latest version from Github')
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,70 @@ 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',
encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
self.logs = logging.getLogger(self.Config.LOGGING_NAME)
self.logs.setLevel(self.Config.DEBUG_LEVEL)
# Add Handlers
file_hanlder = logging.FileHandler(f'logs{self.Config.OS_SEP}defender.log',encoding='UTF-8')
file_hanlder.setLevel(self.Config.DEBUG_LEVEL)
stdout_handler = logging.StreamHandler()
stdout_handler.setLevel(50)
# Define log format
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
# Apply log format
file_hanlder.setFormatter(formatter)
stdout_handler.setFormatter(formatter)
# Add handler to logs
self.logs.addHandler(file_hanlder)
self.logs.addHandler(stdout_handler)
# Apply the filter
self.logs.addFilter(self.replace_filter)
# self.logs.Logger('defender').addFilter(self.replace_filter)
self.logs.info('#################### STARTING DEFENDER ####################')
return None
def replace_filter(self, record: logging.LogRecord) -> bool:
response = True
filter: list[str] = ['PING', f":{self.Config.SERVICE_PREFIX}auth"]
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
if self.Settings.CONSOLE:
print(record.getMessage())
for f in filter:
if f in record.getMessage():
response = False
return response # Retourne True pour permettre l'affichage du message
def delete_logger(self, logger_name: str) -> None:
# Récupérer le logger
logger = logging.getLogger(logger_name)
# Retirer tous les gestionnaires du logger et les fermer
for handler in logger.handlers[:]: # Utiliser une copie de la liste
logger.removeHandler(handler)
handler.close()
# Supprimer le logger du dictionnaire global
logging.Logger.manager.loggerDict.pop(logger_name, None)
return None
def log_cmd(self, user_cmd:str, cmd:str) -> None:
"""Enregistre les commandes envoyées par les utilisateurs
@@ -190,12 +254,12 @@ class Base:
"""
cmd_list = cmd.split()
if len(cmd_list) == 3:
if cmd_list[0].replace('.', '') == 'auth':
if cmd_list[0].replace(self.Config.SERVICE_PREFIX, '') == 'auth':
cmd_list[1] = '*******'
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 +274,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 +292,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 +307,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 +319,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 +342,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 +406,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 +438,9 @@ 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.
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'
if not channel_name:
self.logs.warn(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.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
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:
return False
case _:
return False
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 +453,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)"""
@@ -459,7 +470,7 @@ class Base:
self.running_timers.append(t)
self.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
self.logs.debug(f"-- Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.logs.error(f'Assertion Error -> {ae}')
@@ -484,11 +495,30 @@ class Base:
th.start()
self.running_threads.append(th)
self.logs.debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
self.logs.debug(f"-- Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
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
"""
@@ -498,9 +528,9 @@ class Base:
if not timer.is_alive():
timer.cancel()
self.running_timers.remove(timer)
self.logs.info(f"Timer {str(timer)} removed")
self.logs.info(f"-- Timer {str(timer)} removed")
else:
self.logs.debug(f"===> Timer {str(timer)} Still running ...")
self.logs.debug(f"--* Timer {str(timer)} Still running ...")
except AssertionError as ae:
self.logs.error(f'Assertion Error -> {ae}')
@@ -511,9 +541,10 @@ 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")
self.logs.info(f"-- Thread {str(thread.getName())} {str(thread.native_id)} removed")
# print(threading.enumerate())
except AssertionError as ae:
@@ -528,7 +559,7 @@ class Base:
soc.close()
self.running_sockets.remove(soc)
self.logs.debug(f"Socket ==> closed {str(soc.fileno())}")
self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}")
def shutdown(self) -> None:
"""Methode qui va préparer l'arrêt complêt du service
@@ -564,8 +595,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
@@ -573,19 +604,19 @@ class Base:
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
cursor = engine.connect()
self.logs.info("database connexion has been initiated")
self.logs.info("-- database connexion has been initiated")
return engine, cursor
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 +625,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 +633,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 +642,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 +650,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,
@@ -630,10 +661,25 @@ class Base:
)
'''
table_core_client = f'''CREATE TABLE IF NOT EXISTS {self.Config.TABLE_CLIENT} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
createdOn TEXT,
account TEXT,
nickname TEXT,
hostname TEXT,
vhost TEXT,
realname TEXT,
email TEXT,
password TEXT,
level INTEGER
)
'''
self.db_execute_query(table_core_log)
self.db_execute_query(table_core_log_command)
self.db_execute_query(table_core_module)
self.db_execute_query(table_core_admin)
self.db_execute_query(table_core_client)
self.db_execute_query(table_core_channel)
self.db_execute_query(table_core_config)
@@ -695,7 +741,24 @@ class Base:
except TypeError:
return value
def is_valid_ip(self, ip_to_control:str) -> bool:
def convert_to_int(self, value: any) -> Union[int, None]:
"""Convert a value to int
Args:
value (any): Value to convert to int if possible
Returns:
Union[int, None]: Return the int value or None if not possible
"""
try:
response = int(value)
return response
except ValueError:
return None
except TypeError:
return None
def is_valid_ip(self, ip_to_control: str) -> bool:
try:
if ip_to_control in self.Config.WHITELISTED_IP:
@@ -706,6 +769,26 @@ class Base:
except ValueError:
return False
def is_valid_email(self, email_to_control: str) -> bool:
"""Check if the email is valid
Args:
email_to_control (str): email to control
Returns:
bool: True is the email is correct
"""
try:
pattern = '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
if re.match(pattern, email_to_control):
return True
else:
return False
except Exception as err:
self.logs.error(f'General Error: {err}')
return False
def decode_ip(self, ip_b64encoded: str) -> Union[str, None]:
binary_ip = b64decode(ip_b64encoded)
@@ -717,16 +800,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 +842,28 @@ class Base:
# Vider le dictionnaire de fonction
self.periodic_func.clear()
def clean_uid(self, uid:str) -> str:
def execute_dynamic_method(self, obj: object, method_name: str, params: list) -> None:
"""#### Ajouter les méthodes a éxecuter dans un dictionnaire
Les methodes seront exécuter par heartbeat.
Args:
obj (object): Une instance de la classe qui va etre executer
method_name (str): Le nom de la méthode a executer
params (list): les parametres a faire passer
Returns:
None: aucun retour attendu
"""
self.periodic_func[len(self.periodic_func) + 1] = {
'object': obj,
'method_name': method_name,
'param': params
}
self.logs.debug(f'Method to execute : {str(self.periodic_func)}')
return None
def clean_uid(self, uid:str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
@@ -765,34 +872,13 @@ class Base:
Returns:
str: Clean UID without any sign
"""
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
if uid is None:
return None
pattern = fr'^#'
isChannel = re.findall(pattern, channelToCheck)
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', uid)
if not isChannel:
return False
else:
return True
return parsed_UID
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

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

@@ -0,0 +1,269 @@
from re import findall
from typing import Union, Literal, TYPE_CHECKING
from dataclasses import asdict
from core.classes import user
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
if not self.Is_Channel(newChan.name):
self.Logs.error(f"The channel {newChan.name} is not valid, channel must start with #")
return False
for record in self.UID_CHANNEL_DB:
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, channel_name: str) -> bool:
chanObj = self.get_Channel(channel_name)
if chanObj is None:
return False
self.UID_CHANNEL_DB.remove(chanObj)
return True
def delete_user_from_channel(self, channel_name: str, uid:str) -> bool:
try:
result = False
chanObj = self.get_Channel(channel_name.lower())
if chanObj is None:
return result
for userid in chanObj.uids:
if self.Base.clean_uid(userid) == self.Base.clean_uid(uid):
chanObj.uids.remove(userid)
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 add_user_to_a_channel(self, channel_name: str, uid: str) -> bool:
try:
result = False
chanObj = self.get_Channel(channel_name)
self.Logs.debug(f"** {__name__}")
if chanObj is None:
result = self.insert(MChannel(channel_name, uids=[uid]))
# self.Logs.debug(f"** {__name__} - result: {result}")
# self.Logs.debug(f'New Channel Created: ({chanObj})')
return result
chanObj.uids.append(uid)
del_duplicates = list(set(chanObj.uids))
chanObj.uids = del_duplicates
# self.Logs.debug(f'New Channel Created: ({chanObj})')
return True
except Exception as err:
self.Logs.error(f'{err}')
def is_user_present_in_channel(self, channel_name: str, uid: str) -> bool:
"""Check if a user is present in the channel
Args:
channel_name (str): The channel to check
uid (str): The UID
Returns:
bool: True if the user is present in the channel
"""
user_found = False
chan = self.get_Channel(channel_name=channel_name)
if chan is None:
return user_found
clean_uid = self.Base.clean_uid(uid=uid)
for chan_uid in chan.uids:
if self.Base.clean_uid(chan_uid) == clean_uid:
user_found = True
break
return user_found
def clean_channel(self) -> None:
"""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, channel_name: str) -> Union['MChannel', None]:
Channel = None
for record in self.UID_CHANNEL_DB:
if record.name == channel_name:
Channel = record
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)

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

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

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

@@ -0,0 +1,161 @@
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
"""
cloneObj = self.get_Clone(uidornickname=uidornickname)
if cloneObj is None:
return False
self.UID_CLONE_DB.remove(cloneObj)
return True
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
"""
cloneObj = None
for clone in self.UID_CLONE_DB:
if clone.uid == uidornickname:
cloneObj = clone
if clone.nickname == uidornickname:
cloneObj = clone
return cloneObj
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)

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

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

View File

@@ -0,0 +1,671 @@
from re import match, findall
from datetime import datetime
from typing import TYPE_CHECKING
from ssl import SSLEOFError, SSLError
if TYPE_CHECKING:
from core.irc import Irc
class Inspircd:
def __init__(self, ircInstance: 'Irc'):
self.name = 'InspIRCd-4'
self.__Irc = ircInstance
self.__Config = ircInstance.Config
self.__Base = ircInstance.Base
self.__Base.logs.info(f"** Loading protocol [{__name__}]")
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 as ude:
self.__Base.logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except UnicodeEncodeError as uee:
self.__Base.logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'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 send_priv_msg(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 send_notice(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"CAPAB START 1206")
self.send2socket(f"CAPAB CAPABILITIES :NICKMAX=30 CHANMAX=64 MAXMODES=20 IDENTMAX=10 MAXQUIT=255 MAXTOPIC=307 MAXKICK=255 MAXREAL=128 MAXAWAY=200 MAXHOST=64 MAXLINE=512 CASEMAPPING=ascii GLOBOPS=0")
self.send2socket(f"CAPAB END")
self.send2socket(f"SERVER {link} {password} {server_id} :{info}")
self.send2socket(f"BURST {unixtime}")
self.send2socket(f":{server_id} ENDBURST")
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:
if not reason:
reason = 'Service Shutdown'
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:
if not self.__Irc.Channel.Is_Channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
return None
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} + :{self.__Config.SERVICE_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 send_quit(self, uid: str, reason: str, print_log: True) -> None:
"""Send quit message
Args:
uidornickname (str): The UID or the Nickname
reason (str): The reason for the quit
"""
userObj = self.__Irc.User.get_User(uidornickname=uid)
cloneObj = self.__Irc.Clone.get_Clone(uidornickname=uid)
reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid)
if not userObj is None:
self.send2socket(f":{userObj.uid} QUIT :{reason}", print_log=print_log)
self.__Irc.User.delete(userObj.uid)
if not cloneObj is None:
self.__Irc.Clone.delete(cloneObj.uid)
if not reputationObj is None:
self.__Irc.Reputation.delete(reputationObj.uid)
if not self.__Irc.Channel.delete_user_from_all_channel(uid):
self.__Base.logs.error(f"The UID [{uid}] has not been deleted from all channels")
return None
def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None:
"""Send UID to the server
Args:
nickname (str): Nickname of the client
username (str): Username of the client
hostname (str): Hostname of the client you want to create
uid (str): UID of the client you want to create
umodes (str): umodes of the client you want to create
vhost (str): vhost of the client you want to create
remote_ip (str): remote_ip of the client you want to create
realname (str): realname of the client you want to create
print_log (bool, optional): print logs if true. Defaults to True.
"""
# {self.Config.SERVEUR_ID} UID
# {clone.nickname} 1 {self.Base.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname}
try:
unixtime = self.__Base.get_unixtime()
encoded_ip = self.__Base.encode_ip(remote_ip)
# Create the user
self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser(
uid=uid, nickname=nickname, username=username,
realname=realname,hostname=hostname, umodes=umodes,
vhost=vhost, remote_ip=remote_ip
)
)
uid_msg = f":{self.__Config.SERVEUR_ID} UID {nickname} 1 {unixtime} {username} {hostname} {uid} * {umodes} {vhost} * {encoded_ip} :{realname}"
self.send2socket(uid_msg, print_log=print_log)
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def send_join_chan(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):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
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 send_part_chan(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:
self.__Base.logs.error(f"The user [{uidornickname}] is not valid")
return None
if not self.__Irc.Channel.Is_Channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
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_umode2(self, serverMsg: list[str]) -> None:
"""Handle umode2 coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# [':adator_', 'UMODE2', '-iwx']
userObj = self.__Irc.User.get_User(str(serverMsg[0]).lstrip(':'))
userMode = serverMsg[2]
if userObj is None: # If user is not created
return None
# save previous user modes
old_umodes = userObj.umodes
# TODO : User object should be able to update user modes
if self.__Irc.User.update_mode(userObj.uid, userMode):
return None
# self.__Base.logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]")
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_quit(self, serverMsg: list[str]) -> None:
"""Handle quit coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@unrealircd.org/userhost=...@192.168.1.10;unrealircd.org/userip=...@192.168.1.10;msgid=CssUrV08BzekYuq7BfvPHn;time=2024-11-02T15:03:33.182Z', ':001JKNY0N', 'QUIT', ':Quit:', '....']
uid_who_quit = str(serverMsg[1]).lstrip(':')
self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit)
self.__Irc.User.delete(uid_who_quit)
self.__Irc.Reputation.delete(uid_who_quit)
self.__Irc.Clone.delete(uid_who_quit)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_squit(self, serverMsg: list[str]) -> None:
"""Handle squit coming from a server
Args:
serverMsg (list[str]): Original server message
"""
# ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed']
server_hostname = serverMsg[2]
uid_to_delete = None
for s_user in self.__Irc.User.UID_DB:
if s_user.hostname == server_hostname and 'S' in s_user.umodes:
uid_to_delete = s_user.uid
if uid_to_delete is None:
return None
self.__Irc.User.delete(uid_to_delete)
self.__Irc.Channel.delete_user_from_all_channel(uid_to_delete)
return None
def on_protoctl(self, serverMsg: list[str]) -> None:
"""Handle protoctl coming from a server
Args:
serverMsg (list[str]): Original server message
"""
if len(serverMsg) > 5:
if '=' in serverMsg[5]:
serveur_hosting_id = str(serverMsg[5]).split('=')
self.__Config.HSID = serveur_hosting_id[1]
return None
def on_nick(self, serverMsg: list[str]) -> None:
"""Handle nick coming from a server
new nickname
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname
uid = str(serverMsg[1]).lstrip(':')
newnickname = serverMsg[3]
self.__Irc.User.update_nickname(uid, newnickname)
self.__Irc.Client.update_nickname(uid, newnickname)
self.__Irc.Admin.update_nickname(uid, newnickname)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_sjoin(self, serverMsg: list[str]) -> None:
"""Handle sjoin coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH']
# ['@msgid=bvceb6HthbLJapgGLXn1b0;time=2024-08-15T09:50:11.464Z', ':001', 'SJOIN', '1721564574', '#welcome', '+lnrt', '13', ':001CIVLQF', '+11ZAAAAAB', '001QGR10C', '*@0014UE10B', '001NL1O07', '001SWZR05', '001HB8G04', '@00BAAAAAJ', '0019M7101']
# ['@msgid=SKUeuVzOrTShRDduq8VerX;time=2024-08-23T19:37:04.266Z', ':001', 'SJOIN', '1723993047', '#welcome', '+lnrt', '13',
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
serverMsg.pop(0)
channel = str(serverMsg[3]).lower()
len_cmd = len(serverMsg)
list_users:list = []
occurence = 0
start_boucle = 0
# Trouver le premier user
for i in range(len_cmd):
s: list = findall(fr':', serverMsg[i])
if s:
occurence += 1
if occurence == 2:
start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(serverMsg)):
parsed_UID = str(serverMsg[i])
clean_uid = self.__Irc.User.clean_uid(parsed_UID)
if not clean_uid is None and len(clean_uid) == 9:
list_users.append(parsed_UID)
if list_users:
self.__Irc.Channel.insert(
self.__Irc.Loader.Definition.MChannel(
name=channel,
uids=list_users
)
)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_part(self, serverMsg: list[str]) -> None:
"""Handle part coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
uid = str(serverMsg[1]).lstrip(':')
channel = str(serverMsg[3]).lower()
self.__Irc.Channel.delete_user_from_channel(channel, uid)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_uid(self, serverMsg: list[str]) -> None:
"""Handle uid message coming from the server
Args:
serverMsg (list[str]): Original server message
"""
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
try:
isWebirc = True if 'webirc' in serverMsg[0] else False
isWebsocket = True if 'websocket' in serverMsg[0] else False
uid = str(serverMsg[8])
nickname = str(serverMsg[3])
username = str(serverMsg[6])
hostname = str(serverMsg[7])
umodes = str(serverMsg[10])
vhost = str(serverMsg[11])
if not 'S' in umodes:
remote_ip = self.__Base.decode_ip(str(serverMsg[13]))
else:
remote_ip = '127.0.0.1'
# extract realname
realname = ' '.join(serverMsg[14:]).lstrip(':')
# Extract Geoip information
pattern = r'^.*geoip=cc=(\S{2}).*$'
geoip_match = match(pattern, serverMsg[0])
if geoip_match:
geoip = geoip_match.group(1)
else:
geoip = None
score_connexion = 0
self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser(
uid=uid,
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
vhost=vhost,
isWebirc=isWebirc,
isWebsocket=isWebsocket,
remote_ip=remote_ip,
geoip=geoip,
score_connexion=score_connexion,
connexion_datetime=datetime.now()
)
)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_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:
# InspIRCd 3:
# <- :3IN PING 808
# -> :808 PONG 3IN
hsid = str(serverMsg[0]).replace(':','')
self.send2socket(f":{self.__Config.SERVEUR_ID} PONG {hsid}", print_log=True)
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.send_notice(
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:
"""Handle version coming from the server
Args:
serverMsg (list[str]): Original message from the server
"""
try:
# ['@label=0073', ':0014E7P06', 'VERSION', 'PyDefender']
getUser = self.__Irc.User.get_User(self.__Irc.User.clean_uid(serverMsg[1]))
if getUser is None:
return None
response_351 = f"{self.__Config.SERVICE_NAME.capitalize()}-{self.__Config.CURRENT_VERSION} {self.__Config.SERVICE_HOST} {self.name}"
self.send2socket(f':{self.__Config.SERVICE_HOST} 351 {getUser.nickname} {response_351}')
modules = self.__Base.get_all_modules()
response_005 = ' | '.join(modules)
self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server')
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,158 @@
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
"""
reputationObj = self.get_Reputation(uid)
if reputationObj is None:
return False
reputationObj.nickname = newNickname
return True
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
if not self.is_exist(uid):
return result
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
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
"""
reputationObj = self.get_Reputation(uidornickname)
if reputationObj is None:
return None
return reputationObj.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
"""
reputationObj = self.get_Reputation(uidornickname)
if reputationObj is None:
return None
return reputationObj.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
"""
reputationObj = self.get_Reputation(uidornickname)
if reputationObj is None:
return False
else:
return True

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

@@ -0,0 +1,15 @@
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()
CONSOLE: bool = False
PROTOCTL_USER_MODES: list[str] = []
PROTOCTL_PREFIX: list[str] = []

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

@@ -0,0 +1,220 @@
from re import sub
from typing import Union, TYPE_CHECKING
from dataclasses import asdict
if TYPE_CHECKING:
from core.base import Base
from core.definition import 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
"""
userObj = self.get_User(newUser.uid)
if not userObj is None:
# User already created return False
return False
self.UID_DB.append(newUser)
return True
def update_nickname(self, uid: str, newNickname: str) -> bool:
"""Update the nickname starting from the UID
Args:
uid (str): UID of the user
newNickname (str): New nickname
Returns:
bool: True if updated
"""
userObj = self.get_User(uidornickname=uid)
if userObj is None:
return False
userObj.nickname = newNickname
return True
def update_mode(self, uidornickname: str, modes: str) -> bool:
"""Updating user mode
Args:
uidornickname (str): The UID or Nickname of the user
modes (str): new modes to update
Returns:
bool: True if user mode has been updaed
"""
response = True
userObj = self.get_User(uidornickname=uidornickname)
if userObj is None:
return False
action = modes[0]
new_modes = modes[1:]
existing_umodes = userObj.umodes
umodes = userObj.umodes
if action == '+':
for nm in new_modes:
if nm not in existing_umodes:
umodes += nm
elif action == '-':
for nm in new_modes:
if nm in existing_umodes:
umodes = umodes.replace(nm, '')
else:
return False
liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}"
return response
def delete(self, uid: str) -> bool:
"""Delete the User starting from the UID
Args:
uid (str): UID of the user
Returns:
bool: True if deleted
"""
userObj = self.get_User(uidornickname=uid)
if userObj is None:
return False
self.UID_DB.remove(userObj)
return True
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
return User
def get_uid(self, uidornickname:str) -> Union[str, None]:
"""Get the UID of the user starting from the UID or the Nickname
Args:
uidornickname (str): UID or Nickname
Returns:
str|None: Return the UID
"""
userObj = self.get_User(uidornickname=uidornickname)
if userObj is None:
return None
return userObj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
"""Get the Nickname starting from UID or the nickname
Args:
uidornickname (str): UID or Nickname of the user
Returns:
str|None: the nickname
"""
userObj = self.get_User(uidornickname=uidornickname)
if userObj is None:
return None
return userObj.nickname
def get_User_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
"""Transform User Object to a dictionary
Args:
uidornickname (str): The UID or The nickname
Returns:
Union[dict[str, any], None]: User Object as a dictionary or None
"""
userObj = self.get_User(uidornickname=uidornickname)
if userObj is None:
return None
return asdict(userObj)
def is_exist(self, uidornikname: str) -> bool:
"""Check if the UID or the nickname exist in the USER DB
Args:
uidornickname (str): The UID or the NICKNAME
Returns:
bool: True if exist
"""
userObj = self.get_User(uidornickname=uidornikname)
if userObj is None:
return False
return True
def clean_uid(self, uid: str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
uid (str): The UID to clean
Returns:
str: Clean UID without any sign
"""
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = sub(pattern, '', uid)
if not parsed_UID:
return None
return parsed_UID

View File

@@ -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

320
core/definition.py Normal file
View File

@@ -0,0 +1,320 @@
from datetime import datetime
from dataclasses import dataclass, field
from typing import Literal
from os import sep
@dataclass
class MClient:
"""Model Client for registred nickname"""
uid: str = None
account: 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 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"
underline: str = "\x1F"
@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_UMODES: str = '+iwxz'
"""Clone User modes (ex. +iwxz)"""
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)"""
LOGGING_NAME: str = "defender"
"""The name of the Logging instance"""
TABLE_CLIENT: str = "core_client"
"""Core Client table"""
TABLE_ADMIN: str = "core_admin"
"""Core 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'
umodes: str = None
remote_ip: str = '127.0.0.1'
group: str = 'Default'

View File

@@ -37,6 +37,8 @@ class Install:
self.set_configuration()
if self.skip_install:
self.install_dependencies()
self.check_packages_version()
return None
self.check_packages_version()
@@ -59,7 +61,7 @@ class Install:
venv_folder = '.pyenv'
unix_user_home_directory = os.path.expanduser("~")
unix_systemd_folder = os.path.join(unix_user_home_directory, '.config', 'systemd', 'user')
defender_main_executable = os.path.join(defender_install_folder, 'main.py')
defender_main_executable = os.path.join(defender_install_folder, 'defender.py')
self.config = self.CoreConfig(
install_log_file='install.log',
@@ -83,13 +85,15 @@ 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 /!\\")
exit("/!\\ Configuration file (core/configuration.json) doesn't exist! please create it /!\\")
# Exclude Windows OS from the installation
if os.name == 'nt':
#print('/!\\ Skip installation /!\\')
# If windows, modify pip and python virtual environment executable
self.config.venv_pip_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}pip.exe'
self.config.venv_python_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}python.exe'
self.skip_install = True
return False

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

34
core/loader.py Normal file
View File

@@ -0,0 +1,34 @@
from core.classes import user, admin, client, 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.Client: client.Client = client.Client(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)

74
core/utils.py Normal file
View File

@@ -0,0 +1,74 @@
from typing import Literal, Union
from datetime import datetime
from time import time
from random import choice
from hashlib import md5, sha3_512
def convert_to_int(value: any) -> Union[int, None]:
"""Convert a value to int
Args:
value (any): Value to convert to int if possible
Returns:
Union[int, None]: Return the int value or None if not possible
"""
try:
value_to_int = int(value)
return value_to_int
except ValueError:
return None
except Exception:
return None
def get_unixtime() -> int:
"""Cette fonction retourne un UNIXTIME de type 12365456
Returns:
int: Current time in seconds since the Epoch (int)
"""
return int(time())
def get_datetime() -> str:
"""Retourne une date au format string (24-12-2023 20:50:59)
Returns:
str: Current datetime in this format %d-%m-%Y %H:%M:%S
"""
currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
return currentdate
def generate_random_string(lenght: int) -> str:
"""Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
Returns:
str: The random string
"""
caracteres = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
randomize = ''.join(choice(caracteres) for _ in range(lenght))
return randomize
def hash(password: str, algorithm: Literal["md5, sha3_512"] = 'md5') -> str:
"""Retourne un mot de passe chiffré en fonction de l'algorithme utilisé
Args:
password (str): Le password en clair
algorithm (str): L'algorithm a utilisé
Returns:
str: Le password haché
"""
match algorithm:
case 'md5':
password = md5(password.encode()).hexdigest()
return password
case 'sha3_512':
password = sha3_512(password.encode()).hexdigest()
return password
case _:
password = md5(password.encode()).hexdigest()
return password

View File

@@ -1,23 +1,21 @@
from core import installation
#############################################
# @Version : 1 #
# @Version : 6 #
# Requierements : #
# Python3.10 or higher #
# SQLAlchemy, requests, psutil #
# UnrealIRCD 6.2.2 or higher #
#############################################
#########################
# LANCEMENT DE DEFENDER #
#########################
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,9 @@
from dataclasses import dataclass, fields, field
from dataclasses import dataclass
import random, faker, time, logging
from datetime import datetime
from typing import Union
from core.irc import Irc
from core.connection import Connection
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.irc import Irc
class Clone():
@@ -11,7 +11,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 +19,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,12 +37,13 @@ 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 = {
1: ['clone']
}
self.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
# Init the module (Mandatory)
self.__init_module()
@@ -49,34 +53,24 @@ class Clone():
def __init_module(self) -> None:
# Enrigstrer les nouvelles commandes dans le code
self.__set_commands(self.commands_level)
# Créer les tables necessaire a votre module (ce n'es pas obligatoire)
self.__create_tables()
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.send_join_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
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 __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
@@ -96,7 +90,7 @@ class Clone():
)
'''
self.Base.db_execute_query(table_channel)
# self.Base.db_execute_query(table_channel)
return None
@@ -122,165 +116,176 @@ 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.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):
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
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
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)
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.send_part_chan(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
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_vhost(self) -> str:
def generate_names(self) -> tuple[str, str, str]:
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)
vhost = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
return vhost
def generate_clones(self, group: str = 'Default', auto_remote_ip: bool = False) -> None:
try:
logging.getLogger('faker').setLevel(logging.CRITICAL)
fake = faker.Faker('en_GB')
# nickname = fake.first_name()
# username = fake.last_name()
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)
umodes = self.Config.CLONE_UMODES
# 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):
decoded_ip = fakeEN.ipv4_private() if auto_remote_ip else '127.0.0.1'
hostname = fakeEN.hostname()
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)
return (nickname, username, realname)
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,
hostname=hostname,
umodes=umodes,
uid=uid,
remote_ip=decoded_ip,
vhost=vhost,
group=group,
channels=[]
)
self.Clone.insert(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 = 'Default', auto_remote_ip: bool = False, interval: float = 0.2) -> None:
for i in range(0, number_of_clones):
self.generate_clones(group=group, auto_remote_ip=auto_remote_ip)
for clone in self.Clone.UID_CLONE_DB:
if self.stop:
print(f"Stop creating clones ...")
self.stop = False
break
if not clone.connected:
self.Protocol.send_uid(clone.nickname, clone.username, clone.hostname, clone.uid, clone.umodes, clone.vhost, clone.remote_ip, clone.realname, print_log=False)
self.Protocol.send_join_chan(uidornickname=clone.uid, channel=self.Config.CLONE_CHANNEL, password=self.Config.CLONE_CHANNEL_PASSWORD, print_log=False)
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:
clone_to_kill.append(clone.uid)
for clone_uid in clone_to_kill:
self.Protocol.send_quit(clone_uid, 'Gooood bye', print_log=False)
del clone_to_kill
return None
def cmd(self, data:list) -> None:
try:
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
if len(cmd) < 2:
return None
if len(cmd) < 2:
return None
match cmd[1]:
match cmd[1]:
case 'REPUTATION':
pass
case 'REPUTATION':
pass
if len(cmd) < 3:
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> 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.send_priv_msg(
nick_from=getClone.uid,
msg=final_message,
channel=self.Config.CLONE_CHANNEL
)
except Exception as err:
self.Logs.error(f'General Error: {err}')
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
try:
command = str(cmd[0]).lower()
@@ -293,10 +298,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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | nickname] #channel")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | nickname] #channel")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone list")
option = str(cmd[1]).lower()
@@ -304,46 +310,41 @@ class Clone():
case 'connect':
try:
# clone connect 5
# clone connect 5 GroupName 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.2
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, False, 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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect [number of clone you want to connect] [Group]")
self.Protocol.send_notice(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.Clone.delete(clone_name)
cloneObj = self.Clone.get_Clone(clone_name)
if not cloneObj is None:
self.Protocol.send_quit(cloneObj.uid, 'Goood bye', print_log=False)
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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill all")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill clone_nickname")
case 'join':
try:
@@ -352,21 +353,49 @@ 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.send_join_chan(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False)
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.send_join_chan(uidornickname=clone_name, channel=clone_channel_to_join, print_log=False)
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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join all #channel")
self.Protocol.send_notice(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.send_part_chan(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False)
else:
if self.Clone.exists(clone_name):
clone_uid = self.Clone.get_uid(clone_name)
if not clone_uid is None:
self.Protocol.send_part_chan(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False)
except Exception as err:
self.Logs.error(f'{err}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part all #channel")
self.Protocol.send_notice(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.send_notice(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.send_notice(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 +403,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.send_notice(
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.send_priv_msg(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.send_notice(
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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone connect NUMBER GROUP_NAME INTERVAL")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone kill [all | nickname]")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone join [all | nickname] #channel")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} clone part [all | nickname] #channel")
self.Protocol.send_notice(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():
@@ -10,10 +13,9 @@ class Jsonrpc():
class ModConfModel:
"""The Model containing the module parameters
"""
param_exemple1: str
param_exemple2: int
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()
@@ -21,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
@@ -37,9 +42,8 @@ class Jsonrpc():
self.Channel = ircInstance.Channel
# Create module commands (Mandatory)
self.commands_level = {
1: ['jsonrpc', 'jruser']
}
self.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]')
self.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC')
# Init the module
self.__init_module()
@@ -49,8 +53,8 @@ class Jsonrpc():
def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
logging.getLogger('websockets').setLevel(logging.WARNING)
logging.getLogger('unrealircd-rpc-py').setLevel(logging.CRITICAL)
# Create you own tables (Mandatory)
# self.__create_tables()
@@ -59,13 +63,6 @@ class Jsonrpc():
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
# self.UnrealIrcdRpcLive: Live = Live(
# req_method='unixsocket',
# path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
# callback_object_instance=self,
# callback_method_name='callback_sent_to_irc'
# )
self.UnrealIrcdRpcLive: Live = Live(
req_method='websocket',
url=self.Config.JSONRPC_URL,
@@ -85,24 +82,21 @@ 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.send_priv_msg(
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.send_priv_msg(
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
)
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
if self.ModConfig.jsonrpc == 1:
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
return None
@@ -136,7 +130,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.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"[{bold}{green}JSONRPC{nogc}{bold}] Event activated",
channel=dchanlog)
return None
level = json_response.result.level
@@ -147,7 +144,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.send_priv_msg(nick_from=dnickname, msg=build_msg, channel=dchanlog)
def thread_start_jsonrpc(self):
@@ -155,17 +152,21 @@ 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.send_priv_msg(
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
"""
try:
# Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1)
self.ModConfig = self.ModConfModel(jsonrpc=0)
# Sync the configuration with core configuration (Mandatory)
#self.Base.db_sync_core_config(self.module_name, self.ModConfig)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
@@ -190,10 +191,11 @@ class Jsonrpc():
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
dchannel = self.Config.SERVICE_CHANLOG
fromuser = user
fromchannel = str(channel) if not channel is None else None
@@ -204,16 +206,38 @@ 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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc on')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jsonrpc off')
match option:
case 'on':
# for logger_name, logger in logging.root.manager.loggerDict.items():
# if isinstance(logger, logging.Logger):
# self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"{logger_name} - {logger.level}")
for thread in self.Base.running_threads:
if thread.getName() == 'thread_start_jsonrpc':
if thread.is_alive():
self.Protocol.send_priv_msg(
nick_from=self.Config.SERVICE_NICKNAME,
msg=f"Thread {thread.getName()} is running",
channel=dchannel
)
else:
self.Protocol.send_priv_msg(
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)
case 'off':
self.UnrealIrcdRpcLive.unsubscribe()
self.__update_configuration('jsonrpc', 0)
except IndexError as ie:
self.Logs.error(ie)
@@ -223,7 +247,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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'/msg {dnickname} jruser get nickname')
match option:
@@ -237,34 +261,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.send_notice(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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'UID : {UserInfo.id}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'NICKNAME : {UserInfo.name}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'USERNAME : {UserInfo.user.username}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'REALNAME : {UserInfo.user.realname}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'MODES : {UserInfo.user.modes}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CHANNELS : {chan_list}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SECURITY GROUP : {UserInfo.user.security_groups}')
self.Protocol.send_notice(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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IP : {UserInfo.ip}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'COUNTRY CODE : {UserInfo.geoip.country_code}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASN : {UserInfo.geoip.asn}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'ASNAME : {UserInfo.geoip.asname}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLOAKED HOST : {UserInfo.user.cloakedhost}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'HOSTNAME : {UserInfo.hostname}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'VHOST : {UserInfo.user.vhost}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CLIENT PORT : {UserInfo.client_port}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'SERVER PORT : {UserInfo.server_port}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CERTFP : {UserInfo.tls.certfp}')
self.Protocol.send_notice(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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'IDLE SINCE : {UserInfo.idle_since}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f'CONNECTED SINCE : {UserInfo.connected_since}')
except IndexError as ie:
self.Logs.error(ie)
@@ -274,11 +301,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.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" This is a notice to the sender ...")
self.Protocol.send_priv_msg(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.send_priv_msg(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,5 +1,8 @@
from typing import TYPE_CHECKING
from dataclasses import dataclass, fields
from core.irc import Irc
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,13 +42,15 @@ 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'],
1: ['test_level_1'],
2: ['test_level_2'],
3: ['test_level_3']
}
self.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command')
self.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command')
self.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command')
self.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command')
# Init the module
self.__init_module()
@@ -49,9 +60,6 @@ class Test():
def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables (Mandatory)
self.__create_tables()
@@ -61,20 +69,6 @@ class Test():
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -129,13 +123,13 @@ class Test():
return None
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
self.Logs.error(f"Key Error: {ke}")
except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
@@ -147,11 +141,11 @@ class Test():
case 'test-command':
try:
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.send_notice(nick_from=dnickname, nick_to=fromuser, msg="This is a notice to the sender ...")
self.Protocol.send_priv_msg(nick_from=dnickname, msg=f"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.send_priv_msg(nick_from=dnickname, msg=f"This is private message to the sender ...", channel=fromchannel)
# How to update your module configuration
self.__update_configuration('param_exemple2', 7)

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()
@@ -31,6 +34,12 @@ class Votekick():
# Add Irc Object to the module
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
self.Config = ircInstance.Config
@@ -47,41 +56,23 @@ class Votekick():
self.Channel = ircInstance.Channel
# Créer les nouvelles commandes du module
self.commands_level = {
0: ['vote']
}
self.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module')
# Init the module
self.__init_module()
# Log the module
self.Logs.debug(f'Module {self.module_name} loaded ...')
self.Logs.debug(f'-- Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Add admin object to retrieve admin users
self.Admin = self.Irc.Admin
self.__set_commands(self.commands_level)
self.__create_tables()
self.join_saved_channels()
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
@@ -113,7 +104,7 @@ class Votekick():
def unload(self) -> None:
try:
for chan in self.VOTE_CHANNEL_DB:
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {chan.channel_name}")
self.Protocol.send_part_chan(uidornickname=self.Config.SERVICE_ID, channel=chan.channel_name)
self.VOTE_CHANNEL_DB = []
self.Logs.debug(f'Delete memory DB VOTE_CHANNEL_DB: {self.VOTE_CHANNEL_DB}')
@@ -123,8 +114,8 @@ class Votekick():
self.Logs.error(f'{ne}')
except NameError as ue:
self.Logs.error(f'{ue}')
except:
self.Logs.error('Error on the module')
except Exception as err:
self.Logs.error(f'General Error: {err}')
def init_vote_system(self, channel: str) -> bool:
@@ -195,7 +186,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()
@@ -203,8 +194,8 @@ class Votekick():
for channel in channels:
id, chan = channel
self.insert_vote_channel(self.VoteChannelModel(channel_name=chan, target_user='', voter_users=[], vote_for=0, vote_against=0))
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} SJOIN {unixtime} {chan} + :{self.Config.SERVICE_ID}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {chan} +o {self.Config.SERVICE_NICKNAME}")
self.Protocol.sjoin(channel=chan)
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {chan} +o {self.Config.SERVICE_NICKNAME}")
return None
@@ -229,15 +220,27 @@ class Votekick():
if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it'll be kicked from the channel",
channel=channel
)
self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
self.Channel.delete_user_from_channel(channel, self.User.get_uid(target_user))
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel",
channel=channel
)
# Init the system
if self.init_vote_system(channel):
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg="System vote re initiated",
channel=channel
)
return None
@@ -247,13 +250,13 @@ class Votekick():
return None
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
self.Logs.error(f"Key Error: {ke}")
except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
# cmd is the command starting from the user command
# full cmd is sending the entire server response
@@ -263,18 +266,21 @@ class Votekick():
fromchannel = channel
match command:
case 'vote':
option = str(cmd[1]).lower()
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote activate #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote deactivate #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote +')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote -')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote cancel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote status')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote submit nickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote verdict')
case 'vote':
if len(cmd) == 1:
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict')
return None
option = str(cmd[1]).lower()
match option:
@@ -282,12 +288,12 @@ class Votekick():
try:
# vote activate #channel
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' :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")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
self.insert_vote_channel(
self.VoteChannelModel(
@@ -299,40 +305,43 @@ 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}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {sentchannel} :You can now use !submit <nickname> to decide if he will stay or not on this channel ")
self.Protocol.send_join_chan(uidornickname=dnickname, channel=sentchannel)
self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="You can now use !submit <nickname> to decide if he will stay or not on this channel ",
channel=sentchannel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} #channel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} #welcome')
case 'deactivate':
try:
# vote deactivate #channel
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" 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")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
self.Irc.send2socket(f":{dnickname} PART {sentchannel}")
self.Protocol.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
self.Protocol.send_part_chan(uidornickname=dnickname, channel=sentchannel)
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:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" /msg {dnickname} {command} {option} #channel")
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f" Exemple /msg {dnickname} {command} {option} #welcome")
case '+':
try:
@@ -341,15 +350,21 @@ class Votekick():
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="You already submitted a vote",
channel=channel
)
else:
chan.vote_for += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="Vote recorded, thank you",
channel=channel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
case '-':
try:
@@ -358,54 +373,65 @@ class Votekick():
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="You already submitted a vote",
channel=channel
)
else:
chan.vote_against += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="Vote recorded, thank you",
channel=channel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
case 'cancel':
try:
# vote cancel
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command')
return None
if channel is None:
self.Logs.error(f"The channel is not known, defender can't cancel the vote")
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :You need to specify the channel => /msg {dnickname} vote_cancel #channel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' You need to specify the channel => /msg {dnickname} vote_cancel #channel')
for vote in self.VOTE_CHANNEL_DB:
if vote.channel_name == channel:
self.init_vote_system(channel)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote system re-initiated')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="Vote system re-initiated",
channel=channel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
case 'status':
try:
# vote status
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Channel: {chan.channel_name} | Target: {self.User.get_nickname(chan.target_user)} | For: {chan.vote_for} | Against: {chan.vote_against} | Number of voters: {str(len(chan.voter_users))}')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"Channel: {chan.channel_name} | Target: {self.User.get_nickname(chan.target_user)} | For: {chan.vote_for} | Against: {chan.vote_against} | Number of voters: {str(len(chan.voter_users))}",
channel=channel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
case 'submit':
try:
# vote submit nickname
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Your are not allowed to execute this command')
return None
nickname_submitted = cmd[2]
@@ -418,18 +444,24 @@ class Votekick():
if vote.channel_name == channel:
ongoing_user = self.User.get_nickname(vote.target_user)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :There is an ongoing vote on {ongoing_user}')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"There is an ongoing vote on {ongoing_user}",
channel=channel
)
return False
# check if the user exist
if user_submitted is None:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> do not exist')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"This nickname <{nickname_submitted}> do not exist",
channel=channel
)
return False
uid_cleaned = self.Base.clean_uid(uid_submitted)
ChannelInfo = self.Channel.get_Channel(channel)
if ChannelInfo is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :This channel [{channel}] do not exist in the Channel Object')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' This channel [{channel}] do not exist in the Channel Object')
return False
clean_uids_in_channel: list = []
@@ -437,60 +469,83 @@ class Votekick():
clean_uids_in_channel.append(self.Base.clean_uid(uid))
if not uid_cleaned in clean_uids_in_channel:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> is not available in this channel')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"This nickname <{nickname_submitted}> is not available in this channel",
channel=channel
)
return False
# check if Ircop or Service or Bot
pattern = fr'[o|B|S]'
operator_user = re.findall(pattern, user_submitted.umodes)
if operator_user:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You cant vote for this user ! he/she is protected')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="You cant vote for this user ! he/she is protected",
channel=channel
)
return False
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
chan.target_user = self.User.get_uid(nickname_submitted)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :{nickname_submitted} has been targeted for a vote')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"{nickname_submitted} has been targeted for a vote",
channel=channel
)
self.Base.create_timer(60, self.timer_vote_verdict, (channel, ))
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This vote will end after 60 secondes')
self.Protocol.send_priv_msg(nick_from=dnickname,
msg="This vote will end after 60 secondes",
channel=channel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} nickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} adator')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option} nickname')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option} adator')
case 'verdict':
try:
# vote verdict
if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f'Your are not allowed to execute this command')
return None
for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
self.Protocol.send_priv_msg(nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel",
channel=channel
)
self.Protocol.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel",
channel=channel
)
# Init the system
if self.init_vote_system(channel):
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
self.Protocol.send_priv_msg(
nick_from=dnickname,
msg="System vote re initiated",
channel=channel
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} {command} {option}')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' Exemple /msg {dnickname} {command} {option}')
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote activate #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote deactivate #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote +')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote -')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote cancel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote status')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote submit nickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote verdict')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote activate #channel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote deactivate #channel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote +')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote -')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote cancel')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote status')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote submit nickname')
self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser,msg=f' /msg {dnickname} vote verdict')

View File

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