13
13
from IRCHandler import CmdHandler ,CmdGenerator ,Sender ,Color , EOL
14
14
from ConfigHandler import AdvConfigParser
15
15
16
- # TODO: Refactor the bothandler to be per-bot and have it handle reconnect.
17
- # TODO: There should be a new class BotManager that maintains a list of BotHandlers that are running.
16
+
18
17
class BotHandler (object ):
19
- botList = {}
20
-
21
- @classmethod
22
- def addBot (cls , botname , bot ):
23
- if not botname in cls .botList or not cls .botList [botname ]:
24
- cls .botList [botname ] = bot
25
-
26
- @classmethod
27
- def remBot (cls , botname ):
28
- if botname in cls .botList and cls .botList [botname ]:
29
- retval = cls .botList [botname ]
30
- cls .botList .__delitem__ (botname )
31
- retval .onShuttingDown ()
32
- return retval
33
- return None
34
-
35
- @classmethod
36
- def runAll (cls ):
37
- cls .startAll ()
38
- cls .loop ()
39
-
40
- @classmethod
41
- def setAllKilled (cls ):
42
- for bot in cls .botList .values ():
43
- bot .isTerminating = True
44
-
45
- @classmethod
46
- def stopAll (cls ):
47
- for bot in cls .botList .values ():
48
- bot .onShuttingDown ()
49
-
50
- @classmethod
51
- def loop (cls ):
52
- try :
53
- asyncore .loop ()
54
- except KeyboardInterrupt as e :
55
- print ("Keyboard Interrupt: Shutting down." )
56
- cls .setAllKilled ()
57
- cls .stopAll ()
58
- except :
59
- print ('Other Exception: Shutting down.' )
60
- cls .stopAll ()
61
- raise
62
18
63
- @classmethod
64
- def startAll (cls ):
65
- for bot in cls .botList .values ():
66
- bot .onStartUp ()
67
- bot .connect ()
19
+ def __init__ (self , bot , reconnect_wait = 15 , reset_attempt_secs = 300 , max_reconnects = 10 ):
20
+ self .bot = bot
21
+ self .reconnect_wait = reconnect_wait
22
+ self .reset_attempt_secs = reset_attempt_secs
23
+ self .max_reconnects = max_reconnects
24
+
25
+ def start (self ):
26
+ self .bot .onStartUp ()
27
+ self .bot .connect ()
28
+ return self
29
+
30
+ def stop (self ):
31
+ self .bot .onShuttingDown ()
32
+ return self
33
+
34
+ def setKilled (self ):
35
+ self .bot .isTerminating = True
36
+ return self
37
+
38
+ def run (self ):
39
+ restart = True
40
+ last_start = 0
41
+ reconnect_attempts = 0
42
+
43
+ while restart :
44
+ if last_start != 0 and reconnect_attempts != 0 :
45
+ self .bot .logger .warning ('Attempting IRC reconnection in %d seconds...' % self .reconnect_wait )
46
+ time .sleep (self .reconnect_wait )
47
+
48
+ last_start = time .time ()
49
+
50
+ if not self .bot .isRunning :
51
+ self .bot .resetSockets ()
52
+ self .start ()
53
+
54
+ try :
55
+ asyncore .loop ()
56
+ except KeyboardInterrupt as e :
57
+ print ("Keyboard Interrupt: Shutting down." )
58
+ self .stop ()
59
+ restart = False
60
+ except :
61
+ print ('Other Exception: Shutting down.' )
62
+ self .stop ()
63
+ raise
64
+
65
+ if not self .bot .isTerminating :
66
+ self .bot .logger .warning ('IRC connection was lost.' )
67
+
68
+ if time .time () - last_start > self .reset_attempt_secs :
69
+ reconnect_attempts = 0
70
+
71
+ reconnect_attempts += 1
72
+ restart = reconnect_attempts <= self .max_reconnects
73
+
68
74
69
75
class BotBase (object ):
70
- def __init__ (self , configfile = None , nspass = None , backupcfg = False ):
76
+ def __init__ (self , configfile = None , nspass = None , backupcfg = False ):
71
77
self .configfile = configfile if configfile else 'bot.cfg'
72
78
73
79
if backupcfg and os .path .exists (self .configfile ):
@@ -146,20 +152,17 @@ def __init__(self, configfile = None, nspass = None, backupcfg=False):
146
152
self .isIdentified = False #Turn to true when nick/ident commands are sent
147
153
self .isReady = False #Turn to true after RPL_ENDOFMOTD. Every join/nick etc commands should be sent once this is True.
148
154
self .isTerminating = False #This is set to true by the stop command to bypass restarting
149
- self .isRunning = True
155
+ self .isRunning = False
150
156
151
- self .socket = AsyncSocket .AsyncSocket (self , self .host , self .port , self .floodLimit )
152
- self .dccSocketv4 = DCCSocket .DCCSocket (self , False )
153
- if socket .has_ipv6 :
154
- try :
155
- self .dccSocketv6 = DCCSocket .DCCSocket (self , True )
156
- except socket .error , e :
157
- if e .message .find ('A socket operation was attempted to an unreachable network' ) != - 1 :
158
- self .dccSocketv6 = None
159
- self .cmdHandler = CmdHandler (self , self .socket )
157
+ self .socket = None
158
+ self .dccSocketv4 = None
159
+ self .dccSocketv6 = None
160
+ self .resetSockets ()
161
+ self .cmdHandler = CmdHandler (self )
160
162
161
- self .registerCommand ('dcc' , self .requestDCC , ['any' ], 0 , 0 , "" , "Requests a DCC connection to the bot." )
162
- self .registerCommand ('more' , self .sendMore , ['any' ], 0 , 1 , "[clear]" , 'Gets the next %d queued command results. Commands that can queue results will tell you so.' % self .moreCount )
163
+ self .registerCommand ('dcc' , self .requestDCC , ['any' ], 0 , 0 , "" , "Requests a DCC connection to the bot." )
164
+ #self.registerCommand('pub', self.pubCmd, ['any'], 1, 999, "<command>", "Executes a command and sends the results to the destination channel.")
165
+ self .registerCommand ('more' , self .sendMore , ['any' ], 0 , 1 , "[clear]" , 'Gets the next %d queued command results. Commands that can queue results will tell you so.' % self .moreCount )
163
166
164
167
self .registerCommand ('useradd' , self .useradd , ['admin' ], 2 , 2 , "<user> <group>" ,"Adds user to group." )
165
168
self .registerCommand ('userrm' , self .userrm , ['admin' ], 2 , 2 , "<user> <group>" ,"Removes user from group." )
@@ -171,7 +174,6 @@ def __init__(self, configfile = None, nspass = None, backupcfg=False):
171
174
self .registerCommand ('groupget' , self .groupget , ['admin' ], 0 , 0 , "" , "Returns a list of groups and commands." )
172
175
self .registerCommand ('groupmeta' , self .groupmeta , ['admin' ], 3 , 999 , "<group> <key> <value>" , "Add a value to a group key" )
173
176
174
-
175
177
self .registerCommand ('banadd' , self .banadd , ['admin' ], 2 , 2 , "<user|host> <cmd>" , "Bans the user from using the specified command." )
176
178
self .registerCommand ('banrm' , self .banrm , ['admin' ], 2 , 2 , "<user|host> <cmd>" , "Remove a ban on an user." )
177
179
self .registerCommand ('banget' , self .banget , ['admin' ], 1 , 1 , "<user|host>" , "Returns the ban list for the given user." )
@@ -184,6 +186,62 @@ def __init__(self, configfile = None, nspass = None, backupcfg=False):
184
186
if self .about_msg and self .about_msg != '' :
185
187
self .registerCommand ('about' , self .aboutcmd , ['any' ], 0 , 0 , '' , 'About this bot.' )
186
188
189
+ def resetSockets (self ):
190
+ self .socket = AsyncSocket .AsyncSocket (self , self .host , self .port , self .floodLimit )
191
+ self .dccSocketv4 = DCCSocket .DCCSocket (self , False )
192
+ if socket .has_ipv6 :
193
+ try :
194
+ self .dccSocketv6 = DCCSocket .DCCSocket (self , True )
195
+ except socket .error , e :
196
+ if e .message .find ('A socket operation was attempted to an unreachable network' ) != - 1 :
197
+ self .dccSocketv6 = None
198
+
199
+ def run (self ):
200
+ if self .host == "" :
201
+ self .logger .error ("Please set an IRC server in the config file." )
202
+ return
203
+
204
+ self .onStartUp ()
205
+ self .connect ()
206
+ try :
207
+ asyncore .loop ()
208
+ except KeyboardInterrupt as e :
209
+ self .logger .info ("Shutting down." )
210
+ if not self .isTerminating :
211
+ self .isTerminating = True
212
+ self .onShuttingDown ()
213
+ except :
214
+ self .onShuttingDown ()
215
+ raise
216
+
217
+ def connect (self ):
218
+ self .isRunning = True
219
+ self .socket .doConnect ()
220
+
221
+ def onStartUp (self ):
222
+ pass
223
+
224
+ def onShuttingDown (self ):
225
+ if self .isRunning :
226
+ self .logger .info ('Shutting down bot' )
227
+ if self .cmdHandler .monitor_thread :
228
+ self .cmdHandler .monitor_thread .cancel ()
229
+ for user in self .users .values ():
230
+ if user .dccSocket :
231
+ user .dccSocket .handle_close ()
232
+
233
+ self .isRunning = False
234
+ self .isReady = False
235
+ self .isIdentified = False
236
+ self .dccSocketv4 .handle_close ()
237
+ if self .dccSocketv6 :
238
+ self .dccSocketv6 .handle_close ()
239
+ self .socket .handle_close ()
240
+
241
+ def killSelf (self , bot , sender , dest , cmd , args ):
242
+ self .logger .info ("Killing self." )
243
+ self .isTerminating = True
244
+
187
245
# User handling commands
188
246
def useradd (self , bot , sender , dest , cmd , args ):
189
247
user = args [0 ].lower ()
@@ -348,7 +406,7 @@ def helpcmd(self, bot, sender, dest, cmd, args):
348
406
maxcmdlen = len (max (self .cmdHandler .commands .keys (), key = len ))
349
407
maxargslen = len (max ([i ['descargs' ] for i in self .cmdHandler .commands .values ()], key = len ))
350
408
351
- formatstr = "§B%%%ds %%%ds§N : %%s" % (maxcmdlen * - 1 , maxargslen * - 1 )
409
+ formatstr = "§B%%%ds %%%ds§N : %%s" % (maxcmdlen * - 1 , maxargslen * - 1 )
352
410
showall = len (args ) > 0 and args [0 ] == '*'
353
411
allowedcmds = []
354
412
@@ -507,49 +565,6 @@ def updateConfig(self):
507
565
508
566
self .config .write (fp )
509
567
510
- def run (self ):
511
- if self .host == "" :
512
- self .logger .error ("Please set an IRC server in the config file." )
513
- return
514
-
515
- self .onStartUp ()
516
- self .connect ()
517
- try :
518
- asyncore .loop ()
519
- except KeyboardInterrupt as e :
520
- self .logger .info ("Shutting down." )
521
- if not self .isTerminating :
522
- self .isTerminating = True
523
- self .onShuttingDown ()
524
- except :
525
- self .onShuttingDown ()
526
- raise
527
-
528
- def connect (self ):
529
- self .socket .doConnect ()
530
-
531
- def onStartUp (self ):
532
- pass
533
-
534
- def onShuttingDown (self ):
535
- if self .isRunning :
536
- self .logger .info ('Shutting down bot' )
537
- if self .cmdHandler .monitor_thread :
538
- self .cmdHandler .monitor_thread .cancel ()
539
- for user in self .users .values ():
540
- if user .dccSocket :
541
- user .dccSocket .handle_close ()
542
-
543
- self .isRunning = False
544
- self .dccSocketv4 .handle_close ()
545
- if self .dccSocketv6 :
546
- self .dccSocketv6 .handle_close ()
547
- self .socket .handle_close ()
548
-
549
- def killSelf (self , bot , sender , dest , cmd , args ):
550
- self .logger .info ("Killing self." )
551
- self .isTerminating = True
552
-
553
568
#IRC COMMANDS QUICK ACCESS
554
569
def sendRaw (self , msg ):
555
570
for line in [line .strip ('\r \n ' ) for line in msg .split (EOL ) if line .strip ('\r \n ' ) != '' ]:
0 commit comments