Merge pull request #98 from adator85/asyncio

Fix Asyncio update
This commit is contained in:
adator
2025-11-23 18:24:33 +01:00
committed by GitHub
31 changed files with 826 additions and 711 deletions

View File

@@ -2,17 +2,18 @@ import asyncio
import os
import re
import json
import time
import socket
import threading
import ipaddress
import ast
import requests
import concurrent.futures
from dataclasses import fields
from typing import Any, Callable, Iterable, Optional, TYPE_CHECKING
from typing import Any, Awaitable, Callable, Optional, TYPE_CHECKING, Union
from base64 import b64decode, b64encode
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
import core.definition as dfn
if TYPE_CHECKING:
from core.loader import Loader
@@ -36,12 +37,15 @@ class Base:
# Liste des threads en cours
self.running_threads: list[threading.Thread] = self.Settings.RUNNING_THREADS
# List of all async tasks
self.running_asynctasks: list[asyncio.Task] = self.Settings.RUNNING_ASYNCTASKS
# Les sockets ouvert
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
# List of all asyncio tasks
self.running_iotasks: list[asyncio.Task] = self.Settings.RUNNING_ASYNC_TASKS
# List of all asyncio threads pool executors
self.running_iothreads: list[dfn.MThread] = self.Settings.RUNNING_ASYNC_THREADS
# Liste des fonctions en attentes
self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC
@@ -71,7 +75,6 @@ class Base:
return None
def __get_latest_defender_version(self) -> None:
try:
self.logs.debug(f'-- Looking for a new version available on Github')
token = ''
json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json'
@@ -80,17 +83,18 @@ class Base:
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
}
with requests.Session() as sess:
try:
if token == '':
response = requests.get(json_url, timeout=self.Config.API_TIMEOUT)
response = sess.get(json_url, timeout=self.Config.API_TIMEOUT)
else:
response = requests.get(json_url, headers=headers, timeout=self.Config.API_TIMEOUT)
response = sess.get(json_url, headers=headers, timeout=self.Config.API_TIMEOUT)
response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json()
# self.LATEST_DEFENDER_VERSION = json_response["version"]
self.Config.LATEST_VERSION = json_response['version']
self.Config.LATEST_VERSION = json_response.get('version', '')
return None
except requests.HTTPError as err:
self.logs.error(f'Github not available to fetch latest version: {err}')
except:
@@ -359,8 +363,8 @@ class Base:
except Exception as err:
self.logs.error(err, exc_info=True)
def create_asynctask(self, func: Any, *, async_name: str = None, run_once: bool = False) -> asyncio.Task:
"""Create a new asynchrone and store it into running_asynctasks variable
def create_asynctask(self, func: Callable[..., Awaitable[Any]], *, async_name: str = None, run_once: bool = False) -> Optional[asyncio.Task]:
"""Create a new asynchrone and store it into running_iotasks variable
Args:
func (Callable): The function you want to call in asynchrone way
@@ -379,26 +383,71 @@ class Base:
task = asyncio.create_task(func, name=name)
task.add_done_callback(self.asynctask_done)
self.running_asynctasks.append(task)
self.running_iotasks.append(task)
self.logs.debug(f"++ New asynchrone task created as: {task.get_name()}")
self.logs.debug(f"=== New IO task created as: {task.get_name()}")
return task
def asynctask_done(self, task: asyncio.Task):
async def create_thread_io(self, func: Callable[..., Any], *args, run_once: bool = False, thread_flag: bool = False) -> Optional[Any]:
"""Run threads via asyncio.
Args:
func (Callable[..., Any]): The blocking IO function
run_once (bool, optional): If it should be run once.. Defaults to False.
thread_flag (bool, optional): If you are using a endless loop, use the threading Event object. Defaults to False.
Returns:
Any: The final result of the blocking IO function
"""
if run_once:
for iothread in self.running_iothreads:
if func.__name__.lower() == iothread.name.lower():
return None
with concurrent.futures.ThreadPoolExecutor(max_workers=1, thread_name_prefix=func.__name__) as executor:
loop = asyncio.get_event_loop()
largs = list(args)
thread_event: Optional[threading.Event] = None
if thread_flag:
thread_event = threading.Event()
thread_event.set()
largs.insert(0, thread_event)
future = loop.run_in_executor(executor, func, *tuple(largs))
future.add_done_callback(self.asynctask_done)
id_obj = self.Loader.Definition.MThread(
name=func.__name__,
thread_id=list(executor._threads)[0].native_id,
thread_event=thread_event,
thread_obj=list(executor._threads)[0],
executor=executor,
future=future)
self.running_iothreads.append(id_obj)
self.logs.debug(f"=== New thread started {func.__name__} with max workers set to: {executor._max_workers}")
result = await future
self.running_iothreads.remove(id_obj)
return result
def asynctask_done(self, task: Union[asyncio.Task, asyncio.Future]):
"""Log task when done
Args:
task (asyncio.Task): The Asyncio Task callback
"""
name = task.get_name() if isinstance(task, asyncio.Task) else "Thread"
task_or_future = "Task" if isinstance(task, asyncio.Task) else "Future"
try:
if task.exception():
self.logs.error(f"[ASYNCIO] Task {task.get_name()} failed with exception: {task.exception()}")
self.logs.error(f"[ASYNCIO] {task_or_future} {name} failed with exception: {task.exception()}")
else:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} completed successfully.")
self.logs.debug(f"[ASYNCIO] {task_or_future} {name} completed successfully.")
except asyncio.CancelledError as ce:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with cancelled error.")
self.logs.debug(f"[ASYNCIO] {task_or_future} {name} terminated with cancelled error. {ce}")
except asyncio.InvalidStateError as ie:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with invalid state error.")
self.logs.debug(f"[ASYNCIO] {task_or_future} {name} terminated with invalid state error. {ie}")
def is_thread_alive(self, thread_name: str) -> bool:
"""Check if the thread is still running! using the is_alive method of Threads.
@@ -492,59 +541,6 @@ class Base:
self.running_sockets.remove(soc)
self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}")
async def shutdown(self) -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Stop RpcServer if running
await self.Loader.RpcServer.stop_server()
# unload modules.
self.logs.debug(f"=======> Unloading all modules!")
for module in self.Loader.ModuleUtils.model_get_loaded_modules().copy():
await self.Loader.ModuleUtils.unload_one_module(module.module_name)
self.logs.debug(f"=======> Closing all Coroutines!")
try:
await asyncio.wait_for(asyncio.gather(*self.running_asynctasks), timeout=5)
except asyncio.exceptions.TimeoutError as te:
self.logs.debug(f"Asyncio Timeout reached! {te}")
for task in self.running_asynctasks:
task.cancel()
except asyncio.exceptions.CancelledError as cerr:
self.logs.debug(f"Asyncio CancelledError reached! {cerr}")
# Nettoyage des timers
self.logs.debug(f"=======> Checking for Timers to stop")
for timer in self.running_timers:
while timer.is_alive():
self.logs.debug(f"> waiting for {timer.name} to close")
timer.cancel()
await asyncio.sleep(0.2)
self.running_timers.remove(timer)
self.logs.debug(f"> Cancelling {timer.name} {timer.native_id}")
self.logs.debug(f"=======> Checking for Threads to stop")
for thread in self.running_threads:
if thread.name == 'heartbeat' and thread.is_alive():
self.execute_periodic_action()
self.logs.debug(f"> Running the last periodic action")
self.running_threads.remove(thread)
self.logs.debug(f"> Cancelling {thread.name} {thread.native_id}")
self.logs.debug(f"=======> Checking for Sockets to stop")
for soc in self.running_sockets:
soc.close()
while soc.fileno() != -1:
soc.close()
self.running_sockets.remove(soc)
self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
self.db_close()
return None
def db_init(self) -> tuple[Engine, Connection]:
db_directory = self.Config.DB_PATH

View File

@@ -12,12 +12,14 @@ class IRPC:
self.http_status_code = http_status_code
self.response_model = {
"jsonrpc": "2.0",
"method": 'unknown',
"id": 123
}
def reset(self):
self.response_model = {
"jsonrpc": "2.0",
"method": 'unknown',
"id": 123
}

View File

@@ -14,12 +14,7 @@ class Admin:
Args:
loader (Loader): The Loader Instance.
"""
self.Logs = loader.Logs
self.Base = loader.Base
self.Setting = loader.Settings
self.Config = loader.Config
self.User = loader.User
self.Definition = loader.Definition
self._ctx = loader
def insert(self, new_admin: MAdmin) -> bool:
"""Insert a new admin object model
@@ -33,11 +28,11 @@ class Admin:
for record in self.UID_ADMIN_DB:
if record.uid == new_admin.uid:
self.Logs.debug(f'{record.uid} already exist')
self._ctx.Logs.debug(f'{record.uid} already exist')
return False
self.UID_ADMIN_DB.append(new_admin)
self.Logs.debug(f'A new admin ({new_admin.nickname}) has been created')
self._ctx.Logs.debug(f'A new admin ({new_admin.nickname}) has been created')
return True
def update_nickname(self, uid: str, new_admin_nickname: str) -> bool:
@@ -55,11 +50,11 @@ class Admin:
if record.uid == uid:
# If the admin exist, update and do not go further
record.nickname = new_admin_nickname
self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {new_admin_nickname}')
self._ctx.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {new_admin_nickname}')
return True
self.Logs.debug(f'The new nickname {new_admin_nickname} was not updated, uid = {uid} - The Client is not an admin')
self._ctx.Logs.debug(f'The new nickname {new_admin_nickname} was not updated, uid = {uid} - The Client is not an admin')
return False
def update_level(self, nickname: str, new_admin_level: int) -> bool:
@@ -77,10 +72,10 @@ class Admin:
if record.nickname == nickname:
# If the admin exist, update and do not go further
record.level = new_admin_level
self.Logs.debug(f'Admin ({record.nickname}) has been updated with new level {new_admin_level}')
self._ctx.Logs.debug(f'Admin ({record.nickname}) has been updated with new level {new_admin_level}')
return True
self.Logs.debug(f'The new level {new_admin_level} was not updated, nickname = {nickname} - The Client is not an admin')
self._ctx.Logs.debug(f'The new level {new_admin_level} was not updated, nickname = {nickname} - The Client is not an admin')
return False
@@ -96,10 +91,10 @@ class Admin:
admin_obj = self.get_admin(uidornickname)
if admin_obj:
self.UID_ADMIN_DB.remove(admin_obj)
self.Logs.debug(f'UID ({admin_obj.uid}) has been deleted')
self._ctx.Logs.debug(f'UID ({admin_obj.uid}) has been deleted')
return True
self.Logs.debug(f'The UID {uidornickname} was not deleted')
self._ctx.Logs.debug(f'The UID {uidornickname} was not deleted')
return False
@@ -186,20 +181,20 @@ class Admin:
if fp is None:
return False
query = f"SELECT user, level, language FROM {self.Config.TABLE_ADMIN} WHERE fingerprint = :fp"
query = f"SELECT user, level, language FROM {self._ctx.Config.TABLE_ADMIN} WHERE fingerprint = :fp"
data = {'fp': fp}
exe = await self.Base.db_execute_query(query, data)
exe = await self._ctx.Base.db_execute_query(query, data)
result = exe.fetchone()
if result:
account = result[0]
level = result[1]
language = result[2]
user_obj = self.User.get_user(uidornickname)
user_obj = self._ctx.User.get_user(uidornickname)
if user_obj:
admin_obj = self.Definition.MAdmin(**user_obj.to_dict(), account=account, level=level, language=language)
admin_obj = self._ctx.Definition.MAdmin(**user_obj.to_dict(), account=account, level=level, language=language)
if self.insert(admin_obj):
self.Setting.current_admin = admin_obj
self.Logs.debug(f"[Fingerprint login] {user_obj.nickname} ({admin_obj.account}) has been logged in successfully!")
self._ctx.Settings.current_admin = admin_obj
self._ctx.Logs.debug(f"[Fingerprint login] {user_obj.nickname} ({admin_obj.account}) has been logged in successfully!")
return True
return False
@@ -215,8 +210,8 @@ class Admin:
"""
mes_donnees = {'admin': admin_nickname}
query_search_user = f"SELECT id FROM {self.Config.TABLE_ADMIN} WHERE user = :admin"
r = await self.Base.db_execute_query(query_search_user, mes_donnees)
query_search_user = f"SELECT id FROM {self._ctx.Config.TABLE_ADMIN} WHERE user = :admin"
r = await self._ctx.Base.db_execute_query(query_search_user, mes_donnees)
exist_user = r.fetchone()
if exist_user:
return True

View File

@@ -15,8 +15,7 @@ class Client:
Args:
loader (Loader): The Loader instance.
"""
self.Logs = loader.Logs
self.Base = loader.Base
self._ctx = loader
def insert(self, new_client: 'MClient') -> bool:
"""Insert a new User object
@@ -28,7 +27,7 @@ class Client:
bool: True if inserted
"""
client_obj = self.get_Client(new_client.uid)
client_obj = self.get_client(new_client.uid)
if not client_obj is None:
# User already created return False
@@ -48,7 +47,7 @@ class Client:
Returns:
bool: True if updated
"""
user_obj = self.get_Client(uidornickname=uid)
user_obj = self.get_client(uidornickname=uid)
if user_obj is None:
return False
@@ -68,7 +67,7 @@ class Client:
bool: True if user mode has been updaed
"""
response = True
user_obj = self.get_Client(uidornickname=uidornickname)
user_obj = self.get_client(uidornickname=uidornickname)
if user_obj is None:
return False
@@ -93,7 +92,7 @@ class Client:
return False
liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes_liste = [x for x in self._ctx.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste)
user_obj.umodes = f"+{final_umodes}"
@@ -110,7 +109,7 @@ class Client:
bool: True if deleted
"""
user_obj = self.get_Client(uidornickname=uid)
user_obj = self.get_client(uidornickname=uid)
if user_obj is None:
return False
@@ -119,7 +118,7 @@ class Client:
return True
def get_Client(self, uidornickname: str) -> Optional['MClient']:
def get_client(self, uidornickname: str) -> Optional['MClient']:
"""Get The Client Object model
Args:
@@ -146,7 +145,7 @@ class Client:
str|None: Return the UID
"""
client_obj = self.get_Client(uidornickname=uidornickname)
client_obj = self.get_client(uidornickname=uidornickname)
if client_obj is None:
return None
@@ -162,29 +161,13 @@ class Client:
Returns:
str|None: the nickname
"""
client_obj = self.get_Client(uidornickname=uidornickname)
client_obj = self.get_client(uidornickname=uidornickname)
if client_obj is None:
return None
return client_obj.nickname
def get_client_asdict(self, uidornickname: str) -> Optional[dict[str, Any]]:
"""Transform User Object to a dictionary
Args:
uidornickname (str): The UID or The nickname
Returns:
Union[dict[str, any], None]: User Object as a dictionary or None
"""
client_obj = self.get_Client(uidornickname=uidornickname)
if client_obj is None:
return None
return client_obj.to_dict()
def is_exist(self, uidornickname: str) -> bool:
"""Check if the UID or the nickname exist in the USER DB
@@ -194,7 +177,7 @@ class Client:
Returns:
bool: True if exist
"""
user_obj = self.get_Client(uidornickname=uidornickname)
user_obj = self.get_client(uidornickname=uidornickname)
if user_obj is None:
return False
@@ -211,15 +194,15 @@ class Client:
bool: True if exist
"""
table_client = self.Base.Config.TABLE_CLIENT
table_client = self._ctx.Base.Config.TABLE_CLIENT
account_to_check = {'account': account.lower()}
account_to_check_query = await self.Base.db_execute_query(f"""
account_to_check_query = await self._ctx.Base.db_execute_query(f"""
SELECT id FROM {table_client} WHERE LOWER(account) = :account
""", account_to_check)
account_to_check_result = account_to_check_query.fetchone()
if account_to_check_result:
self.Logs.error(f"Account ({account}) already exist")
self._ctx.Logs.error(f"Account ({account}) already exist")
return True
return False

View File

@@ -13,11 +13,21 @@ class Command:
Args:
loader (Loader): The Loader instance.
"""
self.Loader = loader
self.Base = loader.Base
self.Logs = loader.Logs
self._ctx = loader
def build(self, new_command_obj: MCommand) -> bool:
def build_command(self, level: int, module_name: str, command_name: str, command_description: str) -> bool:
"""This method build the commands variable
Args:
level (int): The Level of the command
module_name (str): The module name
command_name (str): The command name
command_description (str): The description of the command
"""
# Build Model.
return self._build(self._ctx.Definition.MCommand(module_name, command_name, command_description, level))
def _build(self, new_command_obj: MCommand) -> bool:
command = self.get_command(new_command_obj.command_name, new_command_obj.module_name)
if command is None:
@@ -68,7 +78,7 @@ class Command:
for c in tmp_model:
self.DB_COMMANDS.remove(c)
self.Logs.debug(f"[COMMAND] Drop command for module {module_name}")
self._ctx.Logs.debug(f"[COMMAND] Drop command for module {module_name}")
return True
def get_ordered_commands(self) -> list[MCommand]:
@@ -86,7 +96,7 @@ class Command:
return new_list
def is_client_allowed_to_run_command(self, nickname: str, command_name: str) -> bool:
admin = self.Loader.Admin.get_admin(nickname)
admin = self._ctx.Admin.get_admin(nickname)
admin_level = admin.level if admin else 0
commands = self.get_commands_by_level(admin_level)

View File

@@ -1,3 +1,4 @@
import asyncio
import importlib
import sys
import time
@@ -26,7 +27,6 @@ REHASH_MODULES = [
'core.classes.protocols.inspircd'
]
async def restart_service(uplink: 'Loader', reason: str = "Restarting with no reason!") -> None:
"""
@@ -69,7 +69,7 @@ async def rehash_service(uplink: 'Loader', nickname: str) -> None:
need_a_restart = ["SERVEUR_ID"]
uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS)
await uplink.RpcServer.stop_server()
await uplink.RpcServer.stop_rpc_server()
restart_flag = False
config_model_bakcup = uplink.Config
@@ -122,10 +122,68 @@ async def rehash_service(uplink: 'Loader', nickname: str) -> None:
uplink.Irc.Protocol.register_command()
uplink.RpcServer = uplink.RpcServerModule.JSonRpcServer(uplink)
uplink.Base.create_asynctask(uplink.RpcServer.start_server())
uplink.Base.create_asynctask(uplink.RpcServer.start_rpc_server())
# Reload Service modules
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
await uplink.ModuleUtils.reload_one_module(module.module_name, nickname)
return None
async def shutdown(uplink: 'Loader') -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Stop RpcServer if running
await uplink.RpcServer.stop_rpc_server()
# unload modules.
uplink.Logs.debug(f"=======> Unloading all modules!")
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
await uplink.ModuleUtils.unload_one_module(module.module_name)
# Nettoyage des timers
uplink.Logs.debug(f"=======> Closing all timers!")
for timer in uplink.Base.running_timers:
while timer.is_alive():
uplink.Logs.debug(f"> waiting for {timer.name} to close")
timer.cancel()
await asyncio.sleep(0.2)
uplink.Logs.debug(f"> Cancelling {timer.name} {timer.native_id}")
uplink.Logs.debug(f"=======> Closing all Threads!")
for thread in uplink.Base.running_threads:
if thread.name == 'heartbeat' and thread.is_alive():
uplink.Base.execute_periodic_action()
uplink.Logs.debug(f"> Running the last periodic action")
uplink.Logs.debug(f"> Cancelling {thread.name} {thread.native_id}")
uplink.Logs.debug(f"=======> Closing all IO Threads!")
[th.thread_event.clear() for th in uplink.Base.running_iothreads]
uplink.Logs.debug(f"=======> Closing all IO TASKS!")
try:
await asyncio.wait_for(asyncio.gather(*uplink.Base.running_iotasks), timeout=5)
except asyncio.exceptions.TimeoutError as te:
uplink.Logs.debug(f"Asyncio Timeout reached! {te}")
for task in uplink.Base.running_iotasks:
task.cancel()
except asyncio.exceptions.CancelledError as cerr:
uplink.Logs.debug(f"Asyncio CancelledError reached! {cerr}")
uplink.Logs.debug(f"=======> Closing all Sockets!")
for soc in uplink.Base.running_sockets:
soc.close()
while soc.fileno() != -1:
soc.close()
uplink.Base.running_sockets.remove(soc)
uplink.Logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
uplink.Base.running_timers.clear()
uplink.Base.running_threads.clear()
uplink.Base.running_iotasks.clear()
uplink.Base.running_iothreads.clear()
uplink.Base.running_sockets.clear()
uplink.Base.db_close()
return None

View File

@@ -14,9 +14,7 @@ class Reputation:
Args:
loader (Loader): The Loader instance.
"""
self.Logs = loader.Logs
self.MReputation: Optional[MReputation] = None
self._ctx = loader
def insert(self, new_reputation_user: MReputation) -> bool:
"""Insert a new Reputation User object
@@ -34,16 +32,16 @@ class Reputation:
if record.uid == new_reputation_user.uid:
# If the user exist then return False and do not go further
exist = True
self.Logs.debug(f'{record.uid} already exist')
self._ctx.Logs.debug(f'{record.uid} already exist')
return result
if not exist:
self.UID_REPUTATION_DB.append(new_reputation_user)
result = True
self.Logs.debug(f'New Reputation User Captured: ({new_reputation_user})')
self._ctx.Logs.debug(f'New Reputation User Captured: ({new_reputation_user})')
if not result:
self.Logs.critical(f'The Reputation User Object was not inserted {new_reputation_user}')
self._ctx.Logs.critical(f'The Reputation User Object was not inserted {new_reputation_user}')
return result
@@ -86,11 +84,11 @@ class Reputation:
# If the user exist then remove and return True and do not go further
self.UID_REPUTATION_DB.remove(record)
result = True
self.Logs.debug(f'UID ({record.uid}) has been deleted')
self._ctx.Logs.debug(f'UID ({record.uid}) has been deleted')
return result
if not result:
self.Logs.critical(f'The UID {uid} was not deleted')
self._ctx.Logs.critical(f'The UID {uid} was not deleted')
return result

View File

@@ -1 +1,2 @@
__version__ = '1.0.0'
__all__ = ['start_rpc_server', 'stop_rpc_server']

View File

@@ -17,11 +17,11 @@ if TYPE_CHECKING:
class JSonRpcServer:
def __init__(self, context: 'Loader', *, hostname: str = 'localhost', port: int = 5000):
def __init__(self, context: 'Loader'):
self._ctx = context
self.live: bool = False
self.host = hostname
self.port = port
self.host = context.Config.RPC_HOST
self.port = context.Config.RPC_PORT
self.routes: list[Route] = []
self.server: Optional[uvicorn.Server] = None
@@ -34,12 +34,12 @@ class JSonRpcServer:
'command.get.by.module': RPCCommand(context).command_get_by_module
}
async def start_server(self):
async def start_rpc_server(self):
if not self.live:
self.routes = [Route('/api', self.request_handler, methods=['POST'])]
self.app_jsonrpc = Starlette(debug=False, routes=self.routes)
config = uvicorn.Config(self.app_jsonrpc, host=self.host, port=self.port, log_level=self._ctx.Config.DEBUG_LEVEL)
config = uvicorn.Config(self.app_jsonrpc, host=self.host, port=self.port, log_level=self._ctx.Config.DEBUG_LEVEL+10)
self.server = uvicorn.Server(config)
self.live = True
await self._ctx.Irc.Protocol.send_priv_msg(
@@ -52,7 +52,7 @@ class JSonRpcServer:
else:
self._ctx.Logs.debug("Server already running")
async def stop_server(self):
async def stop_rpc_server(self):
if self.server:
self.server.should_exit = True
@@ -77,10 +77,10 @@ class JSonRpcServer:
response_data = {
"jsonrpc": "2.0",
"method": method,
"id": request_data.get('id', 123)
}
response_data['method'] = method
rip = request.client.host
rport = request.client.port
http_code = http_status_code.HTTP_200_OK
@@ -89,6 +89,7 @@ class JSonRpcServer:
r: JSONResponse = self.methods[method](**params)
resp = json.loads(r.body)
resp['id'] = request_data.get('id', 123)
resp['method'] = method
return JSONResponse(resp, r.status_code)
response_data['error'] = rpcerr.create_error_response(rpcerr.JSONRPCErrorCode.METHOD_NOT_FOUND)

View File

@@ -1,5 +1,4 @@
from typing import TYPE_CHECKING
from starlette.responses import JSONResponse
from core.classes.interfaces.irpc_endpoint import IRPC
from core.classes.modules.rpc.rpc_errors import JSONRPCErrorCode

View File

@@ -42,4 +42,4 @@ class RPCUser(IRPC):
return JSONResponse(self.response_model)
self.response_model['result'] = 'User not found!'
return JSONResponse(self.response_model, self.http_status_code.HTTP_204_NO_CONTENT)
return JSONResponse(self.response_model, self.http_status_code.HTTP_404_NOT_FOUND)

View File

@@ -6,7 +6,7 @@ from threading import Timer, Thread, RLock
from asyncio.locks import Lock
from socket import socket
from typing import Any, Optional, TYPE_CHECKING
from core.definition import MSModule, MAdmin
from core.definition import MSModule, MAdmin, MThread
if TYPE_CHECKING:
from core.classes.modules.user import User
@@ -19,10 +19,12 @@ class Settings:
RUNNING_TIMERS: list[Timer] = []
RUNNING_THREADS: list[Thread] = []
RUNNING_ASYNCTASKS: list[asyncio.Task] = []
RUNNING_SOCKETS: list[socket] = []
RUNNING_ASYNC_TASKS: list[asyncio.Task] = []
RUNNING_ASYNC_THREADS: list[MThread] = []
PERIODIC_FUNC: dict[str, Any] = {}
LOCK: RLock = RLock()
THLOCK: RLock = RLock()
AILOCK: Lock = Lock()
CONSOLE: bool = False

View File

@@ -93,4 +93,4 @@ class Translation:
except Exception as err:
self.Logs.error(f'General Error: {err}')
return {}
return dict()

View File

@@ -11,14 +11,16 @@ class User:
UID_DB: list['MUser'] = []
@property
def get_current_user(self) -> 'MUser':
return self.current_user
def current_user(self) -> 'MUser':
return self._current_user
@current_user.setter
def current_user(self, muser: 'MUser') -> None:
self._current_user = muser
def __init__(self, loader: 'Loader'):
self.Logs = loader.Logs
self.Base = loader.Base
self.current_user: Optional['MUser'] = None
self._ctx = loader
self._current_user: Optional['MUser'] = None
def insert(self, new_user: 'MUser') -> bool:
"""Insert a new User object
@@ -55,7 +57,7 @@ class User:
return False
user_obj.nickname = new_nickname
self.Logs.debug(f"UID ({uid}) has benn update with new nickname ({new_nickname}).")
self._ctx.Logs.debug(f"UID ({uid}) has benn update with new nickname ({new_nickname}).")
return True
def update_mode(self, uidornickname: str, modes: str) -> bool:
@@ -94,7 +96,7 @@ class User:
return False
liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes_liste = [x for x in self._ctx.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste)
user_obj.umodes = f"+{final_umodes}"

View File

@@ -4,7 +4,7 @@ from datetime import datetime
from typing import TYPE_CHECKING, Any, Optional
from ssl import SSLEOFError, SSLError
from core.classes.interfaces.iprotocol import IProtocol
from core.utils import tr
from core.utils import is_coroutinefunction, tr
if TYPE_CHECKING:
from core.definition import MClient, MSasl, MUser, MChannel
@@ -258,6 +258,7 @@ class Unrealircd6(IProtocol):
async def send_set_mode(self, modes: str, *, nickname: Optional[str] = None, channel_name: Optional[str] = None, params: Optional[str] = None) -> None:
"""Set a mode to channel or to a nickname or for a user in a channel
This method will always send as the command as Defender's nickname (service_id)
Args:
modes (str): The selected mode
@@ -478,7 +479,7 @@ class Unrealircd6(IProtocol):
c_uid = client_obj.uid
c_nickname = client_obj.nickname
await self.send2socket(f":{self._ctx.Config.SERVEUR_LINK} SVSLOGIN {self._ctx.Settings.MAIN_SERVER_HOSTNAME} {c_uid} 0")
self.send_svs2mode(c_nickname, '-r')
await self.send_svs2mode(c_nickname, '-r')
except Exception as err:
self._ctx.Logs.error(f'General Error: {err}')
@@ -1020,7 +1021,7 @@ class Unrealircd6(IProtocol):
# Send EOF to other modules
for module in self._ctx.ModuleUtils.model_get_loaded_modules().copy():
module.class_instance.cmd(server_msg_copy)
await module.class_instance.cmd(server_msg_copy) if self._ctx.Utils.is_coroutinefunction(module.class_instance.cmd) else module.class_instance.cmd(server_msg_copy)
# Join saved channels & load existing modules
await self._ctx.Channel.db_join_saved_channels()

View File

@@ -1,3 +1,7 @@
import asyncio
import concurrent
import concurrent.futures
import threading
from datetime import datetime
from json import dumps
from dataclasses import dataclass, field, asdict, fields, replace
@@ -210,6 +214,12 @@ class MConfig(MainModel):
PASSWORD: str = "password"
"""The password of the admin of the service"""
RPC_HOST: str = "127.0.0.1"
"""The host to bind. Default: 127.0.0.1"""
RPC_PORT: int = 5000
"""The port of the defender json rpc. Default: 5000"""
RPC_USERS: list[dict] = field(default_factory=list)
"""The Defender rpc users"""
@@ -344,6 +354,15 @@ class MConfig(MainModel):
self.SERVEUR_CHARSET: list = ["utf-8", "iso-8859-1"]
"""0: utf-8 | 1: iso-8859-1"""
@dataclass
class MThread(MainModel):
name: str
thread_id: Optional[int]
thread_event: Optional[threading.Event]
thread_obj: threading.Thread
executor: concurrent.futures.ThreadPoolExecutor
future: asyncio.Future
@dataclass
class MCommand(MainModel):
module_name: str = None

View File

@@ -1,8 +1,7 @@
import asyncio
import socket
import re
import time
from ssl import SSLSocket
import ssl
import threading
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Any, Optional, Union
from core.classes.modules import rehash
@@ -30,6 +29,10 @@ class Irc:
# Load Context class (Loader)
self.ctx = loader
# Define Reader and Writer
self.reader: Optional[asyncio.StreamReader] = None
self.writer: Optional[asyncio.StreamWriter] = None
# Date et heure de la premiere connexion de Defender
self.defender_connexion_datetime = self.ctx.Config.DEFENDER_CONNEXION_DATETIME
@@ -58,50 +61,56 @@ class Irc:
# Load Commands Utils
# self.Commands = self.Loader.Commands
"""Command utils"""
self.ctx.Commands.build_command(0, 'core', 'help', 'This provide the help')
self.ctx.Commands.build_command(0, 'core', 'auth', 'Login to the IRC Service')
self.ctx.Commands.build_command(0, 'core', 'copyright', 'Give some information about the IRC Service')
self.ctx.Commands.build_command(0, 'core', 'uptime', 'Give you since when the service is connected')
self.ctx.Commands.build_command(0, 'core', 'firstauth', 'First authentication of the Service')
self.ctx.Commands.build_command(0, 'core', 'register', f'Register your nickname /msg {self.ctx.Config.SERVICE_NICKNAME} REGISTER <password> <email>')
self.ctx.Commands.build_command(0, 'core', 'identify', f'Identify yourself with your password /msg {self.ctx.Config.SERVICE_NICKNAME} IDENTIFY <account> <password>')
self.ctx.Commands.build_command(0, 'core', 'logout', 'Reverse the effect of the identify command')
self.ctx.Commands.build_command(1, 'core', 'load', 'Load an existing module')
self.ctx.Commands.build_command(1, 'core', 'unload', 'Unload a module')
self.ctx.Commands.build_command(1, 'core', 'reload', 'Reload a module')
self.ctx.Commands.build_command(1, 'core', 'deauth', 'Deauth from the irc service')
self.ctx.Commands.build_command(1, 'core', 'checkversion', 'Check the version of the irc service')
self.ctx.Commands.build_command(2, 'core', 'show_modules', 'Display a list of loaded modules')
self.ctx.Commands.build_command(2, 'core', 'show_timers', 'Display active timers')
self.ctx.Commands.build_command(2, 'core', 'show_threads', 'Display active threads in the system')
self.ctx.Commands.build_command(2, 'core', 'show_asyncio', 'Display active asyncio')
self.ctx.Commands.build_command(2, 'core', 'show_channels', 'Display a list of active channels')
self.ctx.Commands.build_command(2, 'core', 'show_users', 'Display a list of connected users')
self.ctx.Commands.build_command(2, 'core', 'show_clients', 'Display a list of connected clients')
self.ctx.Commands.build_command(2, 'core', 'show_admins', 'Display a list of administrators')
self.ctx.Commands.build_command(2, 'core', 'show_configuration', 'Display the current configuration settings')
self.ctx.Commands.build_command(2, 'core', 'show_cache', 'Display the current cache')
self.ctx.Commands.build_command(2, 'core', 'clear_cache', 'Clear the cache!')
self.ctx.Commands.build_command(3, 'core', 'quit', 'Disconnect the bot or user from the server.')
self.ctx.Commands.build_command(3, 'core', 'restart', 'Restart the bot or service.')
self.ctx.Commands.build_command(3, 'core', 'addaccess', 'Add a user or entity to an access list with specific permissions.')
self.ctx.Commands.build_command(3, 'core', 'editaccess', 'Modify permissions for an existing user or entity in the access list.')
self.ctx.Commands.build_command(3, 'core', 'delaccess', 'Remove a user or entity from the access list.')
self.ctx.Commands.build_command(3, 'core', 'cert', 'Append your new fingerprint to your account!')
self.ctx.Commands.build_command(4, 'core', 'rehash', 'Reload the configuration file without restarting')
self.ctx.Commands.build_command(4, 'core', 'raw', 'Send a raw command directly to the IRC server')
self.ctx.Commands.build_command(4, 'core', 'print_vars', 'Print users in a file.')
self.ctx.Commands.build_command(4, 'core', 'start_rpc', 'Start defender jsonrpc server')
self.ctx.Commands.build_command(4, 'core', 'stop_rpc', 'Stop defender jsonrpc server')
self.build_command(0, 'core', 'help', 'This provide the help')
self.build_command(0, 'core', 'auth', 'Login to the IRC Service')
self.build_command(0, 'core', 'copyright', 'Give some information about the IRC Service')
self.build_command(0, 'core', 'uptime', 'Give you since when the service is connected')
self.build_command(0, 'core', 'firstauth', 'First authentication of the Service')
self.build_command(0, 'core', 'register', f'Register your nickname /msg {self.ctx.Config.SERVICE_NICKNAME} REGISTER <password> <email>')
self.build_command(0, 'core', 'identify', f'Identify yourself with your password /msg {self.ctx.Config.SERVICE_NICKNAME} IDENTIFY <account> <password>')
self.build_command(0, 'core', 'logout', 'Reverse the effect of the identify command')
self.build_command(1, 'core', 'load', 'Load an existing module')
self.build_command(1, 'core', 'unload', 'Unload a module')
self.build_command(1, 'core', 'reload', 'Reload a module')
self.build_command(1, 'core', 'deauth', 'Deauth from the irc service')
self.build_command(1, 'core', 'checkversion', 'Check the version of the irc service')
self.build_command(2, 'core', 'show_modules', 'Display a list of loaded modules')
self.build_command(2, 'core', 'show_timers', 'Display active timers')
self.build_command(2, 'core', 'show_threads', 'Display active threads in the system')
self.build_command(2, 'core', 'show_asyncio', 'Display active asyncio')
self.build_command(2, 'core', 'show_channels', 'Display a list of active channels')
self.build_command(2, 'core', 'show_users', 'Display a list of connected users')
self.build_command(2, 'core', 'show_clients', 'Display a list of connected clients')
self.build_command(2, 'core', 'show_admins', 'Display a list of administrators')
self.build_command(2, 'core', 'show_configuration', 'Display the current configuration settings')
self.build_command(2, 'core', 'show_cache', 'Display the current cache')
self.build_command(2, 'core', 'clear_cache', 'Clear the cache!')
self.build_command(3, 'core', 'quit', 'Disconnect the bot or user from the server.')
self.build_command(3, 'core', 'restart', 'Restart the bot or service.')
self.build_command(3, 'core', 'addaccess', 'Add a user or entity to an access list with specific permissions.')
self.build_command(3, 'core', 'editaccess', 'Modify permissions for an existing user or entity in the access list.')
self.build_command(3, 'core', 'delaccess', 'Remove a user or entity from the access list.')
self.build_command(3, 'core', 'cert', 'Append your new fingerprint to your account!')
self.build_command(4, 'core', 'rehash', 'Reload the configuration file without restarting')
self.build_command(4, 'core', 'raw', 'Send a raw command directly to the IRC server')
self.build_command(4, 'core', 'print_vars', 'Print users in a file.')
self.build_command(4, 'core', 'start_rpc', 'Start defender jsonrpc server')
self.build_command(4, 'core', 'stop_rpc', 'Stop defender jsonrpc server')
##############################################
# CONNEXION IRC #
##############################################
# Define the IrcSocket object
self.IrcSocket: Optional[Union[socket.socket, SSLSocket]] = None
self.reader: Optional[asyncio.StreamReader] = None
self.writer: Optional[asyncio.StreamWriter] = None
self.ctx.Base.create_asynctask(self.heartbeat(self.beat))
async def run(self):
try:
await self.connect()
await self.listen()
except asyncio.exceptions.IncompleteReadError as ie:
# When IRCd server is down
# asyncio.exceptions.IncompleteReadError: 0 bytes read on a total of undefined expected bytes
self.ctx.Logs.critical(f"The IRCd server is no more connected! {ie}")
except asyncio.exceptions.CancelledError as cerr:
self.ctx.Logs.debug(f"Asyncio CancelledError reached! {cerr}")
async def connect(self):
@@ -116,24 +125,36 @@ class Irc:
await self.Protocol.send_link()
async def listen(self):
self.ctx.Base.create_asynctask(
self.ctx.Base.create_thread_io(
self.ctx.Utils.heartbeat,
self.ctx, self.beat,
run_once=True, thread_flag=True
)
)
while self.signal:
data = await self.reader.readuntil(b'\r\n')
await self.send_response(data.splitlines())
async def run(self):
async def send_response(self, responses:list[bytes]) -> None:
try:
await self.connect()
await self.listen()
except asyncio.exceptions.IncompleteReadError as ie:
# When IRCd server is down
# asyncio.exceptions.IncompleteReadError: 0 bytes read on a total of undefined expected bytes
self.ctx.Logs.critical(f"The IRCd server is no more connected! {ie}")
except asyncio.exceptions.CancelledError as cerr:
self.ctx.Logs.debug(f"Asyncio CancelledError reached! {cerr}")
for data in responses:
response = data.decode(self.CHARSET[0]).split()
await self.cmd(response)
##############################################
# CONNEXION IRC #
##############################################
except (UnicodeEncodeError, UnicodeDecodeError) as ue:
for data in responses:
response = data.decode(self.CHARSET[1], 'replace').split()
await self.cmd(response)
self.ctx.Logs.error(f'UnicodeEncodeError: {ue}')
self.ctx.Logs.error(responses)
except AssertionError as ae:
self.ctx.Logs.error(f"Assertion error : {ae}")
# --------------------------------------------
# FIN CONNEXION IRC #
# --------------------------------------------
def init_service_user(self) -> None:
@@ -157,51 +178,6 @@ class Irc:
chan = chan_name[0]
await self.Protocol.send_sjoin(channel=chan)
async def send_response(self, responses:list[bytes]) -> None:
try:
for data in responses:
response = data.decode(self.CHARSET[0]).split()
await self.cmd(response)
except UnicodeEncodeError as ue:
for data in responses:
response = data.decode(self.CHARSET[1],'replace').split()
await self.cmd(response)
self.ctx.Logs.error(f'UnicodeEncodeError: {ue}')
self.ctx.Logs.error(response)
except UnicodeDecodeError as ud:
for data in responses:
response = data.decode(self.CHARSET[1],'replace').split()
await self.cmd(response)
self.ctx.Logs.error(f'UnicodeDecodeError: {ud}')
self.ctx.Logs.error(response)
except AssertionError as ae:
self.ctx.Logs.error(f"Assertion error : {ae}")
def unload(self) -> None:
# This is only to reference the method
return None
# --------------------------------------------
# FIN CONNEXION IRC #
# --------------------------------------------
def build_command(self, level: int, module_name: str, command_name: str, command_description: str) -> None:
"""This method build the commands variable
Args:
level (int): The Level of the command
module_name (str): The module name
command_name (str): The command name
command_description (str): The description of the command
"""
# Build Model.
self.ctx.Commands.build(self.ctx.Definition.MCommand(module_name, command_name, command_description, level))
return None
async def generate_help_menu(self, nickname: str, module: Optional[str] = None) -> None:
# Check if the nickname is an admin
@@ -289,17 +265,6 @@ class Irc:
return uptime
async def heartbeat(self, beat: float) -> None:
"""Execute certaines commandes de nettoyage toutes les x secondes
x étant définit a l'initialisation de cette class (self.beat)
Args:
beat (float): Nombre de secondes entre chaque exécution
"""
while self.hb_active:
await asyncio.sleep(beat)
self.ctx.Base.execute_periodic_action()
def insert_db_admin(self, uid: str, account: str, level: int, language: str) -> None:
user_obj = self.ctx.User.get_user(uid)
@@ -382,8 +347,13 @@ class Irc:
async def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.ctx.Config.SERVICE_NICKNAME
response = self.ctx.Base.create_asynctask(
self.ctx.Base.create_thread_io(
self.ctx.Base.check_for_new_version, True
)
)
if self.ctx.Base.check_for_new_version(True):
if response:
await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" New Version available : {self.ctx.Config.CURRENT_VERSION} >>> {self.ctx.Config.LATEST_VERSION}")
await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" Please run (git pull origin main) in the current folder")
else:
@@ -413,12 +383,6 @@ class Irc:
for module in modules:
await module.class_instance.cmd(original_response) if self.ctx.Utils.is_coroutinefunction(module.class_instance.cmd) else module.class_instance.cmd(original_response)
# if len(original_response) > 2:
# if original_response[2] != 'UID':
# # Envoyer la commande aux classes dynamiquement chargées
# for module in self.ctx.ModuleUtils.model_get_loaded_modules().copy():
# module.class_instance.cmd(original_response)
except IndexError as ie:
self.ctx.Logs.error(f"IndexError: {ie}")
except Exception as err:
@@ -441,7 +405,7 @@ class Irc:
if u is None:
return None
c = self.ctx.Client.get_Client(u.uid)
c = self.ctx.Client.get_client(u.uid)
"""The Client Object"""
fromuser = u.nickname
@@ -627,7 +591,7 @@ class Irc:
level = self.ctx.Base.int_if_possible(cmd[2])
password = str(cmd[3])
self.create_defender_user(fromuser, new_admin, level, password)
await self.create_defender_user(fromuser, new_admin, level, password)
return None
except IndexError as ie:
@@ -982,11 +946,10 @@ class Irc:
try:
final_reason = ' '.join(cmd[1:])
self.hb_active = False
await self.ctx.Base.shutdown()
await rehash.shutdown(self.ctx)
self.ctx.Base.execute_periodic_action()
for chan_name in self.ctx.Channel.UID_CHANNEL_DB:
# self.Protocol.send_mode_chan(chan_name.name, '-l')
await self.Protocol.send_set_mode('-l', channel_name=chan_name.name)
for client in self.ctx.Client.CLIENT_DB:
@@ -1002,7 +965,6 @@ class Irc:
self.ctx.Logs.info(f'Arrêt du server {dnickname}')
self.ctx.Config.DEFENDER_RESTART = 0
await self.writer.drain()
self.writer.close()
await self.writer.wait_closed()
@@ -1011,6 +973,8 @@ class Irc:
except ConnectionResetError:
if self.writer.is_closing():
self.ctx.Logs.debug(f"Defender stopped properly!")
except ssl.SSLError as serr:
self.ctx.Logs.error(f"Defender has ended with an SSL Error! - {serr}")
case 'restart':
final_reason = ' '.join(cmd[1:])
@@ -1080,6 +1044,13 @@ class Irc:
msg=f">> {thread.name} ({thread.is_alive()})"
)
for thread in threading.enumerate():
await self.Protocol.send_notice(
nick_from=dnickname,
nick_to=fromuser,
msg=f">> Thread name: {thread.name} - Is alive: {thread.is_alive()} - Daemon: {thread.daemon}"
)
return None
case 'show_asyncio':
@@ -1209,10 +1180,10 @@ class Irc:
return None
case 'start_rpc':
self.ctx.Base.create_asynctask(self.ctx.RpcServer.start_server())
self.ctx.Base.create_asynctask(self.ctx.RpcServer.start_rpc_server())
case 'stop_rpc':
self.ctx.Base.create_asynctask(self.ctx.RpcServer.stop_server())
self.ctx.Base.create_asynctask(self.ctx.RpcServer.stop_rpc_server())
case _:
pass

View File

@@ -81,7 +81,7 @@ class Loader:
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self)
self.RpcServer: rpc_mod.JSonRpcServer = rpc_mod.JSonRpcServer(self, hostname='0.0.0.0')
self.RpcServer: rpc_mod.JSonRpcServer = rpc_mod.JSonRpcServer(self)
self.Logs.debug(self.Utils.tr("Loader %s success", __name__))

View File

@@ -3,20 +3,20 @@ Main utils library.
"""
import gc
import ssl
import socket
import sys
from pathlib import Path
from re import match, sub
import threading
from typing import Literal, Optional, Any, TYPE_CHECKING
from datetime import datetime
from time import time
from time import time, sleep
from random import choice
from hashlib import md5, sha3_512
from core.classes.modules.settings import global_settings
from asyncio import iscoroutinefunction
if TYPE_CHECKING:
from core.irc import Irc
from threading import Event
from core.loader import Loader
def tr(message: str, *args) -> str:
"""Translation Engine system
@@ -115,39 +115,6 @@ def get_ssl_context() -> ssl.SSLContext:
ctx.verify_mode = ssl.CERT_NONE
return ctx
def create_socket(uplink: 'Irc') -> None:
"""Create a socket to connect SSL or Normal connection
"""
try:
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
connexion_information = (uplink.Config.SERVEUR_IP, uplink.Config.SERVEUR_PORT)
if uplink.Config.SERVEUR_SSL:
# Create SSL Context object
ssl_context = get_ssl_context()
ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=uplink.Config.SERVEUR_HOSTNAME)
ssl_connexion.connect(connexion_information)
uplink.IrcSocket = ssl_connexion
uplink.Config.SSL_VERSION = uplink.IrcSocket.version()
uplink.Logs.info(f"-- Connected using SSL : Version = {uplink.Config.SSL_VERSION}")
else:
soc.connect(connexion_information)
uplink.IrcSocket = soc
uplink.Logs.info("-- Connected in a normal mode!")
return None
except (ssl.SSLEOFError, ssl.SSLError) as soe:
uplink.Logs.critical(f"[SSL ERROR]: {soe}")
except OSError as oe:
uplink.Logs.critical(f"[OS Error]: {oe}")
if 'connection refused' in str(oe).lower():
sys.exit(oe.__str__())
if oe.errno == 10053:
sys.exit(oe.__str__())
except AttributeError as ae:
uplink.Logs.critical(f"AttributeError: {ae}")
def run_python_garbage_collector() -> int:
"""Run Python garbage collector
@@ -167,6 +134,21 @@ def get_number_gc_objects(your_object_to_count: Optional[Any] = None) -> int:
return sum(1 for obj in gc.get_objects() if isinstance(obj, your_object_to_count))
def heartbeat(event: 'Event', loader: 'Loader', beat: float) -> None:
"""Execute certaines commandes de nettoyage toutes les x secondes
x étant définit a l'initialisation de cette class (self.beat)
Args:
beat (float): Nombre de secondes entre chaque exécution
"""
while event.is_set():
loader.Base.execute_periodic_action()
sleep(beat)
loader.Logs.debug("Heartbeat is off!")
return None
def generate_random_string(lenght: int) -> str:
"""Retourn une chaîne aléatoire en fonction de la longueur spécifiée.

View File

@@ -1,6 +1,5 @@
import asyncio
from core import install
#############################################
# @Version : 6.4 #
# Requierements : #
@@ -19,4 +18,4 @@ async def main():
await loader.Irc.run()
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main(), debug=False)

View File

@@ -73,7 +73,7 @@ class Clone(IModule):
self.ctx.Logs.debug(f"Cache Size = {self.ctx.Settings.get_cache_size()}")
# Créer les nouvelles commandes du module
self.ctx.Irc.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
self.ctx.Commands.build_command(1, self.module_name, 'clone', 'Connect, join, part, kill and say clones')
await self.ctx.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.ctx.Config.CLONE_CHANNEL)
await self.ctx.Irc.Protocol.send_sjoin(self.ctx.Config.CLONE_CHANNEL)

View File

@@ -65,56 +65,56 @@ class Command(IModule):
for c in new_cmds:
self.ctx.Irc.Protocol.known_protocol.add(c)
self.ctx.Irc.build_command(2, self.module_name, 'join', 'Join a channel')
self.ctx.Irc.build_command(2, self.module_name, 'assign', 'Assign a user to a role or task')
self.ctx.Irc.build_command(2, self.module_name, 'part', 'Leave a channel')
self.ctx.Irc.build_command(2, self.module_name, 'unassign', 'Remove a user from a role or task')
self.ctx.Irc.build_command(2, self.module_name, 'owner', 'Give channel ownership to a user')
self.ctx.Irc.build_command(2, self.module_name, 'deowner', 'Remove channel ownership from a user')
self.ctx.Irc.build_command(2, self.module_name, 'protect', 'Protect a user from being kicked')
self.ctx.Irc.build_command(2, self.module_name, 'deprotect', 'Remove protection from a user')
self.ctx.Irc.build_command(2, self.module_name, 'op', 'Grant operator privileges to a user')
self.ctx.Irc.build_command(2, self.module_name, 'deop', 'Remove operator privileges from a user')
self.ctx.Irc.build_command(1, self.module_name, 'halfop', 'Grant half-operator privileges to a user')
self.ctx.Irc.build_command(1, self.module_name, 'dehalfop', 'Remove half-operator privileges from a user')
self.ctx.Irc.build_command(1, self.module_name, 'voice', 'Grant voice privileges to a user')
self.ctx.Irc.build_command(1, self.module_name, 'devoice', 'Remove voice privileges from a user')
self.ctx.Irc.build_command(1, self.module_name, 'topic', 'Change the topic of a channel')
self.ctx.Irc.build_command(2, self.module_name, 'opall', 'Grant operator privileges to all users')
self.ctx.Irc.build_command(2, self.module_name, 'deopall', 'Remove operator privileges from all users')
self.ctx.Irc.build_command(2, self.module_name, 'devoiceall', 'Remove voice privileges from all users')
self.ctx.Irc.build_command(2, self.module_name, 'voiceall', 'Grant voice privileges to all users')
self.ctx.Irc.build_command(2, self.module_name, 'ban', 'Ban a user from a channel')
self.ctx.Irc.build_command(2, self.module_name, 'automode', 'Automatically set user modes upon join')
self.ctx.Irc.build_command(2, self.module_name, 'unban', 'Remove a ban from a user')
self.ctx.Irc.build_command(2, self.module_name, 'kick', 'Kick a user from a channel')
self.ctx.Irc.build_command(2, self.module_name, 'kickban', 'Kick and ban a user from a channel')
self.ctx.Irc.build_command(2, self.module_name, 'umode', 'Set user mode')
self.ctx.Irc.build_command(2, self.module_name, 'mode', 'Set channel mode')
self.ctx.Irc.build_command(2, self.module_name, 'get_mode', 'Retrieve current channel mode')
self.ctx.Irc.build_command(2, self.module_name, 'svsjoin', 'Force a user to join a channel')
self.ctx.Irc.build_command(2, self.module_name, 'svspart', 'Force a user to leave a channel')
self.ctx.Irc.build_command(2, self.module_name, 'svsnick', 'Force a user to change their nickname')
self.ctx.Irc.build_command(2, self.module_name, 'wallops', 'Send a message to all operators')
self.ctx.Irc.build_command(2, self.module_name, 'globops', 'Send a global operator message')
self.ctx.Irc.build_command(2, self.module_name, 'gnotice', 'Send a global notice')
self.ctx.Irc.build_command(2, self.module_name, 'whois', 'Get information about a user')
self.ctx.Irc.build_command(2, self.module_name, 'names', 'List users in a channel')
self.ctx.Irc.build_command(2, self.module_name, 'invite', 'Invite a user to a channel')
self.ctx.Irc.build_command(2, self.module_name, 'inviteme', 'Invite yourself to a channel')
self.ctx.Irc.build_command(2, self.module_name, 'sajoin', 'Force yourself into a channel')
self.ctx.Irc.build_command(2, self.module_name, 'sapart', 'Force yourself to leave a channel')
self.ctx.Irc.build_command(2, self.module_name, 'kill', 'Disconnect a user from the server')
self.ctx.Irc.build_command(2, self.module_name, 'gline', 'Ban a user from the entire server')
self.ctx.Irc.build_command(2, self.module_name, 'ungline', 'Remove a global server ban')
self.ctx.Irc.build_command(2, self.module_name, 'kline', 'Ban a user based on their hostname')
self.ctx.Irc.build_command(2, self.module_name, 'unkline', 'Remove a K-line ban')
self.ctx.Irc.build_command(2, self.module_name, 'shun', 'Prevent a user from sending messages')
self.ctx.Irc.build_command(2, self.module_name, 'unshun', 'Remove a shun from a user')
self.ctx.Irc.build_command(2, self.module_name, 'glinelist', 'List all global bans')
self.ctx.Irc.build_command(2, self.module_name, 'shunlist', 'List all shunned users')
self.ctx.Irc.build_command(2, self.module_name, 'klinelist', 'List all K-line bans')
self.ctx.Irc.build_command(3, self.module_name, 'map', 'Show the server network map')
self.ctx.Commands.build_command(2, self.module_name, 'join', 'Join a channel')
self.ctx.Commands.build_command(2, self.module_name, 'assign', 'Assign a user to a role or task')
self.ctx.Commands.build_command(2, self.module_name, 'part', 'Leave a channel')
self.ctx.Commands.build_command(2, self.module_name, 'unassign', 'Remove a user from a role or task')
self.ctx.Commands.build_command(2, self.module_name, 'owner', 'Give channel ownership to a user')
self.ctx.Commands.build_command(2, self.module_name, 'deowner', 'Remove channel ownership from a user')
self.ctx.Commands.build_command(2, self.module_name, 'protect', 'Protect a user from being kicked')
self.ctx.Commands.build_command(2, self.module_name, 'deprotect', 'Remove protection from a user')
self.ctx.Commands.build_command(2, self.module_name, 'op', 'Grant operator privileges to a user')
self.ctx.Commands.build_command(2, self.module_name, 'deop', 'Remove operator privileges from a user')
self.ctx.Commands.build_command(1, self.module_name, 'halfop', 'Grant half-operator privileges to a user')
self.ctx.Commands.build_command(1, self.module_name, 'dehalfop', 'Remove half-operator privileges from a user')
self.ctx.Commands.build_command(1, self.module_name, 'voice', 'Grant voice privileges to a user')
self.ctx.Commands.build_command(1, self.module_name, 'devoice', 'Remove voice privileges from a user')
self.ctx.Commands.build_command(1, self.module_name, 'topic', 'Change the topic of a channel')
self.ctx.Commands.build_command(2, self.module_name, 'opall', 'Grant operator privileges to all users')
self.ctx.Commands.build_command(2, self.module_name, 'deopall', 'Remove operator privileges from all users')
self.ctx.Commands.build_command(2, self.module_name, 'devoiceall', 'Remove voice privileges from all users')
self.ctx.Commands.build_command(2, self.module_name, 'voiceall', 'Grant voice privileges to all users')
self.ctx.Commands.build_command(2, self.module_name, 'ban', 'Ban a user from a channel')
self.ctx.Commands.build_command(2, self.module_name, 'automode', 'Automatically set user modes upon join')
self.ctx.Commands.build_command(2, self.module_name, 'unban', 'Remove a ban from a user')
self.ctx.Commands.build_command(2, self.module_name, 'kick', 'Kick a user from a channel')
self.ctx.Commands.build_command(2, self.module_name, 'kickban', 'Kick and ban a user from a channel')
self.ctx.Commands.build_command(2, self.module_name, 'umode', 'Set user mode')
self.ctx.Commands.build_command(2, self.module_name, 'mode', 'Set channel mode')
self.ctx.Commands.build_command(2, self.module_name, 'get_mode', 'Retrieve current channel mode')
self.ctx.Commands.build_command(2, self.module_name, 'svsjoin', 'Force a user to join a channel')
self.ctx.Commands.build_command(2, self.module_name, 'svspart', 'Force a user to leave a channel')
self.ctx.Commands.build_command(2, self.module_name, 'svsnick', 'Force a user to change their nickname')
self.ctx.Commands.build_command(2, self.module_name, 'wallops', 'Send a message to all operators')
self.ctx.Commands.build_command(2, self.module_name, 'globops', 'Send a global operator message')
self.ctx.Commands.build_command(2, self.module_name, 'gnotice', 'Send a global notice')
self.ctx.Commands.build_command(2, self.module_name, 'whois', 'Get information about a user')
self.ctx.Commands.build_command(2, self.module_name, 'names', 'List users in a channel')
self.ctx.Commands.build_command(2, self.module_name, 'invite', 'Invite a user to a channel')
self.ctx.Commands.build_command(2, self.module_name, 'inviteme', 'Invite yourself to a channel')
self.ctx.Commands.build_command(2, self.module_name, 'sajoin', 'Force yourself into a channel')
self.ctx.Commands.build_command(2, self.module_name, 'sapart', 'Force yourself to leave a channel')
self.ctx.Commands.build_command(2, self.module_name, 'kill', 'Disconnect a user from the server')
self.ctx.Commands.build_command(2, self.module_name, 'gline', 'Ban a user from the entire server')
self.ctx.Commands.build_command(2, self.module_name, 'ungline', 'Remove a global server ban')
self.ctx.Commands.build_command(2, self.module_name, 'kline', 'Ban a user based on their hostname')
self.ctx.Commands.build_command(2, self.module_name, 'unkline', 'Remove a K-line ban')
self.ctx.Commands.build_command(2, self.module_name, 'shun', 'Prevent a user from sending messages')
self.ctx.Commands.build_command(2, self.module_name, 'unshun', 'Remove a shun from a user')
self.ctx.Commands.build_command(2, self.module_name, 'glinelist', 'List all global bans')
self.ctx.Commands.build_command(2, self.module_name, 'shunlist', 'List all shunned users')
self.ctx.Commands.build_command(2, self.module_name, 'klinelist', 'List all K-line bans')
self.ctx.Commands.build_command(3, self.module_name, 'map', 'Show the server network map')
def unload(self) -> None:
self.ctx.Commands.drop_command_by_module(self.module_name)
@@ -214,7 +214,7 @@ class Command(IModule):
user_uid = self.ctx.User.clean_uid(cmd[5])
userObj: MUser = self.ctx.User.get_user(user_uid)
channel_name = cmd[4] if self.ctx.Channel.is_valid_channel(cmd[4]) else None
client_obj = self.ctx.Client.get_Client(user_uid)
client_obj = self.ctx.Client.get_client(user_uid)
nickname = userObj.nickname if userObj is not None else None
if client_obj is not None:

View File

@@ -1,4 +1,5 @@
from dataclasses import dataclass
import logging
from typing import Any, TYPE_CHECKING, Optional
from core.classes.interfaces.imodule import IModule
import mods.defender.schemas as schemas
@@ -26,6 +27,8 @@ class Defender(IModule):
def __init__(self, context: 'Loader') -> None:
super().__init__(context)
self._mod_config: Optional[schemas.ModConfModel] = None
self.Schemas = schemas.RepDB()
self.Threads = thds
@property
def mod_config(self) -> ModConfModel:
@@ -55,40 +58,43 @@ class Defender(IModule):
return None
async def load(self):
# Variable qui va contenir les options de configuration du module Defender
self._mod_config: schemas.ModConfModel = self.ModConfModel()
# sync the database with local variable (Mandatory)
await self.sync_db()
# Add module schemas
self.Schemas = schemas
# Add module utils functions
self.mod_utils = utils
# Create module commands (Mandatory)
self.ctx.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access')
self.ctx.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
self.ctx.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
self.ctx.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
self.ctx.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
self.ctx.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
self.ctx.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
self.ctx.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
self.ctx.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
self.ctx.Commands.build_command(0, self.module_name, 'code', 'Display the code or key for access')
self.ctx.Commands.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
self.ctx.Commands.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
self.ctx.Commands.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
self.ctx.Commands.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
self.ctx.Commands.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
self.ctx.Commands.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
self.ctx.Commands.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
self.ctx.Commands.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
self.timeout = self.ctx.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API
self.Schemas.DB_ABUSEIPDB_USERS = self.Schemas.DB_FREEIPAPI_USERS = self.Schemas.DB_CLOUDFILT_USERS = []
self.Schemas.DB_PSUTIL_USERS = self.Schemas.DB_LOCALSCAN_USERS = []
self.Schemas.DB_ABUSEIPDB_USERS = []
self.Schemas.DB_FREEIPAPI_USERS = []
self.Schemas.DB_CLOUDFILT_USERS = []
self.Schemas.DB_PSUTIL_USERS = []
self.Schemas.DB_LOCALSCAN_USERS = []
# Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning = self.freeipapi_isRunning = self.cloudfilt_isRunning = True
self.psutil_isRunning = self.localscan_isRunning = self.reputationTimer_isRunning = True
self.autolimit_isRunning = True
self.abuseipdb_isRunning = True if self.mod_config.abuseipdb_scan == 1 else False
self.freeipapi_isRunning = True if self.mod_config.freeipapi_scan == 1 else False
self.cloudfilt_isRunning = True if self.mod_config.cloudfilt_scan == 1 else False
self.psutil_isRunning = True if self.mod_config.psutil_scan == 1 else False
self.localscan_isRunning = True if self.mod_config.local_scan == 1 else False
self.reputationTimer_isRunning = True if self.mod_config.reputation == 1 else False
self.autolimit_isRunning = True if self.mod_config.autolimit == 1 else False
# Variable qui va contenir les users
self.flood_system = {}
@@ -101,19 +107,21 @@ class Defender(IModule):
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg'
# Démarrer les threads pour démarrer les api
self.ctx.Base.create_asynctask(thds.coro_freeipapi_scan(self))
self.ctx.Base.create_asynctask(thds.coro_cloudfilt_scan(self))
self.ctx.Base.create_asynctask(thds.coro_abuseipdb_scan(self))
self.ctx.Base.create_asynctask(thds.coro_local_scan(self))
self.ctx.Base.create_asynctask(thds.coro_psutil_scan(self))
self.ctx.Base.create_asynctask(thds.coro_apply_reputation_sanctions(self))
if self.mod_config.autolimit == 1:
self.ctx.Base.create_asynctask(thds.coro_autolimit(self))
self.ctx.Base.create_asynctask(self.Threads.coro_freeipapi_scan(self)) if self.mod_config.freeipapi_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_cloudfilt_scan(self)) if self.mod_config.cloudfilt_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_abuseipdb_scan(self)) if self.mod_config.abuseipdb_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_local_scan(self)) if self.mod_config.local_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_psutil_scan(self)) if self.mod_config.psutil_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_apply_reputation_sanctions(self)) if self.mod_config.reputation == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_autolimit(self)) if self.mod_config.autolimit == 1 else None
if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_sjoin(self.ctx.Config.SALON_JAIL)
await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_NICKNAME} SAMODE {self.ctx.Config.SALON_JAIL} +o {self.ctx.Config.SERVICE_NICKNAME}")
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != self.ctx.Config.SALON_JAIL:
await self.ctx.Irc.Protocol.send_set_mode('+b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode('+eee', channel_name=chan.name, params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users')
def __onload(self):
@@ -138,7 +146,7 @@ class Defender(IModule):
if localscan:
self.Schemas.DB_LOCALSCAN_USERS = localscan
def unload(self) -> None:
async def unload(self) -> None:
"""Cette methode sera executée a chaque désactivation ou
rechargement de module
"""
@@ -158,6 +166,13 @@ class Defender(IModule):
self.ctx.Commands.drop_command_by_module(self.module_name)
if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_part_chan(self.ctx.Config.SERVICE_ID, self.ctx.Config.SALON_JAIL)
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != self.ctx.Config.SALON_JAIL:
await self.ctx.Irc.Protocol.send_set_mode('-b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode('-eee', channel_name=chan.name, params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users')
return None
async def insert_db_trusted(self, uid: str, nickname:str) -> None:
@@ -411,22 +426,26 @@ class Defender(IModule):
if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Already activated", channel=dchanlog)
return False
return None
# self.update_db_configuration('reputation', 1)
await self.update_configuration(key, 1)
self.ctx.Base.create_asynctask(self.Threads.coro_apply_reputation_sanctions(self))
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog)
await self.ctx.Irc.Protocol.send_join_chan(uidornickname=dnickname, channel=jail_chan)
await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}")
await self.ctx.Irc.Protocol.send_set_mode(f'+{jail_chan_mode}', channel_name=jail_chan)
if self.mod_config.reputation_sg == 1:
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan:
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
await self.ctx.Irc.Protocol.send_set_mode('+b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode(
'+eee',
channel_name=chan.name,
params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users'
)
await self.ctx.Channel.db_query_channel('add', self.module_name, jail_chan)
@@ -441,20 +460,26 @@ class Defender(IModule):
return False
await self.update_configuration(key, 0)
self.reputationTimer_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"[ {self.ctx.Config.COLORS.red}REPUTATION{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}",
channel=dchanlog
)
await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} -sS")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} PART {jail_chan}")
await self.ctx.Irc.Protocol.send_set_mode('-sS', channel_name=jail_chan)
await self.ctx.Irc.Protocol.send_part_chan(service_id, jail_chan)
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan:
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -b ~security-group:unknown-users")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
await self.ctx.Irc.Protocol.send_set_mode('-b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode(
'-eee',
channel_name=chan.name,
params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users'
)
await self.ctx.Channel.db_query_channel('del', self.module_name, jail_chan)
@@ -464,12 +489,17 @@ class Defender(IModule):
match get_options:
case 'release':
# .reputation release [nick]
p = await self.ctx.Irc.Protocol
link = self.ctx.Config.SERVEUR_LINK
jailed_salon = self.ctx.Config.SALON_JAIL
welcome_salon = self.ctx.Config.SALON_LIBERER
client_obj = self.ctx.User.get_user(str(cmd[2]))
if self.mod_config.reputation != 1:
await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser,
msg="The reputation system is not activated!")
return None
if client_obj is None:
await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser,
@@ -658,6 +688,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_local_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -667,6 +698,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.localscan_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -676,6 +708,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_psutil_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -685,6 +718,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.psutil_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -694,6 +728,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_abuseipdb_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -703,6 +738,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.abuseipdb_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -712,6 +748,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_freeipapi_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -721,6 +758,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.freeipapi_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -730,6 +768,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_cloudfilt_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -739,6 +778,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.cloudfilt_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -915,3 +955,6 @@ class Defender(IModule):
await self.join_saved_channels()
return None
case _:
pass

View File

@@ -28,6 +28,8 @@ class FloodUser(MainModel):
nbr_msg: int = 0
first_msg_time: int = 0
class RepDB:
DB_FLOOD_USERS: list[FloodUser] = []
DB_ABUSEIPDB_USERS: list[MUser] = []
DB_FREEIPAPI_USERS: list[MUser] = []

View File

@@ -1,89 +1,251 @@
import asyncio
from typing import TYPE_CHECKING
from time import sleep
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from mods.defender.mod_defender import Defender
async def coro_apply_reputation_sanctions(uplink: 'Defender'):
uplink.reputationTimer_isRunning = True
while uplink.reputationTimer_isRunning:
await uplink.mod_utils.action_apply_reputation_santions(uplink)
await asyncio.sleep(5)
async def coro_cloudfilt_scan(uplink: 'Defender'):
uplink.cloudfilt_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.cloudfilt_isRunning:
try:
list_to_remove:list = []
for user in uplink.Schemas.DB_CLOUDFILT_USERS:
await uplink.mod_utils.action_scan_client_with_cloudfilt(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
result: Optional[dict] = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_cloudfilt,
uplink, user
)
list_to_remove.append(user)
if not result:
continue
remote_ip = user.remote_ip
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
r_host = result.get('host', None)
r_countryiso = result.get('countryiso', None)
r_listed = result.get('listed', False)
r_listedby = result.get('listed_by', None)
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}CLOUDFILT_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {r_host} | country: {r_countryiso} | listed: {r_listed} | listed by : {r_listedby}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN] ({fullname}) connected from ({r_countryiso}), Listed: {r_listed}, by: {r_listedby}")
if r_listed:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} Your connexion is listed as dangerous {r_listed} {r_listedby} - detected by cloudfilt")
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN GLINE] Dangerous connection ({fullname}) from ({r_countryiso}) Listed: {r_listed}, by: {r_listedby}")
await asyncio.sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_CLOUDFILT_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_freeipapi_scan(uplink: 'Defender'):
uplink.freeipapi_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.freeipapi_isRunning:
try:
list_to_remove: list = []
for user in uplink.Schemas.DB_FREEIPAPI_USERS:
await uplink.mod_utils.action_scan_client_with_freeipapi(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
result: Optional[dict] = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_freeipapi,
uplink, user
)
if not result:
continue
# pseudo!ident@host
remote_ip = user.remote_ip
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}FREEIPAPI_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN] ({fullname}) connected from ({result['countryCode']}), Proxy: {result['isProxy']}")
if result['isProxy']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN GLINE] Server do not allow proxy connexions {result['isProxy']}")
list_to_remove.append(user)
await asyncio.sleep(1)
# remove users from the list
for user_model in list_to_remove:
uplink.Schemas.DB_FREEIPAPI_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_abuseipdb_scan(uplink: 'Defender'):
while uplink.abuseipdb_isRunning:
uplink.abuseipdb_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.abuseipdb_isRunning:
try:
list_to_remove: list = []
print(uplink.Schemas.DB_ABUSEIPDB_USERS)
for user in uplink.Schemas.DB_ABUSEIPDB_USERS:
await uplink.mod_utils.action_scan_client_with_abuseipdb(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
result: Optional[dict] = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_abuseipdb,
uplink, user
)
list_to_remove.append(user)
if not result:
continue
remote_ip = user.remote_ip
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}ABUSEIPDB_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}",
channel=service_chanlog
)
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN] ({fullname}) connected from ({result['country']}), Score: {result['score']}, Tor: {result['isTor']}")
if result['isTor']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not allow Tor connections Tor: {result['isTor']}, Score: {result['score']}")
elif result['score'] >= 95:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not high risk connections Country: {result['country']}, Score: {result['score']}")
await asyncio.sleep(1)
print(list_to_remove)
for user_model in list_to_remove:
uplink.Schemas.DB_ABUSEIPDB_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}", exc_info=True)
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}", exc_info=True)
async def coro_local_scan(uplink: 'Defender'):
uplink.localscan_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.localscan_isRunning:
try:
list_to_remove:list = []
for user in uplink.Schemas.DB_LOCALSCAN_USERS:
await uplink.mod_utils.action_scan_client_with_local_socket(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
list_to_remove.append(user)
result = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_local_socket,
uplink, user
)
if not result:
continue
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
opened_ports = result['opened_ports']
closed_ports = result['closed_ports']
if opened_ports:
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}LOCAL_SCAN{nogc} ] {fullname} ({user.remote_ip}) : The Port(s) {opened_ports} are opened on this remote ip [{user.remote_ip}]",
channel=service_chanlog
)
if closed_ports:
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}LOCAL_SCAN{nogc} ] {fullname} ({user.remote_ip}) : The Port(s) {closed_ports} are closed on this remote ip [{user.remote_ip}]",
channel=service_chanlog
)
await asyncio.sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_LOCALSCAN_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_psutil_scan(uplink: 'Defender'):
uplink.psutil_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.psutil_isRunning:
try:
list_to_remove:list = []
for user in uplink.Schemas.DB_PSUTIL_USERS:
await uplink.mod_utils.action_scan_client_with_psutil(uplink, user)
result = await uplink.ctx.Base.create_thread_io(uplink.mod_utils.action_scan_client_with_psutil, uplink, user)
list_to_remove.append(user)
if not result:
continue
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}PSUTIL_SCAN{nogc} ] {fullname} ({user.remote_ip}) is using ports {result}",
channel=service_chanlog
)
await asyncio.sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_PSUTIL_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_autolimit(uplink: 'Defender'):
@@ -104,7 +266,6 @@ async def coro_autolimit(uplink: 'Defender'):
chan_list: list[str] = [c.name for c in uplink.ctx.Channel.UID_CHANNEL_DB]
while uplink.autolimit_isRunning:
if uplink.mod_config.autolimit == 0:
uplink.ctx.Logs.debug("autolimit deactivated ... stopping the current thread")
break
@@ -112,7 +273,7 @@ async def coro_autolimit(uplink: 'Defender'):
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
for chan_copy in chanObj_copy:
if chan_copy["name"] == chan.name and len(chan.uids) != chan_copy["uids_count"]:
await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}")
await p.send_set_mode('+l', channel_name=chan.name, params=len(chan.uids) + uplink.mod_config.autolimit_amount)
chan_copy["uids_count"] = len(chan.uids)
if chan.name not in chan_list:
@@ -128,13 +289,13 @@ async def coro_autolimit(uplink: 'Defender'):
# Si c'est la premiere execution
if INIT == 1:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}")
await p.send_set_mode('+l', channel_name=chan.name, params=len(chan.uids) + uplink.mod_config.autolimit_amount)
# Si le nouveau amount est différent de l'initial
if init_amount != uplink.mod_config.autolimit_amount:
init_amount = uplink.mod_config.autolimit_amount
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}")
await p.send_set_mode('+l', channel_name=chan.name, params=len(chan.uids) + uplink.mod_config.autolimit_amount)
INIT = 0
@@ -142,7 +303,6 @@ async def coro_autolimit(uplink: 'Defender'):
await asyncio.sleep(uplink.mod_config.autolimit_interval)
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
# await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} -l")
await p.send_set_mode('-l', channel_name=chan.name)
uplink.ctx.Irc.autolimit_started = False
@@ -158,7 +318,6 @@ async def coro_release_mode_mute(uplink: 'Defender', action: str, channel: str):
channel (str): The related channel
"""
service_id = uplink.ctx.Config.SERVICE_ID
timeout = uplink.mod_config.flood_timer
await asyncio.sleep(timeout)
@@ -169,6 +328,6 @@ async def coro_release_mode_mute(uplink: 'Defender', action: str, channel: str):
match action:
case 'mode-m':
# Action -m sur le salon
await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel} -m")
await uplink.ctx.Irc.Protocol.send_set_mode('-m', channel_name=channel)
case _:
pass

View File

@@ -267,6 +267,7 @@ async def handle_on_uid(uplink: 'Defender', srvmsg: list[str]):
####################
# [:<sid>] UID <uid> <ts> <nick> <real-host> <displayed-host> <real-user> <ip> <signon> <modes> [<mode-parameters>]+ :<real>
# [:<sid>] UID nickname hopcount timestamp username hostname uid servicestamp umodes virthost cloakedhost ip :gecos
async def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
confmodel = uplink.mod_config
@@ -316,17 +317,16 @@ async def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
fu.nbr_msg = 0
get_diff_secondes = unixtime - fu.first_msg_time
elif fu.nbr_msg > flood_message:
uplink.ctx.Logs.info('system de flood detecté')
await p.send_set_mode('+m', channel_name=channel)
await p.send_priv_msg(
nick_from=dnickname,
msg=f"{color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)",
channel=channel
)
await p.send2socket(f":{service_id} MODE {channel} +m")
uplink.ctx.Logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
uplink.ctx.Logs.debug(f'[FLOOD] {get_detected_nickname} triggered +m mode on the channel {channel}')
fu.nbr_msg = 0
fu.first_msg_time = unixtime
uplink.ctx.Base.create_asynctask(dthreads.coro_release_mode_mute(uplink, 'mode-m', channel))
uplink.ctx.Base.create_asynctask(uplink.Threads.coro_release_mode_mute(uplink, 'mode-m', channel))
async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
@@ -336,10 +336,14 @@ async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
confmodel = uplink.mod_config
get_reputation = uplink.ctx.Reputation.get_reputation(jailed_uid)
if get_reputation is None:
uplink.ctx.Logs.warning(f'UID {jailed_uid} has not been found')
return
return None
if get_reputation.isWebirc or get_reputation.isWebsocket:
uplink.ctx.Logs.debug(f'This nickname is exampted from the reputation system (Webirc or Websocket). {get_reputation.nickname} ({get_reputation.uid})')
uplink.ctx.Reputation.delete(get_reputation.uid)
return None
salon_logs = gconfig.SERVICE_CHANLOG
salon_jail = gconfig.SALON_JAIL
@@ -360,7 +364,7 @@ async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
# Si le user ne vient pas de webIrc
await p.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=salon_jail)
await p.send_priv_msg(nick_from=gconfig.SERVICE_NICKNAME,
msg=f" [{color_red} REPUTATION {nogc}] : Connexion de {jailed_nickname} ({jailed_score}) ==> {salon_jail}",
msg=f" [ {color_red}REPUTATION{nogc} ]: The nickname {jailed_nickname} has been sent to {salon_jail} because his reputation score is ({jailed_score})",
channel=salon_logs
)
await p.send_notice(
@@ -371,7 +375,7 @@ async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
if reputation_ban_all_chan == 1:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
if chan.name != salon_jail:
await p.send2socket(f":{service_id} MODE {chan.name} +b {jailed_nickname}!*@*")
await p.send_set_mode('+b', channel_name=chan.name, params=f'{jailed_nickname}!*@*')
await p.send2socket(f":{service_id} KICK {chan.name} {jailed_nickname}")
uplink.ctx.Logs.info(f"[REPUTATION] {jailed_nickname} jailed (UID: {jailed_uid}, score: {jailed_score})")
@@ -419,14 +423,14 @@ async def action_apply_reputation_santions(uplink: 'Defender') -> None:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
if chan.name != salon_jail and ban_all_chan == 1:
get_user_reputation = uplink.ctx.Reputation.get_reputation(uid)
await p.send2socket(f":{service_id} MODE {chan.name} -b {get_user_reputation.nickname}!*@*")
await p.send_set_mode('-b', channel_name=chan.name, params=f"{get_user_reputation.nickname}!*@*")
# Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}.
uplink.ctx.Channel.delete_user_from_all_channel(uid)
uplink.ctx.Reputation.delete(uid)
uplink.ctx.User.delete(uid)
async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""Analyse l'ip avec cloudfilt
Cette methode devra etre lancer toujours via un thread ou un timer.
Args:
@@ -438,11 +442,6 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
"""
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
p = uplink.ctx.Irc.Protocol
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.cloudfilt_scan == 0:
@@ -450,19 +449,11 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
if uplink.cloudfilt_key == '':
return None
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
url = "https://developers18334.cloudfilt.com/"
data = {'ip': remote_ip, 'key': uplink.cloudfilt_key}
with requests.Session() as sess:
response = sess.post(url=url, data=data)
data = {
'ip': remote_ip,
'key': uplink.cloudfilt_key
}
response = requests.post(url=url, data=data)
# Formatted output
decoded_response: dict = loads(response.text)
status_code = response.status_code
@@ -472,30 +463,14 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
result = {
'countryiso': decoded_response.get('countryiso', None),
'listed': decoded_response.get('listed', None),
'listed': decoded_response.get('listed', False),
'listed_by': decoded_response.get('listed_by', None),
'host': decoded_response.get('host', None)
}
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}CLOUDFILT_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN] ({fullname}) connected from ({result['countryiso']}), Listed: {result['listed']}, by: {result['listed_by']}")
if result['listed']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} Your connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt")
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN GLINE] Dangerous connection ({fullname}) from ({result['countryiso']}) Listed: {result['listed']}, by: {result['listed_by']}")
response.close()
return result
async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""Analyse l'ip avec Freeipapi
Cette methode devra etre lancer toujours via un thread ou un timer.
Args:
@@ -505,29 +480,16 @@ async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUs
dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy'
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.freeipapi_scan == 0:
return None
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
with requests.Session() as sess:
url = f'https://freeipapi.com/api/json/{remote_ip}'
headers = {
'Accept': 'application/json',
}
response = requests.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
headers = {'Accept': 'application/json'}
response = sess.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
# Formatted output
decoded_response: dict = loads(response.text)
@@ -544,25 +506,9 @@ async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUs
'countryCode': decoded_response.get('countryCode', None),
'isProxy': decoded_response.get('isProxy', None)
}
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}FREEIPAPI_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN] ({fullname}) connected from ({result['countryCode']}), Proxy: {result['isProxy']}")
if result['isProxy']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN GLINE] Server do not allow proxy connexions {result['isProxy']}")
response.close()
return result
async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""Analyse l'ip avec AbuseIpDB
Cette methode devra etre lancer toujours via un thread ou un timer.
Args:
@@ -572,32 +518,28 @@ async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUs
Returns:
dict[str, str] | None: les informations du provider
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.abuseipdb_scan == 0:
return None
if uplink.abuseipdb_key == '':
return None
with requests.Session() as sess:
url = 'https://api.abuseipdb.com/api/v2/check'
querystring = {
'ipAddress': remote_ip,
'maxAgeInDays': '90'
}
querystring = {'ipAddress': remote_ip, 'maxAgeInDays': '90'}
headers = {
'Accept': 'application/json',
'Key': uplink.abuseipdb_key
}
response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=uplink.timeout)
response = sess.request(method='GET', url=url, headers=headers, params=querystring, timeout=uplink.timeout)
if response.status_code != 200:
uplink.ctx.Logs.warning(f'status code = {str(response.status_code)}')
return None
# Formatted output
decoded_response: dict[str, dict] = loads(response.text)
@@ -612,81 +554,45 @@ async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUs
'totalReports': decoded_response.get('data', {}).get('totalReports', 0)
}
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}ABUSEIPDB_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}",
channel=service_chanlog
)
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN] ({fullname}) connected from ({result['country']}), Score: {result['score']}, Tor: {result['isTor']}")
if result['isTor']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not allow Tor connections Tor: {result['isTor']}, Score: {result['score']}")
elif result['score'] >= 95:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not high risk connections Country: {result['country']}, Score: {result['score']}")
response.close()
return result
async def action_scan_client_with_local_socket(uplink: 'Defender', user_model: 'MUser'):
def action_scan_client_with_local_socket(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""local_scan
Args:
uplink (Defender): Defender instance object
user_model (MUser): l'objet User qui contient l'ip
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
fullname = f'{nickname}!{username}@{hostname}'
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
result = {'opened_ports': [], 'closed_ports': []}
for port in uplink.ctx.Config.PORTS_TO_SCAN:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) as sock:
try:
newSocket = ''
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
newSocket.settimeout(0.5)
sock.settimeout(0.5)
connection = (remote_ip, uplink.ctx.Base.int_if_possible(port))
newSocket.connect(connection)
sock.connect(connection)
await p.send_priv_msg(
nick_from=uplink.ctx.Config.SERVICE_NICKNAME,
msg=f"[ {uplink.ctx.Config.COLORS.red}PROXY_SCAN{uplink.ctx.Config.COLORS.nogc} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]",
channel=uplink.ctx.Config.SERVICE_CHANLOG
)
# print(f"=======> Le port {str(port)} est ouvert !!")
uplink.ctx.Base.running_sockets.append(newSocket)
# print(newSocket)
newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
result['opened_ports'].append(port)
uplink.ctx.Base.running_sockets.append(sock)
sock.shutdown(socket.SHUT_RDWR)
uplink.ctx.Base.running_sockets.remove(sock)
return result
except (socket.timeout, ConnectionRefusedError):
uplink.ctx.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé")
uplink.ctx.Logs.debug(f"[LOCAL SCAN] Port {remote_ip}:{str(port)} is close.")
result['closed_ports'].append(port)
except AttributeError as ae:
uplink.ctx.Logs.warning(f"AttributeError ({remote_ip}): {ae}")
except socket.gaierror as err:
uplink.ctx.Logs.warning(f"Address Info Error ({remote_ip}): {err}")
finally:
# newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
uplink.ctx.Logs.info('=======> Fermeture de la socket')
async def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> list[int]:
return result
def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> list[int]:
"""psutil_scan for Linux (should be run on the same location as the unrealircd server)
Args:
@@ -695,28 +601,16 @@ async def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser'
Returns:
list[int]: list of ports
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.psutil_scan == 0:
return None
try:
connections = psutil.net_connections(kind='inet')
fullname = f'{nickname}!{username}@{hostname}'
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
uplink.ctx.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}")
if matching_ports:
await p.send_priv_msg(
nick_from=uplink.ctx.Config.SERVICE_NICKNAME,
msg=f"[ {uplink.ctx.Config.COLORS.red}PSUTIL_SCAN{uplink.ctx.Config.COLORS.black} ] {fullname} ({remote_ip}) : is using ports {matching_ports}",
channel=uplink.ctx.Config.SERVICE_CHANLOG
)
uplink.ctx.Logs.debug(f"Connexion of ({remote_ip}) using ports : {str(matching_ports)}")
return matching_ports

View File

@@ -91,9 +91,9 @@ class Jsonrpc(IModule):
self.is_streaming = False
# Create module commands (Mandatory)
self.ctx.Irc.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]')
self.ctx.Irc.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC')
self.ctx.Irc.build_command(1, self.module_name, 'jrinstances', 'Get number of instances')
self.ctx.Commands.build_command(1, self.module_name, 'jsonrpc', 'Activate the JSON RPC Live connection [ON|OFF]')
self.ctx.Commands.build_command(1, self.module_name, 'jruser', 'Get Information about a user using JSON RPC')
self.ctx.Commands.build_command(1, self.module_name, 'jrinstances', 'Get number of instances')
try:
self.Rpc = ConnectionFactory(self.ctx.Config.DEBUG_LEVEL).get(self.ctx.Config.JSONRPC_METHOD)
@@ -138,7 +138,6 @@ class Jsonrpc(IModule):
channel=self.ctx.Config.SERVICE_CHANLOG
)
self.ctx.Base.create_asynctask(thds.thread_unsubscribe(self))
# await self.update_configuration('jsonrpc', 0)
self.ctx.Commands.drop_command_by_module(self.module_name)
self.ctx.Logs.debug(f"Unloading {self.module_name}")
return None

View File

@@ -1,4 +1,3 @@
import asyncio
from typing import TYPE_CHECKING
if TYPE_CHECKING:

View File

@@ -53,11 +53,11 @@ class Test(IModule):
"""
# Create module commands (Mandatory)
self.ctx.Irc.build_command(0, self.module_name, 'test-command', 'Execute a test command')
self.ctx.Irc.build_command(0, self.module_name, 'asyncio', 'Create a new asynchron task!')
self.ctx.Irc.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command')
self.ctx.Irc.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command')
self.ctx.Irc.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command')
self.ctx.Commands.build_command(0, self.module_name, 'test-command', 'Execute a test command')
self.ctx.Commands.build_command(0, self.module_name, 'asyncio', 'Create a new asynchron task!')
self.ctx.Commands.build_command(1, self.module_name, 'test_level_1', 'Execute a level 1 test command')
self.ctx.Commands.build_command(2, self.module_name, 'test_level_2', 'Execute a level 2 test command')
self.ctx.Commands.build_command(3, self.module_name, 'test_level_3', 'Execute a level 3 test command')
# Build the default configuration model (Mandatory)
self._mod_config = self.ModConfModel(param_exemple1='str', param_exemple2=1)

View File

@@ -88,7 +88,7 @@ class Votekick(IModule):
self.VoteKickManager.VOTE_CHANNEL_DB = metadata
# Créer les nouvelles commandes du module
self.ctx.Irc.build_command(1, self.module_name, 'vote', 'The kick vote module')
self.ctx.Commands.build_command(1, self.module_name, 'vote', 'The kick vote module')
async def unload(self) -> None:
try:

View File

@@ -12,7 +12,7 @@ async def timer_vote_verdict(uplink: 'Votekick', channel: str) -> None:
if not uplink.VoteKickManager.is_vote_ongoing(channel):
return None
asyncio.sleep(60)
await asyncio.sleep(60)
votec = uplink.VoteKickManager.get_vote_channel_model(channel)
if votec: