17 Commits

Author SHA1 Message Date
adator
860e265979 Latest release for version 5 2024-11-01 14:05:26 +01:00
adator
f7c80d190e V5.3.9 2024-10-13 22:21:01 +02:00
adator
9bfe5925f8 update websocket connection 2024-10-06 21:57:37 +02:00
adator
c3187e81dd V5.3.8 2024-10-06 21:46:16 +02:00
adator
f0c0a2d06a check packages versions just after windows check 2024-10-04 01:07:37 +02:00
adator
2be6ece27f Exclude windows from update packages 2024-10-04 01:03:36 +02:00
adator
6801c981ab V5.3.7 2024-10-04 00:54:31 +02:00
adator
fd88df1017 Exec pip from virtual env 2024-10-04 00:22:50 +02:00
adator
ad5b7ffbf2 Check if virtual env is available 2024-10-04 00:08:14 +02:00
adator
2b7f1d8bf3 V5.3.7 Defender can update packages 2024-10-03 23:56:01 +02:00
adator
f5ff9259e8 V5.3.6 2024-10-02 23:38:42 +02:00
adator
12c7e5e832 V5.3.5:
- core/irc.py : Nothing special except
        a notice sent to the user that the command
        is not available
    - mod_clone:
        When the user unload the module, the
        server will unset modes of #clone channel
    - mod_defender:
        * adding a try block to catch errors
        * adding a mode block to check if
        the jail channel is not having +b mode
        * ensure defender is +o in jail channel
    - mod_test:
        * adding some try block to catch errors
2024-10-02 00:01:13 +02:00
adator
3cdee5fddf V5.3.5 2024-10-01 01:17:00 +02:00
adator
f2b5c48fc9 You need to have unrealirc_rpc_py v1.0.3 2024-09-30 01:40:01 +02:00
adator
e2a1ec5866 V5.3.4 2024-09-29 22:42:28 +02:00
adator
cc53eae875 Adding more commands (V5.3.3) 2024-09-26 01:00:06 +02:00
adator
8a80840a6a V5.3.2 additional commands 2024-09-25 00:21:44 +02:00
13 changed files with 1163 additions and 402 deletions

View File

@@ -32,30 +32,34 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
- Système d'exploitation Linux (Windows non supporté)
- Un server UnrealIRCD corréctement configuré
- Python version 3.10 ou supérieure
Bash:
```bash
# Bash
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
- Renommer le fichier exemple_configuration.json en configuration.json
- Configurer le fichier configuration.json
# Renommer le fichier exemple_configuration.json en configuration.json
# Configurer le fichier configuration.json
$ python3 main.py
```
Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC
Pour Les prochains lancement de defender vous devez utiliser la commande suivante:
Bash:
$ systemctl --user [start | stop | restart | status] defender
```bash
# Bash
$ systemctl --user [start | stop | restart | status] defender
```
# Installation manuelle:
Bash:
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
$ cd IRC_DEFENDER_MODULES
$ python3 -m venv .pyenv
$ source .pyenv/bin/activate
(pyenv)$ pip install sqlalchemy, psutil, requests, faker
- Créer un service nommé "defender.service" pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
- Si le dossier n'existe pas il faut les créer
$ sudo systemctl --user start defender
```bash
# Bash
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
$ cd IRC_DEFENDER_MODULES
$ python3 -m venv .pyenv
$ source .pyenv/bin/activate
(pyenv)$ pip install sqlalchemy, psutil, requests, faker, unrealircd_rpc_py
# Créer un service nommé "defender.service"
# pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/"
# Si le dossier n'existe pas il faut les créer
$ sudo systemctl --user start defender
```
# Configuration
```
SERVEUR (Serveur)

View File

@@ -1,8 +1,21 @@
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast
import os
import re
import json
import time
import random
import socket
import hashlib
import logging
import threading
import ipaddress
import ast
import requests
from dataclasses import fields
from typing import Union, Literal
from base64 import b64decode, b64encode
from datetime import datetime
from datetime import datetime, timedelta, timezone
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
from core.loadConf import ConfigDataModel
@@ -35,7 +48,6 @@ class Base:
with open(version_filename, 'r') as version_data:
current_version:dict[str, str] = json.load(version_data)
# self.DEFENDER_VERSION = current_version["version"]
self.Config.current_version = current_version['version']
return None
@@ -68,6 +80,14 @@ class Base:
self.logs.warning(f'Github not available to fetch latest version')
def check_for_new_version(self, online:bool) -> bool:
"""Check if there is a new version available
Args:
online (bool): True if you want to get the version from github (main branch)
Returns:
bool: True if there is a new version available
"""
try:
self.logs.debug(f'Checking for a new service version')
@@ -106,7 +126,11 @@ class Base:
Cette fonction retourne un UNIXTIME de type 12365456
Return: Current time in seconds since the Epoch (int)
"""
cet_offset = timezone(timedelta(hours=2))
now_cet = datetime.now(cet_offset)
unixtime_cet = int(now_cet.timestamp())
unixtime = int( time.time() )
return unixtime
def get_datetime(self) -> str:
@@ -212,6 +236,19 @@ class Base:
return False
def db_update_module(self, user_cmd: str, module_name: str) -> None:
"""Modifie la date et le user qui a rechargé le module
Args:
user_cmd (str): le user qui a rechargé le module
module_name (str): le module a rechargé
"""
update_cmd_query = f"UPDATE {self.Config.table_module} SET datetime = :datetime, user = :user WHERE module_name = :module_name"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module_name': module_name}
self.db_execute_query(update_cmd_query, mes_donnees)
return False
def db_delete_module(self, module_name:str) -> None:
"""Supprime les modules de la base de données
@@ -574,7 +611,7 @@ class Base:
)
'''
table_core_channel = '''CREATE TABLE IF NOT EXISTS core_channel (
table_core_channel = f'''CREATE TABLE IF NOT EXISTS {self.Config.table_channel} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
module_name TEXT,

View File

@@ -2,10 +2,10 @@ import socket
import ssl
import traceback
from ssl import SSLSocket
from typing import Union
from core.loadConf import Config
from core.Model import Clones
from core.base import Base
from typing import Union
class Connection:

View File

@@ -1,8 +1,9 @@
from dataclasses import dataclass
from subprocess import check_call, run, CalledProcessError, PIPE
from platform import python_version, python_version_tuple
from sys import exit
import os
import json
from sys import exit, prefix
from dataclasses import dataclass
from subprocess import check_call, run, CalledProcessError, PIPE, check_output
from platform import python_version, python_version_tuple
class Install:
@@ -24,13 +25,23 @@ class Install:
venv_pip_executable: str
venv_python_executable: str
@dataclass
class Package:
name: str = None
version: str = None
DB_PACKAGES: list[Package] = []
def __init__(self) -> None:
self.set_configuration()
if self.skip_install:
self.check_packages_version()
return None
self.check_packages_version()
# Sinon tester les dependances python et les installer avec pip
if self.do_install():
@@ -75,11 +86,13 @@ class Install:
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')):
# If configuration file do not exist
exit("/!\\ Configuration file (configuration.json) doesn't exist /!\\")
exit("/!\\ Configuration file (core/configuration.json) doesn't exist! please create it /!\\")
# Exclude Windows OS from the installation
if os.name == 'nt':
#print('/!\\ Skip installation /!\\')
# If windows, modify pip and python virtual environment executable
self.config.venv_pip_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}pip.exe'
self.config.venv_python_executable = f'{os.path.join(defender_install_folder, venv_folder, "Scripts")}{os.sep}python.exe'
self.skip_install = True
return False
@@ -91,7 +104,7 @@ class Install:
def is_root(self) -> bool:
if os.geteuid() != 0:
print('User without privileges ==> PASS')
print('> User without privileges ==> OK')
return False
elif os.geteuid() == 0:
print('/!\\ Do not use root to install Defender /!\\')
@@ -123,6 +136,75 @@ class Install:
print(f"Try to install dependencies ...")
exit(5)
def get_packages_version_from_json(self) -> None:
"""This will create Package model with package names and required version
"""
try:
version_filename = f'.{os.sep}version.json'
with open(version_filename, 'r') as version_data:
package_info:dict[str, str] = json.load(version_data)
for name, version in package_info.items():
if name == 'version':
continue
self.DB_PACKAGES.append(
self.Package(name=name, version=version)
)
return None
except FileNotFoundError as fe:
print(f"File not found: {fe}")
except Exception as err:
print(f"General Error: {err}")
def check_packages_version(self) -> bool:
try:
newVersion = False
self.get_packages_version_from_json()
if not self.config.venv_folder in prefix:
print(f"You are probably running a new installation or you are not using your virtual env {self.config.venv_folder}")
return newVersion
print(f"> Checking for dependencies versions ==> WAIT")
for package in self.DB_PACKAGES:
newVersion = False
required_version = package.version
installed_version = None
output = check_output([self.config.venv_pip_executable, 'show', package.name])
for line in output.decode().splitlines():
if line.startswith('Version:'):
installed_version = line.split(':')[1].strip()
break
required_major, required_minor, required_patch = required_version.split('.')
installed_major, installed_minor, installed_patch = installed_version.split('.')
if required_major > installed_major:
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
newVersion = True
elif required_major == installed_major and required_minor > installed_minor:
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
newVersion = True
elif required_major == installed_major and required_minor == installed_minor and required_patch > installed_patch:
print(f'> New version of {package.name} is available {installed_version} ==> {required_version}')
newVersion = True
if newVersion:
self.run_subprocess([self.config.venv_pip_executable, 'install', '--upgrade', package.name])
print(f"> Dependencies versions ==> OK")
return newVersion
except CalledProcessError:
print(f"/!\\ Package {package.name} not installed /!\\")
except Exception as err:
print(f"General Error: {err}")
def check_python_version(self) -> bool:
"""Test si la version de python est autorisée ou non

View File

@@ -1,10 +1,17 @@
import ssl, re, importlib, sys, time, threading, socket, traceback
import sys
import socket
import threading
import ssl
import re
import importlib
import time
import traceback
from ssl import SSLSocket
from datetime import datetime, timedelta
from typing import Union, Literal
from typing import Union
from core.loadConf import Config
from core.Model import User, Admin, Channel, Clones
from core.base import Base
from core.Model import User, Admin, Channel, Clones
class Irc:
@@ -88,13 +95,15 @@ class Irc:
return None
except ssl.SSLEOFError as soe:
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
self.Base.logs.critical(f"SSLEOFError: {soe} - {soc.fileno()}")
except ssl.SSLError as se:
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
self.Base.logs.critical(f"SSLError: {se} - {soc.fileno()}")
except OSError as oe:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
self.Base.logs.critical(f"OSError: {oe} - {soc.fileno()}")
if 'connection refused' in str(oe).lower():
sys.exit(oe)
except AttributeError as ae:
self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}")
self.Base.logs.critical(f"AttributeError: {ae} - {soc.fileno()}")
def __ssl_context(self) -> ssl.SSLContext:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -304,7 +313,7 @@ class Irc:
def send_response(self, responses:list[bytes]) -> None:
try:
# print(data)
# print(responses)
for data in responses:
response = data.decode(self.CHARSET[0]).split()
self.cmd(response)
@@ -481,7 +490,8 @@ class Irc:
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.Base.logs.info(self.loaded_classes)
# self.Base.logs.info(self.loaded_classes)
self.Base.logs.info(f"Module {class_name} has been loaded")
return True
except ModuleNotFoundError as moduleNotFound:
@@ -492,11 +502,94 @@ class Irc:
self.Base.logs.error(f"Something went wrong with a module you want to load : {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}ERROR{self.Config.COLORS.black} ]: {e}")
def unload_module(self, mod_name: str) -> bool:
"""Unload a module
Args:
mod_name (str): Module name ex mod_defender
Returns:
bool: True if success
"""
try:
module_name = mod_name.lower() # Le nom du module. exemple: mod_defender
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
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():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name]
# Supprimer le module de la base de données
self.Base.db_delete_module(module_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
return True
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
return False
def reload_module(self, from_user: str, mod_name: str) -> bool:
try:
module_name = mod_name.lower() # ==> mod_defender
class_name = module_name.split('_')[1].capitalize() # ==> Defender
if 'mods.' + module_name in sys.modules:
self.Base.logs.info('Unload the module ...')
self.loaded_classes[class_name].unload()
self.Base.logs.info('Module Already Loaded ... reloading the module ...')
the_module = sys.modules['mods.' + module_name]
importlib.reload(the_module)
# Supprimer la class déja instancier
if class_name in self.loaded_classes:
# Supprimer les commandes déclarer dans la classe
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name]
my_class = getattr(the_module, class_name, None)
new_instance = my_class(self.ircObject)
self.loaded_classes[class_name] = new_instance
self.Base.db_update_module(from_user, mod_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} rechargé")
return False
else:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
except TypeError as te:
self.Base.logs.error(f"A TypeError raised: {te}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :A TypeError raised: {te}")
self.Base.db_delete_module(module_name)
except AttributeError as ae:
self.Base.logs.error(f"Missing Attribute: {ae}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Missing Attribute: {ae}")
self.Base.db_delete_module(module_name)
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Key Error: {ke}")
self.Base.db_delete_module(module_name)
except Exception as e:
self.Base.logs.error(f"Something went wrong with a module you want to reload: {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Something went wrong with the module: {e}")
self.Base.db_delete_module(module_name)
def insert_db_admin(self, uid:str, level:int) -> None:
if self.User.get_User(uid) is None:
return None
getUser = self.User.get_User(uid)
nickname = getUser.nickname
@@ -781,7 +874,7 @@ class Irc:
self.Base.logs.info(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
self.Base.logs.info(f"# VERSION : {version} ")
self.Base.logs.info(f"################################################")
if self.Base.check_for_new_version(False):
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} : New Version available {version}")
@@ -789,6 +882,13 @@ class Irc:
self.sendPrivMsg(msg=f'[{self.Config.COLORS.green}INFORMATION{self.Config.COLORS.nogc}] >> Defender is ready', channel=self.Config.SERVICE_CHANLOG)
self.INIT = 0
# Send EOF to other modules
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
# Stop here When EOS
return None
case _:
pass
@@ -963,7 +1063,8 @@ class Irc:
arg.remove(f':{self.Config.SERVICE_PREFIX}')
if not arg[0].lower() in self.commands:
self.Base.logs.debug(f"This command {arg[0]} is not available")
return False
self.sendNotice(f"This command [{self.Config.COLORS.bold}{arg[0]}{self.Config.COLORS.bold}] is not available", user_trigger)
return None
cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(user_trigger, cmd_to_send)
@@ -997,7 +1098,9 @@ class Irc:
recieved_unixtime = int(arg[1].replace('\x01',''))
current_unixtime = self.Base.get_unixtime()
ping_response = current_unixtime - recieved_unixtime
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01PING {str(ping_response)} secs\x01')
self.send2socket(f'PONG :{recieved_unixtime}')
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01PING {ping_response} secs\x01')
return False
if not arg[0].lower() in self.commands:
@@ -1285,7 +1388,6 @@ class Irc:
case 'help':
help = ''
count_level_definition = 0
get_admin = self.Admin.get_Admin(uid)
if not get_admin is None:
@@ -1300,93 +1402,42 @@ class Irc:
if int(user_level) >= int(count_level_definition):
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** {self.Config.COLORS.nogc}[ {self.Config.COLORS.green}LEVEL {str(levDef)} {self.Config.COLORS.nogc}] *****************')
count_commands = 0
help = ''
for comm in self.commands_level[count_level_definition]:
help += f"{comm.upper()}"
if int(count_commands) < len(self.commands_level[count_level_definition])-1:
help += ' | '
count_commands += 1
batch = 7
for i in range(0, len(self.commands_level[count_level_definition]), batch):
groupe = self.commands_level[count_level_definition][i:i + batch] # Extraire le groupe
batch_commands = ' | '.join(groupe)
self.send2socket(f':{dnickname} NOTICE {fromuser} : {batch_commands}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : {help}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ')
count_level_definition += 1
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** FIN DES COMMANDES *****************')
case 'load':
self.load_module(fromuser, str(cmd[1]))
try:
# Load a module ex: .load mod_defender
mod_name = str(cmd[1])
self.load_module(fromuser, mod_name)
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke} - list recieved: {cmd}")
except Exception as err:
self.Base.logs.error(f"General Error: {ke} - list recieved: {cmd}")
case 'unload':
# unload mod_dktmb
# unload mod_defender
try:
module_name = str(cmd[1]).lower() # Le nom du module. exemple: mod_defender
class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender
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():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name]
# Supprimer le module de la base de données
self.Base.db_delete_module(module_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
except:
self.Base.logs.error(f"Something went wrong with a module you want to load")
self.unload_module(module_name)
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
case 'reload':
# reload mod_dktmb
# reload mod_defender
try:
module_name = str(cmd[1]).lower() # ==> mod_defender
class_name = module_name.split('_')[1].capitalize() # ==> Defender
if 'mods.' + module_name in sys.modules:
self.Base.logs.info('Unload the module ...')
self.loaded_classes[class_name].unload()
self.Base.logs.info('Module Already Loaded ... reloading the module ...')
the_module = sys.modules['mods.' + module_name]
importlib.reload(the_module)
# Supprimer la class déja instancier
if class_name in self.loaded_classes:
# Supprimer les commandes déclarer dans la classe
for level, command in self.loaded_classes[class_name].commands_level.items():
# Supprimer la commande de la variable commands
for c in self.loaded_classes[class_name].commands_level[level]:
self.commands.remove(c)
self.commands_level[level].remove(c)
del self.loaded_classes[class_name]
my_class = getattr(the_module, class_name, None)
new_instance = my_class(self.ircObject)
self.loaded_classes[class_name] = new_instance
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} rechargé")
return False
else:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
except TypeError as te:
self.Base.logs.error(f"A TypeError raised: {te}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :A TypeError raised: {te}")
self.Base.db_delete_module(module_name)
except AttributeError as ae:
self.Base.logs.error(f"Missing Attribute: {ae}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Missing Attribute: {ae}")
self.Base.db_delete_module(module_name)
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Key Error: {ke}")
self.Base.db_delete_module(module_name)
module_name = str(cmd[1]).lower() # ==> mod_defender
self.reload_module(from_user=fromuser, mod_name=module_name)
except Exception as e:
self.Base.logs.error(f"Something went wrong with a module you want to reload: {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Something went wrong with the module: {e}")
@@ -1437,24 +1488,24 @@ class Irc:
self.Base.logs.debug(self.loaded_classes)
all_modules = self.Base.get_all_modules()
loaded = False
results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}')
results = self.Base.db_execute_query(f'SELECT datetime, user, module_name FROM {self.Config.table_module}')
results = results.fetchall()
found = False
for module in all_modules:
for loaded_mod in results:
if module == loaded_mod[0]:
found = True
if module == loaded_mod[2]:
loaded_datetime = loaded_mod[0]
loaded_user = loaded_mod[1]
loaded = True
if found:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.green}Loaded{self.Config.COLORS.nogc}")
if loaded:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.green}Loaded{self.Config.COLORS.nogc} by {loaded_user} on {loaded_datetime}")
loaded = False
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.red}Not Loaded{self.Config.COLORS.nogc}")
found = False
case 'show_timers':
if self.Base.running_timers:

View File

@@ -1,4 +1,5 @@
import json, sys
import sys
import json
from os import sep
from typing import Union, Literal
from dataclasses import dataclass, field
@@ -16,7 +17,7 @@ class ColorModel:
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
nogc: str = "\x03"
@dataclass
class ConfigDataModel:
@@ -85,10 +86,19 @@ class ConfigDataModel:
"""The password of the admin of the service"""
JSONRPC_URL: str
"""The RPC url, if local https://127.0.0.1:PORT/api should be fine"""
JSONRPC_PATH_TO_SOCKET_FILE: str
"""The full path of the socket file (/PATH/TO/YOUR/UNREALIRCD/SOCKET/FILE.socket)"""
JSONRPC_METHOD: str
"""3 methods are available; requests/socket/unixsocket"""
JSONRPC_USER: str
"""The RPC User defined in your unrealircd.conf"""
JSONRPC_PASSWORD: str
"""The RPC Password defined in your unrealircd.conf"""
SALON_JAIL: str
"""The JAIL channel (ex. #jail)"""

View File

@@ -60,6 +60,7 @@ class Clone():
self.Base.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} JOIN {self.Config.CLONE_CHANNEL}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
@@ -126,6 +127,8 @@ class Clone():
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
self.Base.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -k {self.Config.CLONE_CHANNEL_PASSWORD}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {self.Config.CLONE_CHANNEL}")
return None
@@ -147,7 +150,7 @@ class Clone():
del clone_to_kill
# If LIST empty then stop this thread
# If no more clones then stop this thread
if not self.Clone.UID_CLONE_DB:
break

View File

@@ -35,9 +35,14 @@ class Command():
# Create module commands (Mandatory)
self.commands_level = {
1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice',
2: ['owner', 'deowner', 'protect', 'deprotect', 'op', 'deop', 'halfop', 'dehalfop', 'voice',
'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban',
'unban','kick', 'kickban', 'umode', 'svsjoin', 'svspart', 'svsnick']
'unban','kick', 'kickban', 'umode', 'mode', 'svsjoin', 'svspart', 'svsnick', 'topic',
'wallops', 'globops','gnotice','whois', 'names', 'invite', 'inviteme',
'sajoin', 'sapart',
'kill', 'gline', 'ungline', 'kline', 'unkline', 'shun', 'unshun',
'glinelist', 'shunlist', 'klinelist'],
3: ['map']
}
# Init the module
@@ -58,6 +63,9 @@ class Command():
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
self.user_to_notice: str = ''
self.show_219: bool = True
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -91,7 +99,7 @@ class Command():
)
'''
self.Base.db_execute_query(table_logs)
# self.Base.db_execute_query(table_logs)
return None
def __load_module_configuration(self) -> None:
@@ -122,45 +130,81 @@ class Command():
return None
def add_defender_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnees = {'channel': channel}
response = self.Base.db_execute_query("SELECT id FROM def_channels WHERE channel = :channel", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel': channel}
insert = self.Base.db_execute_query(f"INSERT INTO def_channels (datetime, channel) VALUES (:datetime, :channel)", mes_donnees)
if insert.rowcount >=0:
return True
else:
return False
else:
return False
def delete_defender_channel(self, channel:str) -> bool:
"""Cette fonction supprime les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnes = {'channel': channel}
response = self.Base.db_execute_query("DELETE FROM def_channels WHERE channel = :channel", mes_donnes)
affected_row = response.rowcount
if affected_row > 0:
return True
else:
return False
def cmd(self, data:list) -> None:
service_id = self.Config.SERVICE_ID
dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG
red = self.Config.COLORS.red
green = self.Config.COLORS.green
bold = self.Config.COLORS.bold
nogc = self.Config.COLORS.nogc
cmd = list(data).copy()
if len(cmd) < 2:
return None
match cmd[1]:
# [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel']
case '403' | '401':
try:
message = ' '.join(cmd[3:])
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} :[{red}ERROR MSG{nogc}] {message}")
self.Base.logs.error(f"{cmd[1]} - {message}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case '006' | '018':
try:
# [':irc.deb.biz.st', '006', 'Dev-PyDefender', ':`-services.deb.biz.st', '------', '|', 'Users:', '9', '(47.37%)', '[00B]']
# [':irc.deb.biz.st', '018', 'Dev-PyDefender', ':4', 'servers', 'and', '19', 'users,', 'average', '4.75', 'users', 'per', 'server']
message = ' '.join(cmd[3:])
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : [{green}SERVER MSG{nogc}] {message}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case '219':
try:
# [':irc.deb.biz.st', '219', 'Dev-PyDefender', 's', ':End', 'of', '/STATS', 'report']
if not self.show_219:
# If there is a result in 223 then stop here
self.show_219 = True
return None
type_of_stats = str(cmd[3])
match type_of_stats:
case 's':
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No shun")
case 'G':
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No gline")
case 'k':
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No kline")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case '223':
try:
# [':irc.deb.biz.st', '223', 'Dev-PyDefender', 'G', '*@162.142.125.217', '67624', '18776', 'irc.deb.biz.st', ':Proxy/Drone', 'detected.', 'Check', 'https://dronebl.org/lookup?ip=162.142.125.217', 'for', 'details.']
self.show_219 = False
host = str(cmd[4])
author = str(cmd[7])
reason = ' '.join(cmd[8:])
self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : {bold}Author{nogc}: {author} - {bold}Host{nogc}: {host} - {bold}Reason{nogc}: {reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
@@ -169,6 +213,7 @@ class Command():
dnickname = self.Config.SERVICE_NICKNAME
service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG
self.user_to_notice = user
fromuser = user
fromchannel = channel
@@ -346,6 +391,60 @@ class Command():
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'protect':
# /mode #channel +a user
# .protect #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'deprotect':
# /mode #channel -a user
# .deprotect #channel user
try:
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}")
return True
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}")
except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'halfop':
# /mode #channel +h user
# .halfop #channel user
@@ -459,7 +558,7 @@ class Command():
try:
sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None
if sentchannel is None:
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} {command.upper()} [#SALON] [NICKNAME]')
return False
nickname = cmd[2]
@@ -468,7 +567,7 @@ class Command():
self.Logs.debug(f'{fromuser} has banned {nickname} from {sentchannel}')
except IndexError as e:
self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
@@ -577,6 +676,174 @@ class Command():
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'topic':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE")
return None
chan = str(cmd[1])
if not self.Base.Is_Channel(chan):
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE")
return None
topic_msg = ' '.join(cmd[2:]).strip()
if topic_msg:
self.Irc.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the topic")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'wallops':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} WALLOPS THE_WALLOPS_MESSAGE")
return None
wallops_msg = ' '.join(cmd[1:]).strip()
if wallops_msg:
self.Irc.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the wallops message")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'globops':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} GLOBOPS THE_GLOBOPS_MESSAGE")
return None
globops_msg = ' '.join(cmd[1:]).strip()
if globops_msg:
self.Irc.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the globops message")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'gnotice':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} THE_GLOBAL_NOTICE_MESSAGE")
return None
gnotice_msg = ' '.join(cmd[1:]).strip()
if gnotice_msg:
self.Irc.send2socket(f':{dnickname} NOTICE $*.* :[{self.Config.COLORS.red}GLOBAL NOTICE{self.Config.COLORS.nogc}] {gnotice_msg}')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the global notice message")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'whois':
try:
self.user_to_notice = fromuser
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} NICKNAME")
return None
nickname = str(cmd[1])
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :Nickname not found !")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} NICKNAME")
return None
self.Irc.send2socket(f':{dnickname} WHOIS {nickname}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'names':
try:
if len(cmd) == 1:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL")
return None
chan = str(cmd[1])
if not self.Base.Is_Channel(chan):
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel")
return None
self.Irc.send2socket(f':{dnickname} NAMES {chan}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'invite':
try:
if len(cmd) < 3:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL NICKNAME")
return None
chan = str(cmd[1])
nickname = str(cmd[2])
if not self.Base.Is_Channel(chan):
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel nickname")
return None
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :Nickname not found !")
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel NICKNAME")
return None
self.Irc.send2socket(f':{dnickname} INVITE {nickname} {chan}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'inviteme':
try:
if len(cmd) == 0:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()}")
return None
self.Irc.send2socket(f':{dnickname} INVITE {fromuser} {self.Config.SERVICE_CHANLOG}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'map':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f':{dnickname} MAP')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'umode':
try:
# .umode nickname +mode
@@ -589,6 +856,39 @@ class Command():
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'mode':
# .mode #channel +/-mode
# .mode +/-mode
try:
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode')
return None
if fromchannel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode')
return None
if len(cmd) == 2:
channel_mode = cmd[1]
if self.Base.Is_Channel(fromchannel):
self.Irc.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}")
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : Right command : Channel [{fromchannel}] is not correct should start with #")
return None
if len(cmd) == 3:
provided_channel = cmd[1]
channel_mode = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {provided_channel} {channel_mode}")
return None
except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode')
except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsjoin':
try:
# .svsjoin nickname #channel
@@ -621,6 +921,38 @@ class Command():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'sajoin':
try:
# .sajoin nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) < 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SAJOIN {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'sapart':
try:
# .sapart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) < 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SAPART {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsnick':
try:
# .svsnick nickname newnickname
@@ -633,12 +965,203 @@ class Command():
return None
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname newnickname')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kill':
try:
# 'kill', 'gline', 'ungline', 'shun', 'unshun'
# .kill nickname reason
if len(cmd) < 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname reason')
return None
nickname = str(cmd[1])
kill_reason = ' '.join(cmd[2:])
self.Irc.send2socket(f":{service_id} KILL {nickname} {kill_reason} ({self.Config.COLORS.red}{dnickname}{self.Config.COLORS.nogc})")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'gline':
try:
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .gline [nickname] [host] [reason]
if len(cmd) < 4:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp
gline_reason = ' '.join(cmd[3:])
if nickname == '*' and hostname == '*':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + G {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{gline_reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'ungline':
try:
# 'shun', 'unshun'
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .ungline nickname host
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'kline':
try:
# TKL + k user host set_by expire_timestamp set_at_timestamp :reason
# .gline [nickname] [host] [reason]
if len(cmd) < 4:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp
gline_reason = ' '.join(cmd[3:])
if nickname == '*' and hostname == '*':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + k {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{gline_reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unkline':
try:
# 'shun', 'unshun'
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .ungline nickname host
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - k {nickname} {hostname} {dnickname}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'shun':
try:
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .shun [nickname] [host] [reason]
if len(cmd) < 4:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
set_at_timestamp = self.Base.get_unixtime()
expire_time = (60 * 60 * 24) + set_at_timestamp
shun_reason = ' '.join(cmd[3:])
if nickname == '*' and hostname == '*':
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
return None
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + s {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{shun_reason}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'unshun':
try:
# 'shun', 'unshun'
# TKL + G user host set_by expire_timestamp set_at_timestamp :reason
# .unshun nickname host
if len(cmd) < 2:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
return None
nickname = str(cmd[1])
hostname = str(cmd[2])
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - s {nickname} {hostname} {dnickname}")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'glinelist':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS G")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'shunlist':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS s")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'klinelist':
try:
self.user_to_notice = fromuser
self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS k")
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}')
self.Logs.warning(f'Unknown Error: {str(err)}')

View File

@@ -1,7 +1,12 @@
import socket
import json
import time
import re
import psutil
import requests
from dataclasses import dataclass, fields, field
from datetime import datetime
from typing import Union
import re, socket, psutil, requests, json, time
from sys import exit
from core.irc import Irc
from core.Model import User
@@ -106,9 +111,6 @@ class Defender():
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
# # Rejoindre les salons
# self.join_saved_channels()
self.timeout = self.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API
@@ -145,7 +147,8 @@ class Defender():
self.Base.create_thread(func=self.thread_reputation_timer)
if self.ModConfig.reputation == 1:
self.Irc.send2socket(f":{self.Config.SERVICE_ID} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} SJOIN {self.Base.get_unixtime()} {self.Config.SALON_JAIL} + :{self.Config.SERVICE_NICKNAME}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.SALON_JAIL} +o {self.Config.SERVICE_NICKNAME}")
return None
@@ -244,40 +247,6 @@ class Defender():
self.reputationTimer_isRunning:bool = False
return None
def add_defender_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnees = {'channel': channel}
response = self.Base.db_execute_query("SELECT id FROM def_channels WHERE channel = :channel", mes_donnees)
isChannelExist = response.fetchone()
if isChannelExist is None:
mes_donnees = {'datetime': self.Base.get_datetime(), 'channel': channel}
insert = self.Base.db_execute_query(f"INSERT INTO def_channels (datetime, channel) VALUES (:datetime, :channel)", mes_donnees)
return True
else:
return False
def delete_defender_channel(self, channel:str) -> bool:
"""Cette fonction supprime les salons de join de Defender
Args:
channel (str): le salon à enregistrer.
"""
mes_donnes = {'channel': channel}
response = self.Base.db_execute_query("DELETE FROM def_channels WHERE channel = :channel", mes_donnes)
affected_row = response.rowcount
if affected_row > 0:
return True
else:
return False
def reputation_insert(self, reputationModel: ReputationModel) -> bool:
response = False
@@ -367,7 +336,7 @@ class Defender():
def join_saved_channels(self) -> None:
result = self.Base.db_execute_query("SELECT id, channel FROM def_channels")
result = self.Base.db_execute_query(f"SELECT distinct channel_name FROM {self.Config.table_channel}")
channels = result.fetchall()
jail_chan = self.Config.SALON_JAIL
jail_chan_mode = self.Config.SALON_JAIL_MODES
@@ -378,7 +347,7 @@ class Defender():
unixtime = self.Base.get_unixtime()
for channel in channels:
id, chan = channel
chan = channel[0]
self.Irc.send2socket(f":{self.Config.SERVEUR_ID} SJOIN {unixtime} {chan} + :{self.Config.SERVICE_ID}")
if chan == jail_chan:
self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}")
@@ -995,190 +964,206 @@ class Defender():
self.Logs.error(f"Thread_cloudfilt_scan Error : {ve}")
def cmd(self, data: list) -> None:
try:
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy()
match cmd[1]:
if len(cmd) < 2:
return None
case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118
try:
self.reputation_first_connexion['ip'] = cmd[2]
self.reputation_first_connexion['score'] = cmd[3]
if str(cmd[3]).find('*') != -1:
# If the reputation changed, we do not need to scan the IP
return None
match cmd[1]:
if not self.Base.is_valid_ip(cmd[2]):
return None
case 'EOS':
if self.Irc.INIT == 0:
self.Irc.send2socket(f":{service_id} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'cmd reputation: index error: {ie}')
case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118
try:
self.reputation_first_connexion['ip'] = cmd[2]
self.reputation_first_connexion['score'] = cmd[3]
if str(cmd[3]).find('*') != -1:
# If the reputation changed, we do not need to scan the IP
if len(cmd) < 3:
return None
match cmd[2]:
case 'MODE':
# ['...', ':001XSCU0Q', 'MODE', '#jail', '+b', '~security-group:unknown-users']
channel = str(cmd[3])
mode = str(cmd[4])
group_to_check = str(cmd[5:])
group_to_unban = '~security-group:unknown-users'
if self.Config.SALON_JAIL == channel:
if mode == '+b' and group_to_unban in group_to_check:
self.Irc.send2socket(f":{service_id} MODE {self.Config.SALON_JAIL} -b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {self.Config.SALON_JAIL} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
case 'PRIVMSG':
cmd.pop(0)
user_trigger = str(cmd[0]).replace(':','')
channel = cmd[2]
find_nickname = self.User.get_nickname(user_trigger)
self.flood(find_nickname, channel)
case 'UID':
# If Init then do nothing
if self.Irc.INIT == 1:
return None
if not self.Base.is_valid_ip(cmd[2]):
return None
# Supprimer la premiere valeur et finir le code normalement
cmd.pop(0)
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'cmd reputation: index error: {ie}')
# Get User information
_User = self.User.get_User(str(cmd[7]))
match cmd[2]:
case 'PRIVMSG':
cmd.pop(0)
user_trigger = str(cmd[0]).replace(':','')
channel = cmd[2]
find_nickname = self.User.get_nickname(user_trigger)
self.flood(find_nickname, channel)
case 'UID':
# If Init then do nothing
if self.Irc.INIT == 1:
return None
# Supprimer la premiere valeur et finir le code normalement
cmd.pop(0)
# Get User information
_User = self.User.get_User(str(cmd[7]))
# If user is not service or IrcOp then scan them
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
if _User is None:
self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why')
return None
reputation_flag = self.ModConfig.reputation
reputation_seuil = self.ModConfig.reputation_seuil
if self.Irc.INIT == 0:
# Si le user n'es pas un service ni un IrcOP
# If user is not service or IrcOp then scan them
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
if reputation_flag == 1 and _User.score_connexion <= reputation_seuil:
currentDateTime = self.Base.get_datetime()
self.reputation_insert(
self.ReputationModel(
uid=_User.uid, nickname=_User.nickname, username=_User.username, realname=_User.realname,
hostname=_User.hostname, umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion,
secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, isWebsocket=_User.isWebsocket, connected_datetime=currentDateTime,
updated_datetime=currentDateTime
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
if _User is None:
self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why')
return None
reputation_flag = self.ModConfig.reputation
reputation_seuil = self.ModConfig.reputation_seuil
if self.Irc.INIT == 0:
# Si le user n'es pas un service ni un IrcOP
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
if reputation_flag == 1 and _User.score_connexion <= reputation_seuil:
currentDateTime = self.Base.get_datetime()
self.reputation_insert(
self.ReputationModel(
uid=_User.uid, nickname=_User.nickname, username=_User.username, realname=_User.realname,
hostname=_User.hostname, umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion,
secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, isWebsocket=_User.isWebsocket, connected_datetime=currentDateTime,
updated_datetime=currentDateTime
)
)
)
# self.Irc.send2socket(f":{service_id} WHOIS {nickname}")
if self.reputation_check(_User.uid):
if reputation_flag == 1 and _User.score_connexion <= reputation_seuil:
self.system_reputation(_User.uid)
self.Logs.info('Démarrer le systeme de reputation')
# self.Irc.send2socket(f":{service_id} WHOIS {nickname}")
if self.reputation_check(_User.uid):
if reputation_flag == 1 and _User.score_connexion <= reputation_seuil:
self.system_reputation(_User.uid)
self.Logs.info('Démarrer le systeme de reputation')
case 'SJOIN':
# ['@msgid=F9B7JeHL5pj9nN57cJ5pEr;time=2023-12-28T20:47:24.305Z', ':001', 'SJOIN', '1702138958', '#welcome', ':0015L1AHL']
try:
case 'SJOIN':
# ['@msgid=F9B7JeHL5pj9nN57cJ5pEr;time=2023-12-28T20:47:24.305Z', ':001', 'SJOIN', '1702138958', '#welcome', ':0015L1AHL']
try:
cmd.pop(0)
parsed_chan = cmd[3]
if self.ModConfig.reputation == 1:
parsed_UID = cmd[4]
pattern = fr'^:[@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', parsed_UID)
get_reputation = self.reputation_get_Reputation(parsed_UID)
if parsed_chan != self.Config.SALON_JAIL:
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
if not get_reputation is None:
isWebirc = get_reputation.isWebirc
if not isWebirc:
if parsed_chan != self.Config.SALON_JAIL:
self.Irc.send2socket(f":{service_id} SAPART {get_reputation.nickname} {parsed_chan}")
if self.ModConfig.reputation_ban_all_chan == 1 and not isWebirc:
if parsed_chan != self.Config.SALON_JAIL:
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b {get_reputation.nickname}!*@*")
self.Irc.send2socket(f":{service_id} KICK {parsed_chan} {get_reputation.nickname}")
self.Logs.debug(f'SJOIN parsed_uid : {parsed_UID}')
except KeyError as ke:
self.Logs.error(f"key error SJOIN : {ke}")
case 'SLOG':
# self.Base.scan_ports(cmd[7])
cmd.pop(0)
parsed_chan = cmd[3]
if self.ModConfig.reputation == 1:
parsed_UID = cmd[4]
pattern = fr'^:[@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', parsed_UID)
if not self.Base.is_valid_ip(cmd[7]):
return None
get_reputation = self.reputation_get_Reputation(parsed_UID)
# if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.localscan_remote_ip.append(cmd[7])
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
# if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.psutil_remote_ip.append(cmd[7])
if not get_reputation is None:
isWebirc = get_reputation.isWebirc
# if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.abuseipdb_remote_ip.append(cmd[7])
if not isWebirc:
if parsed_chan != self.Config.SALON_JAIL:
self.Irc.send2socket(f":{service_id} SAPART {get_reputation.nickname} {parsed_chan}")
# if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.freeipapi_remote_ip.append(cmd[7])
if self.ModConfig.reputation_ban_all_chan == 1 and not isWebirc:
if parsed_chan != self.Config.SALON_JAIL:
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b {get_reputation.nickname}!*@*")
self.Irc.send2socket(f":{service_id} KICK {parsed_chan} {get_reputation.nickname}")
# if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.cloudfilt_remote_ip.append(cmd[7])
self.Logs.debug(f'SJOIN parsed_uid : {parsed_UID}')
except KeyError as ke:
self.Logs.error(f"key error SJOIN : {ke}")
case 'NICK':
# :0010BS24L NICK [NEWNICK] 1697917711
# Changement de nickname
try:
cmd.pop(0)
uid = str(cmd[0]).replace(':','')
get_Reputation = self.reputation_get_Reputation(uid)
jail_salon = self.Config.SALON_JAIL
service_id = self.Config.SERVICE_ID
case 'SLOG':
# self.Base.scan_ports(cmd[7])
cmd.pop(0)
if get_Reputation is None:
self.Logs.debug(f'This UID: {uid} is not listed in the reputation dataclass')
return None
if not self.Base.is_valid_ip(cmd[7]):
return None
# Update the new nickname
oldnick = get_Reputation.nickname
newnickname = cmd[2]
get_Reputation.nickname = newnickname
# if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.localscan_remote_ip.append(cmd[7])
# If ban in all channel is ON then unban old nickname an ban the new nickname
if self.ModConfig.reputation_ban_all_chan == 1:
for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_salon:
self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {oldnick}!*@*")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*")
# if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.psutil_remote_ip.append(cmd[7])
except KeyError as ke:
self.Logs.error(f'cmd - NICK - KeyError: {ke}')
# if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.abuseipdb_remote_ip.append(cmd[7])
# if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.freeipapi_remote_ip.append(cmd[7])
# if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.cloudfilt_remote_ip.append(cmd[7])
case 'NICK':
# :0010BS24L NICK [NEWNICK] 1697917711
# Changement de nickname
try:
case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1
cmd.pop(0)
uid = str(cmd[0]).replace(':','')
get_Reputation = self.reputation_get_Reputation(uid)
ban_all_chan = self.Base.int_if_possible(self.ModConfig.reputation_ban_all_chan)
user_id = str(cmd[0]).replace(':','')
final_UID = user_id
jail_salon = self.Config.SALON_JAIL
service_id = self.Config.SERVICE_ID
if get_Reputation is None:
self.Logs.debug(f'This UID: {uid} is not listed in the reputation dataclass')
return None
get_user_reputation = self.reputation_get_Reputation(final_UID)
# Update the new nickname
oldnick = get_Reputation.nickname
newnickname = cmd[2]
get_Reputation.nickname = newnickname
# If ban in all channel is ON then unban old nickname an ban the new nickname
if self.ModConfig.reputation_ban_all_chan == 1:
if not get_user_reputation is None:
final_nickname = get_user_reputation.nickname
for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_salon:
self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {oldnick}!*@*")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +b {newnickname}!*@*")
if chan.name != jail_salon and ban_all_chan == 1:
self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*")
self.reputation_delete(final_UID)
except KeyError as ke:
self.Logs.error(f'cmd - NICK - KeyError: {ke}')
case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1
cmd.pop(0)
ban_all_chan = self.Base.int_if_possible(self.ModConfig.reputation_ban_all_chan)
user_id = str(cmd[0]).replace(':','')
final_UID = user_id
jail_salon = self.Config.SALON_JAIL
service_id = self.Config.SERVICE_ID
get_user_reputation = self.reputation_get_Reputation(final_UID)
if not get_user_reputation is None:
final_nickname = get_user_reputation.nickname
for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_salon and ban_all_chan == 1:
self.Irc.send2socket(f":{service_id} MODE {chan.name} -b {final_nickname}!*@*")
self.reputation_delete(final_UID)
except KeyError as ke:
self.Logs.error(f"{ke} / {cmd} / length {str(len(cmd))}")
except IndexError as ie:
self.Logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
except Exception as err:
self.Logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:

View File

@@ -10,8 +10,7 @@ class Jsonrpc():
class ModConfModel:
"""The Model containing the module parameters
"""
param_exemple1: str
param_exemple2: int
jsonrpc: int = 0
def __init__(self, ircInstance:Irc) -> None:
@@ -59,16 +58,41 @@ class Jsonrpc():
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
self.UnrealIrcdRpcLive: Live = None
# self.UnrealIrcdRpcLive: Live = Live(
# req_method='unixsocket',
# path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
# callback_object_instance=self,
# callback_method_name='callback_sent_to_irc'
# )
self.UnrealIrcdRpcLive: Live = Live(
req_method='websocket',
url=self.Config.JSONRPC_URL,
username=self.Config.JSONRPC_USER,
password=self.Config.JSONRPC_PASSWORD,
callback_object_instance=self,
callback_method_name='callback_sent_to_irc',
debug_level=10
)
self.Rpc: Loader = Loader(
req_method=self.Config.JSONRPC_METHOD,
url=self.Config.JSONRPC_URL,
username=self.Config.JSONRPC_USER,
password=self.Config.JSONRPC_PASSWORD
)
self.subscribed = False
if self.Rpc.Error.code != 0:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}", self.Config.SERVICE_CHANLOG)
if self.UnrealIrcdRpcLive.Error.code != 0:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG)
if self.ModConfig.jsonrpc == 1:
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -109,17 +133,30 @@ class Jsonrpc():
dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG
green = self.Config.COLORS.green
nogc = self.Config.COLORS.nogc
bold = self.Config.COLORS.bold
red = self.Config.COLORS.red
self.Irc.sendPrivMsg(msg=json_response, channel=dchanlog)
if json_response.result == True:
self.Irc.sendPrivMsg(msg=f"[{bold}{green}JSONRPC{nogc}{bold}] Event activated", channel=dchanlog)
return None
level = json_response.result.level
subsystem = json_response.result.subsystem
event_id = json_response.result.event_id
log_source = json_response.result.log_source
msg = json_response.result.msg
build_msg = f"{green}{log_source}{nogc}: [{bold}{level}{bold}] {subsystem}.{event_id} - {msg}"
self.Irc.sendPrivMsg(msg=build_msg, channel=dchanlog)
def thread_start_jsonrpc(self):
self.UnrealIrcdRpcLive = Live(path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
callback_object_instance=self,
callback_method_name='callback_sent_to_irc'
)
if self.UnrealIrcdRpcLive.Error.code == 0:
self.UnrealIrcdRpcLive.subscribe()
self.UnrealIrcdRpcLive.subscribe(["all"])
self.subscribed = True
else:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG)
@@ -128,10 +165,10 @@ class Jsonrpc():
"""
try:
# Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1)
self.ModConfig = self.ModConfModel(jsonrpc=0)
# Sync the configuration with core configuration (Mandatory)
#self.Base.db_sync_core_config(self.module_name, self.ModConfig)
self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
@@ -148,7 +185,8 @@ class Jsonrpc():
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None:
self.UnrealIrcdRpcLive.unsubscribe()
if self.UnrealIrcdRpcLive.Error.code != -1:
self.UnrealIrcdRpcLive.unsubscribe()
return None
def cmd(self, data:list) -> None:
@@ -159,6 +197,7 @@ class Jsonrpc():
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
dchannel = self.Config.SERVICE_CHANLOG
fromuser = user
fromchannel = str(channel) if not channel is None else None
@@ -175,10 +214,19 @@ class Jsonrpc():
match option:
case 'on':
for thread in self.Base.running_threads:
if thread.getName() == 'thread_start_jsonrpc':
if thread.is_alive():
self.Irc.sendPrivMsg(f"Thread {thread.getName()} is running", dchannel)
else:
self.Irc.sendPrivMsg(f"Thread {thread.getName()} is not running, wait untill the process will be cleaned up", dchannel)
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
self.__update_configuration('jsonrpc', 1)
case 'off':
self.UnrealIrcdRpcLive.unsubscribe()
self.__update_configuration('jsonrpc', 0)
except IndexError as ie:
self.Logs.error(ie)
@@ -198,42 +246,45 @@ class Jsonrpc():
if uid_to_get is None:
return None
rpc = Loader(
req_method=self.Config.JSONRPC_METHOD,
url=self.Config.JSONRPC_URL,
username=self.Config.JSONRPC_USER,
password=self.Config.JSONRPC_PASSWORD
)
rpc = self.Rpc
UserInfo = rpc.User.get(uid_to_get)
if rpc.Error.code != 0:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{rpc.Error.message}')
return None
chan_list = []
for chan in UserInfo.user.channels:
chan_list.append(chan.name)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {UserInfo.channels}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.security_groups}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.user.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.user.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :MODES : {UserInfo.user.modes}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {chan_list}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.user.security_groups}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.user.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IP : {UserInfo.ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :COUNTRY CODE : {UserInfo.country_code}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASN : {UserInfo.asn}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASNAME : {UserInfo.asname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLOAKED HOST : {UserInfo.cloakedhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :COUNTRY CODE : {UserInfo.geoip.country_code}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASN : {UserInfo.geoip.asn}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASNAME : {UserInfo.geoip.asname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLOAKED HOST : {UserInfo.user.cloakedhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :HOSTNAME : {UserInfo.hostname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :VHOST : {UserInfo.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :VHOST : {UserInfo.user.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLIENT PORT : {UserInfo.client_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SERVER PORT : {UserInfo.server_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CERTFP : {UserInfo.tls.certfp}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CIPHER : {UserInfo.tls.cipher}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IDLE SINCE : {UserInfo.idle_since}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CONNECTED SINCE : {UserInfo.connected_since}')
except IndexError as ie:
self.Logs.error(ie)
case 'ia':
try:

View File

@@ -124,8 +124,16 @@ class Test():
return None
def cmd(self, data:list) -> None:
try:
cmd = list(data).copy()
return None
return None
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:

View File

@@ -242,15 +242,16 @@ class Votekick():
return None
def cmd(self, data:list) -> None:
cmd = list(data).copy()
try:
cmd = list(data).copy()
return None
match cmd[2]:
case 'SJOIN':
pass
case _:
pass
return None
except KeyError as ke:
self.Base.logs.error(f"Key Error: {ke}")
except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
# cmd is the command starting from the user command

View File

@@ -1,3 +1,9 @@
{
"version": "5.3.1"
"version": "5.4.0",
"requests": "2.32.3",
"psutil": "6.0.0",
"unrealircd_rpc_py": "1.0.6",
"sqlalchemy": "2.0.35",
"faker": "30.1.0"
}