40 Commits

Author SHA1 Message Date
adator
7796d05206 Merge pull request #32 from adator85/dev
Update vote kick commands
2024-09-01 18:55:57 +02:00
adator
3ba884216f Update vote kick commands 2024-09-01 18:55:30 +02:00
adator
5f2567f9e5 Merge pull request #31 from adator85/dev
mod_command update
2024-09-01 17:30:05 +02:00
adator
2ce19ee877 mod_command update 2024-09-01 17:29:33 +02:00
adator
aaa1dd9a1a Merge pull request #30 from adator85/dev
adding Say command for clones
2024-09-01 16:40:25 +02:00
adator
351fd6edaf adding Say command for clones 2024-09-01 16:39:49 +02:00
adator
a02f2f9a26 Merge pull request #29 from adator85/dev
update mod_clone module
2024-09-01 15:54:50 +02:00
adator
d0c17d69de update mod_clone module 2024-09-01 15:54:23 +02:00
adator
d73adb6f0b Merge pull request #28 from adator85/dev
update readme
2024-09-01 15:35:59 +02:00
adator
eb9402dd8e update readme 2024-09-01 15:34:49 +02:00
adator
b812e64992 Merge pull request #27 from adator85/dev
Dev
2024-09-01 15:24:18 +02:00
adator
35d5e7a2b5 Update readme.md 2024-09-01 15:23:48 +02:00
adator
21a825c92d Remove install.py 2024-09-01 15:18:32 +02:00
adator
9bd1f68df2 Merge pull request #26 from adator85/dev
Dev
2024-09-01 14:59:38 +02:00
adator
3fcfa0296d . 2024-09-01 14:40:28 +02:00
adator
bcf972d08b . 2024-09-01 14:34:57 +02:00
adator
1348ead6cd remove logging library 2024-09-01 14:33:41 +02:00
adator
f6ebab4780 . 2024-09-01 14:21:25 +02:00
adator
608ec57593 . 2024-09-01 13:59:26 +02:00
adator
f392f2fb2f . 2024-09-01 13:56:55 +02:00
adator
489e1e7b0a . 2024-09-01 13:47:18 +02:00
adator
3d79270ca0 . 2024-09-01 13:42:39 +02:00
adator
e60ada4260 Fix logging instance 2024-09-01 13:41:15 +02:00
adator
ccb9f307b4 Few changes see changelog 2024-09-01 13:39:02 +02:00
adator
2fc8f2d346 Fix issue unexpected argument 2024-09-01 13:14:51 +02:00
adator
e3ada04f2a daemon_reload cmd. 2024-09-01 13:09:47 +02:00
adator
6ba0551fee v5.1.5 2024-09-01 12:51:54 +02:00
adator
f44b08bf36 Merge pull request #25 from adator85/dev
fix Installation
2024-08-29 01:36:38 +02:00
adator
6142b4257f fix Installation 2024-08-29 01:36:12 +02:00
adator
1a19e1613a Merge pull request #24 from adator85/dev
Fix Bug installation
2024-08-29 01:31:19 +02:00
adator
ab15cce82b Fix Bug installation 2024-08-29 01:30:10 +02:00
adator
cdc15b7b47 Merge pull request #23 from adator85/dev
Dev
2024-08-29 01:16:55 +02:00
adator
01dcc90d63 V5.1.0 2024-08-29 01:13:55 +02:00
adator
31fe9f62ec Merge pull request #22 from adator85/dev
Dev
2024-08-24 01:39:11 +02:00
adator
f0853e3afb Merge pull request #21 from adator85/dev
New Installation file created for unix system
2024-08-22 01:02:00 +02:00
adator
6dade09257 Merge pull request #20 from adator85/dev
README Update
2024-08-21 00:50:31 +02:00
adator
9533b010b2 Merge pull request #19 from adator85/dev
V5.0.4 - Delete a user when a user has been kicked
2024-08-20 02:24:37 +02:00
adator
824db73590 Merge pull request #18 from adator85/dev
Delete channel mode information
2024-08-20 02:14:31 +02:00
adator
96bf4b6f80 Merge pull request #17 from adator85/dev
Fix channel update
2024-08-20 02:08:09 +02:00
adator
922336363e Merge pull request #16 from adator85/dev
Dev
2024-08-20 01:56:04 +02:00
13 changed files with 698 additions and 588 deletions

View File

@@ -25,16 +25,19 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Prérequis: Prérequis:
- Système d'exploitation Linux (Windows non supporté) - Système d'exploitation Linux (Windows non supporté)
- Droits d'administrateur (root) pour l'exécution du script
- Python version 3.10 ou supérieure - Python version 3.10 ou supérieure
Bash: Bash:
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
- Renommer le fichier exemple_configuration.json en configuration.json - Renommer le fichier exemple_configuration.json en configuration.json
- Configurer le fichier configuration.json - Configurer le fichier configuration.json
$ sudo python3 install.py $ python3 main.py
Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC
Pour Les prochains lancement de defender vous devez utiliser la commande suivante:
Bash:
$ systemctl --user [start | stop | restart | status] defender
# Installation manuelle: # Installation manuelle:
Bash: Bash:
@@ -42,8 +45,10 @@ Si votre configuration est bonne, votre service est censé etre connecté a votr
$ cd IRC_DEFENDER_MODULES $ cd IRC_DEFENDER_MODULES
$ python3 -m venv .pyenv $ python3 -m venv .pyenv
$ source .pyenv/bin/activate $ source .pyenv/bin/activate
- Créer un service nommé "Defender.service" pour votre service et placer le dans "/etc/systemd/system/" (pyenv)$ pip install sqlalchemy, psutil, requests, faker
$ sudo systemctl start Defender - Créer un service nommé "defender.service" pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
- Si le dossier n'existe pas il faut les créer
$ sudo systemctl --user start defender
# Configuration # Configuration

View File

@@ -315,7 +315,7 @@ class Channel:
for user in newChan.uids: for user in newChan.uids:
record.uids.append(user) record.uids.append(user)
# Supprimer les doublons # Supprimer les doublons
del_duplicates = list(set(record.uids)) del_duplicates = list(set(record.uids))
record.uids = del_duplicates record.uids = del_duplicates
self.log.debug(f'Updating a new UID to the channel {record}') self.log.debug(f'Updating a new UID to the channel {record}')
@@ -402,3 +402,97 @@ class Channel:
self.log.debug(f'Search {name} -- result = {Channel}') self.log.debug(f'Search {name} -- result = {Channel}')
return Channel return Channel
class Clones:
@dataclass
class CloneModel:
alive: bool
nickname: str
username: str
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

@@ -9,9 +9,9 @@ from core.loadConf import ConfigDataModel
class Base: class Base:
CORE_DB_PATH = 'core' + os.sep + 'db' + os.sep # Le dossier bases de données core # CORE_DB_PATH = 'core' + os.sep + 'db' + os.sep # Le dossier bases de données core
MODS_DB_PATH = 'mods' + os.sep + 'db' + os.sep # Le dossier bases de données des modules # MODS_DB_PATH = 'mods' + os.sep + 'db' + os.sep # Le dossier bases de données des modules
PYTHON_MIN_VERSION = '3.10' # Version min de python # PYTHON_MIN_VERSION = '3.10' # Version min de python
def __init__(self, Config: ConfigDataModel) -> None: def __init__(self, Config: ConfigDataModel) -> None:
@@ -26,6 +26,7 @@ class Base:
self.lock = threading.RLock() # Création du lock self.lock = threading.RLock() # Création du 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 self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
self.__create_db() # Initialisation de la base de données self.__create_db() # Initialisation de la base de données
@@ -200,7 +201,7 @@ class Base:
else: else:
return False return False
def db_record_module(self, user_cmd:str, module_name:str) -> None: def db_record_module(self, user_cmd:str, module_name:str, isdefault:int = 0) -> None:
"""Enregistre les modules dans la base de données """Enregistre les modules dans la base de données
Args: Args:
@@ -210,7 +211,7 @@ class Base:
if not self.db_isModuleExist(module_name): if not self.db_isModuleExist(module_name):
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer") 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': 0} mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name, 'isdefault': isdefault}
self.db_execute_query(insert_cmd_query, mes_donnees) self.db_execute_query(insert_cmd_query, mes_donnees)
else: else:
self.logs.debug(f"Le module {module_name} existe déja dans la base de données") self.logs.debug(f"Le module {module_name} existe déja dans la base de données")
@@ -532,6 +533,7 @@ class Base:
full_path_db = self.Config.db_path + self.Config.db_name full_path_db = self.Config.db_path + self.Config.db_name
if not os.path.exists(db_directory): if not os.path.exists(db_directory):
self.install = True
os.makedirs(db_directory) os.makedirs(db_directory)
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False) engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
@@ -600,6 +602,11 @@ class Base:
self.db_execute_query(table_core_channel) self.db_execute_query(table_core_channel)
self.db_execute_query(table_core_config) self.db_execute_query(table_core_config)
if self.install:
self.db_record_module('sys', 'mod_command', 1)
self.db_record_module('sys', 'mod_defender', 1)
self.install = False
return None return None
def db_execute_query(self, query:str, params:dict = {}) -> CursorResult: def db_execute_query(self, query:str, params:dict = {}) -> CursorResult:

View File

@@ -1,20 +1,26 @@
import socket, ssl, time import socket, ssl, time
from ssl import SSLSocket from ssl import SSLSocket
from core.loadConf import Config from core.loadConf import Config
from core.Model import Clones
from core.base import Base from core.base import Base
from typing import Union from typing import Union
class Connection: class Connection:
def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], ssl:bool = False) -> None: def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
self.Config = Config().ConfigObject self.Config = Config().ConfigObject
self.Base = Base(self.Config) self.Base = Base(self.Config)
self.IrcSocket: Union[socket.socket, SSLSocket] = None self.IrcSocket: Union[socket.socket, SSLSocket] = None
self.signal: bool = True
self.nickname = nickname self.nickname = nickname
self.username = username self.username = username
self.channels:list[str] = channels self.channels:list[str] = channels
self.CHARSET = ['utf-8', 'iso-8859-1'] 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.create_socket(self.Config.SERVEUR_IP, self.Config.SERVEUR_HOSTNAME, server_port, ssl)
self.send_connection_information_to_server(self.IrcSocket) self.send_connection_information_to_server(self.IrcSocket)
@@ -126,7 +132,6 @@ class Connection:
break break
self.parser(data) self.parser(data)
except ssl.SSLEOFError as soe: except ssl.SSLEOFError as soe:
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}") self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
self.signal = False self.signal = False
@@ -156,6 +161,7 @@ class Connection:
try: try:
for data in cmd: for data in cmd:
response = data.decode(self.CHARSET[0]).split() response = data.decode(self.CHARSET[0]).split()
self.signal = self.currentCloneObject.alive
# print(response) # print(response)
match response[0]: match response[0]:
@@ -174,16 +180,28 @@ class Connection:
self.send2socket(f"JOIN {channel}") self.send2socket(f"JOIN {channel}")
return None return None
case 'PRIVMSG': case 'PRIVMSG':
self.Base.logs.debug(response)
self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}')
fullname = str(response[0]).replace(':', '') fullname = str(response[0]).replace(':', '')
nickname = fullname.split('!')[0].replace(':','') nickname = fullname.split('!')[0].replace(':','')
if nickname == self.Config.SERVICE_NICKNAME: if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','') command = str(response[3]).replace(':','')
if command == 'KILL': if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye') self.send2socket(f'QUIT :Thanks and goodbye')
self.signal = False self.signal = self.currentCloneObject.alive
if command == 'JOIN': if command == 'JOIN':
channel_to_join = str(response[4]) channel_to_join = str(response[4])
self.send2socket(f"JOIN {channel_to_join}") 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: except UnicodeEncodeError:
for data in cmd: for data in cmd:
@@ -193,7 +211,6 @@ class Connection:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
except AssertionError as ae: except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}") self.Base.logs.error(f"Assertion error : {ae}")
pass
def __ssl_context(self) -> ssl.SSLContext: def __ssl_context(self) -> ssl.SSLContext:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

View File

@@ -1,6 +1,4 @@
from importlib.util import find_spec
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path
from subprocess import check_call, run, CalledProcessError, PIPE from subprocess import check_call, run, CalledProcessError, PIPE
from platform import python_version, python_version_tuple from platform import python_version, python_version_tuple
from sys import exit from sys import exit
@@ -10,9 +8,11 @@ class Install:
@dataclass @dataclass
class CoreConfig: class CoreConfig:
install_log_file: str
unix_systemd_folder: str unix_systemd_folder: str
service_file_name: str service_file_name: str
service_cmd_executable: list service_cmd_executable: list
service_cmd_daemon_reload: list
defender_main_executable: str defender_main_executable: str
python_min_version: str python_min_version: str
python_current_version_tuple: tuple[str, str, str] python_current_version_tuple: tuple[str, str, str]
@@ -32,6 +32,12 @@ class Install:
# Tester si c'est la bonne version de python # Tester si c'est la bonne version de python
exit("Python Version Error") exit("Python Version Error")
else: else:
if self.skip_install:
return None
print(f'Configuration loaded : {self.config}')
# Sinon tester les dependances python et les installer avec pip # Sinon tester les dependances python et les installer avec pip
if self.do_install(): if self.do_install():
@@ -45,6 +51,7 @@ class Install:
def set_configuration(self): def set_configuration(self):
self.skip_install = False
defender_install_folder = os.getcwd() defender_install_folder = os.getcwd()
venv_folder = '.pyenv' venv_folder = '.pyenv'
unix_user_home_directory = os.path.expanduser("~") unix_user_home_directory = os.path.expanduser("~")
@@ -52,9 +59,11 @@ class Install:
defender_main_executable = os.path.join(defender_install_folder, 'main.py') defender_main_executable = os.path.join(defender_install_folder, 'main.py')
self.config = self.CoreConfig( self.config = self.CoreConfig(
install_log_file='install.log',
unix_systemd_folder=unix_systemd_folder, unix_systemd_folder=unix_systemd_folder,
service_file_name='defender.service', service_file_name='defender.service',
service_cmd_executable=['systemctl', '--user', 'start', 'defender'], service_cmd_executable=['systemctl', '--user', 'start', 'defender'],
service_cmd_daemon_reload=['systemctl', '--user', 'daemon-reload'],
defender_main_executable=defender_main_executable, defender_main_executable=defender_main_executable,
python_min_version='3.10', python_min_version='3.10',
python_current_version_tuple=python_version_tuple(), python_current_version_tuple=python_version_tuple(),
@@ -67,6 +76,24 @@ class Install:
venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python' venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
) )
# Exclude Windows OS
if os.name == 'nt':
#print('/!\\ Skip installation /!\\')
self.skip_install = True
else:
if self.is_root():
self.skip_install = True
def is_root(self) -> bool:
if os.geteuid() != 0:
print('User without privileges ==> PASS')
return False
elif os.geteuid() == 0:
print('/!\\ Do not use root to install Defender /!\\')
exit("Do not use root to install Defender")
return True
def do_install(self) -> bool: def do_install(self) -> bool:
full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name) full_service_file_path = os.path.join(self.config.unix_systemd_folder, self.config.service_file_name)
@@ -83,12 +110,12 @@ class Install:
def run_subprocess(self, command:list) -> None: def run_subprocess(self, command:list) -> None:
print(command) print(f'> {command}')
try: try:
check_call(command) check_call(command)
print("La commande s'est terminée avec succès.") print("The command completed successfully.")
except CalledProcessError as e: except CalledProcessError as e:
print(f"La commande a échoué avec le code de retour :{e.returncode}") print(f"The command failed with the return code: {e.returncode}")
print(f"Try to install dependencies ...") print(f"Try to install dependencies ...")
exit(5) exit(5)
@@ -113,7 +140,7 @@ class Install:
print(f"## Your python version must be greather than or equal to {self.config.python_current_version} ##") print(f"## Your python version must be greather than or equal to {self.config.python_current_version} ##")
return False return False
print(f"===> Version of python : {self.config.python_current_version} ==> OK") print(f"> Version of python : {self.config.python_current_version} ==> OK")
return True return True
@@ -123,7 +150,8 @@ class Install:
# Run a command in the virtual environment's Python to check if the package is installed # Run a command in the virtual environment's Python to check if the package is installed
run([self.config.venv_python_executable, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE) run([self.config.venv_python_executable, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE)
return True return True
except CalledProcessError: except CalledProcessError as cpe:
print(cpe)
return False return False
def install_dependencies(self) -> None: def install_dependencies(self) -> None:
@@ -152,7 +180,7 @@ class Install:
print("===> Verifier si pip est a jour") print("===> Verifier si pip est a jour")
self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip']) self.run_subprocess([self.config.venv_python_executable, '-m', 'pip', 'install', '--upgrade', 'pip'])
if find_spec('greenlet') is None: if not self.check_package('greenlet'):
self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet']) self.run_subprocess([self.config.venv_pip_executable, 'install', '--only-binary', ':all:', 'greenlet'])
print('====> Module Greenlet installé') print('====> Module Greenlet installé')
@@ -170,13 +198,10 @@ class Install:
if os.path.exists(full_service_file_path): if os.path.exists(full_service_file_path):
print(f'/!\\ Service file already exist /!\\') print(f'/!\\ Service file already exist /!\\')
self.run_subprocess(self.config.service_cmd_executable)
return None return None
# Check if user systemd is available (.config/systemd/user/) contain = f'''[Unit]
if not os.path.exists(self.config.unix_systemd_folder):
self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder])
contain = f'''[Unit]
Description=Defender IRC Service Description=Defender IRC Service
[Service] [Service]
@@ -186,20 +211,34 @@ SyslogIdentifier=Defender
Restart=on-failure Restart=on-failure
[Install] [Install]
WantedBy=multi-user.target WantedBy=default.target
''' '''
# Check if user systemd is available (.config/systemd/user/)
if not os.path.exists(self.config.unix_systemd_folder):
self.run_subprocess(['mkdir', '-p', self.config.unix_systemd_folder])
with open(full_service_file_path, 'w+') as servicefile: with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain) servicefile.write(contain)
servicefile.close() servicefile.close()
print(f'Service file generated with current configuration') print(f'Service file generated with current configuration')
print(f'Running Defender IRC Service ...') print(f'Running Defender IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable)
else:
with open(full_service_file_path, 'w+') as servicefile:
servicefile.write(contain)
servicefile.close()
print(f'Service file generated with current configuration')
print(f'Running Defender IRC Service ...')
self.run_subprocess(self.config.service_cmd_daemon_reload)
self.run_subprocess(self.config.service_cmd_executable) self.run_subprocess(self.config.service_cmd_executable)
def print_final_message(self) -> None: def print_final_message(self) -> None:
print(f"#"*24) print(f"#"*24)
print("Installation complete ...") print("Installation complete ...")
print("You must change environment using the command below") print("If the configuration is correct, then you must see your service connected to your irc server")
print(f"source {self.config.defender_install_folder}{os.sep}{self.config.venv_folder}{os.sep}bin{os.sep}activate") print(f"If any issue, you can see the log file for debug {self.config.defender_install_folder}{os.sep}logs{os.sep}defender.log")
print(f"#"*24) print(f"#"*24)
exit(1) exit(1)

View File

@@ -1,9 +1,9 @@
import ssl, re, importlib, sys, time, threading, socket import ssl, re, importlib, sys, time, threading, socket
from ssl import SSLSocket from ssl import SSLSocket
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union from typing import Union, Literal
from core.loadConf import Config from core.loadConf import Config
from core.Model import User, Admin, Channel from core.Model import User, Admin, Channel, Clones
from core.base import Base from core.base import Base
class Irc: class Irc:
@@ -21,6 +21,8 @@ class Irc:
self.INIT = 1 # Variable d'intialisation | 1 -> indique si le programme est en cours d'initialisation self.INIT = 1 # Variable d'intialisation | 1 -> indique si le programme est en cours d'initialisation
self.RESTART = 0 # Variable pour le redemarrage du bot | 0 -> indique que le programme n'es pas en cours de redemarrage self.RESTART = 0 # Variable pour le redemarrage du bot | 0 -> indique que le programme n'es pas en cours de redemarrage
self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages
"""0: utf-8 | 1: iso-8859-1"""
self.SSL_VERSION = None # Version SSL self.SSL_VERSION = None # Version SSL
self.Config = Config().ConfigObject self.Config = Config().ConfigObject
@@ -30,7 +32,7 @@ class Irc:
0: ['help', 'auth', 'copyright', 'uptime'], 0: ['help', 'auth', 'copyright', 'uptime'],
1: ['load','reload','unload', 'deauth', 'checkversion'], 1: ['load','reload','unload', 'deauth', 'checkversion'],
2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins'], 2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins'],
3: ['quit', 'restart','addaccess','editaccess', 'delaccess','umode'] 3: ['quit', 'restart','addaccess','editaccess', 'delaccess']
} }
# l'ensemble des commandes. # l'ensemble des commandes.
@@ -43,6 +45,7 @@ class Irc:
self.User = User(self.Base) self.User = User(self.Base)
self.Admin = Admin(self.Base) self.Admin = Admin(self.Base)
self.Channel = Channel(self.Base) self.Channel = Channel(self.Base)
self.Clones = Clones(self.Base)
self.__create_table() self.__create_table()
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, )) self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
@@ -199,19 +202,22 @@ class Irc:
version = self.Config.current_version version = self.Config.current_version
unixtime = self.Base.get_unixtime() unixtime = self.Base.get_unixtime()
charset = self.CHARSET[0]
# Envoyer un message d'identification # Envoyer un message d'identification
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8')) writer.send(f":{sid} PASS :{password}\r\n".encode(charset))
writer.send(f":{sid} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS\r\n".encode('utf-8')) writer.send(f":{sid} PROTOCTL SID NOQUIT NICKv2 SJOIN SJ3 NICKIP TKLEXT2 NEXTBANS CLK EXTSWHOIS MLOCK MTAGS\r\n".encode(charset))
# writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode('utf-8')) # writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode(charset))
writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode('utf-8')) writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode(charset))
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode('utf-8')) writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode('utf-8')) writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode('utf-8')) writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode('utf-8')) writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode('utf-8')) writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset))
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8')) writer.send(f":{sid} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services\r\n".encode(charset))
writer.send(f":{sid} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
writer.send(f":{service_id} MODE {chan} +{cmodes}\r\n".encode(charset))
writer.send(f":{service_id} MODE {chan} +{umodes} {service_id}\r\n".encode(charset))
self.Base.logs.debug('Link information sent to the server') self.Base.logs.debug('Link information sent to the server')
@@ -220,9 +226,9 @@ class Irc:
self.Base.logs.critical(f'{ae}') self.Base.logs.critical(f'{ae}')
def __join_saved_channels(self) -> None: def __join_saved_channels(self) -> None:
"""## Joining saved channels"""
core_table = 'core_channel' core_table = self.Config.table_channel
query = f'''SELECT distinct channel_name FROM {core_table}''' query = f'''SELECT distinct channel_name FROM {core_table}'''
exec_query = self.Base.db_execute_query(query) exec_query = self.Base.db_execute_query(query)
result_query = exec_query.fetchall() result_query = exec_query.fetchall()
@@ -685,7 +691,10 @@ class Irc:
else: else:
version = f'{current_version}' version = f'{current_version}'
self.send2socket(f"JOIN {self.Config.SERVICE_CHANLOG}") # self.send2socket(f":{self.Config.SERVICE_NICKNAME} SVSJOIN {self.Config.SERVICE_NICKNAME} {self.Config.SERVICE_CHANLOG}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +o {self.Config.SERVICE_NICKNAME}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +{self.Config.SERVICE_CMODES}")
print(f"################### DEFENDER ###################") print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ") print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ") print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
@@ -774,8 +783,8 @@ class Irc:
for i in range(start_boucle, len(cmd)): for i in range(start_boucle, len(cmd)):
parsed_UID = str(cmd[i]) parsed_UID = str(cmd[i])
# pattern = fr'[:|@|%|\+|~|\*]*' # pattern = fr'[:|@|%|\+|~|\*]*'
pattern = fr':' # pattern = fr':'
parsed_UID = re.sub(pattern, '', parsed_UID) # parsed_UID = re.sub(pattern, '', parsed_UID)
clean_uid = self.Base.clean_uid(parsed_UID) clean_uid = self.Base.clean_uid(parsed_UID)
if len(clean_uid) == 9: if len(clean_uid) == 9:
list_users.append(parsed_UID) list_users.append(parsed_UID)
@@ -1341,15 +1350,5 @@ class Irc:
(fromuser, ) (fromuser, )
) )
case 'umode':
try:
# .umode nickname +mode
nickname = str(cmd[1])
umode = str(cmd[2])
self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke:
self.Base.logs.error(ke)
case _: case _:
pass pass

View File

@@ -1,6 +1,6 @@
import json, sys import json, sys
from os import sep from os import sep
from typing import Union from typing import Union, Literal
from dataclasses import dataclass, field from dataclasses import dataclass, field
########################################## ##########################################
@@ -11,58 +11,128 @@ from dataclasses import dataclass, field
class ConfigDataModel: class ConfigDataModel:
SERVEUR_IP: str SERVEUR_IP: str
SERVEUR_HOSTNAME: str # Le hostname du serveur IRC """Server public IP (could be 127.0.0.1 localhost)"""
SERVEUR_LINK: str # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
SERVEUR_PORT: int # Port du link
SERVEUR_PASSWORD: str # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
SERVEUR_ID: str # SID (identification) du bot en tant que Services
SERVEUR_SSL: bool # Activer la connexion SSL
SERVICE_NAME: str # Le nom du service SERVEUR_HOSTNAME: str
SERVICE_NICKNAME: str # Nick du bot sur IRC """IRC Server Hostname (your.hostname.extension)"""
SERVICE_REALNAME: str # Realname du bot
SERVICE_USERNAME: str # Ident du bot
SERVICE_HOST: str # Host du bot
SERVICE_INFO: str # swhois du bot
SERVICE_CHANLOG: str # Salon des logs et autres messages issus du bot
SERVICE_SMODES: str # Mode du service
SERVICE_CMODES: str # Mode du salon (#ChanLog) que le bot appliquera à son entrée
SERVICE_UMODES: str # Mode que le bot pourra se donner à sa connexion au salon chanlog
SERVICE_PREFIX: str # Prefix pour envoyer les commandes au bot
SERVICE_ID: str = field(init=False) # L'identifiant du service
OWNER: str # Identifiant du compte admin SERVEUR_LINK: str
PASSWORD: str # Mot de passe du compte admin """The link hostname (should be the same as your unrealircd link block)"""
SALON_JAIL: str # Salon pot de miel SERVEUR_PORT: int
SALON_JAIL_MODES: str # Mode du salon pot de miel """Server port as configured in your unrealircd link block"""
SALON_LIBERER: str # Le salon ou sera envoyé l'utilisateur clean
API_TIMEOUT: int # Timeout des api's SERVEUR_PASSWORD: str
"""Your link password"""
PORTS_TO_SCAN: list # Liste des ports a scanné pour une detection de proxy SERVEUR_ID: str
WHITELISTED_IP: list # IP a ne pas scanner """Service identification could be Z01 should be unique"""
GLINE_DURATION: str # La durée du gline
DEBUG_LEVEL: int # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50 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"""
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"""
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] # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
CONFIG_COLOR: dict[str, str] CONFIG_COLOR: dict[str, str]
table_admin: str table_admin: str
"""Admin table"""
table_commande: str table_commande: str
"""Core command table"""
table_log: str table_log: str
"""Core log table"""
table_module: str table_module: str
"""Core module table"""
table_config: str table_config: str
"""Core configuration table"""
table_channel: str table_channel: str
"""Core channel table"""
current_version: str current_version: str
"""Current version of Defender"""
latest_version: str latest_version: str
"""The Latest version fetched from github"""
db_name: str db_name: str
"""The database name"""
db_path: str db_path: str
"""The database path"""
def __post_init__(self): def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet # Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB" self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
"""The service ID which is SERVEUR_ID and AAAAAB"""
class Config: class Config:

View File

@@ -1,275 +0,0 @@
from subprocess import check_call, run, CalledProcessError, PIPE
from platform import python_version, python_version_tuple, system
from sys import exit
import os, logging, shutil
try:
import pwd
except ModuleNotFoundError as err:
print(err)
class Install:
def __init__(self) -> None:
# Python required version
self.python_min_version = '3.10'
self.log_file = 'install.log'
self.ServiceName = 'Defender'
self.venv_name = '.pyenv'
self.venv_dependencies: list[str] = ['sqlalchemy','psutil','requests']
self.install_folder = os.getcwd()
self.osname = os.name
self.system_name = system()
self.cmd_linux_requirements: list[str] = ['apt', 'install', '-y', 'python3', 'python3-pip', 'python3-venv']
self.venv_pip_full_path = os.path.join(self.venv_name, f'bin{os.sep}pip')
self.venv_python_full_path = os.path.join(self.venv_name, f'bin{os.sep}python')
self.systemd_folder = '/etc/systemd/system/'
# Init log system
self.init_log_system()
# Exclude Windows OS
if self.osname == 'nt':
print('/!\\ Windows OS is not supported by this automatic installation /!\\')
self.Logs.critical('/!\\ Windows OS is not supported by this automatic install /!\\')
print(self.system_name)
exit(5)
if not self.is_root():
exit(5)
# Get the current user
self.system_username: str = input(f'What is the user ro run defender with ? [{os.getlogin()}] : ')
if str(self.system_username).strip() == '':
self.system_username = os.getlogin()
self.get_user_information(self.system_username)
self.Logs.debug(f'The user selected is: {self.system_username}')
self.Logs.debug(f'Operating system: {self.osname}')
# Install linux dependencies
self.install_linux_dependencies()
# Check python version
self.check_python_version()
# Create systemd service file
self.create_service_file()
# Check if Env Exist | install environment | Install python dependencies
self.check_venv()
# Create and start service
if self.osname != 'nt':
self.run_subprocess(['systemctl','daemon-reload'])
self.run_subprocess(['systemctl','start', self.ServiceName])
self.run_subprocess(['systemctl','status', self.ServiceName])
# Clean the Installation
self.clean_installation()
return None
def is_installed(self) -> bool:
is_installed = False
# Check logs folder
if os.path.exists('logs'):
is_installed = True
# Check db folder
if os.path.exists('db'):
is_installed = True
return is_installed
def is_root(self) -> bool:
if os.geteuid() != 0:
print('/!\\ user must run install.py as root /!\\')
self.Logs.critical('/!\\ user must run install.py as root /!\\')
return False
elif os.geteuid() == 0:
return True
def get_user_information(self, system_user: str) -> None:
try:
username: tuple = pwd.getpwnam(system_user)
self.system_uid = username.pw_uid
self.system_gid = username.pw_gid
return None
except KeyError as ke:
self.Logs.critical(f"This user [{system_user}] doesn't exist: {ke}")
print(f"This user [{system_user}] doesn't exist: {ke}")
exit(5)
def init_log_system(self) -> None:
# Init logs object
self.Logs = logging
self.Logs.basicConfig(level=logging.DEBUG,
filename=self.log_file,
encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
self.Logs.debug('#################### STARTING INSTALLATION ####################')
return None
def clean_installation(self) -> None:
# Chown the Python Env to non user privilege
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}',
f'{os.path.join(self.install_folder, self.venv_name)}'
]
)
# Chown the installation log file
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}',
f'{os.path.join(self.install_folder, self.log_file)}'
]
)
return None
def run_subprocess(self, command:list) -> None:
try:
run_command = check_call(command)
self.Logs.debug(f'{command} - {run_command}')
print(f'{command} - {run_command}')
except CalledProcessError as e:
print(f"Command failed :{e.returncode}")
self.Logs.critical(f"Command failed :{e.returncode}")
exit(5)
def check_python_version(self) -> bool:
"""Test si la version de python est autorisée ou non
Returns:
bool: True si la version de python est autorisé sinon False
"""
self.Logs.debug(f'The current python version is: {python_version()}')
# Current system version
sys_major, sys_minor, sys_patch = python_version_tuple()
# min python version required
python_required_version = self.PYTHON_MIN_VERSION.split('.')
min_major, min_minor = tuple((python_required_version[0], python_required_version[1]))
if int(sys_major) < int(min_major):
print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##")
self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}')
return False
elif (int(sys_major) <= int(min_major)) and (int(sys_minor) < int(min_minor)):
print(f"## Your python version must be greather than or equal to {self.PYTHON_MIN_VERSION} ##")
self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}')
return False
print(f"===> Version of python : {python_version()} ==> OK")
self.Logs.debug(f'Version of python : {python_version()} ==> OK')
return True
def check_packages(self, package_name) -> bool:
try:
# Run a command in the virtual environment's Python to check if the package is installed
run([self.venv_python_full_path, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE)
return True
except CalledProcessError:
return False
def check_venv(self) -> bool:
if os.path.exists(self.venv_name):
# Installer les dependances
self.install_dependencies()
return True
else:
self.run_subprocess(['python3', '-m', 'venv', self.venv_name])
self.Logs.debug(f'Python Virtual env installed {self.venv_name}')
print(f'Python Virtual env installed {self.venv_name}')
self.install_dependencies()
return False
def create_service_file(self) -> None:
if self.systemd_folder is None:
# If Windows, do not install systemd
return None
if os.path.exists(f'{self.systemd_folder}{os.sep}{self.ServiceName}.service'):
print(f'/!\\ Service already created in the system /!\\')
self.Logs.warning('/!\\ Service already created in the system /!\\')
print(f'The service file will be regenerated')
self.Logs.warning('The service file will be regenerated')
contain = f'''[Unit]
Description={self.ServiceName} IRC Service
[Service]
User={self.system_username}
ExecStart={os.path.join(self.install_folder, self.venv_python_full_path)} {os.path.join(self.install_folder, 'main.py')}
WorkingDirectory={self.install_folder}
SyslogIdentifier={self.ServiceName}
Restart=on-failure
[Install]
WantedBy=multi-user.target
'''
with open(f'{self.ServiceName}.service.generated', 'w+') as servicefile:
servicefile.write(contain)
servicefile.close()
print('Service file generated with current configuration')
self.Logs.debug('Service file generated with current configuration')
source = f'{self.install_folder}{os.sep}{self.ServiceName}.service.generated'
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}', source])
destination = f'{self.systemd_folder}'
shutil.copy(source, destination)
os.rename(f'{self.systemd_folder}{os.sep}{self.ServiceName}.service.generated', f'{self.systemd_folder}{os.sep}{self.ServiceName}.service')
print(f'Service file moved to systemd folder {self.systemd_folder}')
self.Logs.debug(f'Service file moved to systemd folder {self.systemd_folder}')
def install_linux_dependencies(self) -> None:
self.run_subprocess(self.cmd_linux_requirements)
return None
def install_dependencies(self) -> None:
try:
self.run_subprocess([self.venv_pip_full_path, 'cache', 'purge'])
self.run_subprocess([self.venv_python_full_path, '-m', 'pip', 'install', '--upgrade', 'pip'])
if self.check_packages('greenlet') is None:
self.run_subprocess(
[self.venv_pip_full_path, 'install', '--only-binary', ':all:', 'greenlet']
)
for module in self.venv_dependencies:
if not self.check_packages(module):
### Trying to install missing python packages ###
self.run_subprocess([self.venv_pip_full_path, 'install', module])
else:
self.Logs.debug(f'{module} already installed')
print(f"==> {module} already installed")
except CalledProcessError as cpe:
self.Logs.critical(f'{cpe}')
Install()

View File

@@ -9,7 +9,6 @@ class Clone():
@dataclass @dataclass
class ModConfModel: class ModConfModel:
clone_count: int
clone_nicknames: list[str] clone_nicknames: list[str]
def __init__(self, ircInstance:Irc) -> None: def __init__(self, ircInstance:Irc) -> None:
@@ -35,9 +34,11 @@ class Clone():
# Add Channel object to the module (Mandatory) # Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel self.Channel = ircInstance.Channel
self.Clone = ircInstance.Clones
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.commands_level = {
1: ['clone_connect', 'clone_join', 'clone_kill', 'clone_list'] 1: ['clone']
} }
# Init the module (Mandatory) # Init the module (Mandatory)
@@ -97,15 +98,13 @@ class Clone():
"""### Load Module Configuration """### Load Module Configuration
""" """
try: try:
# Variable qui va contenir les options de configuration du module Defender
# Variable qui va contenir les options de configuration du module Defender # Variable qui va contenir les options de configuration du module Defender
self.ModConfig = self.ModConfModel( self.ModConfig = self.ModConfModel(
clone_count=0,
clone_nicknames=[] clone_nicknames=[]
) )
# Sync the configuration with core configuration (Mandatory) # 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 return None
@@ -123,22 +122,23 @@ class Clone():
return None return None
def thread_create_clones(self, nickname: str, username: str, channels:list, server_port:int, ssl:bool) -> None: def thread_create_clones(self, nickname: str, username: str, channels: list, server_port: int, ssl: bool) -> None:
Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, ssl=ssl) Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, CloneObject=self.Clone, ssl=ssl)
return None return None
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None): def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
if clone_name is None: if clone_name is None:
for clone in self.ModConfig.clone_nicknames: for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :JOIN {channel_name}') self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
time.sleep(wait) time.sleep(wait)
else: else:
for clone in self.ModConfig.clone_nicknames: for clone in self.Clone.UID_CLONE_DB:
if clone_name == clone: if clone_name == clone.nickname:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :JOIN {channel_name}') self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
time.sleep(wait)
def generate_names(self) -> tuple[str, str]: def generate_names(self) -> tuple[str, str]:
try: try:
@@ -146,13 +146,25 @@ class Clone():
nickname = fake.first_name() nickname = fake.first_name()
username = fake.last_name() username = fake.last_name()
if not nickname in self.ModConfig.clone_nicknames: if self.Clone.exists(nickname=nickname):
self.ModConfig.clone_nicknames.append(nickname)
else:
caracteres = '0123456789' caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2)) randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize) nickname = nickname + str(randomize)
self.ModConfig.clone_nicknames.append(nickname) self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username)
)
else:
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username)
)
# if not nickname in self.ModConfig.clone_nicknames:
# self.ModConfig.clone_nicknames.append(nickname)
# else:
# caracteres = '0123456789'
# randomize = ''.join(random.choice(caracteres) for _ in range(2))
# nickname = nickname + str(randomize)
# self.ModConfig.clone_nicknames.append(nickname)
return (nickname, username) return (nickname, username)
@@ -183,60 +195,110 @@ class Clone():
match command: match command:
case 'clone_connect': case 'clone':
# clone_connect 25 option = str(cmd[1]).lower()
try:
number_of_clones = int(cmd[1])
for i in range(number_of_clones):
nickname, username = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, [], 6697, True)
)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network') if len(command) == 1:
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 list')
except Exception as err: match option:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_connect [number of clone you want to connect]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone_kill 6')
case 'clone_kill': case 'connect':
try: try:
clone_name = str(cmd[1]) number_of_clones = int(cmd[2])
for i in range(number_of_clones):
nickname, username = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, [], 6697, True)
)
if clone_name.lower() == 'all': self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
for clone in self.ModConfig.clone_nicknames: except Exception as err:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone} :KILL') self.Logs.error(f'{err}')
self.ModConfig.clone_nicknames.remove(clone) self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]')
else: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6')
for clone in self.ModConfig.clone_nicknames:
if clone_name == clone:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone} :KILL')
self.ModConfig.clone_nicknames.remove(clone)
except Exception as err: case 'kill':
self.Logs.error(f'{err}') try:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_kill all') # clone kill [all | nickname]
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone_kill [clone_name]') clone_name = str(cmd[2])
clone_to_kill: list[str] = []
case 'clone_join': if clone_name.lower() == 'all':
try: for clone in self.Clone.UID_CLONE_DB:
# clone_join nickname #channel self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
clone_name = str(cmd[1]) clone_to_kill.append(clone.nickname)
clone_channel_to_join = cmd[2] clone.alive = False
if clone_name.lower() == 'all': for clone_nickname in clone_to_kill:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 4)) self.Clone.delete(clone_nickname)
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 4, clone_name))
except Exception as err: del clone_to_kill
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')
case 'clone_list': 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)
for clone_name in self.ModConfig.clone_nicknames: except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name}') 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')
case 'join':
try:
# clone join [all | nickname] #channel
clone_name = str(cmd[2])
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))
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
case 'list':
try:
for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}')
pass
except Exception as err:
self.Logs.error(f'{err}')
case 'say':
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
message = []
for i in range(4, len(cmd)):
message.append(cmd[i])
final_message = ' '.join(message)
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')
return None
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/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')

View File

@@ -35,7 +35,7 @@ class Command():
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.commands_level = {
1: ['join', 'part'], 1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'] 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban', 'unban','kick', 'kickban', 'umode']
} }
# Init the module # Init the module
@@ -97,7 +97,7 @@ class Command():
""" """
try: try:
# Build the default configuration model (Mandatory) # Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1) self.ModConfig = self.ModConfModel()
# Sync the configuration with core configuration (Mandatory) # 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)
@@ -172,6 +172,54 @@ class Command():
match command: match command:
case 'deopall':
try:
self.Irc.send2socket(f":{service_id} SVSMODE {fromchannel} -o")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
case 'devoiceall':
try:
self.Irc.send2socket(f":{service_id} SVSMODE {fromchannel} -v")
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
case 'voiceall':
chan_info = self.Channel.get_Channel(fromchannel)
set_mode = 'v'
mode:str = ''
users:str = ''
uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{set_mode} {dnickname}")
for uid in uids_split:
for i in range(0, len(uid)):
mode += set_mode
users += f'{self.User.get_nickname(self.Base.clean_uid(uid[i]))} '
if i == len(uid) - 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = ''
users = ''
case 'opall':
chan_info = self.Channel.get_Channel(fromchannel)
set_mode = 'o'
mode:str = ''
users:str = ''
uids_split = [chan_info.uids[i:i + 6] for i in range(0, len(chan_info.uids), 6)]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{set_mode} {dnickname}")
for uid in uids_split:
for i in range(0, len(uid)):
mode += set_mode
users += f'{self.User.get_nickname(self.Base.clean_uid(uid[i]))} '
if i == len(uid) - 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +{mode} {users}")
mode = ''
users = ''
case 'op': case 'op':
# /mode #channel +o user # /mode #channel +o user
# .op #channel user # .op #channel user
@@ -481,3 +529,13 @@ class Command():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
case 'umode':
try:
# .umode nickname +mode
nickname = str(cmd[1])
umode = str(cmd[2])
self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke:
self.Base.logs.error(ke)

View File

@@ -272,14 +272,14 @@ class Defender():
return False return False
def reputation_insert(self, reputationModel: ReputationModel) -> bool: def reputation_insert(self, reputationModel: ReputationModel) -> bool:
response = False response = False
# Check if the user already exist # Check if the user already exist
for reputation in self.UID_REPUTATION_DB: for reputation in self.UID_REPUTATION_DB:
if reputation.uid == reputationModel.uid: if reputation.uid == reputationModel.uid:
return response return response
self.UID_REPUTATION_DB.append(reputationModel) self.UID_REPUTATION_DB.append(reputationModel)
self.Logs.debug(f'Reputation inserted: {reputationModel}') self.Logs.debug(f'Reputation inserted: {reputationModel}')
response = True response = True

View File

@@ -48,7 +48,7 @@ class Votekick():
# Créer les nouvelles commandes du module # Créer les nouvelles commandes du module
self.commands_level = { self.commands_level = {
0: ['vote_for', 'vote_against'], 0: ['vote'],
1: ['activate', 'deactivate', 'submit', 'vote_stat', 'vote_verdict', 'vote_cancel'] 1: ['activate', 'deactivate', 'submit', 'vote_stat', 'vote_verdict', 'vote_cancel']
} }
@@ -60,6 +60,8 @@ class Votekick():
def __init_module(self) -> None: def __init_module(self) -> None:
self.Admin = self.Irc.Admin
self.__set_commands(self.commands_level) self.__set_commands(self.commands_level)
self.__create_tables() self.__create_tables()
self.join_saved_channels() self.join_saved_channels()
@@ -192,7 +194,9 @@ class Votekick():
def join_saved_channels(self) -> None: def join_saved_channels(self) -> None:
result = self.Base.db_execute_query("SELECT id, channel FROM votekick_channel") 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)
channels = result.fetchall() channels = result.fetchall()
unixtime = self.Base.get_unixtime() unixtime = self.Base.get_unixtime()
@@ -218,15 +222,18 @@ class Votekick():
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if not self.is_vote_ongoing(channel):
return None
for chan in self.VOTE_CHANNEL_DB: for chan in self.VOTE_CHANNEL_DB:
if chan.channel_name == channel: if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user) target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against: if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :The user {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} will be kicked from this channel') self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["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.Irc.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)) self.Channel.delete_user_from_channel(channel, self.User.get_uid(target_user))
elif chan.vote_for <= chan.vote_against: elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This user [{target_user}] will stay on this channel') self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system # Init the system
if self.init_vote_system(channel): if self.init_vote_system(channel):
@@ -255,186 +262,213 @@ class Votekick():
fromchannel = channel fromchannel = channel
match command: match command:
case 'vote':
option = str(cmd[1]).lower()
match option:
case 'vote_cancel': case 'activate':
try: try:
if channel is None: # vote activate #channel
self.Logs.error(f"The channel is not known, defender can't cancel the vote") if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :You need to specify the channel => /msg {dnickname} vote_cancel #channel') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
for vote in self.VOTE_CHANNEL_DB: sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
if vote.channel_name == channel: if sentchannel is None:
self.init_vote_system(channel) self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote system re-initiated')
except IndexError as ke: self.insert_vote_channel(
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote_cancel #channel') self.VoteChannelModel(
self.Logs.error(f'Index Error: {ke}') channel_name=sentchannel,
target_user='',
voter_users=[],
vote_for=0,
vote_against=0
)
)
case 'vote_for': self.Base.db_query_channel('add', self.module_name, sentchannel)
try:
# vote_for
channel = fromchannel
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')
else:
chan.vote_for += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
except KeyError as ke: self.Irc.send2socket(f":{dnickname} JOIN {sentchannel}")
self.Logs.error(f'Key Error: {ke}') self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}")
except IndexError as ie: 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.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} vote_cancel #channel') except Exception as err:
self.Logs.error(f'Index Error: {ie}') 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')
case 'vote_against': case 'deactivate':
try: try:
# vote_against # vote deactivate #channel
channel = fromchannel if self.Admin.get_Admin(fromuser) is None:
for chan in self.VOTE_CHANNEL_DB: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
if chan.channel_name == channel: return None
if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
else:
chan.vote_against += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
except KeyError as ke: sentchannel = str(cmd[2]).lower() if self.Base.Is_Channel(str(cmd[2]).lower()) else None
self.Logs.error(f'Key Error: {ke}') if sentchannel is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}{command} {option} #CHANNEL")
case 'vote_stat': self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}")
try: self.Irc.send2socket(f":{dnickname} PART {sentchannel}")
# channel = str(fullcmd[2]).lower()
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))}')
except KeyError as ke: for chan in self.VOTE_CHANNEL_DB:
self.Logs.error(f'Key Error: {ke}') if chan.channel_name == sentchannel:
self.VOTE_CHANNEL_DB.remove(chan)
self.Base.db_query_channel('del', self.module_name, chan.channel_name)
case 'vote_verdict': self.Logs.debug(f"The Channel {sentchannel} has been deactivated from the vote system")
try: except Exception as err:
# channel = str(fullcmd[2]).lower() self.Logs.error(f'{err}')
for chan in self.VOTE_CHANNEL_DB: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} #channel')
if chan.channel_name == channel: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} #welcome')
target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :The user {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} will be kicked from this channel')
self.Irc.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} :This user will stay on this channel')
# Init the system
if self.init_vote_system(channel):
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
except KeyError as ke: case '+':
self.Logs.error(f'Key Error: {ke}') try:
# vote +
channel = fromchannel
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')
else:
chan.vote_for += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
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}')
case 'submit': case '-':
# submit nickname try:
try: # vote -
nickname_submitted = cmd[1] channel = fromchannel
# channel = str(fullcmd[2]).lower() for chan in self.VOTE_CHANNEL_DB:
uid_submitted = self.User.get_uid(nickname_submitted) if chan.channel_name == channel:
user_submitted = self.User.get_User(nickname_submitted) if fromuser in chan.voter_users:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You already submitted a vote')
else:
chan.vote_against += 1
chan.voter_users.append(fromuser)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote recorded, thank you')
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}')
# check if there is an ongoing vote case 'cancel':
if self.is_vote_ongoing(channel): try:
for vote in self.VOTE_CHANNEL_DB: # vote cancel
if vote.channel_name == channel: if self.Admin.get_Admin(fromuser) is None:
ongoing_user = self.User.get_nickname(vote.target_user) self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return None
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :There is an ongoing vote on {ongoing_user}') if channel is None:
return False 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')
# check if the user exist for vote in self.VOTE_CHANNEL_DB:
if user_submitted is None: if vote.channel_name == channel:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> do not exist') self.init_vote_system(channel)
return False self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :Vote system re-initiated')
uid_cleaned = self.Base.clean_uid(uid_submitted) except Exception as err:
ChannelInfo = self.Channel.get_Channel(channel) self.Logs.error(f'{err}')
if ChannelInfo is None: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :This channel [{channel}] do not exist in the Channel Object') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
return False
clean_uids_in_channel: list = [] case 'status':
for uid in ChannelInfo.uids: try:
clean_uids_in_channel.append(self.Base.clean_uid(uid)) # 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))}')
if not uid_cleaned in clean_uids_in_channel: except Exception as err:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> is not available in this channel') self.Logs.error(f'{err}')
return False self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option}')
# check if Ircop or Service or Bot case 'submit':
pattern = fr'[o|B|S]' try:
operator_user = re.findall(pattern, user_submitted.umodes) # vote submit nickname
if operator_user: if self.Admin.get_Admin(fromuser) is None:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :You cant vote for this user ! he/she is protected') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Your are not allowed to execute this command')
return False return None
for chan in self.VOTE_CHANNEL_DB: nickname_submitted = cmd[2]
if chan.channel_name == channel: uid_submitted = self.User.get_uid(nickname_submitted)
chan.target_user = self.User.get_uid(nickname_submitted) user_submitted = self.User.get_User(nickname_submitted)
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :{nickname_submitted} has been targeted for a vote') # check if there is an ongoing vote
if self.is_vote_ongoing(channel):
for vote in self.VOTE_CHANNEL_DB:
if vote.channel_name == channel:
ongoing_user = self.User.get_nickname(vote.target_user)
self.Base.create_timer(60, self.timer_vote_verdict, (channel, )) self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :There is an ongoing vote on {ongoing_user}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This vote will end after 60 secondes') return False
except KeyError as ke: # check if the user exist
self.Logs.error(f'Key Error: {ke}') if user_submitted is None:
except TypeError as te: self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This nickname <{nickname_submitted}> do not exist')
self.Logs.error(te) return False
case 'activate': uid_cleaned = self.Base.clean_uid(uid_submitted)
try: ChannelInfo = self.Channel.get_Channel(channel)
# activate #channel if ChannelInfo is None:
sentchannel = str(cmd[1]).lower() if self.Base.Is_Channel(str(cmd[1]).lower()) else None self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :This channel [{channel}] do not exist in the Channel Object')
if sentchannel is None: return False
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}ACTIVATE #CHANNEL")
self.insert_vote_channel( clean_uids_in_channel: list = []
self.VoteChannelModel( for uid in ChannelInfo.uids:
channel_name=sentchannel, clean_uids_in_channel.append(self.Base.clean_uid(uid))
target_user='',
voter_users=[],
vote_for=0,
vote_against=0
)
)
self.Base.db_query_channel('add', self.module_name, sentchannel) 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')
return False
self.Irc.send2socket(f":{dnickname} JOIN {sentchannel}") # check if Ircop or Service or Bot
self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} +o {dnickname}") pattern = fr'[o|B|S]'
self.Irc.send2socket(f":{dnickname} PRIVMSG {sentchannel} :You can now use !submit <nickname> to decide if he will stay or not on this channel ") 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')
return False
except KeyError as ke: for chan in self.VOTE_CHANNEL_DB:
self.Logs.error(f"Key Error : {ke}") if chan.channel_name == channel:
chan.target_user = self.User.get_uid(nickname_submitted)
case 'deactivate': self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :{nickname_submitted} has been targeted for a vote')
try:
# deactivate #channel
sentchannel = str(cmd[1]).lower() if self.Base.Is_Channel(str(cmd[1]).lower()) else None
if sentchannel is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The correct command is {self.Config.SERVICE_PREFIX}DEACTIVATE #CHANNEL")
self.Irc.send2socket(f":{dnickname} SAMODE {sentchannel} -o {dnickname}") self.Base.create_timer(60, self.timer_vote_verdict, (channel, ))
self.Irc.send2socket(f":{dnickname} PART {sentchannel}") self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :This vote will end after 60 secondes')
for chan in self.VOTE_CHANNEL_DB: except Exception as err:
if chan.channel_name == sentchannel: self.Logs.error(f'{err}')
self.VOTE_CHANNEL_DB.remove(chan) self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} {command} {option} nickname')
self.Base.db_query_channel('del', self.module_name, chan.channel_name) self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} {command} {option} adator')
# self.db_delete_vote_channel(chan.channel_name)
self.Logs.debug(f"The Channel {sentchannel} has been deactivated from the vote system") 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')
return None
except KeyError as ke: for chan in self.VOTE_CHANNEL_DB:
self.Logs.error(f"Key Error : {ke}") 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.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["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}")
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system
if self.init_vote_system(channel):
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :System vote re initiated')
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}')

View File

@@ -1,3 +1,3 @@
{ {
"version": "5.1.0" "version": "5.1.5"
} }