mirror of
https://github.com/iio612/DEFENDER.git
synced 2026-02-13 19:24:23 +00:00
138
core/base.py
138
core/base.py
@@ -1,8 +1,9 @@
|
|||||||
import time, threading, os, random, socket, hashlib
|
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlalchemy import create_engine, Engine, Connection, CursorResult
|
from sqlalchemy import create_engine, Engine, Connection, CursorResult
|
||||||
from sqlalchemy.sql import text
|
from sqlalchemy.sql import text
|
||||||
from core.configuration import Config
|
from core.configuration import Config
|
||||||
|
from core.sys_configuration import SysConfig
|
||||||
|
|
||||||
class Base:
|
class Base:
|
||||||
|
|
||||||
@@ -19,6 +20,10 @@ class Base:
|
|||||||
def __init__(self, Config: Config) -> None:
|
def __init__(self, Config: Config) -> None:
|
||||||
|
|
||||||
self.Config = Config # Assigner l'objet de configuration
|
self.Config = Config # Assigner l'objet de configuration
|
||||||
|
self.SysConfig = SysConfig() # Importer les information pour le systeme
|
||||||
|
self.init_log_system() # Demarrer le systeme de log
|
||||||
|
|
||||||
|
self.get_latest_defender_version()
|
||||||
|
|
||||||
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
|
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
|
||||||
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
|
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
|
||||||
@@ -32,6 +37,26 @@ class Base:
|
|||||||
|
|
||||||
self.db_create_first_admin() # Créer un nouvel admin si la base de données est vide
|
self.db_create_first_admin() # Créer un nouvel admin si la base de données est vide
|
||||||
|
|
||||||
|
def get_latest_defender_version(self) -> None:
|
||||||
|
try:
|
||||||
|
#token = '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'
|
||||||
|
token = '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'
|
||||||
|
json_url = f'https://github.com/adator85/IRC_DEFENDER_MODULES/blob/e27a027ae99a6c11171635b2a120803e8682aac6/version.json'
|
||||||
|
headers = {
|
||||||
|
'Authorization': f'token {token}',
|
||||||
|
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
|
||||||
|
}
|
||||||
|
response = requests.get(json_url)
|
||||||
|
response.raise_for_status() # Vérifie si la requête a réussi
|
||||||
|
json_response:dict = response.json()
|
||||||
|
self.SysConfig.LATEST_DEFENDER_VERSION = json_response["version"]
|
||||||
|
|
||||||
|
return None
|
||||||
|
except requests.HTTPError as err:
|
||||||
|
self.logs.error(f'Github not available to fetch latest version: {err}')
|
||||||
|
except:
|
||||||
|
self.logs.warning(f'Github not available to fetch latest version')
|
||||||
|
|
||||||
def get_unixtime(self) -> int:
|
def get_unixtime(self) -> int:
|
||||||
"""
|
"""
|
||||||
Cette fonction retourne un UNIXTIME de type 12365456
|
Cette fonction retourne un UNIXTIME de type 12365456
|
||||||
@@ -62,12 +87,36 @@ class Base:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def init_log_system(self) -> None:
|
||||||
|
# Create folder if not available
|
||||||
|
logs_directory = f'logs{os.sep}'
|
||||||
|
if not os.path.exists(f'{logs_directory}'):
|
||||||
|
os.makedirs(logs_directory)
|
||||||
|
|
||||||
|
# Init logs object
|
||||||
|
self.logs = logging
|
||||||
|
self.logs.basicConfig(level=self.Config.DEBUG_LEVEL,
|
||||||
|
filename='logs/defender.log',
|
||||||
|
encoding='UTF-8',
|
||||||
|
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
|
||||||
|
|
||||||
|
self.logs.info('#################### STARTING INTERCEPTOR HQ ####################')
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def log_cmd(self, user_cmd:str, cmd:str) -> None:
|
def log_cmd(self, user_cmd:str, cmd:str) -> None:
|
||||||
"""Enregistre les commandes envoyées par les utilisateurs
|
"""Enregistre les commandes envoyées par les utilisateurs
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmd (str): la commande a enregistrer
|
cmd (str): la commande a enregistrer
|
||||||
"""
|
"""
|
||||||
|
cmd_list = cmd.split()
|
||||||
|
if len(cmd_list) == 3:
|
||||||
|
if cmd_list[0].replace('.', '') == 'auth':
|
||||||
|
cmd_list[1] = '*******'
|
||||||
|
cmd_list[2] = '*******'
|
||||||
|
cmd = ' '.join(cmd_list)
|
||||||
|
|
||||||
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['commandes']} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
|
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['commandes']} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
|
||||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
|
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
|
||||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||||
@@ -100,13 +149,13 @@ class Base:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.db_isModuleExist(module_name):
|
if not self.db_isModuleExist(module_name):
|
||||||
self.__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.DB_SCHEMA['modules']} (datetime, user, module) VALUES (:datetime, :user, :module)"
|
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['modules']} (datetime, user, module) VALUES (:datetime, :user, :module)"
|
||||||
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module': module_name}
|
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module': module_name}
|
||||||
self.db_execute_query(insert_cmd_query, mes_donnees)
|
self.db_execute_query(insert_cmd_query, mes_donnees)
|
||||||
# self.db_close_session(self.session)
|
# self.db_close_session(self.session)
|
||||||
else:
|
else:
|
||||||
self.__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")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -137,7 +186,7 @@ class Base:
|
|||||||
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
|
(:createdOn, :user, :password, :hostname, :vhost, :level)"""
|
||||||
, mes_donnees)
|
, mes_donnees)
|
||||||
|
|
||||||
pass
|
return None
|
||||||
|
|
||||||
def create_timer(self, time_to_wait: float, func: object, func_args: tuple = ()) -> None:
|
def create_timer(self, time_to_wait: float, func: object, func_args: tuple = ()) -> None:
|
||||||
|
|
||||||
@@ -148,14 +197,20 @@ class Base:
|
|||||||
|
|
||||||
self.running_timers.append(t)
|
self.running_timers.append(t)
|
||||||
|
|
||||||
self.__debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
|
self.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
|
||||||
|
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.__debug(f'Assertion Error -> {ae}')
|
self.logs.error(f'Assertion Error -> {ae}')
|
||||||
|
|
||||||
def create_thread(self, func:object, func_args: tuple = ()) -> None:
|
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None:
|
||||||
try:
|
try:
|
||||||
func_name = func.__name__
|
func_name = func.__name__
|
||||||
|
|
||||||
|
if run_once:
|
||||||
|
for thread in self.running_threads:
|
||||||
|
if thread.getName() == func_name:
|
||||||
|
return None
|
||||||
|
|
||||||
# if func_name in self.running_threads:
|
# if func_name in self.running_threads:
|
||||||
# print(f"HeartBeat is running")
|
# print(f"HeartBeat is running")
|
||||||
# return None
|
# return None
|
||||||
@@ -164,98 +219,95 @@ class Base:
|
|||||||
th.start()
|
th.start()
|
||||||
|
|
||||||
self.running_threads.append(th)
|
self.running_threads.append(th)
|
||||||
self.__debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
|
self.logs.debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
|
||||||
|
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.__debug(f'Assertion Error -> {ae}')
|
self.logs.error(f'{ae}')
|
||||||
|
|
||||||
def garbage_collector_timer(self) -> None:
|
def garbage_collector_timer(self) -> None:
|
||||||
"""Methode qui supprime les timers qui ont finis leurs job
|
"""Methode qui supprime les timers qui ont finis leurs job
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.__debug(f"=======> Checking for Timers to stop")
|
|
||||||
# print(f"{self.running_timers}")
|
|
||||||
for timer in self.running_timers:
|
for timer in self.running_timers:
|
||||||
if not timer.is_alive():
|
if not timer.is_alive():
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
self.running_timers.remove(timer)
|
self.running_timers.remove(timer)
|
||||||
self.__debug(f"Timer {str(timer)} removed")
|
self.logs.info(f"Timer {str(timer)} removed")
|
||||||
else:
|
else:
|
||||||
self.__debug(f"===> Timer {str(timer)} Still running ...")
|
self.logs.debug(f"===> Timer {str(timer)} Still running ...")
|
||||||
|
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
print(f'Assertion Error -> {ae}')
|
self.logs.error(f'Assertion Error -> {ae}')
|
||||||
|
|
||||||
def garbage_collector_thread(self) -> None:
|
def garbage_collector_thread(self) -> None:
|
||||||
"""Methode qui supprime les threads qui ont finis leurs job
|
"""Methode qui supprime les threads qui ont finis leurs job
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.__debug(f"=======> Checking for Threads to stop")
|
|
||||||
for thread in self.running_threads:
|
for thread in self.running_threads:
|
||||||
if thread.getName() != 'heartbeat':
|
if thread.getName() != 'heartbeat':
|
||||||
if not thread.is_alive():
|
if not thread.is_alive():
|
||||||
self.running_threads.remove(thread)
|
self.running_threads.remove(thread)
|
||||||
self.__debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
self.logs.debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
||||||
|
|
||||||
# print(threading.enumerate())
|
# print(threading.enumerate())
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.__debug(f'Assertion Error -> {ae}')
|
self.logs.error(f'Assertion Error -> {ae}')
|
||||||
|
|
||||||
def garbage_collector_sockets(self) -> None:
|
def garbage_collector_sockets(self) -> None:
|
||||||
|
|
||||||
self.__debug(f"=======> Checking for Sockets to stop")
|
|
||||||
for soc in self.running_sockets:
|
for soc in self.running_sockets:
|
||||||
while soc.fileno() != -1:
|
while soc.fileno() != -1:
|
||||||
self.__debug(soc.fileno())
|
self.logs.debug(soc.fileno())
|
||||||
soc.close()
|
soc.close()
|
||||||
|
|
||||||
soc.close()
|
soc.close()
|
||||||
self.running_sockets.remove(soc)
|
self.running_sockets.remove(soc)
|
||||||
self.__debug(f"Socket ==> closed {str(soc.fileno())}")
|
self.logs.debug(f"Socket ==> closed {str(soc.fileno())}")
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
"""Methode qui va préparer l'arrêt complêt du service
|
"""Methode qui va préparer l'arrêt complêt du service
|
||||||
"""
|
"""
|
||||||
# Nettoyage des timers
|
# Nettoyage des timers
|
||||||
print(f"=======> Checking for Timers to stop")
|
self.logs.debug(f"=======> Checking for Timers to stop")
|
||||||
for timer in self.running_timers:
|
for timer in self.running_timers:
|
||||||
while timer.is_alive():
|
while timer.is_alive():
|
||||||
print(f"> waiting for {timer.getName()} to close")
|
self.logs.debug(f"> waiting for {timer.getName()} to close")
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
self.running_timers.remove(timer)
|
self.running_timers.remove(timer)
|
||||||
print(f"> Cancelling {timer.getName()} {timer.native_id}")
|
self.logs.debug(f"> Cancelling {timer.getName()} {timer.native_id}")
|
||||||
|
|
||||||
print(f"=======> Checking for Threads to stop")
|
self.logs.debug(f"=======> Checking for Threads to stop")
|
||||||
for thread in self.running_threads:
|
for thread in self.running_threads:
|
||||||
if thread.getName() == 'heartbeat' and thread.is_alive():
|
if thread.getName() == 'heartbeat' and thread.is_alive():
|
||||||
self.execute_periodic_action()
|
self.execute_periodic_action()
|
||||||
print(f"> Running the last periodic action")
|
self.logs.debug(f"> Running the last periodic action")
|
||||||
self.running_threads.remove(thread)
|
self.running_threads.remove(thread)
|
||||||
print(f"> Cancelling {thread.getName()} {thread.native_id}")
|
self.logs.debug(f"> Cancelling {thread.getName()} {thread.native_id}")
|
||||||
|
|
||||||
print(f"=======> Checking for Sockets to stop")
|
self.logs.debug(f"=======> Checking for Sockets to stop")
|
||||||
for soc in self.running_sockets:
|
for soc in self.running_sockets:
|
||||||
soc.close()
|
soc.close()
|
||||||
while soc.fileno() != -1:
|
while soc.fileno() != -1:
|
||||||
soc.close()
|
soc.close()
|
||||||
|
|
||||||
self.running_sockets.remove(soc)
|
self.running_sockets.remove(soc)
|
||||||
print(f"> Socket ==> closed {str(soc.fileno())}")
|
self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
|
||||||
|
|
||||||
pass
|
return None
|
||||||
|
|
||||||
def db_init(self) -> tuple[Engine, Connection]:
|
def db_init(self) -> tuple[Engine, Connection]:
|
||||||
|
|
||||||
db_directory = self.Config.DEFENDER_DB_PATH
|
db_directory = self.SysConfig.DEFENDER_DB_PATH
|
||||||
full_path_db = self.Config.DEFENDER_DB_PATH + self.Config.DEFENDER_DB_NAME
|
full_path_db = self.SysConfig.DEFENDER_DB_PATH + self.SysConfig.DEFENDER_DB_NAME
|
||||||
|
|
||||||
if not os.path.exists(db_directory):
|
if not os.path.exists(db_directory):
|
||||||
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)
|
||||||
cursor = engine.connect()
|
cursor = engine.connect()
|
||||||
|
self.logs.info("database connexion has been initiated")
|
||||||
return engine, cursor
|
return engine, cursor
|
||||||
|
|
||||||
def __create_db(self) -> None:
|
def __create_db(self) -> None:
|
||||||
@@ -319,7 +371,7 @@ class Base:
|
|||||||
try:
|
try:
|
||||||
self.cursor.close()
|
self.cursor.close()
|
||||||
except AttributeError as ae:
|
except AttributeError as ae:
|
||||||
self.__debug(f"Attribute Error : {ae}")
|
self.logs.error(f"Attribute Error : {ae}")
|
||||||
|
|
||||||
def crypt_password(self, password:str) -> str:
|
def crypt_password(self, password:str) -> str:
|
||||||
"""Retourne un mot de passe chiffré en MD5
|
"""Retourne un mot de passe chiffré en MD5
|
||||||
@@ -352,6 +404,17 @@ class Base:
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def is_valid_ip(self, ip_to_control:str) -> bool:
|
||||||
|
|
||||||
|
try:
|
||||||
|
if ip_to_control in self.Config.WHITELISTED_IP:
|
||||||
|
return False
|
||||||
|
|
||||||
|
ipaddress.ip_address(ip_to_control)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
def get_random(self, lenght:int) -> str:
|
def get_random(self, lenght:int) -> str:
|
||||||
"""
|
"""
|
||||||
Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
|
Retourn une chaîne aléatoire en fonction de la longueur spécifiée.
|
||||||
@@ -367,7 +430,7 @@ class Base:
|
|||||||
# Run Garbage Collector Timer
|
# Run Garbage Collector Timer
|
||||||
self.garbage_collector_timer()
|
self.garbage_collector_timer()
|
||||||
self.garbage_collector_thread()
|
self.garbage_collector_thread()
|
||||||
self.garbage_collector_sockets()
|
# self.garbage_collector_sockets()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for key, value in self.periodic_func.items():
|
for key, value in self.periodic_func.items():
|
||||||
@@ -379,10 +442,3 @@ class Base:
|
|||||||
|
|
||||||
# Vider le dictionnaire de fonction
|
# Vider le dictionnaire de fonction
|
||||||
self.periodic_func.clear()
|
self.periodic_func.clear()
|
||||||
|
|
||||||
def __debug(self, debug_msg:str) -> None:
|
|
||||||
|
|
||||||
if self.Config.DEBUG == 1:
|
|
||||||
print(f"[{self.get_datetime()}] - {debug_msg}")
|
|
||||||
|
|
||||||
return None
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import os
|
|
||||||
##########################################
|
##########################################
|
||||||
# CONFIGURATION FILE : #
|
# CONFIGURATION FILE : #
|
||||||
# Rename file to : configuration.py #
|
# Rename file to : configuration.py #
|
||||||
@@ -6,18 +5,15 @@ import os
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
|
||||||
DEFENDER_VERSION = '3.2.2' # MAJOR.MINOR.BATCH
|
SERVEUR_IP = '0.0.0.0' # IP ou host du serveur à rejoindre
|
||||||
DEFENDER_DB_PATH = 'db' + os.sep # Séparateur en fonction de l'OS
|
|
||||||
DEFENDER_DB_NAME = 'defender' # Le nom de la base de données principale
|
|
||||||
SERVICE_NAME = 'defender' # Le nom du service
|
|
||||||
|
|
||||||
SERVEUR_IP = '8.8.8.8' # IP ou host du serveur à rejoindre
|
|
||||||
SERVEUR_HOSTNAME = 'your hostname' # Le hostname du serveur IRC
|
SERVEUR_HOSTNAME = 'your hostname' # Le hostname du serveur IRC
|
||||||
SERVEUR_LINK = 'your link' # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
|
SERVEUR_LINK = 'your link' # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
|
||||||
SERVEUR_PORT = 6666 # Port du link
|
SERVEUR_PORT = 6666 # Port du link
|
||||||
SERVEUR_PASSWORD = 'your link password' # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
|
SERVEUR_PASSWORD = 'your link password' # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
|
||||||
SERVEUR_ID = '002' # SID (identification) du bot en tant que Services
|
SERVEUR_ID = '002' # SID (identification) du bot en tant que Services
|
||||||
|
SERVEUR_SSL = True # Activer / Desactiver la connexion SSL
|
||||||
|
|
||||||
|
SERVICE_NAME = 'defender' # Le nom du service
|
||||||
SERVICE_NICKNAME = 'BotName' # Nick du bot sur IRC
|
SERVICE_NICKNAME = 'BotName' # Nick du bot sur IRC
|
||||||
SERVICE_REALNAME = 'BotRealname' # Realname du bot
|
SERVICE_REALNAME = 'BotRealname' # Realname du bot
|
||||||
SERVICE_USERNAME = 'BotIdent' # Ident du bot
|
SERVICE_USERNAME = 'BotIdent' # Ident du bot
|
||||||
@@ -37,9 +33,13 @@ class Config:
|
|||||||
SALON_JAIL_MODES = 'sS' # Mode du salon pot de miel
|
SALON_JAIL_MODES = 'sS' # Mode du salon pot de miel
|
||||||
SALON_LIBERER = '#welcome' # Le salon ou sera envoyé l'utilisateur clean
|
SALON_LIBERER = '#welcome' # Le salon ou sera envoyé l'utilisateur clean
|
||||||
|
|
||||||
PORTS_TO_SCAN = [3028, 8080, 1080, 1085, 4145, 9050] # Liste des ports a scanné pour une detection de proxy
|
API_TIMEOUT = 2 # Timeout des api's
|
||||||
|
|
||||||
DEBUG = 0 # Afficher l'ensemble des messages du serveurs dans la console
|
PORTS_TO_SCAN = [3028, 8080, 1080, 1085, 4145, 9050] # Liste des ports a scanné pour une detection de proxy
|
||||||
|
WHITELISTED_IP = ['127.0.0.1'] # IP a ne pas scanner
|
||||||
|
GLINE_DURATION = '1d' # La durée du gline
|
||||||
|
|
||||||
|
DEBUG_LEVEL = 10 # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50
|
||||||
|
|
||||||
CONFIG_COLOR = {
|
CONFIG_COLOR = {
|
||||||
'blanche': '\x0300', # Couleur blanche
|
'blanche': '\x0300', # Couleur blanche
|
||||||
|
|||||||
255
core/irc.py
255
core/irc.py
@@ -1,7 +1,9 @@
|
|||||||
import ssl, re, importlib, sys, time, threading, socket
|
import ssl, re, importlib, sys, time, threading, socket
|
||||||
|
from ssl import SSLSocket
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Union
|
||||||
from core.configuration import Config
|
from core.configuration import Config
|
||||||
|
from core.sys_configuration import SysConfig
|
||||||
from core.base import Base
|
from core.base import Base
|
||||||
|
|
||||||
class Irc:
|
class Irc:
|
||||||
@@ -16,12 +18,15 @@ class Irc:
|
|||||||
self.beat = 30 # Lancer toutes les 30 secondes des actions de nettoyages
|
self.beat = 30 # Lancer toutes les 30 secondes des actions de nettoyages
|
||||||
self.hb_active = True # Heartbeat active
|
self.hb_active = True # Heartbeat active
|
||||||
self.HSID = '' # ID du serveur qui accueil le service ( Host Serveur Id )
|
self.HSID = '' # ID du serveur qui accueil le service ( Host Serveur Id )
|
||||||
|
self.IrcSocket:Union[socket.socket, SSLSocket] = None
|
||||||
|
|
||||||
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
|
||||||
|
self.SSL_VERSION = None # Version SSL
|
||||||
|
|
||||||
self.Config = Config()
|
self.Config = Config()
|
||||||
|
self.SysConfig = SysConfig()
|
||||||
|
|
||||||
# Liste des commandes internes du bot
|
# Liste des commandes internes du bot
|
||||||
self.commands_level = {
|
self.commands_level = {
|
||||||
@@ -38,7 +43,7 @@ class Irc:
|
|||||||
self.commands.append(command)
|
self.commands.append(command)
|
||||||
|
|
||||||
self.Base = Base(self.Config)
|
self.Base = Base(self.Config)
|
||||||
self.Base.create_thread(self.heartbeat, (self.beat, ))
|
self.Base.create_thread(func=self.heartbeat, func_args=(self.beat, ))
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# CONNEXION IRC #
|
# CONNEXION IRC #
|
||||||
@@ -48,26 +53,46 @@ class Irc:
|
|||||||
self.__create_socket()
|
self.__create_socket()
|
||||||
self.__connect_to_irc(ircInstance)
|
self.__connect_to_irc(ircInstance)
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.debug(f'Assertion error : {ae}')
|
self.Base.logs.critical(f'Assertion error: {ae}')
|
||||||
|
|
||||||
def __create_socket(self) -> None:
|
def __create_socket(self) -> None:
|
||||||
|
|
||||||
self.IrcSocket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
try:
|
||||||
|
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
|
||||||
connexion_information = (self.Config.SERVEUR_IP, self.Config.SERVEUR_PORT)
|
connexion_information = (self.Config.SERVEUR_IP, self.Config.SERVEUR_PORT)
|
||||||
self.IrcSocket.connect(connexion_information)
|
|
||||||
|
|
||||||
|
if self.Config.SERVEUR_SSL:
|
||||||
# Créer un object ssl
|
# Créer un object ssl
|
||||||
ssl_context = self.__ssl_context()
|
ssl_context = self.__ssl_context()
|
||||||
ssl_connexion = ssl_context.wrap_socket(self.IrcSocket, server_hostname=self.Config.SERVEUR_HOSTNAME)
|
ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=self.Config.SERVEUR_HOSTNAME)
|
||||||
self.IrcSocket = ssl_connexion
|
ssl_connexion.connect(connexion_information)
|
||||||
|
self.IrcSocket:SSLSocket = ssl_connexion
|
||||||
|
|
||||||
|
self.Base.logs.info(f"Connexion en mode SSL : Version = {self.IrcSocket.version()}")
|
||||||
|
self.SSL_VERSION = self.IrcSocket.version()
|
||||||
|
else:
|
||||||
|
soc.connect(connexion_information)
|
||||||
|
self.IrcSocket:socket.socket = soc
|
||||||
|
self.Base.logs.info("Connexion en mode normal")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
except ssl.SSLEOFError as soe:
|
||||||
|
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
|
||||||
|
except ssl.SSLError as se:
|
||||||
|
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
|
||||||
|
except OSError as oe:
|
||||||
|
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
|
||||||
|
except AttributeError as ae:
|
||||||
|
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
|
||||||
|
|
||||||
def __ssl_context(self) -> ssl.SSLContext:
|
def __ssl_context(self) -> ssl.SSLContext:
|
||||||
ctx = ssl.create_default_context()
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||||
ctx.check_hostname = False
|
ctx.check_hostname = False
|
||||||
ctx.verify_mode = ssl.CERT_NONE
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
self.Base.logs.debug(f'SSLContext initiated with verified mode {ctx.verify_mode}')
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
def __connect_to_irc(self, ircInstance: 'Irc') -> None:
|
def __connect_to_irc(self, ircInstance: 'Irc') -> None:
|
||||||
@@ -78,23 +103,25 @@ class Irc:
|
|||||||
self.load_existing_modules() # Charger les modules existant dans la base de données
|
self.load_existing_modules() # Charger les modules existant dans la base de données
|
||||||
|
|
||||||
while self.signal:
|
while self.signal:
|
||||||
|
try:
|
||||||
if self.RESTART == 1:
|
if self.RESTART == 1:
|
||||||
|
self.Base.logs.debug('Restarting Defender ...')
|
||||||
self.IrcSocket.shutdown(socket.SHUT_RDWR)
|
self.IrcSocket.shutdown(socket.SHUT_RDWR)
|
||||||
self.IrcSocket.close()
|
self.IrcSocket.close()
|
||||||
|
|
||||||
while self.IrcSocket.fileno() != -1:
|
while self.IrcSocket.fileno() != -1:
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.debug("--> En attente de la fermeture du socket ...")
|
self.Base.logs.warning('--> Waiting for socket to close ...')
|
||||||
|
|
||||||
self.__create_socket()
|
self.__create_socket()
|
||||||
self.__link(self.IrcSocket)
|
self.__link(self.IrcSocket)
|
||||||
self.load_existing_modules()
|
self.load_existing_modules()
|
||||||
self.RESTART = 0
|
self.RESTART = 0
|
||||||
|
|
||||||
# 4072 max what the socket can grab
|
# 4072 max what the socket can grab
|
||||||
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
|
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
|
||||||
# data = self.IrcSocket.recv(buffer_size).splitlines(True)
|
|
||||||
|
|
||||||
data_in_bytes = self.IrcSocket.recv(buffer_size)
|
data_in_bytes = self.IrcSocket.recv(buffer_size)
|
||||||
|
data = data_in_bytes.splitlines(True)
|
||||||
count_bytes = len(data_in_bytes)
|
count_bytes = len(data_in_bytes)
|
||||||
|
|
||||||
while count_bytes > 4070:
|
while count_bytes > 4070:
|
||||||
@@ -102,26 +129,35 @@ class Irc:
|
|||||||
new_data = self.IrcSocket.recv(buffer_size)
|
new_data = self.IrcSocket.recv(buffer_size)
|
||||||
data_in_bytes += new_data
|
data_in_bytes += new_data
|
||||||
count_bytes = len(new_data)
|
count_bytes = len(new_data)
|
||||||
# print("========================================================")
|
|
||||||
|
|
||||||
data = data_in_bytes.splitlines()
|
data = data_in_bytes.splitlines(True)
|
||||||
|
|
||||||
# print(f"{str(buffer_size)} - {str(len(data_in_bytes))}")
|
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
|
|
||||||
self.send_response(data)
|
self.send_response(data)
|
||||||
|
|
||||||
|
except ssl.SSLEOFError as soe:
|
||||||
|
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
|
||||||
|
except ssl.SSLError as se:
|
||||||
|
self.Base.logs.error(f"SSLError __connect_to_irc: {se} - {data}")
|
||||||
|
except OSError as oe:
|
||||||
|
self.Base.logs.error(f"SSLError __connect_to_irc: {oe} - {data}")
|
||||||
|
|
||||||
self.IrcSocket.shutdown(socket.SHUT_RDWR)
|
self.IrcSocket.shutdown(socket.SHUT_RDWR)
|
||||||
self.IrcSocket.close()
|
self.IrcSocket.close()
|
||||||
|
self.Base.logs.info("--> Fermeture de Defender ...")
|
||||||
|
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.debug(f'Assertion error : {ae}')
|
self.Base.logs.error(f'Assertion error : {ae}')
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
self.debug(f'Value Error : {ve}')
|
self.Base.logs.error(f'Value Error : {ve}')
|
||||||
except OSError as oe:
|
except ssl.SSLEOFError as soe:
|
||||||
self.debug(f"OS Error : {oe}")
|
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
|
||||||
|
except AttributeError as atte:
|
||||||
|
self.Base.logs.critical(f"{atte}")
|
||||||
|
# except Exception as e:
|
||||||
|
# self.debug(f"Exception: {e}")
|
||||||
|
|
||||||
def __link(self, writer:socket.socket) -> None:
|
def __link(self, writer:socket.socket) -> None:
|
||||||
"""Créer le link et envoyer les informations nécessaires pour la
|
"""Créer le link et envoyer les informations nécessaires pour la
|
||||||
@@ -130,7 +166,7 @@ class Irc:
|
|||||||
Args:
|
Args:
|
||||||
writer (StreamWriter): permet l'envoi des informations au serveur.
|
writer (StreamWriter): permet l'envoi des informations au serveur.
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
nickname = self.Config.SERVICE_NICKNAME
|
nickname = self.Config.SERVICE_NICKNAME
|
||||||
username = self.Config.SERVICE_USERNAME
|
username = self.Config.SERVICE_USERNAME
|
||||||
realname = self.Config.SERVICE_REALNAME
|
realname = self.Config.SERVICE_REALNAME
|
||||||
@@ -147,12 +183,10 @@ class Irc:
|
|||||||
sid = self.Config.SERVEUR_ID
|
sid = self.Config.SERVEUR_ID
|
||||||
service_id = self.Config.SERVICE_ID
|
service_id = self.Config.SERVICE_ID
|
||||||
|
|
||||||
version = self.Config.DEFENDER_VERSION
|
version = self.SysConfig.DEFENDER_VERSION
|
||||||
unixtime = self.Base.get_unixtime()
|
unixtime = self.Base.get_unixtime()
|
||||||
|
|
||||||
# Envoyer un message d'identification
|
# Envoyer un message d'identification
|
||||||
# strtobytes = bytes(":" + sid + " PASS :" + password + "\r\n", 'utf-8')
|
|
||||||
# self.IrcSocket.send(strtobytes)
|
|
||||||
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8'))
|
writer.send(f":{sid} PASS :{password}\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('utf-8'))
|
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 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('utf-8'))
|
||||||
@@ -164,9 +198,11 @@ class Irc:
|
|||||||
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8'))
|
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8'))
|
||||||
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
|
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
|
||||||
|
|
||||||
# writer.write(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8'))
|
self.Base.logs.debug('Link information sent to the server')
|
||||||
# writer.write(f"USER {username} {username} {username} :{username}\r\n".encode('utf-8'))
|
|
||||||
# writer.write(f"NICK {nickname}\r\n".encode('utf-8'))
|
return None
|
||||||
|
except AttributeError as ae:
|
||||||
|
self.Base.logs.critical(f'{ae}')
|
||||||
|
|
||||||
def send2socket(self, send_message:str) -> None:
|
def send2socket(self, send_message:str) -> None:
|
||||||
"""Envoit les commandes à envoyer au serveur.
|
"""Envoit les commandes à envoyer au serveur.
|
||||||
@@ -175,18 +211,25 @@ class Irc:
|
|||||||
string (Str): contient la commande à envoyer au serveur.
|
string (Str): contient la commande à envoyer au serveur.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
with self.Base.lock:
|
||||||
|
# print(f">{str(send_message)}")
|
||||||
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
|
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0]))
|
||||||
|
self.Base.logs.debug(f'{send_message}')
|
||||||
|
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
self.debug('Write Decode impossible try iso-8859-1')
|
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
|
||||||
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
|
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
self.debug('Write Encode impossible ... try iso-8859-1')
|
self.Base.logs.error(f'Encode Error try iso-8859-1 - message: {send_message}')
|
||||||
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
|
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.debug(f"Assertion error : {ae}")
|
self.Base.logs.warning(f'Assertion Error {ae} - message: {send_message}')
|
||||||
|
except ssl.SSLEOFError as soe:
|
||||||
|
self.Base.logs.error(f"SSLEOFError: {soe} - {send_message}")
|
||||||
|
except ssl.SSLError as se:
|
||||||
|
self.Base.logs.error(f"SSLError: {se} - {send_message}")
|
||||||
except OSError as oe:
|
except OSError as oe:
|
||||||
self.debug(f"OS Error : {oe}")
|
self.Base.logs.error(f"OSError: {oe} - {send_message}")
|
||||||
|
|
||||||
def send_response(self, responses:list[bytes]) -> None:
|
def send_response(self, responses:list[bytes]) -> None:
|
||||||
try:
|
try:
|
||||||
@@ -194,6 +237,7 @@ class Irc:
|
|||||||
for data in responses:
|
for data in responses:
|
||||||
response = data.decode(self.CHARSET[0]).split()
|
response = data.decode(self.CHARSET[0]).split()
|
||||||
self.cmd(response)
|
self.cmd(response)
|
||||||
|
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
for data in responses:
|
for data in responses:
|
||||||
response = data.decode(self.CHARSET[1],'replace').split()
|
response = data.decode(self.CHARSET[1],'replace').split()
|
||||||
@@ -203,10 +247,12 @@ class Irc:
|
|||||||
response = data.decode(self.CHARSET[1],'replace').split()
|
response = data.decode(self.CHARSET[1],'replace').split()
|
||||||
self.cmd(response)
|
self.cmd(response)
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.debug(f"Assertion error : {ae}")
|
self.Base.logs.error(f"Assertion error : {ae}")
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# FIN CONNEXION IRC #
|
# FIN CONNEXION IRC #
|
||||||
##############################################
|
##############################################
|
||||||
|
|
||||||
def load_existing_modules(self) -> None:
|
def load_existing_modules(self) -> None:
|
||||||
"""Charge les modules qui existe déja dans la base de données
|
"""Charge les modules qui existe déja dans la base de données
|
||||||
|
|
||||||
@@ -252,7 +298,7 @@ class Irc:
|
|||||||
# 2. Executer la fonction
|
# 2. Executer la fonction
|
||||||
try:
|
try:
|
||||||
if not class_name in self.loaded_classes:
|
if not class_name in self.loaded_classes:
|
||||||
self.debug(f"La class [{class_name} n'existe pas !!]")
|
self.Base.logs.error(f"La class [{class_name} n'existe pas !!]")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class_instance = self.loaded_classes[class_name]
|
class_instance = self.loaded_classes[class_name]
|
||||||
@@ -262,12 +308,12 @@ class Irc:
|
|||||||
|
|
||||||
self.Base.running_timers.append(t)
|
self.Base.running_timers.append(t)
|
||||||
|
|
||||||
self.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
|
self.Base.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
|
||||||
|
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.debug(f'Assertion Error -> {ae}')
|
self.Base.logs.error(f'Assertion Error -> {ae}')
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
self.debug(f"Type error -> {te}")
|
self.Base.logs.error(f"Type error -> {te}")
|
||||||
|
|
||||||
def __create_tasks(self, obj: object, method_name: str, param:list) -> None:
|
def __create_tasks(self, obj: object, method_name: str, param:list) -> None:
|
||||||
"""#### Ajouter les méthodes a éxecuter dans un dictionnaire
|
"""#### Ajouter les méthodes a éxecuter dans un dictionnaire
|
||||||
@@ -286,7 +332,7 @@ class Irc:
|
|||||||
'param': param
|
'param': param
|
||||||
}
|
}
|
||||||
|
|
||||||
self.debug(f'Function to execute : {str(self.Base.periodic_func)}')
|
self.Base.logs.debug(f'Function to execute : {str(self.Base.periodic_func)}')
|
||||||
self.send_ping_to_sereur()
|
self.send_ping_to_sereur()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -300,7 +346,6 @@ class Irc:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def load_module(self, fromuser:str, module_name:str, init:bool = False) -> bool:
|
def load_module(self, fromuser:str, module_name:str, init:bool = False) -> bool:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# module_name : mod_voice
|
# module_name : mod_voice
|
||||||
module_name = module_name.lower()
|
module_name = module_name.lower()
|
||||||
@@ -310,8 +355,8 @@ class Irc:
|
|||||||
|
|
||||||
# Si le module est déja chargé
|
# Si le module est déja chargé
|
||||||
if 'mods.' + module_name in sys.modules:
|
if 'mods.' + module_name in sys.modules:
|
||||||
self.debug("Module déja chargé ...")
|
self.Base.logs.info("Module déja chargé ...")
|
||||||
self.debug('module name = ' + module_name)
|
self.Base.logs.info('module name = ' + module_name)
|
||||||
if class_name in self.loaded_classes:
|
if class_name in self.loaded_classes:
|
||||||
# Si le module existe dans la variable globale retourne False
|
# Si le module existe dans la variable globale retourne False
|
||||||
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.Config.SERVICE_PREFIX}reload {module_name}")
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.Config.SERVICE_PREFIX}reload {module_name}")
|
||||||
@@ -342,14 +387,14 @@ class Irc:
|
|||||||
self.Base.db_record_module(fromuser, module_name)
|
self.Base.db_record_module(fromuser, module_name)
|
||||||
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} chargé")
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} chargé")
|
||||||
|
|
||||||
self.debug(self.loaded_classes)
|
self.Base.logs.info(self.loaded_classes)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except ModuleNotFoundError as moduleNotFound:
|
except ModuleNotFoundError as moduleNotFound:
|
||||||
self.debug(f"MODULE_NOT_FOUND: {moduleNotFound}")
|
self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}")
|
||||||
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}")
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}")
|
||||||
except:
|
except Exception as e:
|
||||||
self.debug(f"Something went wrong with a module you want to load")
|
self.Base.logs.error(f"Something went wrong with a module you want to load : {e}")
|
||||||
|
|
||||||
def insert_db_uid(self, uid:str, nickname:str, username:str, hostname:str, umodes:str, vhost:str, isWebirc: bool) -> None:
|
def insert_db_uid(self, uid:str, nickname:str, username:str, hostname:str, umodes:str, vhost:str, isWebirc: bool) -> None:
|
||||||
|
|
||||||
@@ -399,10 +444,10 @@ class Irc:
|
|||||||
if oldnickname in self.db_uid:
|
if oldnickname in self.db_uid:
|
||||||
del self.db_uid[oldnickname]
|
del self.db_uid[oldnickname]
|
||||||
else:
|
else:
|
||||||
self.debug(f"L'ancien nickname {oldnickname} n'existe pas dans UID_DB")
|
self.Base.logs.debug(f"L'ancien nickname {oldnickname} n'existe pas dans UID_DB")
|
||||||
response = False
|
response = False
|
||||||
|
|
||||||
self.debug(f"{oldnickname} changed to {newnickname}")
|
self.Base.logs.debug(f"{oldnickname} changed to {newnickname}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -431,8 +476,6 @@ class Irc:
|
|||||||
vhost = self.db_uid[uid]['vhost']
|
vhost = self.db_uid[uid]['vhost']
|
||||||
level = int(level)
|
level = int(level)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.db_admin[uid] = {
|
self.db_admin[uid] = {
|
||||||
'nickname': nickname,
|
'nickname': nickname,
|
||||||
'username': username,
|
'username': username,
|
||||||
@@ -489,7 +532,7 @@ class Irc:
|
|||||||
# Supprimer les doublons de la liste
|
# Supprimer les doublons de la liste
|
||||||
self.db_chan = list(set(self.db_chan))
|
self.db_chan = list(set(self.db_chan))
|
||||||
|
|
||||||
self.debug(f"Le salon {channel} a été ajouté à la liste CHAN_DB")
|
self.Base.logs.debug(f"Le salon {channel} a été ajouté à la liste CHAN_DB")
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@@ -500,13 +543,13 @@ class Irc:
|
|||||||
|
|
||||||
if level > 4:
|
if level > 4:
|
||||||
response = "Impossible d'ajouter un niveau > 4"
|
response = "Impossible d'ajouter un niveau > 4"
|
||||||
self.debug(response)
|
self.Base.logs.warning(response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Verification si le user existe dans notre UID_DB
|
# Verification si le user existe dans notre UID_DB
|
||||||
if not nickname in self.db_uid:
|
if not nickname in self.db_uid:
|
||||||
response = f"{nickname} n'est pas connecté, impossible de l'enregistrer pour le moment"
|
response = f"{nickname} n'est pas connecté, impossible de l'enregistrer pour le moment"
|
||||||
self.debug(response)
|
self.Base.logs.warning(response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
hostname = self.db_uid[nickname]['hostname']
|
hostname = self.db_uid[nickname]['hostname']
|
||||||
@@ -527,15 +570,15 @@ class Irc:
|
|||||||
''', mes_donnees)
|
''', mes_donnees)
|
||||||
response = f"{nickname} ajouté en tant qu'administrateur de niveau {level}"
|
response = f"{nickname} ajouté en tant qu'administrateur de niveau {level}"
|
||||||
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
|
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
|
||||||
self.debug(response)
|
self.Base.logs.info(response)
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
response = f'{nickname} Existe déjà dans les users enregistrés'
|
response = f'{nickname} Existe déjà dans les users enregistrés'
|
||||||
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
|
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
|
||||||
self.debug(response)
|
self.Base.logs.info(response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get_uid(self, uidornickname:str) -> str | None:
|
def get_uid(self, uidornickname:str) -> Union[str, None]:
|
||||||
|
|
||||||
uid_recherche = uidornickname
|
uid_recherche = uidornickname
|
||||||
response = None
|
response = None
|
||||||
@@ -548,7 +591,7 @@ class Irc:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get_nickname(self, uidornickname:str) -> str | None:
|
def get_nickname(self, uidornickname:str) -> Union[str, None]:
|
||||||
|
|
||||||
nickname_recherche = uidornickname
|
nickname_recherche = uidornickname
|
||||||
|
|
||||||
@@ -606,13 +649,27 @@ class Irc:
|
|||||||
|
|
||||||
def cmd(self, data:list) -> None:
|
def cmd(self, data:list) -> None:
|
||||||
try:
|
try:
|
||||||
|
|
||||||
cmd_to_send:list[str] = data.copy()
|
cmd_to_send:list[str] = data.copy()
|
||||||
cmd = data.copy()
|
cmd = data.copy()
|
||||||
|
|
||||||
|
cmd_to_debug = data.copy()
|
||||||
|
cmd_to_debug.pop(0)
|
||||||
|
|
||||||
if len(cmd) == 0 or len(cmd) == 1:
|
if len(cmd) == 0 or len(cmd) == 1:
|
||||||
|
self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.debug(cmd)
|
# self.debug(cmd_to_debug)
|
||||||
|
if len(data) == 7:
|
||||||
|
if data[2] == 'PRIVMSG' and data[4] == ':auth':
|
||||||
|
data_copy = data.copy()
|
||||||
|
data_copy[6] = '**********'
|
||||||
|
self.Base.logs.debug(data_copy)
|
||||||
|
else:
|
||||||
|
self.Base.logs.debug(data)
|
||||||
|
else:
|
||||||
|
self.Base.logs.debug(data)
|
||||||
|
|
||||||
match cmd[0]:
|
match cmd[0]:
|
||||||
|
|
||||||
@@ -654,8 +711,8 @@ class Irc:
|
|||||||
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
|
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
|
||||||
pass
|
pass
|
||||||
# Possibilité de déclancher les bans a ce niveau.
|
# Possibilité de déclancher les bans a ce niveau.
|
||||||
except IndexError:
|
except IndexError as ie:
|
||||||
self.debug(f'cmd reputation: index error')
|
self.Base.logs.error(f'{ie}')
|
||||||
|
|
||||||
case '320':
|
case '320':
|
||||||
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
|
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
|
||||||
@@ -680,11 +737,24 @@ class Irc:
|
|||||||
print(f"# SERVICE CONNECTE ")
|
print(f"# SERVICE CONNECTE ")
|
||||||
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
|
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
|
||||||
print(f"# PORT : {self.Config.SERVEUR_PORT} ")
|
print(f"# PORT : {self.Config.SERVEUR_PORT} ")
|
||||||
|
print(f"# SSL : {self.Config.SERVEUR_SSL} ")
|
||||||
|
print(f"# SSL VER : {self.SSL_VERSION} ")
|
||||||
print(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
|
print(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
|
||||||
print(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
|
print(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
|
||||||
print(f"# VERSION : {self.Config.DEFENDER_VERSION} ")
|
print(f"# VERSION : {self.SysConfig.DEFENDER_VERSION} ")
|
||||||
print(f"################################################")
|
print(f"################################################")
|
||||||
|
|
||||||
|
self.Base.logs.info(f"################### DEFENDER ###################")
|
||||||
|
self.Base.logs.info(f"# SERVICE CONNECTE ")
|
||||||
|
self.Base.logs.info(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
|
||||||
|
self.Base.logs.info(f"# PORT : {self.Config.SERVEUR_PORT} ")
|
||||||
|
self.Base.logs.info(f"# SSL : {self.Config.SERVEUR_SSL} ")
|
||||||
|
self.Base.logs.info(f"# SSL VER : {self.SSL_VERSION} ")
|
||||||
|
self.Base.logs.info(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
|
||||||
|
self.Base.logs.info(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
|
||||||
|
self.Base.logs.info(f"# VERSION : {self.SysConfig.DEFENDER_VERSION} ")
|
||||||
|
self.Base.logs.info(f"################################################")
|
||||||
|
|
||||||
# Initialisation terminé aprés le premier PING
|
# Initialisation terminé aprés le premier PING
|
||||||
self.INIT = 0
|
self.INIT = 0
|
||||||
# self.send2socket(f':{self.Config.SERVICE_ID} PING :{hsid}')
|
# self.send2socket(f':{self.Config.SERVICE_ID} PING :{hsid}')
|
||||||
@@ -750,6 +820,15 @@ class Irc:
|
|||||||
cmd.pop(0)
|
cmd.pop(0)
|
||||||
|
|
||||||
get_uid_or_nickname = str(cmd[0].replace(':',''))
|
get_uid_or_nickname = str(cmd[0].replace(':',''))
|
||||||
|
if len(cmd) == 6:
|
||||||
|
if cmd[1] == 'PRIVMSG' and cmd[3] == ':auth':
|
||||||
|
cmd_copy = cmd.copy()
|
||||||
|
cmd_copy[5] = '**********'
|
||||||
|
self.Base.logs.debug(cmd_copy)
|
||||||
|
else:
|
||||||
|
self.Base.logs.info(cmd)
|
||||||
|
else:
|
||||||
|
self.Base.logs.info(f'{cmd}')
|
||||||
# user_trigger = get_user.split('!')[0]
|
# user_trigger = get_user.split('!')[0]
|
||||||
user_trigger = self.get_nickname(get_uid_or_nickname)
|
user_trigger = self.get_nickname(get_uid_or_nickname)
|
||||||
dnickname = self.Config.SERVICE_NICKNAME
|
dnickname = self.Config.SERVICE_NICKNAME
|
||||||
@@ -783,7 +862,7 @@ class Irc:
|
|||||||
|
|
||||||
# Réponse a un CTCP VERSION
|
# Réponse a un CTCP VERSION
|
||||||
if arg[0] == '\x01VERSION\x01':
|
if arg[0] == '\x01VERSION\x01':
|
||||||
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01VERSION Service {self.Config.SERVICE_NICKNAME} V{self.Config.DEFENDER_VERSION}\x01')
|
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01VERSION Service {self.Config.SERVICE_NICKNAME} V{self.SysConfig.DEFENDER_VERSION}\x01')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Réponse a un TIME
|
# Réponse a un TIME
|
||||||
@@ -809,8 +888,8 @@ class Irc:
|
|||||||
|
|
||||||
self._hcmds(user_trigger, arg)
|
self._hcmds(user_trigger, arg)
|
||||||
|
|
||||||
except IndexError:
|
except IndexError as io:
|
||||||
self.debug(f'cmd --> PRIVMSG --> List index out of range')
|
self.Base.logs.error(f'{io}')
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
@@ -821,7 +900,7 @@ class Irc:
|
|||||||
classe_object.cmd(cmd_to_send)
|
classe_object.cmd(cmd_to_send)
|
||||||
|
|
||||||
except IndexError as ie:
|
except IndexError as ie:
|
||||||
self.debug(f"IRC CMD -> IndexError : {ie} - {cmd} - length {str(len(cmd))}")
|
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
|
||||||
|
|
||||||
def _hcmds(self, user: str, cmd:list) -> None:
|
def _hcmds(self, user: str, cmd:list) -> None:
|
||||||
|
|
||||||
@@ -853,8 +932,8 @@ class Irc:
|
|||||||
current_command = cmd[0]
|
current_command = cmd[0]
|
||||||
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["rouge"]}{current_command}{self.Config.CONFIG_COLOR["noire"]} ] - Accès Refusé à {self.get_nickname(fromuser)}')
|
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["rouge"]}{current_command}{self.Config.CONFIG_COLOR["noire"]} ] - Accès Refusé à {self.get_nickname(fromuser)}')
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
|
||||||
except IndexError:
|
except IndexError as ie:
|
||||||
self.debug(f'_hcmd notallowed : Index Error')
|
self.Base.logs.error(f'{ie}')
|
||||||
|
|
||||||
case 'deauth':
|
case 'deauth':
|
||||||
|
|
||||||
@@ -879,6 +958,7 @@ class Irc:
|
|||||||
uid_user = self.get_uid(user_to_log)
|
uid_user = self.get_uid(user_to_log)
|
||||||
self.insert_db_admin(uid_user, user_from_db[1])
|
self.insert_db_admin(uid_user, user_from_db[1])
|
||||||
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} est désormais connecté a {dnickname}")
|
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} est désormais connecté a {dnickname}")
|
||||||
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
|
||||||
else:
|
else:
|
||||||
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} a tapé un mauvais mot de pass")
|
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} a tapé un mauvais mot de pass")
|
||||||
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
|
||||||
@@ -895,13 +975,13 @@ class Irc:
|
|||||||
|
|
||||||
response = self.create_defender_user(newnickname, newlevel, password)
|
response = self.create_defender_user(newnickname, newlevel, password)
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : {response}')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : {response}')
|
||||||
self.debug(response)
|
self.Base.logs.info(response)
|
||||||
|
|
||||||
except IndexError as ie:
|
except IndexError as ie:
|
||||||
self.debug(f'_hcmd addaccess: {ie}')
|
self.Base.logs.error(f'_hcmd addaccess: {ie}')
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
self.debug(f'_hcmd addaccess: out of index : {te}')
|
self.Base.logs.error(f'_hcmd addaccess: out of index : {te}')
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
|
||||||
|
|
||||||
case 'editaccess':
|
case 'editaccess':
|
||||||
@@ -949,9 +1029,9 @@ class Irc:
|
|||||||
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de modifier l'utilisateur {str(user_new_level)}")
|
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de modifier l'utilisateur {str(user_new_level)}")
|
||||||
|
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
self.debug(f"Type error : {te}")
|
self.Base.logs.error(f"Type error : {te}")
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
self.debug(f"Value Error : {ve}")
|
self.Base.logs.error(f"Value Error : {ve}")
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : .editaccess [USER] [NEWPASSWORD] [NEWLEVEL]')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : .editaccess [USER] [NEWPASSWORD] [NEWLEVEL]')
|
||||||
|
|
||||||
case 'delaccess':
|
case 'delaccess':
|
||||||
@@ -961,9 +1041,9 @@ class Irc:
|
|||||||
|
|
||||||
if user_to_del != user_confirmation:
|
if user_to_del != user_confirmation:
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
|
||||||
|
self.Base.logs.warning(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
print(len(cmd))
|
|
||||||
if len(cmd) < 3:
|
if len(cmd) < 3:
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : .delaccess [USER] [CONFIRMUSER]')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : .delaccess [USER] [CONFIRMUSER]')
|
||||||
return None
|
return None
|
||||||
@@ -982,6 +1062,7 @@ class Irc:
|
|||||||
level_user_to_del = info_user[1]
|
level_user_to_del = info_user[1]
|
||||||
if current_user_level <= level_user_to_del:
|
if current_user_level <= level_user_to_del:
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
|
||||||
|
self.Base.logs.warning(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
data_to_delete = {'user': user_to_del}
|
data_to_delete = {'user': user_to_del}
|
||||||
@@ -991,6 +1072,7 @@ class Irc:
|
|||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : User {user_to_del} has been deleted !')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : User {user_to_del} has been deleted !')
|
||||||
else:
|
else:
|
||||||
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
|
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
|
||||||
|
self.Base.logs.warning(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
|
||||||
|
|
||||||
case 'help':
|
case 'help':
|
||||||
|
|
||||||
@@ -1034,7 +1116,7 @@ class Irc:
|
|||||||
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
|
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
|
||||||
|
|
||||||
if class_name in self.loaded_classes:
|
if class_name in self.loaded_classes:
|
||||||
|
self.loaded_classes[class_name].unload()
|
||||||
for level, command in self.loaded_classes[class_name].commands_level.items():
|
for level, command in self.loaded_classes[class_name].commands_level.items():
|
||||||
# Supprimer la commande de la variable commands
|
# Supprimer la commande de la variable commands
|
||||||
for c in self.loaded_classes[class_name].commands_level[level]:
|
for c in self.loaded_classes[class_name].commands_level[level]:
|
||||||
@@ -1048,7 +1130,7 @@ class Irc:
|
|||||||
|
|
||||||
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
|
||||||
except:
|
except:
|
||||||
self.debug(f"Something went wrong with a module you want to load")
|
self.Base.logs.error(f"Something went wrong with a module you want to load")
|
||||||
|
|
||||||
case 'reload':
|
case 'reload':
|
||||||
# reload mod_dktmb
|
# reload mod_dktmb
|
||||||
@@ -1057,7 +1139,8 @@ class Irc:
|
|||||||
class_name = module_name.split('_')[1].capitalize() # ==> Defender
|
class_name = module_name.split('_')[1].capitalize() # ==> Defender
|
||||||
|
|
||||||
if 'mods.' + module_name in sys.modules:
|
if 'mods.' + module_name in sys.modules:
|
||||||
self.debug('Module Already Loaded ... reload the module ...')
|
self.loaded_classes[class_name].unload()
|
||||||
|
self.Base.logs.info('Module Already Loaded ... reload the module ...')
|
||||||
the_module = sys.modules['mods.' + module_name]
|
the_module = sys.modules['mods.' + module_name]
|
||||||
importlib.reload(the_module)
|
importlib.reload(the_module)
|
||||||
|
|
||||||
@@ -1081,7 +1164,7 @@ class Irc:
|
|||||||
else:
|
else:
|
||||||
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
|
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
|
||||||
except:
|
except:
|
||||||
self.debug(f"Something went wrong with a module you want to reload")
|
self.Base.logs.error(f"Something went wrong with a module you want to reload")
|
||||||
|
|
||||||
case 'quit':
|
case 'quit':
|
||||||
try:
|
try:
|
||||||
@@ -1096,12 +1179,12 @@ class Irc:
|
|||||||
|
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : Arrêt du service {dnickname}')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : Arrêt du service {dnickname}')
|
||||||
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
|
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
|
||||||
self.debug(f'Arrêt du server {dnickname}')
|
self.Base.logs.info(f'Arrêt du server {dnickname}')
|
||||||
self.RESTART = 0
|
self.RESTART = 0
|
||||||
self.signal = False
|
self.signal = False
|
||||||
|
|
||||||
except IndexError:
|
except IndexError as ie:
|
||||||
self.debug('_hcmd die: out of index')
|
self.Base.logs.error(f'{ie}')
|
||||||
|
|
||||||
self.send2socket(f"QUIT Good bye")
|
self.send2socket(f"QUIT Good bye")
|
||||||
|
|
||||||
@@ -1114,16 +1197,19 @@ class Irc:
|
|||||||
self.db_uid.clear() #Vider UID_DB
|
self.db_uid.clear() #Vider UID_DB
|
||||||
self.db_chan = [] #Vider les salons
|
self.db_chan = [] #Vider les salons
|
||||||
|
|
||||||
|
for class_name in self.loaded_classes:
|
||||||
|
self.loaded_classes[class_name].unload()
|
||||||
|
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : Redémarrage du service {dnickname}')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : Redémarrage du service {dnickname}')
|
||||||
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
|
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
|
||||||
self.debug(f'Redémarrage du server {dnickname}')
|
self.Base.logs.info(f'Redémarrage du server {dnickname}')
|
||||||
self.loaded_classes.clear()
|
self.loaded_classes.clear()
|
||||||
self.RESTART = 1 # Set restart status to 1 saying that the service will restart
|
self.RESTART = 1 # Set restart status to 1 saying that the service will restart
|
||||||
self.INIT = 1 # set init to 1 saying that the service will be re initiated
|
self.INIT = 1 # set init to 1 saying that the service will be re initiated
|
||||||
|
|
||||||
case 'show_modules':
|
case 'show_modules':
|
||||||
|
|
||||||
self.debug(self.loaded_classes)
|
self.Base.logs.debug(self.loaded_classes)
|
||||||
|
|
||||||
results = self.Base.db_execute_query(f'SELECT module FROM {self.Base.DB_SCHEMA["modules"]}')
|
results = self.Base.db_execute_query(f'SELECT module FROM {self.Base.DB_SCHEMA["modules"]}')
|
||||||
results = results.fetchall()
|
results = results.fetchall()
|
||||||
@@ -1134,25 +1220,28 @@ class Irc:
|
|||||||
|
|
||||||
for r in results:
|
for r in results:
|
||||||
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Le module {r[0]} chargé")
|
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Le module {r[0]} chargé")
|
||||||
self.debug(r[0])
|
|
||||||
|
|
||||||
case 'show_timers':
|
case 'show_timers':
|
||||||
|
|
||||||
if self.Base.running_timers:
|
if self.Base.running_timers:
|
||||||
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_timers}")
|
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_timers}")
|
||||||
self.debug(self.Base.running_timers)
|
|
||||||
else:
|
else:
|
||||||
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun timers en cours d'execution")
|
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun timers en cours d'execution")
|
||||||
|
|
||||||
case 'show_threads':
|
case 'show_threads':
|
||||||
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_threads}")
|
|
||||||
|
running_thread_name:list = []
|
||||||
|
for thread in self.Base.running_threads:
|
||||||
|
running_thread_name.append(f"{thread.getName()} ({thread.is_alive()})")
|
||||||
|
|
||||||
|
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{str(running_thread_name)}")
|
||||||
|
|
||||||
case 'uptime':
|
case 'uptime':
|
||||||
uptime = self.get_defender_uptime()
|
uptime = self.get_defender_uptime()
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}')
|
||||||
|
|
||||||
case 'copyright':
|
case 'copyright':
|
||||||
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.DEFENDER_VERSION} Developped by adator® and dktmb® #')
|
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.SysConfig.DEFENDER_VERSION} Developped by adator® and dktmb® #')
|
||||||
|
|
||||||
case 'sentinel':
|
case 'sentinel':
|
||||||
# .sentinel on
|
# .sentinel on
|
||||||
|
|||||||
22
core/sys_configuration.py
Normal file
22
core/sys_configuration.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import os, json
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# DO NOT TOUCH THIS FILE #
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
class SysConfig:
|
||||||
|
|
||||||
|
DEFENDER_VERSION = '4.0.0' # MAJOR.MINOR.BATCH
|
||||||
|
LATEST_DEFENDER_VERSION = '0.0.0' # Latest Version of Defender in git
|
||||||
|
DEFENDER_DB_PATH = 'db' + os.sep # Séparateur en fonction de l'OS
|
||||||
|
DEFENDER_DB_NAME = 'defender' # Le nom de la base de données principale
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
version_filename = f'.{os.sep}version.json'
|
||||||
|
with open(version_filename, 'r') as version_data:
|
||||||
|
self.global_configuration:dict[str, str] = json.load(version_data)
|
||||||
|
|
||||||
|
self.DEFENDER_VERSION = self.global_configuration["version"]
|
||||||
|
|
||||||
|
return None
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import re, socket, psutil, requests, json
|
from typing import Union
|
||||||
|
import re, socket, psutil, requests, json, time
|
||||||
from core.irc import Irc
|
from core.irc import Irc
|
||||||
|
|
||||||
# Le module crée devra réspecter quelques conditions
|
# Le module crée devra réspecter quelques conditions
|
||||||
@@ -9,7 +10,10 @@ from core.irc import Irc
|
|||||||
# 2 . Récuperer la configuration dans une variable
|
# 2 . Récuperer la configuration dans une variable
|
||||||
# 3 . Définir et enregistrer les nouvelles commandes
|
# 3 . Définir et enregistrer les nouvelles commandes
|
||||||
# 4 . Créer vos tables, en utilisant toujours le nom des votre classe en minuscule ==> defender_votre-table
|
# 4 . Créer vos tables, en utilisant toujours le nom des votre classe en minuscule ==> defender_votre-table
|
||||||
# 3. une methode _hcmds(self, user:str, cmd: list) devra toujours etre crée.
|
# 3. Methode suivantes:
|
||||||
|
# cmd(self, data:list)
|
||||||
|
# _hcmds(self, user:str, cmd: list)
|
||||||
|
# unload(self)
|
||||||
|
|
||||||
class Defender():
|
class Defender():
|
||||||
|
|
||||||
@@ -18,8 +22,22 @@ class Defender():
|
|||||||
self.Irc = ircInstance # Ajouter l'object mod_irc a la classe ( Obligatoire )
|
self.Irc = ircInstance # Ajouter l'object mod_irc a la classe ( Obligatoire )
|
||||||
self.Config = ircInstance.Config # Ajouter la configuration a la classe ( Obligatoire )
|
self.Config = ircInstance.Config # Ajouter la configuration a la classe ( Obligatoire )
|
||||||
self.Base = ircInstance.Base # Ajouter l'objet Base au module ( Obligatoire )
|
self.Base = ircInstance.Base # Ajouter l'objet Base au module ( Obligatoire )
|
||||||
|
self.timeout = self.Config.API_TIMEOUT # API Timeout
|
||||||
|
|
||||||
self.Irc.debug(f'Module {self.__class__.__name__} loaded ...')
|
self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi
|
||||||
|
self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt
|
||||||
|
self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb
|
||||||
|
self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan
|
||||||
|
self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan
|
||||||
|
|
||||||
|
self.abuseipdb_isRunning:bool = True
|
||||||
|
self.freeipapi_isRunning:bool = True
|
||||||
|
self.cloudfilt_isRunning:bool = True
|
||||||
|
self.psutil_isRunning:bool = True
|
||||||
|
self.localscan_isRunning:bool = True
|
||||||
|
self.reputationTimer_isRunning:bool = True
|
||||||
|
|
||||||
|
self.Irc.Base.logs.info(f'Module {self.__class__.__name__} loaded ...')
|
||||||
|
|
||||||
# Créer les nouvelles commandes du module
|
# Créer les nouvelles commandes du module
|
||||||
self.commands_level = {
|
self.commands_level = {
|
||||||
@@ -39,9 +57,6 @@ class Defender():
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
commands (list): Liste des commandes du module
|
commands (list): Liste des commandes du module
|
||||||
|
|
||||||
Returns:
|
|
||||||
None: Aucun retour attendu
|
|
||||||
"""
|
"""
|
||||||
for level, com in commands.items():
|
for level, com in commands.items():
|
||||||
for c in commands[level]:
|
for c in commands[level]:
|
||||||
@@ -51,6 +66,24 @@ class Defender():
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def unload(self) -> None:
|
||||||
|
"""Cette methode sera executée a chaque désactivation ou
|
||||||
|
rechargement de module
|
||||||
|
"""
|
||||||
|
self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb
|
||||||
|
self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi
|
||||||
|
self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt
|
||||||
|
self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan
|
||||||
|
self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan
|
||||||
|
|
||||||
|
self.abuseipdb_isRunning:bool = False
|
||||||
|
self.freeipapi_isRunning:bool = False
|
||||||
|
self.cloudfilt_isRunning:bool = False
|
||||||
|
self.psutil_isRunning:bool = False
|
||||||
|
self.localscan_isRunning:bool = False
|
||||||
|
self.reputationTimer_isRunning:bool = False
|
||||||
|
return None
|
||||||
|
|
||||||
def __create_tables(self) -> None:
|
def __create_tables(self) -> None:
|
||||||
"""Methode qui va créer la base de donnée si elle n'existe pas.
|
"""Methode qui va créer la base de donnée si elle n'existe pas.
|
||||||
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
|
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
|
||||||
@@ -95,7 +128,10 @@ class Defender():
|
|||||||
self.db_reputation = {} # Definir la variable qui contiendra la liste des user concerné par la réputation
|
self.db_reputation = {} # Definir la variable qui contiendra la liste des user concerné par la réputation
|
||||||
self.flood_system = {} # Variable qui va contenir les users
|
self.flood_system = {} # Variable qui va contenir les users
|
||||||
self.reputation_first_connexion = {'ip': '', 'score': -1} # Contient les premieres informations de connexion
|
self.reputation_first_connexion = {'ip': '', 'score': -1} # Contient les premieres informations de connexion
|
||||||
|
# 13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674
|
||||||
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674' # Laisser vide si aucune clé
|
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674' # Laisser vide si aucune clé
|
||||||
|
# r1gEtjtfgRQjtNBDMxsg
|
||||||
|
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg' # Laisser vide si aucune clé
|
||||||
|
|
||||||
# Rejoindre les salons
|
# Rejoindre les salons
|
||||||
self.join_saved_channels()
|
self.join_saved_channels()
|
||||||
@@ -109,6 +145,8 @@ class Defender():
|
|||||||
'local_scan': 0,
|
'local_scan': 0,
|
||||||
'psutil_scan': 0,
|
'psutil_scan': 0,
|
||||||
'abuseipdb_scan': 0,
|
'abuseipdb_scan': 0,
|
||||||
|
'freeipapi_scan': 0,
|
||||||
|
'cloudfilt_scan': 0,
|
||||||
'flood': 0,
|
'flood': 0,
|
||||||
'flood_message': 5,
|
'flood_message': 5,
|
||||||
'flood_time': 1,
|
'flood_time': 1,
|
||||||
@@ -118,6 +156,14 @@ class Defender():
|
|||||||
# Syncrhoniser la variable defConfig avec la configuration de la base de données.
|
# Syncrhoniser la variable defConfig avec la configuration de la base de données.
|
||||||
self.sync_db_configuration()
|
self.sync_db_configuration()
|
||||||
|
|
||||||
|
# Démarrer les threads pour démarrer les api
|
||||||
|
self.Base.create_thread(func=self.thread_freeipapi_scan)
|
||||||
|
self.Base.create_thread(func=self.thread_cloudfilt_scan)
|
||||||
|
self.Base.create_thread(func=self.thread_abuseipdb_scan)
|
||||||
|
self.Base.create_thread(func=self.thread_local_scan)
|
||||||
|
self.Base.create_thread(func=self.thread_psutil_scan)
|
||||||
|
self.Base.create_thread(func=self.thread_reputation_timer)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def sync_db_configuration(self) -> None:
|
def sync_db_configuration(self) -> None:
|
||||||
@@ -135,7 +181,7 @@ class Defender():
|
|||||||
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
|
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
|
||||||
insert_rows = insert.rowcount
|
insert_rows = insert.rowcount
|
||||||
if insert_rows > 0:
|
if insert_rows > 0:
|
||||||
self.Irc.debug(f'Row affected into def_config : {insert_rows}')
|
self.Irc.Base.logs.debug(f'Row affected into def_config : {insert_rows}')
|
||||||
|
|
||||||
# Inserer une nouvelle configuration
|
# Inserer une nouvelle configuration
|
||||||
for param, value in self.defConfig.items():
|
for param, value in self.defConfig.items():
|
||||||
@@ -149,7 +195,7 @@ class Defender():
|
|||||||
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
|
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
|
||||||
insert_rows = insert.rowcount
|
insert_rows = insert.rowcount
|
||||||
if insert_rows > 0:
|
if insert_rows > 0:
|
||||||
self.Irc.debug(f'DB_Def_config - new param included : {insert_rows}')
|
self.Irc.Base.logs.debug(f'DB_Def_config - new param included : {insert_rows}')
|
||||||
|
|
||||||
# Supprimer un parameter si il n'existe plus dans la variable global
|
# Supprimer un parameter si il n'existe plus dans la variable global
|
||||||
query = "SELECT parameter FROM def_config"
|
query = "SELECT parameter FROM def_config"
|
||||||
@@ -162,7 +208,7 @@ class Defender():
|
|||||||
delete = self.Base.db_execute_query('DELETE FROM def_config WHERE parameter = :parameter', mes_donnees)
|
delete = self.Base.db_execute_query('DELETE FROM def_config WHERE parameter = :parameter', mes_donnees)
|
||||||
row_affected = delete.rowcount
|
row_affected = delete.rowcount
|
||||||
if row_affected > 0:
|
if row_affected > 0:
|
||||||
self.Irc.debug(f'DB_Def_config - param [{dbparam[0]}] has been deleted')
|
self.Irc.Base.logs.debug(f'DB_Def_config - param [{dbparam[0]}] has been deleted')
|
||||||
|
|
||||||
# Synchroniser la base de données avec la variable global
|
# Synchroniser la base de données avec la variable global
|
||||||
query = "SELECT parameter, value FROM def_config"
|
query = "SELECT parameter, value FROM def_config"
|
||||||
@@ -172,13 +218,13 @@ class Defender():
|
|||||||
for param, value in result:
|
for param, value in result:
|
||||||
self.defConfig[param] = self.Base.int_if_possible(value)
|
self.defConfig[param] = self.Base.int_if_possible(value)
|
||||||
|
|
||||||
self.Irc.debug(self.defConfig)
|
self.Irc.Base.logs.debug(self.defConfig)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def update_db_configuration(self, param:str, value:str) -> None:
|
def update_db_configuration(self, param:str, value:str) -> None:
|
||||||
|
|
||||||
if not param in self.defConfig:
|
if not param in self.defConfig:
|
||||||
self.Irc.debug(f"Le parametre {param} n'existe pas dans la variable global")
|
self.Irc.Base.logs.error(f"Le parametre {param} n'existe pas dans la variable global")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mes_donnees = {'parameter': param}
|
mes_donnees = {'parameter': param}
|
||||||
@@ -192,9 +238,9 @@ class Defender():
|
|||||||
updated_rows = update.rowcount
|
updated_rows = update.rowcount
|
||||||
if updated_rows > 0:
|
if updated_rows > 0:
|
||||||
self.defConfig[param] = self.Base.int_if_possible(value)
|
self.defConfig[param] = self.Base.int_if_possible(value)
|
||||||
self.Irc.debug(f'DB_Def_config - new param updated : {param} {value}')
|
self.Irc.Base.logs.debug(f'DB_Def_config - new param updated : {param} {value}')
|
||||||
|
|
||||||
self.Irc.debug(self.defConfig)
|
self.Irc.Base.logs.debug(self.defConfig)
|
||||||
|
|
||||||
def add_defender_channel(self, channel:str) -> bool:
|
def add_defender_channel(self, channel:str) -> bool:
|
||||||
"""Cette fonction ajoute les salons de join de Defender
|
"""Cette fonction ajoute les salons de join de Defender
|
||||||
@@ -261,7 +307,7 @@ class Defender():
|
|||||||
secret_code = self.Base.get_random(8)
|
secret_code = self.Base.get_random(8)
|
||||||
|
|
||||||
if not uid in self.Irc.db_uid:
|
if not uid in self.Irc.db_uid:
|
||||||
self.Irc.debug(f'Etrange UID {uid}')
|
self.Irc.Base.logs.error(f'Etrange UID {uid}')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if uid in self.db_reputation:
|
if uid in self.db_reputation:
|
||||||
@@ -269,7 +315,7 @@ class Defender():
|
|||||||
self.db_reputation[uid]['updated_datetime'] = currentDateTime
|
self.db_reputation[uid]['updated_datetime'] = currentDateTime
|
||||||
self.db_reputation[uid]['secret_code'] = secret_code
|
self.db_reputation[uid]['secret_code'] = secret_code
|
||||||
else:
|
else:
|
||||||
self.Irc.debug(f"L'UID {uid} n'existe pas dans REPUTATION_DB")
|
self.Irc.Base.logs.error(f"L'UID {uid} n'existe pas dans REPUTATION_DB")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -284,7 +330,7 @@ class Defender():
|
|||||||
if uid in self.db_reputation:
|
if uid in self.db_reputation:
|
||||||
# Si le nickname existe dans le dictionnaire alors le supprimer
|
# Si le nickname existe dans le dictionnaire alors le supprimer
|
||||||
del self.db_reputation[uid]
|
del self.db_reputation[uid]
|
||||||
self.Irc.debug(f"Le UID {uid} a été supprimé du REPUTATION_DB")
|
self.Irc.Base.logs.debug(f"Le UID {uid} a été supprimé du REPUTATION_DB")
|
||||||
|
|
||||||
def insert_db_trusted(self, uid: str, nickname:str) -> None:
|
def insert_db_trusted(self, uid: str, nickname:str) -> None:
|
||||||
|
|
||||||
@@ -393,21 +439,22 @@ class Defender():
|
|||||||
self.Irc.send2socket(f":{service_id} MODE {chan} +b {jailed_nickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {chan} +b {jailed_nickname}!*@*")
|
||||||
self.Irc.send2socket(f":{service_id} KICK {chan} {jailed_nickname}")
|
self.Irc.send2socket(f":{service_id} KICK {chan} {jailed_nickname}")
|
||||||
|
|
||||||
self.Irc.debug(f"system_reputation : {jailed_nickname} à été capturé par le système de réputation")
|
self.Irc.Base.logs.info(f"system_reputation : {jailed_nickname} à été capturé par le système de réputation")
|
||||||
# self.Irc.create_ping_timer(int(self.defConfig['reputation_timer']) * 60, 'Defender', 'system_reputation_timer')
|
# self.Irc.create_ping_timer(int(self.defConfig['reputation_timer']) * 60, 'Defender', 'system_reputation_timer')
|
||||||
self.Base.create_timer(int(self.defConfig['reputation_timer']) * 60, self.system_reputation_timer)
|
# self.Base.create_timer(int(self.defConfig['reputation_timer']) * 60, self.system_reputation_timer)
|
||||||
else:
|
else:
|
||||||
self.Irc.debug(f"system_reputation : {jailed_nickname} à été supprimé du système de réputation car connecté via WebIrc ou il est dans la 'Trusted list'")
|
self.Irc.Base.logs.info(f"system_reputation : {jailed_nickname} à été supprimé du système de réputation car connecté via WebIrc ou il est dans la 'Trusted list'")
|
||||||
self.delete_db_reputation(uid)
|
self.delete_db_reputation(uid)
|
||||||
|
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
self.Irc.debug(f"system_reputation : {str(e)}")
|
self.Irc.Base.logs.error(f"system_reputation : {str(e)}")
|
||||||
|
|
||||||
def system_reputation_timer(self) -> None:
|
def system_reputation_timer(self) -> None:
|
||||||
try:
|
try:
|
||||||
reputation_flag = int(self.defConfig['reputation'])
|
reputation_flag = int(self.defConfig['reputation'])
|
||||||
reputation_timer = int(self.defConfig['reputation_timer'])
|
reputation_timer = int(self.defConfig['reputation_timer'])
|
||||||
reputation_seuil = self.defConfig['reputation_seuil']
|
reputation_seuil = self.defConfig['reputation_seuil']
|
||||||
|
ban_all_chan = self.Base.int_if_possible(self.defConfig['reputation_ban_all_chan'])
|
||||||
service_id = self.Config.SERVICE_ID
|
service_id = self.Config.SERVICE_ID
|
||||||
dchanlog = self.Config.SERVICE_CHANLOG
|
dchanlog = self.Config.SERVICE_CHANLOG
|
||||||
color_red = self.Config.CONFIG_COLOR['rouge']
|
color_red = self.Config.CONFIG_COLOR['rouge']
|
||||||
@@ -419,25 +466,25 @@ class Defender():
|
|||||||
elif reputation_timer == 0:
|
elif reputation_timer == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# self.Irc.debug(self.db_reputation)
|
|
||||||
uid_to_clean = []
|
uid_to_clean = []
|
||||||
|
|
||||||
for uid in self.db_reputation:
|
for uid in self.db_reputation:
|
||||||
if not self.db_reputation[uid]['isWebirc']: # Si il ne vient pas de WebIRC
|
if not self.db_reputation[uid]['isWebirc']: # Si il ne vient pas de WebIRC
|
||||||
self.Irc.debug(f"Nickname: {self.db_reputation[uid]['nickname']} | uptime: {self.get_user_uptime_in_minutes(uid)} | reputation time: {reputation_timer}")
|
# self.Irc.debug(f"Nickname: {self.db_reputation[uid]['nickname']} | uptime: {self.get_user_uptime_in_minutes(uid)} | reputation time: {reputation_timer}")
|
||||||
if self.get_user_uptime_in_minutes(uid) >= reputation_timer and int(self.db_reputation[uid]['score']) <= int(reputation_seuil):
|
if self.get_user_uptime_in_minutes(uid) >= reputation_timer and int(self.db_reputation[uid]['score']) <= int(reputation_seuil):
|
||||||
self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {self.db_reputation[uid]['nickname']} aprés {str(reputation_timer)} minutes d'inactivité")
|
self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {self.db_reputation[uid]['nickname']} aprés {str(reputation_timer)} minutes d'inactivité")
|
||||||
# if not system_reputation_timer_action(cglobal['reputation_timer_action'], uid, self.db_reputation[uid]['nickname']):
|
# if not system_reputation_timer_action(cglobal['reputation_timer_action'], uid, self.db_reputation[uid]['nickname']):
|
||||||
# return False
|
# return False
|
||||||
self.Irc.send2socket(f":{service_id} KILL {self.db_reputation[uid]['nickname']} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ")
|
self.Irc.send2socket(f":{service_id} KILL {self.db_reputation[uid]['nickname']} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ")
|
||||||
|
|
||||||
self.Irc.debug(f"Action sur {self.db_reputation[uid]['nickname']} aprés {str(reputation_timer)} minutes d'inactivité")
|
self.Irc.Base.logs.info(f"Nickname: {self.db_reputation[uid]['nickname']} KILLED after {str(reputation_timer)} minutes of inactivity")
|
||||||
|
|
||||||
uid_to_clean.append(uid)
|
uid_to_clean.append(uid)
|
||||||
|
|
||||||
for uid in uid_to_clean:
|
for uid in uid_to_clean:
|
||||||
# Suppression des éléments dans {UID_DB} et {REPUTATION_DB}
|
# Suppression des éléments dans {UID_DB} et {REPUTATION_DB}
|
||||||
for chan in self.Irc.db_chan:
|
for chan in self.Irc.db_chan:
|
||||||
if chan != salon_jail:
|
if chan != salon_jail and ban_all_chan == 1:
|
||||||
self.Irc.send2socket(f":{service_id} MODE {chan} -b {self.db_reputation[uid]['nickname']}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {chan} -b {self.db_reputation[uid]['nickname']}!*@*")
|
||||||
|
|
||||||
# Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}.
|
# Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}.
|
||||||
@@ -445,7 +492,17 @@ class Defender():
|
|||||||
self.delete_db_reputation(uid)
|
self.delete_db_reputation(uid)
|
||||||
|
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
self.Irc.debug(f'Assertion Error -> {ae}')
|
self.Irc.Base.logs.error(f'Assertion Error -> {ae}')
|
||||||
|
|
||||||
|
def thread_reputation_timer(self) -> None:
|
||||||
|
try:
|
||||||
|
while self.reputationTimer_isRunning:
|
||||||
|
self.system_reputation_timer()
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
return None
|
||||||
|
except ValueError as ve:
|
||||||
|
self.Irc.Base.logs.error(f"thread_reputation_timer Error : {ve}")
|
||||||
|
|
||||||
def _execute_flood_action(self, action:str, channel:str) -> None:
|
def _execute_flood_action(self, action:str, channel:str) -> None:
|
||||||
"""DO NOT EXECUTE THIS FUNCTION WITHOUT THREADING
|
"""DO NOT EXECUTE THIS FUNCTION WITHOUT THREADING
|
||||||
@@ -505,10 +562,10 @@ class Defender():
|
|||||||
get_diff_secondes = unixtime - self.flood_system[get_detected_uid]['first_msg_time']
|
get_diff_secondes = unixtime - self.flood_system[get_detected_uid]['first_msg_time']
|
||||||
|
|
||||||
elif self.flood_system[get_detected_uid]['nbr_msg'] > flood_message:
|
elif self.flood_system[get_detected_uid]['nbr_msg'] > flood_message:
|
||||||
self.Irc.debug('system de flood detecté')
|
self.Irc.Base.logs.info('system de flood detecté')
|
||||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} : {color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)')
|
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} : {color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)')
|
||||||
self.Irc.send2socket(f":{service_id} MODE {channel} +m")
|
self.Irc.send2socket(f":{service_id} MODE {channel} +m")
|
||||||
self.Irc.debug(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
|
self.Irc.Base.logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
|
||||||
self.flood_system[get_detected_uid]['nbr_msg'] = 0
|
self.flood_system[get_detected_uid]['nbr_msg'] = 0
|
||||||
self.flood_system[get_detected_uid]['first_msg_time'] = unixtime
|
self.flood_system[get_detected_uid]['first_msg_time'] = unixtime
|
||||||
|
|
||||||
@@ -533,10 +590,17 @@ class Defender():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def scan_ports(self, remote_ip: str) -> None:
|
def scan_ports(self, remote_ip: str) -> None:
|
||||||
|
"""local_scan
|
||||||
|
|
||||||
|
Args:
|
||||||
|
remote_ip (str): _description_
|
||||||
|
"""
|
||||||
|
if remote_ip in self.Config.WHITELISTED_IP:
|
||||||
|
return None
|
||||||
|
|
||||||
for port in self.Config.PORTS_TO_SCAN:
|
for port in self.Config.PORTS_TO_SCAN:
|
||||||
newSocket = ''
|
newSocket = ''
|
||||||
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
|
||||||
newSocket.settimeout(0.5)
|
newSocket.settimeout(0.5)
|
||||||
try:
|
try:
|
||||||
connection = (remote_ip, self.Base.int_if_possible(port))
|
connection = (remote_ip, self.Base.int_if_possible(port))
|
||||||
@@ -548,25 +612,76 @@ class Defender():
|
|||||||
newSocket.shutdown(socket.SHUT_RDWR)
|
newSocket.shutdown(socket.SHUT_RDWR)
|
||||||
newSocket.close()
|
newSocket.close()
|
||||||
except (socket.timeout, ConnectionRefusedError):
|
except (socket.timeout, ConnectionRefusedError):
|
||||||
self.Irc.debug(f"Le port {str(port)} est fermé")
|
self.Base.logs.info(f"Le port {remote_ip}:{str(port)} est fermé")
|
||||||
except AttributeError as ae:
|
except AttributeError as ae:
|
||||||
self.Irc.debug(f"AttributeError : {ae}")
|
self.Base.logs.warning(f"AttributeError ({remote_ip}): {ae}")
|
||||||
|
except socket.gaierror as err:
|
||||||
|
self.Base.logs.warning(f"Address Info Error ({remote_ip}): {err}")
|
||||||
finally:
|
finally:
|
||||||
# newSocket.shutdown(socket.SHUT_RDWR)
|
# newSocket.shutdown(socket.SHUT_RDWR)
|
||||||
newSocket.close()
|
newSocket.close()
|
||||||
self.Irc.debug('=======> Fermeture de la socket')
|
self.Base.logs.info('=======> Fermeture de la socket')
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def thread_local_scan(self) -> None:
|
||||||
|
try:
|
||||||
|
while self.localscan_isRunning:
|
||||||
|
|
||||||
|
list_to_remove:list = []
|
||||||
|
for ip in self.localscan_remote_ip:
|
||||||
|
self.scan_ports(ip)
|
||||||
|
list_to_remove.append(ip)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
for ip_to_remove in list_to_remove:
|
||||||
|
self.localscan_remote_ip.remove(ip_to_remove)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
except ValueError as ve:
|
||||||
|
self.Base.logs.warning(f"thread_local_scan Error : {ve}")
|
||||||
|
|
||||||
def get_ports_connexion(self, remote_ip: str) -> list[int]:
|
def get_ports_connexion(self, remote_ip: str) -> list[int]:
|
||||||
|
"""psutil_scan
|
||||||
|
|
||||||
|
Args:
|
||||||
|
remote_ip (str): _description_
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[int]: _description_
|
||||||
|
"""
|
||||||
|
if remote_ip in self.Config.WHITELISTED_IP:
|
||||||
|
return None
|
||||||
|
|
||||||
connections = psutil.net_connections(kind='inet')
|
connections = psutil.net_connections(kind='inet')
|
||||||
|
|
||||||
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
|
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
|
||||||
self.Irc.debug(f"Connexion of {remote_ip} using ports : {str(matching_ports)}")
|
self.Base.logs.info(f"Connexion of {remote_ip} using ports : {str(matching_ports)}")
|
||||||
|
|
||||||
return matching_ports
|
return matching_ports
|
||||||
|
|
||||||
def abuseipdb_scan(self, remote_ip:str) -> dict[str, any] | None:
|
def thread_psutil_scan(self) -> None:
|
||||||
|
try:
|
||||||
|
while self.psutil_isRunning:
|
||||||
|
|
||||||
|
list_to_remove:list = []
|
||||||
|
for ip in self.psutil_remote_ip:
|
||||||
|
self.get_ports_connexion(ip)
|
||||||
|
list_to_remove.append(ip)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
for ip_to_remove in list_to_remove:
|
||||||
|
self.psutil_remote_ip.remove(ip_to_remove)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
except ValueError as ve:
|
||||||
|
self.Base.logs.warning(f"thread_psutil_scan Error : {ve}")
|
||||||
|
|
||||||
|
def abuseipdb_scan(self, remote_ip:str) -> Union[dict[str, any], None]:
|
||||||
"""Analyse l'ip avec AbuseIpDB
|
"""Analyse l'ip avec AbuseIpDB
|
||||||
Cette methode devra etre lancer toujours via un thread ou un timer.
|
Cette methode devra etre lancer toujours via un thread ou un timer.
|
||||||
Args:
|
Args:
|
||||||
@@ -576,6 +691,8 @@ class Defender():
|
|||||||
dict[str, any] | None: les informations du provider
|
dict[str, any] | None: les informations du provider
|
||||||
keys : 'score', 'country', 'isTor', 'totalReports'
|
keys : 'score', 'country', 'isTor', 'totalReports'
|
||||||
"""
|
"""
|
||||||
|
if remote_ip in self.Config.WHITELISTED_IP:
|
||||||
|
return None
|
||||||
if self.defConfig['abuseipdb_scan'] == 0:
|
if self.defConfig['abuseipdb_scan'] == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -593,11 +710,14 @@ class Defender():
|
|||||||
'Key': self.abuseipdb_key
|
'Key': self.abuseipdb_key
|
||||||
}
|
}
|
||||||
|
|
||||||
response = requests.request(method='GET', url=url, headers=headers, params=querystring)
|
response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=self.timeout)
|
||||||
|
|
||||||
# Formatted output
|
# Formatted output
|
||||||
decodedResponse = json.loads(response.text)
|
decodedResponse = json.loads(response.text)
|
||||||
try:
|
try:
|
||||||
|
if not 'data' in decodedResponse:
|
||||||
|
return None
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
'score': decodedResponse['data']['abuseConfidenceScore'],
|
'score': decodedResponse['data']['abuseConfidenceScore'],
|
||||||
'country': decodedResponse['data']['countryCode'],
|
'country': decodedResponse['data']['countryCode'],
|
||||||
@@ -610,13 +730,191 @@ class Defender():
|
|||||||
color_red = self.Config.CONFIG_COLOR['rouge']
|
color_red = self.Config.CONFIG_COLOR['rouge']
|
||||||
color_black = self.Config.CONFIG_COLOR['noire']
|
color_black = self.Config.CONFIG_COLOR['noire']
|
||||||
|
|
||||||
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}ABUSEIPDB_SCAN{color_black} ] : Connexion de {remote_ip} Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}")
|
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}ABUSEIPDB_SCAN{color_black} ] : Connexion de {remote_ip} ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}")
|
||||||
|
|
||||||
|
if result['isTor']:
|
||||||
|
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
|
||||||
|
elif result['score'] >= 95:
|
||||||
|
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb")
|
||||||
|
|
||||||
response.close()
|
response.close()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
self.Irc.debug(f"AbuseIpDb KeyError : {ke}")
|
self.Base.logs.error(f"AbuseIpDb KeyError : {ke}")
|
||||||
|
except requests.ReadTimeout as rt:
|
||||||
|
self.Base.logs.error(f"AbuseIpDb Timeout : {rt}")
|
||||||
|
except requests.ConnectionError as ce:
|
||||||
|
self.Base.logs.error(f"AbuseIpDb Connection Error : {ce}")
|
||||||
|
|
||||||
|
def thread_abuseipdb_scan(self) -> None:
|
||||||
|
try:
|
||||||
|
while self.abuseipdb_isRunning:
|
||||||
|
|
||||||
|
list_to_remove:list = []
|
||||||
|
for ip in self.abuseipdb_remote_ip:
|
||||||
|
self.abuseipdb_scan(ip)
|
||||||
|
list_to_remove.append(ip)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
for ip_to_remove in list_to_remove:
|
||||||
|
self.abuseipdb_remote_ip.remove(ip_to_remove)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
except ValueError as ve:
|
||||||
|
self.Base.logs.error(f"thread_abuseipdb_scan Error : {ve}")
|
||||||
|
|
||||||
|
def freeipapi_scan(self, remote_ip:str) -> Union[dict[str, any], None]:
|
||||||
|
"""Analyse l'ip avec Freeipapi
|
||||||
|
Cette methode devra etre lancer toujours via un thread ou un timer.
|
||||||
|
Args:
|
||||||
|
remote_ip (_type_): l'ip a analyser
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[str, any] | None: les informations du provider
|
||||||
|
keys : 'countryCode', 'isProxy'
|
||||||
|
"""
|
||||||
|
if remote_ip in self.Config.WHITELISTED_IP:
|
||||||
|
return None
|
||||||
|
if self.defConfig['freeipapi_scan'] == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
service_id = self.Config.SERVICE_ID
|
||||||
|
service_chanlog = self.Config.SERVICE_CHANLOG
|
||||||
|
color_red = self.Config.CONFIG_COLOR['rouge']
|
||||||
|
color_black = self.Config.CONFIG_COLOR['noire']
|
||||||
|
|
||||||
|
url = f'https://freeipapi.com/api/json/{remote_ip}'
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.request(method='GET', url=url, headers=headers, timeout=self.timeout)
|
||||||
|
|
||||||
|
# Formatted output
|
||||||
|
decodedResponse = json.loads(response.text)
|
||||||
|
try:
|
||||||
|
status_code = response.status_code
|
||||||
|
if status_code == 429:
|
||||||
|
self.Base.logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.')
|
||||||
|
return None
|
||||||
|
elif status_code != 200:
|
||||||
|
self.Base.logs.warning(f'status code = {str(status_code)}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'countryCode': decodedResponse['countryCode'] if 'countryCode' in decodedResponse else None,
|
||||||
|
'isProxy': decodedResponse['isProxy'] if 'isProxy' in decodedResponse else None
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}FREEIPAPI_SCAN{color_black} ] : Connexion de {remote_ip} ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}")
|
||||||
|
|
||||||
|
if result['isProxy']:
|
||||||
|
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
return result
|
||||||
|
except KeyError as ke:
|
||||||
|
self.Base.logs.error(f"FREEIPAPI_SCAN KeyError : {ke}")
|
||||||
|
|
||||||
|
def thread_freeipapi_scan(self) -> None:
|
||||||
|
try:
|
||||||
|
while self.freeipapi_isRunning:
|
||||||
|
|
||||||
|
list_to_remove:list = []
|
||||||
|
for ip in self.freeipapi_remote_ip:
|
||||||
|
self.freeipapi_scan(ip)
|
||||||
|
list_to_remove.append(ip)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
for ip_to_remove in list_to_remove:
|
||||||
|
self.freeipapi_remote_ip.remove(ip_to_remove)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
except ValueError as ve:
|
||||||
|
self.Base.logs.error(f"thread_freeipapi_scan Error : {ve}")
|
||||||
|
|
||||||
|
def cloudfilt_scan(self, remote_ip:str) -> Union[dict[str, any], None]:
|
||||||
|
"""Analyse l'ip avec cloudfilt
|
||||||
|
Cette methode devra etre lancer toujours via un thread ou un timer.
|
||||||
|
Args:
|
||||||
|
remote_ip (_type_): l'ip a analyser
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[str, any] | None: les informations du provider
|
||||||
|
keys : 'countryCode', 'isProxy'
|
||||||
|
"""
|
||||||
|
if remote_ip in self.Config.WHITELISTED_IP:
|
||||||
|
return None
|
||||||
|
if self.defConfig['cloudfilt_scan'] == 0:
|
||||||
|
return None
|
||||||
|
if self.cloudfilt_key == '':
|
||||||
|
return None
|
||||||
|
|
||||||
|
service_id = self.Config.SERVICE_ID
|
||||||
|
service_chanlog = self.Config.SERVICE_CHANLOG
|
||||||
|
color_red = self.Config.CONFIG_COLOR['rouge']
|
||||||
|
color_black = self.Config.CONFIG_COLOR['noire']
|
||||||
|
|
||||||
|
url = f"https://developers18334.cloudfilt.com/"
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'ip': remote_ip,
|
||||||
|
'key': self.cloudfilt_key
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url=url, data=data)
|
||||||
|
|
||||||
|
# Formatted output
|
||||||
|
decodedResponse = json.loads(response.text)
|
||||||
|
try:
|
||||||
|
status_code = response.status_code
|
||||||
|
if status_code != 200:
|
||||||
|
self.Base.logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'countryiso': decodedResponse['countryiso'] if 'countryiso' in decodedResponse else None,
|
||||||
|
'listed': decodedResponse['listed'] if 'listed' in decodedResponse else None,
|
||||||
|
'listed_by': decodedResponse['listed_by'] if 'listed_by' in decodedResponse else None,
|
||||||
|
'host': decodedResponse['host'] if 'host' in decodedResponse else None
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}CLOUDFILT_SCAN{color_black} ] : Connexion de {str(remote_ip)} ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}")
|
||||||
|
|
||||||
|
if result['listed']:
|
||||||
|
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} You connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt")
|
||||||
|
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
return result
|
||||||
|
except KeyError as ke:
|
||||||
|
self.Base.logs.error(f"CLOUDFILT_SCAN KeyError : {ke}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def thread_cloudfilt_scan(self) -> None:
|
||||||
|
try:
|
||||||
|
while self.cloudfilt_isRunning:
|
||||||
|
|
||||||
|
list_to_remove:list = []
|
||||||
|
for ip in self.cloudfilt_remote_ip:
|
||||||
|
self.cloudfilt_scan(ip)
|
||||||
|
list_to_remove.append(ip)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
for ip_to_remove in list_to_remove:
|
||||||
|
self.cloudfilt_remote_ip.remove(ip_to_remove)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
except ValueError as ve:
|
||||||
|
self.Base.logs.error(f"Thread_cloudfilt_scan Error : {ve}")
|
||||||
|
|
||||||
def cmd(self, data:list) -> None:
|
def cmd(self, data:list) -> None:
|
||||||
|
|
||||||
@@ -634,18 +932,28 @@ class Defender():
|
|||||||
self.reputation_first_connexion['ip'] = cmd[2]
|
self.reputation_first_connexion['ip'] = cmd[2]
|
||||||
self.reputation_first_connexion['score'] = cmd[3]
|
self.reputation_first_connexion['score'] = cmd[3]
|
||||||
|
|
||||||
|
if not self.Base.is_valid_ip(cmd[2]):
|
||||||
|
return None
|
||||||
|
|
||||||
# self.Base.scan_ports(cmd[2])
|
# self.Base.scan_ports(cmd[2])
|
||||||
if self.defConfig['local_scan'] == 1:
|
if self.defConfig['local_scan'] == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
|
||||||
self.Base.create_thread(self.scan_ports, (cmd[2], ))
|
self.localscan_remote_ip.append(cmd[2])
|
||||||
|
|
||||||
if self.defConfig['psutil_scan'] == 1:
|
if self.defConfig['psutil_scan'] == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
|
||||||
self.Base.create_thread(self.get_ports_connexion, (cmd[2], ))
|
self.psutil_remote_ip.append(cmd[2])
|
||||||
|
|
||||||
|
if self.defConfig['abuseipdb_scan'] == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
|
||||||
|
self.abuseipdb_remote_ip.append(cmd[2])
|
||||||
|
|
||||||
|
if self.defConfig['freeipapi_scan'] == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
|
||||||
|
self.freeipapi_remote_ip.append(cmd[2])
|
||||||
|
|
||||||
|
if self.defConfig['cloudfilt_scan'] == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
|
||||||
|
self.cloudfilt_remote_ip.append(cmd[2])
|
||||||
|
|
||||||
if self.defConfig['abuseipdb_scan'] == 1:
|
|
||||||
self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
|
|
||||||
# Possibilité de déclancher les bans a ce niveau.
|
# Possibilité de déclancher les bans a ce niveau.
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.Irc.debug(f'cmd reputation: index error')
|
self.Irc.Base.logs.error(f'cmd reputation: index error')
|
||||||
|
|
||||||
match cmd[2]:
|
match cmd[2]:
|
||||||
|
|
||||||
@@ -698,7 +1006,7 @@ class Defender():
|
|||||||
if uid in self.db_reputation:
|
if uid in self.db_reputation:
|
||||||
if reputation_flag == 1 and int(client_score) <= int(reputation_seuil):
|
if reputation_flag == 1 and int(client_score) <= int(reputation_seuil):
|
||||||
self.system_reputation(uid)
|
self.system_reputation(uid)
|
||||||
self.Irc.debug('Démarrer le systeme de reputation')
|
self.Base.logs.info('Démarrer le systeme de reputation')
|
||||||
|
|
||||||
case 'SJOIN':
|
case 'SJOIN':
|
||||||
# ['@msgid=F9B7JeHL5pj9nN57cJ5pEr;time=2023-12-28T20:47:24.305Z', ':001', 'SJOIN', '1702138958', '#welcome', ':0015L1AHL']
|
# ['@msgid=F9B7JeHL5pj9nN57cJ5pEr;time=2023-12-28T20:47:24.305Z', ':001', 'SJOIN', '1702138958', '#welcome', ':0015L1AHL']
|
||||||
@@ -719,21 +1027,31 @@ class Defender():
|
|||||||
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b {self.db_reputation[parsed_UID]['nickname']}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b {self.db_reputation[parsed_UID]['nickname']}!*@*")
|
||||||
self.Irc.send2socket(f":{service_id} KICK {parsed_chan} {self.db_reputation[parsed_UID]['nickname']}")
|
self.Irc.send2socket(f":{service_id} KICK {parsed_chan} {self.db_reputation[parsed_UID]['nickname']}")
|
||||||
|
|
||||||
self.Irc.debug(f'SJOIN parsed_uid : {parsed_UID}')
|
self.Base.logs.debug(f'SJOIN parsed_uid : {parsed_UID}')
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
self.Irc.debug(f"key error SJOIN : {ke}")
|
self.Base.logs.error(f"key error SJOIN : {ke}")
|
||||||
|
|
||||||
case 'SLOG':
|
case 'SLOG':
|
||||||
# self.Base.scan_ports(cmd[7])
|
# self.Base.scan_ports(cmd[7])
|
||||||
cmd.pop(0)
|
cmd.pop(0)
|
||||||
if self.defConfig['local_scan'] == 1:
|
|
||||||
self.Base.create_thread(self.scan_ports, (cmd[7], ))
|
|
||||||
|
|
||||||
if self.defConfig['psutil_scan'] == 1:
|
if not self.Base.is_valid_ip(cmd[7]):
|
||||||
self.Base.create_thread(self.get_ports_connexion, (cmd[7], ))
|
return None
|
||||||
|
|
||||||
if self.defConfig['abuseipdb_scan'] == 1:
|
if self.defConfig['local_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
|
||||||
self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
|
self.localscan_remote_ip.append(cmd[7])
|
||||||
|
|
||||||
|
if self.defConfig['psutil_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
|
||||||
|
self.psutil_remote_ip.append(cmd[7])
|
||||||
|
|
||||||
|
if self.defConfig['abuseipdb_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
|
||||||
|
self.abuseipdb_remote_ip.append(cmd[7])
|
||||||
|
|
||||||
|
if self.defConfig['freeipapi_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
|
||||||
|
self.freeipapi_remote_ip.append(cmd[7])
|
||||||
|
|
||||||
|
if self.defConfig['cloudfilt_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
|
||||||
|
self.cloudfilt_remote_ip.append(cmd[7])
|
||||||
|
|
||||||
case 'NICK':
|
case 'NICK':
|
||||||
# :0010BS24L NICK [NEWNICK] 1697917711
|
# :0010BS24L NICK [NEWNICK] 1697917711
|
||||||
@@ -755,11 +1073,12 @@ class Defender():
|
|||||||
self.Irc.send2socket(f":{service_id} MODE {chan} -b {oldnick}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {chan} -b {oldnick}!*@*")
|
||||||
self.Irc.send2socket(f":{service_id} MODE {chan} +b {newnickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {chan} +b {newnickname}!*@*")
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
self.Irc.debug(f'cmd - NICK - KeyError: {ke}')
|
self.Base.logs.error(f'cmd - NICK - KeyError: {ke}')
|
||||||
|
|
||||||
case 'QUIT':
|
case 'QUIT':
|
||||||
# :001N1WD7L QUIT :Quit: free_znc_1
|
# :001N1WD7L QUIT :Quit: free_znc_1
|
||||||
cmd.pop(0)
|
cmd.pop(0)
|
||||||
|
ban_all_chan = self.Base.int_if_possible(self.defConfig['reputation_ban_all_chan'])
|
||||||
user_id = str(cmd[0]).replace(':','')
|
user_id = str(cmd[0]).replace(':','')
|
||||||
final_UID = user_id
|
final_UID = user_id
|
||||||
|
|
||||||
@@ -769,7 +1088,7 @@ class Defender():
|
|||||||
if final_UID in self.db_reputation:
|
if final_UID in self.db_reputation:
|
||||||
final_nickname = self.db_reputation[user_id]['nickname']
|
final_nickname = self.db_reputation[user_id]['nickname']
|
||||||
for chan in self.Irc.db_chan:
|
for chan in self.Irc.db_chan:
|
||||||
if chan != jail_salon:
|
if chan != jail_salon and ban_all_chan == 1:
|
||||||
self.Irc.send2socket(f":{service_id} MODE {chan} -b {final_nickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {chan} -b {final_nickname}!*@*")
|
||||||
self.delete_db_reputation(final_UID)
|
self.delete_db_reputation(final_UID)
|
||||||
|
|
||||||
@@ -796,9 +1115,9 @@ class Defender():
|
|||||||
# self.Base.create_timer(timer_sent, self.Base.garbage_collector_sockets)
|
# self.Base.create_timer(timer_sent, self.Base.garbage_collector_sockets)
|
||||||
|
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
self.Irc.debug(f"Type Error -> {te}")
|
self.Base.logs.error(f"Type Error -> {te}")
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
self.Irc.debug(f"Value Error -> {ve}")
|
self.Base.logs.error(f"Value Error -> {ve}")
|
||||||
|
|
||||||
case 'show_reputation':
|
case 'show_reputation':
|
||||||
|
|
||||||
@@ -822,12 +1141,11 @@ class Defender():
|
|||||||
reputation_seuil = self.defConfig['reputation_seuil']
|
reputation_seuil = self.defConfig['reputation_seuil']
|
||||||
welcome_salon = self.Config.SALON_LIBERER
|
welcome_salon = self.Config.SALON_LIBERER
|
||||||
|
|
||||||
self.Irc.debug(f"IP de {jailed_nickname} : {jailed_IP}")
|
self.Base.logs.debug(f"IP de {jailed_nickname} : {jailed_IP}")
|
||||||
link = self.Config.SERVEUR_LINK
|
link = self.Config.SERVEUR_LINK
|
||||||
color_green = self.Config.CONFIG_COLOR['verte']
|
color_green = self.Config.CONFIG_COLOR['verte']
|
||||||
color_black = self.Config.CONFIG_COLOR['noire']
|
color_black = self.Config.CONFIG_COLOR['noire']
|
||||||
|
|
||||||
|
|
||||||
if jailed_UID in self.db_reputation:
|
if jailed_UID in self.db_reputation:
|
||||||
if release_code == self.db_reputation[jailed_UID]['secret_code']:
|
if release_code == self.db_reputation[jailed_UID]['secret_code']:
|
||||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {jailed_salon} : Bon mot de passe. Allez du vent !')
|
self.Irc.send2socket(f':{dnickname} PRIVMSG {jailed_salon} : Bon mot de passe. Allez du vent !')
|
||||||
@@ -838,7 +1156,7 @@ class Defender():
|
|||||||
self.Irc.send2socket(f":{service_id} MODE {chan} -b {jailed_nickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {chan} -b {jailed_nickname}!*@*")
|
||||||
|
|
||||||
del self.db_reputation[jailed_UID]
|
del self.db_reputation[jailed_UID]
|
||||||
self.Irc.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB')
|
self.Base.logs.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB')
|
||||||
self.Irc.send2socket(f":{service_id} SAPART {jailed_nickname} {jailed_salon}")
|
self.Irc.send2socket(f":{service_id} SAPART {jailed_nickname} {jailed_salon}")
|
||||||
self.Irc.send2socket(f":{service_id} SAJOIN {jailed_nickname} {welcome_salon}")
|
self.Irc.send2socket(f":{service_id} SAJOIN {jailed_nickname} {welcome_salon}")
|
||||||
self.Irc.send2socket(f":{link} REPUTATION {jailed_IP} {int(reputation_seuil) + 1}")
|
self.Irc.send2socket(f":{link} REPUTATION {jailed_IP} {int(reputation_seuil) + 1}")
|
||||||
@@ -851,14 +1169,13 @@ class Defender():
|
|||||||
self.Irc.send2socket(f":{dnickname} PRIVMSG {jailed_salon} : Ce n'est pas à toi de taper le mot de passe !")
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {jailed_salon} : Ce n'est pas à toi de taper le mot de passe !")
|
||||||
|
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.Irc.debug('_hcmd code: out of index')
|
self.Base.logs.error('_hcmd code: out of index')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]')
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
self.Irc.debug(f'_hcmd code: KeyError {ke}')
|
self.Base.logs.error(f'_hcmd code: KeyError {ke}')
|
||||||
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]')
|
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
case 'reputation':
|
case 'reputation':
|
||||||
# .reputation [on/off] --> activate or deactivate reputation system
|
# .reputation [on/off] --> activate or deactivate reputation system
|
||||||
# .reputation set banallchan [on/off] --> activate or deactivate ban in all channel
|
# .reputation set banallchan [on/off] --> activate or deactivate ban in all channel
|
||||||
@@ -919,7 +1236,6 @@ class Defender():
|
|||||||
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Activated by {fromuser}')
|
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Activated by {fromuser}')
|
||||||
|
|
||||||
elif get_value == 'off':
|
elif get_value == 'off':
|
||||||
print(get_value)
|
|
||||||
if self.defConfig[key] == 0:
|
if self.defConfig[key] == 0:
|
||||||
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
|
||||||
return False
|
return False
|
||||||
@@ -944,15 +1260,16 @@ class Defender():
|
|||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
except IndexError:
|
except IndexError as ie:
|
||||||
self.Irc.debug('_hcmd reputation: out of index')
|
self.Base.logs.warning(f'{ie}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set banallchan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set banallchan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set limit [1234]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set limit [1234]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set timer [1234]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set timer [1234]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set action [kill|None]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set action [kill|None]')
|
||||||
|
|
||||||
except ValueError:
|
except ValueError as ve:
|
||||||
|
self.Base.logs.warning(f'{ie}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : La valeur devrait etre un entier >= 0')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : La valeur devrait etre un entier >= 0')
|
||||||
|
|
||||||
case 'proxy_scan':
|
case 'proxy_scan':
|
||||||
@@ -972,6 +1289,8 @@ class Defender():
|
|||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]')
|
||||||
|
|
||||||
option = str(cmd[2]).lower() # => local_scan, psutil_scan, abuseipdb_scan
|
option = str(cmd[2]).lower() # => local_scan, psutil_scan, abuseipdb_scan
|
||||||
action = str(cmd[3]).lower() # => on / off
|
action = str(cmd[3]).lower() # => on / off
|
||||||
@@ -1019,14 +1338,49 @@ class Defender():
|
|||||||
self.update_db_configuration(option, 0)
|
self.update_db_configuration(option, 0)
|
||||||
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
|
||||||
|
|
||||||
|
case 'freeipapi_scan':
|
||||||
|
if action == 'on':
|
||||||
|
if self.defConfig[option] == 1:
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
|
||||||
|
return None
|
||||||
|
self.update_db_configuration(option, 1)
|
||||||
|
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
|
||||||
|
elif action == 'off':
|
||||||
|
if self.defConfig[option] == 0:
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.update_db_configuration(option, 0)
|
||||||
|
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
|
||||||
|
|
||||||
|
case 'cloudfilt_scan':
|
||||||
|
if action == 'on':
|
||||||
|
if self.defConfig[option] == 1:
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated")
|
||||||
|
return None
|
||||||
|
self.update_db_configuration(option, 1)
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}")
|
||||||
|
elif action == 'off':
|
||||||
|
if self.defConfig[option] == 0:
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Already Deactivated")
|
||||||
|
return None
|
||||||
|
self.update_db_configuration(option, 0)
|
||||||
|
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}")
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]')
|
||||||
else:
|
else:
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set local_scan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set psutil_scan [ON/OFF]')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set abuseipdb_scan [ON/OFF]')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set freeipapi_scan [ON/OFF]')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} proxy_scan set cloudfilt_scan [ON/OFF]')
|
||||||
|
|
||||||
case 'flood':
|
case 'flood':
|
||||||
# .flood on/off
|
# .flood on/off
|
||||||
@@ -1083,7 +1437,7 @@ class Defender():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
self.Irc.debug(f"{self.__class__.__name__} Value Error : {ve}")
|
self.Base.logs.error(f"{self.__class__.__name__} Value Error : {ve}")
|
||||||
|
|
||||||
case 'status':
|
case 'status':
|
||||||
color_green = self.Config.CONFIG_COLOR['verte']
|
color_green = self.Config.CONFIG_COLOR['verte']
|
||||||
@@ -1098,13 +1452,15 @@ class Defender():
|
|||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["local_scan"] == 1 else color_red}local_scan{color_black} ==> {self.defConfig["local_scan"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["local_scan"] == 1 else color_red}local_scan{color_black} ==> {self.defConfig["local_scan"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["psutil_scan"] == 1 else color_red}psutil_scan{color_black} ==> {self.defConfig["psutil_scan"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["psutil_scan"] == 1 else color_red}psutil_scan{color_black} ==> {self.defConfig["psutil_scan"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["abuseipdb_scan"] == 1 else color_red}abuseipdb_scan{color_black} ==> {self.defConfig["abuseipdb_scan"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["abuseipdb_scan"] == 1 else color_red}abuseipdb_scan{color_black} ==> {self.defConfig["abuseipdb_scan"]}')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["freeipapi_scan"] == 1 else color_red}freeipapi_scan{color_black} ==> {self.defConfig["freeipapi_scan"]}')
|
||||||
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {color_green if self.defConfig["cloudfilt_scan"] == 1 else color_red}cloudfilt_scan{color_black} ==> {self.defConfig["cloudfilt_scan"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [{color_green if self.defConfig["flood"] == 1 else color_red}Flood{color_black}] ==> {self.defConfig["flood"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [{color_green if self.defConfig["flood"] == 1 else color_red}Flood{color_black}] ==> {self.defConfig["flood"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_action ==> Coming soon')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_action ==> Coming soon')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_message ==> {self.defConfig["flood_message"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_message ==> {self.defConfig["flood_message"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_time ==> {self.defConfig["flood_time"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_time ==> {self.defConfig["flood_time"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_timer ==> {self.defConfig["flood_timer"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_timer ==> {self.defConfig["flood_timer"]}')
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
self.Irc.debug(f"Key Error : {ke}")
|
self.Base.logs.error(f"Key Error : {ke}")
|
||||||
|
|
||||||
case 'join':
|
case 'join':
|
||||||
|
|
||||||
@@ -1113,8 +1469,8 @@ class Defender():
|
|||||||
self.Irc.send2socket(f':{service_id} JOIN {channel}')
|
self.Irc.send2socket(f':{service_id} JOIN {channel}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} JOINED {channel}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} JOINED {channel}')
|
||||||
self.add_defender_channel(channel)
|
self.add_defender_channel(channel)
|
||||||
except IndexError:
|
except IndexError as ie:
|
||||||
self.Irc.debug('_hcmd join: out of index')
|
self.Base.logs.error(f'{ie}')
|
||||||
|
|
||||||
case 'part':
|
case 'part':
|
||||||
|
|
||||||
@@ -1127,8 +1483,8 @@ class Defender():
|
|||||||
self.Irc.send2socket(f':{service_id} PART {channel}')
|
self.Irc.send2socket(f':{service_id} PART {channel}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} LEFT {channel}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} LEFT {channel}')
|
||||||
self.delete_defender_channel(channel)
|
self.delete_defender_channel(channel)
|
||||||
except IndexError:
|
except IndexError as ie:
|
||||||
self.Irc.debug('_hcmd part: out of index')
|
self.Base.logs.error(f'{ie}')
|
||||||
|
|
||||||
case 'op' | 'o':
|
case 'op' | 'o':
|
||||||
# /mode #channel +o user
|
# /mode #channel +o user
|
||||||
@@ -1140,7 +1496,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd OP: {str(e)}')
|
self.Base.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' | 'do':
|
||||||
@@ -1151,7 +1507,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd DEOP: {str(e)}')
|
self.Base.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' | 'q':
|
||||||
@@ -1162,7 +1518,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd OWNER: {str(e)}')
|
self.Base.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' | 'dq':
|
||||||
@@ -1173,7 +1529,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd DEOWNER: {str(e)}')
|
self.Base.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' | 'h':
|
||||||
@@ -1184,7 +1540,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd halfop: {str(e)}')
|
self.Base.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' | 'dh':
|
||||||
@@ -1195,7 +1551,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd DEHALFOP: {str(e)}')
|
self.Base.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' | 'v':
|
||||||
@@ -1206,7 +1562,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd VOICE: {str(e)}')
|
self.Base.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' | 'dv':
|
||||||
@@ -1217,7 +1573,7 @@ class Defender():
|
|||||||
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.Irc.debug(f'_hcmd DEVOICE: {str(e)}')
|
self.Base.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' | 'b':
|
||||||
@@ -1227,9 +1583,9 @@ class Defender():
|
|||||||
nickname = cmd[2]
|
nickname = cmd[2]
|
||||||
|
|
||||||
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
|
||||||
self.Irc.debug(f'{fromuser} has banned {nickname} from {channel}')
|
self.Base.logs.debug(f'{fromuser} has banned {nickname} from {channel}')
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
self.Irc.debug(f'_hcmd BAN: {str(e)}')
|
self.Base.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' | 'ub':
|
||||||
@@ -1239,9 +1595,9 @@ class Defender():
|
|||||||
nickname = cmd[2]
|
nickname = cmd[2]
|
||||||
|
|
||||||
self.Irc.send2socket(f":{service_id} MODE {channel} -b {nickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {channel} -b {nickname}!*@*")
|
||||||
self.Irc.debug(f'{fromuser} has unbanned {nickname} from {channel}')
|
self.Base.logs.debug(f'{fromuser} has unbanned {nickname} from {channel}')
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
self.Irc.debug(f'_hcmd UNBAN: {str(e)}')
|
self.Base.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' | 'k':
|
||||||
@@ -1257,9 +1613,9 @@ class Defender():
|
|||||||
final_reason = ' '.join(reason)
|
final_reason = ' '.join(reason)
|
||||||
|
|
||||||
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
|
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
|
||||||
self.Irc.debug(f'{fromuser} has kicked {nickname} from {channel} : {final_reason}')
|
self.Base.logs.debug(f'{fromuser} has kicked {nickname} from {channel} : {final_reason}')
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
self.Irc.debug(f'_hcmd KICK: {str(e)}')
|
self.Base.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' | 'kb':
|
||||||
@@ -1276,9 +1632,9 @@ class Defender():
|
|||||||
|
|
||||||
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
|
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
|
||||||
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
|
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
|
||||||
self.Irc.debug(f'{fromuser} has kicked and banned {nickname} from {channel} : {final_reason}')
|
self.Base.logs.debug(f'{fromuser} has kicked and banned {nickname} from {channel} : {final_reason}')
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
self.Irc.debug(f'_hcmd KICKBAN: {str(e)}')
|
self.Base.logs.warning(f'_hcmd KICKBAN: {str(e)}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]')
|
||||||
|
|
||||||
case 'info':
|
case 'info':
|
||||||
@@ -1304,7 +1660,7 @@ class Defender():
|
|||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {self.Irc.db_uid[uid_query]["umodes"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {self.Irc.db_uid[uid_query]["umodes"]}')
|
||||||
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {self.Irc.db_uid[uid_query]["datetime"]}')
|
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {self.Irc.db_uid[uid_query]["datetime"]}')
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
self.Irc.debug(f"Key error info user : {ke}")
|
self.Base.logs.warning(f"Key error info user : {ke}")
|
||||||
|
|
||||||
case 'show_users':
|
case 'show_users':
|
||||||
for uid, infousers in self.Irc.db_uid.items():
|
for uid, infousers in self.Irc.db_uid.items():
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ class Test():
|
|||||||
self.core.db_execute_query(self.session, table_logs)
|
self.core.db_execute_query(self.session, table_logs)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def unload(self) -> None:
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _hcmds(self, user:str, cmd: list) -> None:
|
def _hcmds(self, user:str, cmd: list) -> None:
|
||||||
|
|
||||||
command = cmd[0].lower()
|
command = cmd[0].lower()
|
||||||
|
|||||||
3
version.json
Normal file
3
version.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"version": "4.0.0"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user