44import ssl
55import time
66import json
7- import base64
7+ import inspect
88import sys
99import os
1010import importlib .util
1111from src .channel_manager import ChannelManager
1212from src .logger import Logger
1313from src .plugin_base import PluginBase
14- from src .sasl import handle_sasl , handle_authenticate , handle_903
14+ from src .sasl import SASLHandler
15+ from src .event_handlers import handle_ping , handle_cap , handle_authenticate , handle_903 , handle_privmsg , handle_001 , handle_invite , handle_version
1516
1617class Bot :
1718def __init__ (self , config_file ):
@@ -22,20 +23,41 @@ def __init__(self, config_file):
2223self .ircsock = None
2324self .running = True
2425self .plugins = []
26+ self .sasl_handler = SASLHandler (self .config , self ._ircsend )
2527self .load_plugins ()
2628
27- def load_plugins (self , plugin_name = None ):
28- self .plugins = [p for p in self .plugins if plugin_name is None or p .__class__ .__name__ != plugin_name ]
29+ self .command_handlers = {
30+ 'PING' : handle_ping ,
31+ 'CAP' : handle_cap ,
32+ 'AUTHENTICATE' : handle_authenticate ,
33+ '903' : handle_903 ,
34+ 'PRIVMSG' : handle_privmsg ,
35+ '001' : handle_001 ,
36+ 'INVITE' : handle_invite ,
37+ 'VERSION' : handle_version ,
38+ }
39+
40+ def process_command (self , command , args ):
41+ handler = self .command_handlers .get (command )
42+ if handler :
43+ handler (self , args )
44+ else :
45+ self .logger .debug (f'Received: source: { self } | command: { command } | args: { args } ' )
46+ self .logger .info (f'No handler for command { command } ' )
47+
48+ def load_plugins (self ):
49+ self .plugins = []
2950plugin_folder = "./plugins"
3051for filename in os .listdir (plugin_folder ):
31- if filename .endswith ('.py' )and ( plugin_name is None or filename == plugin_name + '.py' ) :
52+ if filename .endswith ('.py' ):
3253filepath = os .path .join (plugin_folder , filename )
3354spec = importlib .util .spec_from_file_location ("module.name" , filepath )
3455module = importlib .util .module_from_spec (spec )
3556spec .loader .exec_module (module )
36- if hasattr (module , 'Plugin' ) and issubclass (module .Plugin , PluginBase ):
37- plugin_instance = module .Plugin (self )
38- self .plugins .append (plugin_instance )
57+ for name , obj in inspect .getmembers (module ):
58+ if inspect .isclass (obj ) and issubclass (obj , PluginBase ) and obj is not PluginBase :
59+ plugin_instance = obj (self )
60+ self .plugins .append (plugin_instance )
3961
4062def _load_config (self , config_file ):
4163try :
@@ -95,20 +117,20 @@ def connect(self):
95117self .ircsock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
96118self .ircsock .settimeout (240 )
97119
98- if str (self .config ['BPORT' ] )[:1 ] == '+' :
120+ if str (self .config ["Connection" ]. get ( "Port" ) )[:1 ] == '+' :
99121context = ssl .create_default_context ()
100- self .ircsock = context .wrap_socket (self .ircsock , server_hostname = self .config ['BSERVER' ] )
101- port = int (self .config ['BPORT' ] [1 :])
122+ self .ircsock = context .wrap_socket (self .ircsock , server_hostname = self .config ["Connection" ]. get ( "Hostname" ) )
123+ port = int (self .config ['Connection' ]. get ( 'Port' ) [1 :])
102124else :
103- port = int (self .config ['BPORT' ] )
125+ port = int (self .config ['Connection' ]. get ( 'Port' ) )
104126
105- if 'BBINDHOST ' in self .config :
106- self .ircsock .bind ((self .config ['BBINDHOST' ] , 0 ))
127+ if 'BindHost ' in self .config :
128+ self .ircsock .bind ((self .config ['Connection' ]. get ( 'BindHost' ) , 0 ))
107129
108- self .ircsock .connect_ex ((self .config ['BSERVER' ] , port ))
109- self ._ircsend (f'NICK { self .config ["BNICK" ] } ' )
110- self ._ircsend (f'USER { self .config ["BIDENT" ] } * * :{ self .config ["BNAME" ] } ' )
111- if self .config ['UseSASL' ] :
130+ self .ircsock .connect_ex ((self .config ['Connection' ]. get ( 'Hostname' ) , port ))
131+ self ._ircsend (f'NICK { self .config ["Connection" ]. get ( "Nick" ) } ' )
132+ self ._ircsend (f'USER { self .config ["Connection" ]. get ( "Ident" ) } * * :{ self .config ["Connection" ]. get ( "Name" ) } ' )
133+ if self .config ["SASL" ]. get ( "UseSASL" ) :
112134self ._ircsend ('CAP REQ :sasl' )
113135except Exception as e :
114136self .logger .error (f"Error establishing connection: { e } " )
@@ -136,44 +158,7 @@ def start(self):
136158source , command , args = self ._parse_message (ircmsg )
137159self .logger .debug (f'Received: source: { source } | command: { command } | args: { args } ' )
138160
139- if command == 'PING' :
140- nospoof = args [0 ][1 :] if args [0 ].startswith (':' ) else args [0 ]
141- self ._ircsend (f'PONG :{ nospoof } ' )
142-
143-
144- if command == 'CAP' and args [1 ] == 'ACK' and 'sasl' in args [2 ]:
145- handle_sasl (self .config , self ._ircsend )
146-
147- elif command == 'AUTHENTICATE' :
148- handle_authenticate (args , self .config , self ._ircsend )
149-
150- elif command == '903' :
151- handle_903 (self ._ircsend )
152-
153- if command == 'PRIVMSG' and args [1 ].startswith ('\x01 VERSION\x01 ' ):
154- source_nick = source .split ('!' )[0 ]
155- self ._ircsend (f'NOTICE { source_nick } :\x01 VERSION EliteBot 0.1\x01 ' )
156-
157- if command == 'PRIVMSG' :
158- channel , message = args [0 ], args [1 ]
159- source_nick = source .split ('!' )[0 ]
160- if message .startswith ('&' ):
161- cmd , * cmd_args = message [1 :].split ()
162- self .handle_command (source_nick , channel , cmd , cmd_args )
163- for plugin in self .plugins :
164- plugin .handle_message (source_nick , channel , message )
165-
166- if command == '001' :
167- for channel in self .channel_manager .get_channels ():
168- self ._ircsend (f'JOIN { channel } ' )
169-
170- if command == 'INVITE' :
171- channel = args [1 ]
172- self ._ircsend (f'join { channel } ' )
173- self .channel_manager .save_channel (channel )
174-
175- if command == 'VERSION' :
176- self ._ircsend ('NOTICE' , f'{ source_nick } :I am a bot version 1.0.0' )
161+ self .process_command (command , args )
177162
178163except socket .timeout :
179164self .connected = False
0 commit comments