diff --git a/README.md b/README.md index 72fd0cf..a2a672e 100644 --- a/README.md +++ b/README.md @@ -32,30 +32,34 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u - Système d'exploitation Linux (Windows non supporté) - Un server UnrealIRCD corréctement configuré - Python version 3.10 ou supérieure - - Bash: +```bash + # Bash $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git - - Renommer le fichier exemple_configuration.json en configuration.json - - Configurer le fichier configuration.json + # Renommer le fichier exemple_configuration.json en configuration.json + # Configurer le fichier configuration.json $ python3 main.py - +``` Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC Pour Les prochains lancement de defender vous devez utiliser la commande suivante: - Bash: - $ systemctl --user [start | stop | restart | status] defender - +```bash + # Bash + $ systemctl --user [start | stop | restart | status] defender +``` # Installation manuelle: - Bash: - $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git - $ cd IRC_DEFENDER_MODULES - $ python3 -m venv .pyenv - $ source .pyenv/bin/activate - (pyenv)$ pip install sqlalchemy, psutil, requests, faker - - Créer un service nommé "defender.service" pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/" - - Si le dossier n'existe pas il faut les créer - $ sudo systemctl --user start defender +```bash + # Bash + $ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git + $ cd IRC_DEFENDER_MODULES + $ python3 -m venv .pyenv + $ source .pyenv/bin/activate + (pyenv)$ pip install sqlalchemy, psutil, requests, faker, unrealircd_rpc_py + # Créer un service nommé "defender.service" + # pour votre service et placer le dans "/PATH/TO/USER/.config/systemd/user/" + # Si le dossier n'existe pas il faut les créer + $ sudo systemctl --user start defender +``` # Configuration ``` SERVEUR (Serveur) diff --git a/core/base.py b/core/base.py index 6a2e1cc..ce38702 100644 --- a/core/base.py +++ b/core/base.py @@ -1,8 +1,21 @@ -import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast +import os +import re +import json +import time +import random +import socket +import hashlib +import logging +import threading +import ipaddress + +import ast +import requests + from dataclasses import fields from typing import Union, Literal from base64 import b64decode, b64encode -from datetime import datetime +from datetime import datetime, timedelta, timezone from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy.sql import text from core.loadConf import ConfigDataModel @@ -106,7 +119,11 @@ class Base: Cette fonction retourne un UNIXTIME de type 12365456 Return: Current time in seconds since the Epoch (int) """ + cet_offset = timezone(timedelta(hours=2)) + now_cet = datetime.now(cet_offset) + unixtime_cet = int(now_cet.timestamp()) unixtime = int( time.time() ) + return unixtime def get_datetime(self) -> str: diff --git a/core/connection.py b/core/connection.py index 4aca76c..a6e2583 100644 --- a/core/connection.py +++ b/core/connection.py @@ -2,10 +2,10 @@ import socket import ssl import traceback from ssl import SSLSocket +from typing import Union from core.loadConf import Config from core.Model import Clones from core.base import Base -from typing import Union class Connection: diff --git a/core/installation.py b/core/installation.py index 4d14d80..ec7400b 100644 --- a/core/installation.py +++ b/core/installation.py @@ -1,8 +1,8 @@ +import os +from sys import exit from dataclasses import dataclass from subprocess import check_call, run, CalledProcessError, PIPE from platform import python_version, python_version_tuple -from sys import exit -import os class Install: diff --git a/core/irc.py b/core/irc.py index 0de7a53..cc6612a 100644 --- a/core/irc.py +++ b/core/irc.py @@ -1,10 +1,17 @@ -import ssl, re, importlib, sys, time, threading, socket, traceback +import sys +import socket +import threading +import ssl +import re +import importlib +import time +import traceback from ssl import SSLSocket from datetime import datetime, timedelta -from typing import Union, Literal +from typing import Union from core.loadConf import Config -from core.Model import User, Admin, Channel, Clones from core.base import Base +from core.Model import User, Admin, Channel, Clones class Irc: @@ -492,6 +499,39 @@ class Irc: self.Base.logs.error(f"Something went wrong with a module you want to load : {e}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}ERROR{self.Config.COLORS.black} ]: {e}") + def unload_module(self, mod_name: str) -> bool: + """Unload a module + + Args: + mod_name (str): Module name ex mod_defender + + Returns: + bool: True if success + """ + try: + module_name = mod_name.lower() # Le nom du module. exemple: mod_defender + class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender + + if class_name in self.loaded_classes: + self.loaded_classes[class_name].unload() + for level, command in self.loaded_classes[class_name].commands_level.items(): + # Supprimer la commande de la variable commands + for c in self.loaded_classes[class_name].commands_level[level]: + self.commands.remove(c) + self.commands_level[level].remove(c) + + del self.loaded_classes[class_name] + + # Supprimer le module de la base de données + self.Base.db_delete_module(module_name) + + self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé") + return True + + except Exception as err: + self.Base.logs.error(f"General Error: {err}") + return False + def insert_db_admin(self, uid:str, level:int) -> None: if self.User.get_User(uid) is None: @@ -997,7 +1037,9 @@ class Irc: recieved_unixtime = int(arg[1].replace('\x01','')) current_unixtime = self.Base.get_unixtime() ping_response = current_unixtime - recieved_unixtime - self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01PING {str(ping_response)} secs\x01') + + self.send2socket(f'PONG :{recieved_unixtime}') + self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01PING {recieved_unixtime} secs\x01') return False if not arg[0].lower() in self.commands: @@ -1285,7 +1327,6 @@ class Irc: case 'help': - help = '' count_level_definition = 0 get_admin = self.Admin.get_Admin(uid) if not get_admin is None: @@ -1300,18 +1341,15 @@ class Irc: if int(user_level) >= int(count_level_definition): self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** {self.Config.COLORS.nogc}[ {self.Config.COLORS.green}LEVEL {str(levDef)} {self.Config.COLORS.nogc}] *****************') - count_commands = 0 - help = '' - for comm in self.commands_level[count_level_definition]: - help += f"{comm.upper()}" - if int(count_commands) < len(self.commands_level[count_level_definition])-1: - help += ' | ' - count_commands += 1 - - self.send2socket(f':{dnickname} NOTICE {fromuser} : {help}') + batch = 7 + for i in range(0, len(self.commands_level[count_level_definition]), batch): + groupe = self.commands_level[count_level_definition][i:i + batch] # Extraire le groupe + batch_commands = ' | '.join(groupe) + self.send2socket(f':{dnickname} NOTICE {fromuser} : {batch_commands}') count_level_definition += 1 + self.send2socket(f':{dnickname} NOTICE {fromuser} : ') self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** FIN DES COMMANDES *****************') @@ -1320,27 +1358,12 @@ class Irc: self.load_module(fromuser, str(cmd[1])) case 'unload': - # unload mod_dktmb + # unload mod_defender try: module_name = str(cmd[1]).lower() # Le nom du module. exemple: mod_defender - class_name = module_name.split('_')[1].capitalize() # Nom de la class. exemple: Defender - - if class_name in self.loaded_classes: - self.loaded_classes[class_name].unload() - for level, command in self.loaded_classes[class_name].commands_level.items(): - # Supprimer la commande de la variable commands - for c in self.loaded_classes[class_name].commands_level[level]: - self.commands.remove(c) - self.commands_level[level].remove(c) - - del self.loaded_classes[class_name] - - # Supprimer le module de la base de données - self.Base.db_delete_module(module_name) - - self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé") - except: - self.Base.logs.error(f"Something went wrong with a module you want to load") + self.unload_module(module_name) + except Exception as err: + self.Base.logs.error(f"General Error: {err}") case 'reload': # reload mod_dktmb diff --git a/core/loadConf.py b/core/loadConf.py index 675f08f..cbf4d49 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -1,4 +1,5 @@ -import json, sys +import sys +import json from os import sep from typing import Union, Literal from dataclasses import dataclass, field @@ -16,7 +17,7 @@ class ColorModel: red: str = "\x0304" yellow: str = "\x0306" bold: str = "\x02" - nogc: str = "\x03" + nogc: str = "\x03" @dataclass class ConfigDataModel: @@ -85,10 +86,19 @@ class ConfigDataModel: """The password of the admin of the service""" JSONRPC_URL: str + """The RPC url, if local https://127.0.0.1:PORT/api should be fine""" + JSONRPC_PATH_TO_SOCKET_FILE: str + """The full path of the socket file (/PATH/TO/YOUR/UNREALIRCD/SOCKET/FILE.socket)""" + JSONRPC_METHOD: str + """3 methods are available; requests/socket/unixsocket""" + JSONRPC_USER: str + """The RPC User defined in your unrealircd.conf""" + JSONRPC_PASSWORD: str + """The RPC Password defined in your unrealircd.conf""" SALON_JAIL: str """The JAIL channel (ex. #jail)""" diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 6c3c8fb..b3d2865 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -147,7 +147,7 @@ class Clone(): del clone_to_kill - # If LIST empty then stop this thread + # If no more clones then stop this thread if not self.Clone.UID_CLONE_DB: break diff --git a/mods/mod_command.py b/mods/mod_command.py index d86720e..8a04a6e 100644 --- a/mods/mod_command.py +++ b/mods/mod_command.py @@ -35,9 +35,14 @@ class Command(): # Create module commands (Mandatory) self.commands_level = { 1: ['join', 'part'], - 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', + 2: ['owner', 'deowner', 'protect', 'deprotect', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban', - 'unban','kick', 'kickban', 'umode', 'svsjoin', 'svspart', 'svsnick'] + 'unban','kick', 'kickban', 'umode', 'mode', 'svsjoin', 'svspart', 'svsnick', 'topic', + 'wallops', 'globops','gnotice','whois', 'names', 'invite', 'inviteme', + 'sajoin', 'sapart', + 'kill', 'gline', 'ungline', 'kline', 'unkline', 'shun', 'unshun', + 'glinelist', 'shunlist', 'klinelist'], + 3: ['map'] } # Init the module @@ -58,6 +63,9 @@ class Command(): self.__load_module_configuration() # End of mandatory methods you can start your customization # + self.user_to_notice: str = '' + self.show_219: bool = True + return None def __set_commands(self, commands:dict[int, list[str]]) -> None: @@ -91,7 +99,7 @@ class Command(): ) ''' - self.Base.db_execute_query(table_logs) + # self.Base.db_execute_query(table_logs) return None def __load_module_configuration(self) -> None: @@ -122,45 +130,80 @@ class Command(): return None - def add_defender_channel(self, channel:str) -> bool: - """Cette fonction ajoute les salons de join de Defender - - Args: - channel (str): le salon à enregistrer. - """ - mes_donnees = {'channel': channel} - response = self.Base.db_execute_query("SELECT id FROM def_channels WHERE channel = :channel", mes_donnees) - - isChannelExist = response.fetchone() - - if isChannelExist is None: - mes_donnees = {'datetime': self.Base.get_datetime(), 'channel': channel} - insert = self.Base.db_execute_query(f"INSERT INTO def_channels (datetime, channel) VALUES (:datetime, :channel)", mes_donnees) - if insert.rowcount >=0: - return True - else: - return False - else: - return False - - def delete_defender_channel(self, channel:str) -> bool: - """Cette fonction supprime les salons de join de Defender - - Args: - channel (str): le salon à enregistrer. - """ - mes_donnes = {'channel': channel} - response = self.Base.db_execute_query("DELETE FROM def_channels WHERE channel = :channel", mes_donnes) - - affected_row = response.rowcount - - if affected_row > 0: - return True - else: - return False - def cmd(self, data:list) -> None: + service_id = self.Config.SERVICE_ID + dnickname = self.Config.SERVICE_NICKNAME + dchanlog = self.Config.SERVICE_CHANLOG + red = self.Config.COLORS.red + green = self.Config.COLORS.green + bold = self.Config.COLORS.bold + nogc = self.Config.COLORS.nogc + cmd = list(data).copy() + + if len(cmd) < 2: + return None + + match cmd[1]: + # [':irc.deb.biz.st', '403', 'Dev-PyDefender', '#Z', ':No', 'such', 'channel'] + case '403' | '401': + try: + message = ' '.join(cmd[3:]) + self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} :[{red}ERROR MSG{nogc}] {message}") + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case '006' | '018': + try: + # [':irc.deb.biz.st', '006', 'Dev-PyDefender', ':`-services.deb.biz.st', '------', '|', 'Users:', '9', '(47.37%)', '[00B]'] + # [':irc.deb.biz.st', '018', 'Dev-PyDefender', ':4', 'servers', 'and', '19', 'users,', 'average', '4.75', 'users', 'per', 'server'] + message = ' '.join(cmd[3:]) + self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : [{green}SERVER MSG{nogc}] {message}") + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case '219': + try: + # [':irc.deb.biz.st', '219', 'Dev-PyDefender', 's', ':End', 'of', '/STATS', 'report'] + if not self.show_219: + # If there is a result in 223 then stop here + self.show_219 = True + return None + + type_of_stats = str(cmd[3]) + + match type_of_stats: + case 's': + self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No shun") + case 'G': + self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No gline") + case 'k': + self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : No kline") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case '223': + try: + # [':irc.deb.biz.st', '223', 'Dev-PyDefender', 'G', '*@162.142.125.217', '67624', '18776', 'irc.deb.biz.st', ':Proxy/Drone', 'detected.', 'Check', 'https://dronebl.org/lookup?ip=162.142.125.217', 'for', 'details.'] + self.show_219 = False + host = str(cmd[4]) + author = str(cmd[7]) + reason = ' '.join(cmd[8:]) + + self.Irc.send2socket(f":{dnickname} NOTICE {self.user_to_notice} : {bold}Author{nogc}: {author} - {bold}Host{nogc}: {host} - {bold}Reason{nogc}: {reason}") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + return None def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: @@ -346,6 +389,60 @@ class Command(): except Exception as err: self.Logs.warning(f'Unknown Error: {str(err)}') + case 'protect': + # /mode #channel +a user + # .protect #channel user + try: + if fromchannel is None: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]') + return False + + if len(cmd) == 1: + self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {fromuser}") + return True + + # deowner nickname + if len(cmd) == 2: + nickname = cmd[1] + self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}") + return True + + nickname = cmd[2] + self.Irc.send2socket(f":{service_id} MODE {fromchannel} +a {nickname}") + + except IndexError as e: + self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]') + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'deprotect': + # /mode #channel -a user + # .deprotect #channel user + try: + if fromchannel is None: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]') + return False + + if len(cmd) == 1: + self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {fromuser}") + return True + + # deowner nickname + if len(cmd) == 2: + nickname = cmd[1] + self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}") + return True + + nickname = cmd[2] + self.Irc.send2socket(f":{service_id} MODE {fromchannel} -a {nickname}") + + except IndexError as e: + self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]') + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + case 'halfop': # /mode #channel +h user # .halfop #channel user @@ -459,7 +556,7 @@ class Command(): try: sentchannel = str(cmd[1]) if self.Base.Is_Channel(cmd[1]) else None if sentchannel is None: - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]') return False nickname = cmd[2] @@ -468,7 +565,7 @@ class Command(): self.Logs.debug(f'{fromuser} has banned {nickname} from {sentchannel}') except IndexError as e: self.Logs.warning(f'_hcmd BAN: {str(e)}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#SALON] [NICKNAME]') except Exception as err: self.Logs.warning(f'Unknown Error: {str(err)}') @@ -577,6 +674,174 @@ class Command(): except Exception as err: self.Logs.warning(f'Unknown Error: {str(err)}') + case 'topic': + try: + if len(cmd) == 1: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") + return None + + chan = str(cmd[1]) + if not self.Base.Is_Channel(chan): + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #") + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} TOPIC #channel THE_TOPIC_MESSAGE") + return None + + topic_msg = ' '.join(cmd[2:]).strip() + + if topic_msg: + self.Irc.send2socket(f':{dnickname} TOPIC {chan} :{topic_msg}') + else: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the topic") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'wallops': + try: + if len(cmd) == 1: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} WALLOPS THE_WALLOPS_MESSAGE") + return None + + wallops_msg = ' '.join(cmd[1:]).strip() + + if wallops_msg: + self.Irc.send2socket(f':{dnickname} WALLOPS {wallops_msg} ({dnickname})') + else: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the wallops message") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'globops': + try: + if len(cmd) == 1: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} GLOBOPS THE_GLOBOPS_MESSAGE") + return None + + globops_msg = ' '.join(cmd[1:]).strip() + + if globops_msg: + self.Irc.send2socket(f':{dnickname} GLOBOPS {globops_msg} ({dnickname})') + else: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the globops message") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'gnotice': + try: + if len(cmd) == 1: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} THE_GLOBAL_NOTICE_MESSAGE") + return None + + gnotice_msg = ' '.join(cmd[1:]).strip() + + if gnotice_msg: + self.Irc.send2socket(f':{dnickname} NOTICE $*.* :[{self.Config.COLORS.red}GLOBAL NOTICE{self.Config.COLORS.nogc}] {gnotice_msg}') + else: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :You need to specify the global notice message") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'whois': + try: + self.user_to_notice = fromuser + if len(cmd) == 1: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} NICKNAME") + return None + + nickname = str(cmd[1]) + + if self.User.get_nickname(nickname) is None: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :Nickname not found !") + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} NICKNAME") + return None + + self.Irc.send2socket(f':{dnickname} WHOIS {nickname}') + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'names': + try: + if len(cmd) == 1: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL") + return None + + chan = str(cmd[1]) + + if not self.Base.Is_Channel(chan): + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #") + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel") + return None + + self.Irc.send2socket(f':{dnickname} NAMES {chan}') + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'invite': + try: + if len(cmd) < 3: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #CHANNEL NICKNAME") + return None + + chan = str(cmd[1]) + nickname = str(cmd[2]) + + if not self.Base.Is_Channel(chan): + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :The channel must start with #") + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel nickname") + return None + + if self.User.get_nickname(nickname) is None: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :Nickname not found !") + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()} #channel NICKNAME") + return None + + self.Irc.send2socket(f':{dnickname} INVITE {nickname} {chan}') + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'inviteme': + try: + if len(cmd) == 0: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} :/msg {dnickname} {str(cmd[0]).upper()}") + return None + + self.Irc.send2socket(f':{dnickname} INVITE {fromuser} {self.Config.SERVICE_CHANLOG}') + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'map': + try: + self.user_to_notice = fromuser + self.Irc.send2socket(f':{dnickname} MAP') + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + case 'umode': try: # .umode nickname +mode @@ -589,6 +854,39 @@ class Command(): except Exception as err: self.Logs.warning(f'Unknown Error: {str(err)}') + case 'mode': + # .mode #channel +/-mode + # .mode +/-mode + try: + + if len(cmd) < 2: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode') + return None + + if fromchannel is None: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode') + return None + + if len(cmd) == 2: + channel_mode = cmd[1] + if self.Base.Is_Channel(fromchannel): + self.Irc.send2socket(f":{dnickname} MODE {fromchannel} {channel_mode}") + else: + self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : Right command : Channel [{fromchannel}] is not correct should start with #") + return None + + if len(cmd) == 3: + provided_channel = cmd[1] + channel_mode = cmd[2] + self.Irc.send2socket(f":{service_id} MODE {provided_channel} {channel_mode}") + return None + + except IndexError as e: + self.Logs.warning(f'_hcmd OP: {str(e)}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} {command.upper()} [#CHANNEL] [+/-]mode') + except Exception as err: + self.Logs.warning(f'Unknown Error: {str(err)}') + case 'svsjoin': try: # .svsjoin nickname #channel @@ -621,6 +919,38 @@ class Command(): self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel') self.Logs.warning(f'Unknown Error: {str(err)}') + case 'sajoin': + try: + # .sajoin nickname #channel + nickname = str(cmd[1]) + channel = str(cmd[2]) + if len(cmd) < 3: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel') + return None + + self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SAJOIN {nickname} {channel}') + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'sapart': + try: + # .sapart nickname #channel + nickname = str(cmd[1]) + channel = str(cmd[2]) + if len(cmd) < 3: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel') + return None + + self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SAPART {nickname} {channel}') + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname #channel') + self.Logs.warning(f'Unknown Error: {str(err)}') + case 'svsnick': try: # .svsnick nickname newnickname @@ -633,12 +963,203 @@ class Command(): return None if len(cmd) != 3: - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname newnickname') return None self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}') + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname newnickname') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'kill': + try: + # 'kill', 'gline', 'ungline', 'shun', 'unshun' + # .kill nickname reason + if len(cmd) < 3: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname reason') + return None + + nickname = str(cmd[1]) + kill_reason = ' '.join(cmd[2:]) + + self.Irc.send2socket(f":{service_id} KILL {nickname} {kill_reason} ({self.Config.COLORS.red}{dnickname}{self.Config.COLORS.nogc})") except KeyError as ke: self.Base.logs.error(ke) except Exception as err: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname') self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'gline': + try: + # TKL + G user host set_by expire_timestamp set_at_timestamp :reason + # .gline [nickname] [host] [reason] + if len(cmd) < 4: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + return None + + nickname = str(cmd[1]) + hostname = str(cmd[2]) + set_at_timestamp = self.Base.get_unixtime() + expire_time = (60 * 60 * 24) + set_at_timestamp + gline_reason = ' '.join(cmd[3:]) + + if nickname == '*' and hostname == '*': + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + return None + + self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + G {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{gline_reason}") + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'ungline': + try: + # 'shun', 'unshun' + # TKL + G user host set_by expire_timestamp set_at_timestamp :reason + # .ungline nickname host + if len(cmd) < 2: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname') + return None + + nickname = str(cmd[1]) + hostname = str(cmd[2]) + + self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - G {nickname} {hostname} {dnickname}") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'kline': + try: + # TKL + k user host set_by expire_timestamp set_at_timestamp :reason + # .gline [nickname] [host] [reason] + if len(cmd) < 4: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + return None + + nickname = str(cmd[1]) + hostname = str(cmd[2]) + set_at_timestamp = self.Base.get_unixtime() + expire_time = (60 * 60 * 24) + set_at_timestamp + gline_reason = ' '.join(cmd[3:]) + + if nickname == '*' and hostname == '*': + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + return None + + self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + k {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{gline_reason}") + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'unkline': + try: + # 'shun', 'unshun' + # TKL + G user host set_by expire_timestamp set_at_timestamp :reason + # .ungline nickname host + if len(cmd) < 2: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname') + return None + + nickname = str(cmd[1]) + hostname = str(cmd[2]) + + self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - k {nickname} {hostname} {dnickname}") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'shun': + try: + # TKL + G user host set_by expire_timestamp set_at_timestamp :reason + # .shun [nickname] [host] [reason] + + if len(cmd) < 4: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + return None + + nickname = str(cmd[1]) + hostname = str(cmd[2]) + set_at_timestamp = self.Base.get_unixtime() + expire_time = (60 * 60 * 24) + set_at_timestamp + shun_reason = ' '.join(cmd[3:]) + + if nickname == '*' and hostname == '*': + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : You want to close the server ? i would recommand ./unrealircd stop :)') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + return None + + self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL + s {nickname} {hostname} {dnickname} {expire_time} {set_at_timestamp} :{shun_reason}") + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname host reason') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'unshun': + try: + # 'shun', 'unshun' + # TKL + G user host set_by expire_timestamp set_at_timestamp :reason + # .unshun nickname host + if len(cmd) < 2: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname') + return None + + nickname = str(cmd[1]) + hostname = str(cmd[2]) + + self.Irc.send2socket(f":{self.Config.SERVEUR_ID} TKL - s {nickname} {hostname} {dnickname}") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()} nickname hostname') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'glinelist': + try: + self.user_to_notice = fromuser + self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS G") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'shunlist': + try: + self.user_to_notice = fromuser + self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS s") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}') + self.Logs.warning(f'Unknown Error: {str(err)}') + + case 'klinelist': + try: + self.user_to_notice = fromuser + self.Irc.send2socket(f":{self.Config.SERVICE_ID} STATS k") + + except KeyError as ke: + self.Base.logs.error(ke) + except Exception as err: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} {command.upper()}') + self.Logs.warning(f'Unknown Error: {str(err)}') \ No newline at end of file diff --git a/mods/mod_defender.py b/mods/mod_defender.py index 5c196bb..15674e0 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -1,7 +1,12 @@ +import socket +import json +import time +import re +import psutil +import requests from dataclasses import dataclass, fields, field from datetime import datetime from typing import Union -import re, socket, psutil, requests, json, time from sys import exit from core.irc import Irc from core.Model import User diff --git a/mods/mod_jsonrpc.py b/mods/mod_jsonrpc.py index c5e0bec..6754194 100644 --- a/mods/mod_jsonrpc.py +++ b/mods/mod_jsonrpc.py @@ -59,13 +59,20 @@ class Jsonrpc(): self.__load_module_configuration() # End of mandatory methods you can start your customization # - self.UnrealIrcdRpcLive: Live = None + self.UnrealIrcdRpcLive: Live = Live(path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE, + callback_object_instance=self, + callback_method_name='callback_sent_to_irc' + ) + self.Rpc: Loader = Loader( req_method=self.Config.JSONRPC_METHOD, url=self.Config.JSONRPC_URL, username=self.Config.JSONRPC_USER, password=self.Config.JSONRPC_PASSWORD ) + + self.subscribed = False + if self.Rpc.Error.code != 0: self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}", self.Config.SERVICE_CHANLOG) @@ -114,12 +121,9 @@ class Jsonrpc(): def thread_start_jsonrpc(self): - self.UnrealIrcdRpcLive = Live(path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE, - callback_object_instance=self, - callback_method_name='callback_sent_to_irc' - ) if self.UnrealIrcdRpcLive.Error.code == 0: self.UnrealIrcdRpcLive.subscribe() + self.subscribed = True else: self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG) @@ -148,7 +152,8 @@ class Jsonrpc(): self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value) def unload(self) -> None: - self.UnrealIrcdRpcLive.unsubscribe() + if self.UnrealIrcdRpcLive.Error.code != -1: + self.UnrealIrcdRpcLive.unsubscribe() return None def cmd(self, data:list) -> None: @@ -198,23 +203,22 @@ class Jsonrpc(): if uid_to_get is None: return None - rpc = Loader( - req_method=self.Config.JSONRPC_METHOD, - url=self.Config.JSONRPC_URL, - username=self.Config.JSONRPC_USER, - password=self.Config.JSONRPC_PASSWORD - ) + rpc = self.Rpc UserInfo = rpc.User.get(uid_to_get) if rpc.Error.code != 0: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{rpc.Error.message}') return None + chan_list = [] + for chan in UserInfo.channels: + chan_list.append(chan["name"]) + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.username}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.realname}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {UserInfo.channels}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {chan_list}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.security_groups}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.reputation}') diff --git a/version.json b/version.json index 85ed6d2..e9f87fd 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.3.1" + "version": "5.3.3" } \ No newline at end of file