Merge pull request #20 from adator85/dev

README Update
This commit is contained in:
adator
2024-08-21 00:50:31 +02:00
committed by GitHub
5 changed files with 225 additions and 87 deletions

View File

@@ -2,70 +2,54 @@
Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD ) Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD )
Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau
en demandant aux user un code de validation. en demandant aux user un code de validation.
Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux utilisateurs des outils d'interaction et de décision collective.
Pré-requis : # Fonctionnalités principales
Commandes opérateurs complètes:
Kick: Expulser un utilisateur du canal.
Ban: Interdire définitivement l'accès au canal.
Unban: Lever une interdiction.
Op/Deop: Attribuer ou retirer les droits d'opérateur.
Halfop/Dehalfop: Attribuer ou retirer les droits
Voice/Devoice: Attribuer ou retirer les droits de voix.
Système de quarantaine:
Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé.
Libération: Permettre à un utilisateur de quitter la quarantaine en entrant un code spécifique.
Système de vote:
Kick: Les utilisateurs peuvent voter pour expulser un membre du canal.
Autres actions: Possibilité d'étendre le système de vote à d'autres actions (ban, etc.).
# Installation et utilisation
Prérequis:
- Python version >= 3.10 - Python version >= 3.10
- Pip de python installé sur la machine - Pip de python installé sur la machine
- Python librairies psutil & sqlalchemy & requests - Python librairies psutil & sqlalchemy & requests
- IRC Serveur Version >= UnrealIRCd-6.1.2.2 - IRC Serveur Version >= UnrealIRCd-6.1.2.2
Lancement de Defender : Installation:
- Installer les librairies python : psutil & sqlalchemy & requests Cloner le dépôt:
- pip3 install psutil sqlalchemy requests ou pip install psutil sqlalchemy requests Bash
- Ne pas lancer Defender en tant que root git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
- Créer plutot un service qui lancera Defender en tant qu'utilisateur non root Utilisez ce code avec précaution.
- Un fichier PID sera crée.
# TO DO LIST Configuration (configuration.json):
Le fichier configuration.json permet de personnaliser le service:
Serveur IRC: Adresse du serveur IRC.
Port: Port du serveur IRC.
Canal: Canal auquel se connecter.
Nom du Service: Nom d'utilisateur du bot sur le serveur.
Mot de passe: Mot de passe du link (si nécessaire).
Préfixes de commandes: Caractères utilisés pour déclencher les commandes.
Et bien d'autres...
- Optimiser le systeme de réputation: Extension:
- lorsque les users ce connectent, Ils entrent dans un salon puis une fraction de seconde le service les bans Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module).
# VERSION 1 Contributions:
Les contributions sont les bienvenues ! N'hésitez pas à ouvrir des issues ou des pull requests.
[02.01.2024] Avertissement:
- Rajout de l'activation de la commande flood Ce bot est fourni "tel quel" sans aucune garantie. Utilisez-le à vos risques et périls.
- Les deux variables RESTART et INIT ont été déplacées vers le module Irc
- Nouvelle class Install:
- Le programme va vérifier si les 3 librairies sont installées (SQLAlchemy & requests & psutil)
- Une fois la vérification, il va mêtre a jour pip puis installera les dépendances
[28.12.2023]
- Changement de méthode pour récuperer la version actuelle de python
- Ajout de la réponse a une PING de la part d'un utilisateur
- Installation automatique des packages sqlalchemy, requests et psutil
# BUG FIX
[29.12.2023]
- Correction des messages de receptions trop longs > 4070 caractéres;
- la méthode boucle et incrémente la réponse tant que le nombre de caractére reçu est supérieur a 4072
- Rajout du protocol MTAGS a la connexion du service
- Impact majeur dans la lecture des messages reçu du serveur ( PRIVMSG, SLOGS, UID, QUIT, NICK, PONG, SJOIN)
# ALREADY IMPLEMENTED
- Connexion en tant que service
- Gestion des messages reçus/envoyés par le serveur
- Gestion des caractéres spéciaux
- Gestion des logs (salon, fichiers et console)
- Mode debug : gestion des logs coté console
- Création du systeme de gestion de commandes
- Defender reconnait les commandes qui commence par le suffix définit dans la configuration
- Defender reconnait aussi reconnaitre les commandes qui viennent de /msg Defender [commande]
- Identifications
- Systéme d'identification [OK]
- Systéme de changement d'information [OK]
- Suppression d'un admin
- Systéme de groupe d'accés [OK]
Reputation security
- Activation ou désaction du systéme --> OK | .reputation ON/off
- Le user sera en mesure de changer la limite de la réputation --> OK | .reputation set limit 120
- Defender devra envoyer l'utilisateur dans un salon définit dans la configuration --> OK
- Defender bannira l'utilisateur de la totalité des salons, il le bannira aussi lorsqu'il souhaitera accéder a de nouveau salon --> OK
- Defender devra envoyer un message du type "Merci de taper cette comande /msg {nomdudefender} code {un code générer aléatoirement} --> OK
- Defender devra reconnaitre le code --> OK
- Defender devra liberer l'utilisateur et l'envoyer vers un salon définit dans la configuration --> OK

View File

@@ -56,9 +56,9 @@ class Base:
} }
if token == '': if token == '':
response = requests.get(json_url) response = requests.get(json_url, timeout=self.Config.API_TIMEOUT)
else: else:
response = requests.get(json_url, headers=headers) response = requests.get(json_url, headers=headers, timeout=self.Config.API_TIMEOUT)
response.raise_for_status() # Vérifie si la requête a réussi response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json() json_response:dict = response.json()
@@ -120,6 +120,16 @@ class Base:
currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S') currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
return currentdate return currentdate
def get_all_modules(self) -> list:
all_files = os.listdir('mods/')
all_modules: list = []
for module in all_files:
if module.endswith('.py') and not module == '__init__.py':
all_modules.append(module.replace('.py', '').lower())
return all_modules
def create_log(self, log_message: str) -> None: def create_log(self, log_message: str) -> None:
"""Enregiste les logs """Enregiste les logs

View File

@@ -1208,16 +1208,31 @@ class Irc:
case 'show_modules': case 'show_modules':
self.Base.logs.debug(self.loaded_classes) self.Base.logs.debug(self.loaded_classes)
all_modules = self.Base.get_all_modules()
results = self.Base.db_execute_query(f'SELECT module FROM {self.Config.table_module}') results = self.Base.db_execute_query(f'SELECT module FROM {self.Config.table_module}')
results = results.fetchall() results = results.fetchall()
if len(results) == 0: # if len(results) == 0:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun module chargé") # self.send2socket(f":{dnickname} NOTICE {fromuser} :There is no module loaded")
return False # return False
for r in results: found = False
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Le module {r[0]} chargé")
for module in all_modules:
for loaded_mod in results:
if module == loaded_mod[0]:
found = True
if found:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['rouge']}Not Loaded{self.Config.CONFIG_COLOR['nogc']}")
found = False
# for r in results:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :{r[0]} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
case 'show_timers': case 'show_timers':

View File

@@ -80,7 +80,7 @@ class Defender():
self.commands_level = { self.commands_level = {
0: ['code'], 0: ['code'],
1: ['join','part', 'info'], 1: ['join','part', 'info'],
2: ['q', 'dq', 'o', 'do', 'h', 'dh', 'v', 'dv', 'b', 'ub','k', 'kb'], 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'],
3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'show_users', 'sentinel'] 3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'show_users', 'sentinel']
} }
self.__set_commands(self.commands_level) # Enrigstrer les nouvelles commandes dans le code self.__set_commands(self.commands_level) # Enrigstrer les nouvelles commandes dans le code
@@ -1224,6 +1224,23 @@ class Defender():
jail_chan = self.Config.SALON_JAIL # Salon pot de miel jail_chan = self.Config.SALON_JAIL # Salon pot de miel
jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel" jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel"
if len(fullcmd) >= 3:
fromchannel = str(fullcmd[2]).lower() if self.Base.Is_Channel(str(fullcmd[2]).lower()) else None
else:
fromchannel = None
if len(cmd) >= 2:
sentchannel = str(cmd[1]).lower() if self.Base.Is_Channel(str(cmd[1]).lower()) else None
else:
sentchannel = None
if not fromchannel is None:
channel = fromchannel
elif not sentchannel is None:
channel = sentchannel
else:
channel = None
match command: match command:
case 'timer': case 'timer':
@@ -1606,97 +1623,209 @@ class Defender():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
case 'op' | 'o': case 'op':
# /mode #channel +o user # /mode #channel +o user
# .op #channel user # .op #channel user
# /msg dnickname op #channel user
# [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb'] # [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb']
try: try:
print(cmd) if channel is None:
channel = cmd[1] self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}') self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
case 'deop' | 'do': case 'deop':
# /mode #channel -o user # /mode #channel -o user
# .deop #channel user # .deop #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}') self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
case 'owner' | 'q': case 'owner':
# /mode #channel +q user # /mode #channel +q user
# .owner #channel user # .owner #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +q {fromuser}")
return True
# owner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}') self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
case 'deowner' | 'dq': case 'deowner':
# /mode #channel -q user # /mode #channel -q user
# .deowner #channel user # .deowner #channel user
try: try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -q {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
return True
channel = cmd[1] channel = cmd[1]
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
case 'halfop' | 'h': case 'halfop':
# /mode #channel +h user # /mode #channel +h user
# .halfop #channel user # .halfop #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +h {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}') self.Logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
case 'dehalfop' | 'dh': case 'dehalfop':
# /mode #channel -h user # /mode #channel -h user
# .dehalfop #channel user # .dehalfop #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -h {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}') self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
case 'voice' | 'v': case 'voice':
# /mode #channel +v user # /mode #channel +v user
# .voice #channel user # .voice #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +v {fromuser}")
return True
# voice nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}') self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
case 'devoice' | 'dv': case 'devoice':
# /mode #channel -v user # /mode #channel -v user
# .devoice #channel user # .devoice #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -v {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}') self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
case 'ban' | 'b': case 'ban':
# .ban #channel nickname # .ban #channel nickname
try: try:
channel = cmd[1] channel = cmd[1]
@@ -1708,7 +1837,7 @@ class Defender():
self.Logs.warning(f'_hcmd BAN: {str(e)}') self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
case 'unban' | 'ub': case 'unban':
# .unban #channel nickname # .unban #channel nickname
try: try:
channel = cmd[1] channel = cmd[1]
@@ -1720,7 +1849,7 @@ class Defender():
self.Logs.warning(f'_hcmd UNBAN: {str(e)}') self.Logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]')
case 'kick' | 'k': case 'kick':
# .kick #channel nickname reason # .kick #channel nickname reason
try: try:
channel = cmd[1] channel = cmd[1]
@@ -1738,7 +1867,7 @@ class Defender():
self.Logs.warning(f'_hcmd KICK: {str(e)}') self.Logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]')
case 'kickban' | 'kb': case 'kickban':
# .kickban #channel nickname reason # .kickban #channel nickname reason
try: try:
channel = cmd[1] channel = cmd[1]

View File

@@ -1,3 +1,3 @@
{ {
"version": "5.0.4" "version": "5.0.5"
} }