77import re
88import warnings
99from collections import defaultdict
10+ from functools import partial
1011from itertools import chain
1112from operator import attrgetter
1213
1516
1617import time
1718
18- from cloudbot .event import Event
19+ from cloudbot .event import Event , PostHookEvent
1920from cloudbot .hook import Priority , Action
2021from cloudbot .util import database , async_util
2122
@@ -42,9 +43,11 @@ def find_hooks(parent, module):
4243 on_cap_available = []
4344 on_connect = []
4445 out_sieve = []
46+ post_hooks = []
4547 type_lists = {"command" : command , "regex" : regex , "irc_raw" : raw , "sieve" : sieve , "event" : event ,
4648 "periodic" : periodic , "on_start" : on_start , "on_stop" : on_stop , "on_cap_ack" : on_cap_ack ,
47- "on_cap_available" : on_cap_available , "on_connect" : on_connect , "irc_out" : out_sieve }
49+ "on_cap_available" : on_cap_available , "on_connect" : on_connect , "irc_out" : out_sieve ,
50+ "post_hook" : post_hooks }
4851 for name , func in module .__dict__ .items ():
4952 if hasattr (func , "_cloudbot_hook" ):
5053 # if it has cloudbot hook
@@ -56,7 +59,7 @@ def find_hooks(parent, module):
5659 # delete the hook to free memory
5760 del func ._cloudbot_hook
5861
59- return command , regex , raw , sieve , event , periodic , on_start , on_stop , on_cap_ack , on_cap_available , on_connect , out_sieve
62+ return command , regex , raw , sieve , event , periodic , on_start , on_stop , on_cap_ack , on_cap_available , on_connect , out_sieve , post_hooks
6063
6164
6265def find_tables (code ):
@@ -114,6 +117,7 @@ def __init__(self, bot):
114117 self .cap_hooks = {"on_available" : defaultdict (list ), "on_ack" : defaultdict (list )}
115118 self .connect_hooks = []
116119 self .out_sieves = []
120+ self .hook_hooks = defaultdict (list )
117121 self ._hook_waiting_queues = {}
118122
119123 @asyncio .coroutine
@@ -262,9 +266,13 @@ def load_plugin(self, path):
262266 self .out_sieves .append (out_hook )
263267 self ._log_hook (out_hook )
264268
269+ for post_hook in plugin .post_hook_hooks :
270+ self .hook_hooks ["post" ].append (post_hook )
271+ self ._log_hook (post_hook )
272+
265273 # Sort hooks
266274 self .regex_hooks .sort (key = lambda x : x [1 ].priority )
267- dicts_of_lists_of_hooks = (self .event_type_hooks , self .raw_triggers )
275+ dicts_of_lists_of_hooks = (self .event_type_hooks , self .raw_triggers , self . hook_hooks )
268276 lists_of_hooks = [self .catch_all_triggers , self .sieves , self .connect_hooks , self .out_sieves ]
269277 lists_of_hooks .extend (chain .from_iterable (d .values () for d in dicts_of_lists_of_hooks ))
270278
@@ -358,6 +366,9 @@ def unload_plugin(self, path):
358366 for out_hook in plugin .irc_out_hooks :
359367 self .out_sieves .remove (out_hook )
360368
369+ for post_hook in plugin .post_hook_hooks :
370+ self .hook_hooks ["post" ].remove (post_hook )
371+
361372 # Run on_stop hooks
362373 for on_stop_hook in plugin .run_on_stop :
363374 event = Event (bot = self .bot , hook = on_stop_hook )
@@ -450,9 +461,9 @@ def internal_launch(self, hook, event):
450461 out = yield from self .bot .loop .run_in_executor (None , self ._execute_hook_threaded , hook , event )
451462 else :
452463 out = yield from self ._execute_hook_sync (hook , event )
453- except Exception :
464+ except Exception as e :
454465 logger .exception ("Error in hook {}" .format (hook .description ))
455- return False , None
466+ return False , e
456467
457468 return True , out
458469
@@ -468,13 +479,22 @@ def _execute_hook(self, hook, event):
468479 :rtype: bool
469480 """
470481 ok , out = yield from self .internal_launch (hook , event )
471- if out is not None :
472- if isinstance (out , (list , tuple )):
473- # if there are multiple items in the response, return them on multiple lines
474- event .reply (* out )
475- else :
476- event .reply (* str (out ).split ('\n ' ))
477- return True
482+ result , error = None , None
483+ if ok is True :
484+ result = out
485+ else :
486+ error = out
487+
488+ post_event = partial (
489+ PostHookEvent , launched_hook = hook , launched_event = event , bot = event .bot ,
490+ conn = event .conn , result = result , error = error
491+ )
492+ for post_hook in self .hook_hooks ["post" ]:
493+ success , res = yield from self .internal_launch (post_hook , post_event (hook = post_hook ))
494+ if success and res is False :
495+ break
496+
497+ return ok
478498
479499 @asyncio .coroutine
480500 def _sieve (self , sieve , event , hook ):
@@ -524,10 +544,6 @@ def launch(self, hook, event):
524544 if event is None :
525545 return False
526546
527- if hook .type == "command" and hook .auto_help and not event .text and hook .doc is not None :
528- event .notice_doc ()
529- return False
530-
531547 if hook .single_thread :
532548 # There should only be one running instance of this hook, so let's wait for the last event to be processed
533549 # before starting this one.
@@ -600,6 +616,7 @@ def __init__(self, filepath, filename, title, code):
600616 self .sieves , self .events , self .periodic , * hooks = hooks
601617 self .run_on_start , self .run_on_stop , self .on_cap_ack , * hooks = hooks
602618 self .on_cap_available , self .connect_hooks , self .irc_out_hooks , * hooks = hooks
619+ self .post_hook_hooks , * hooks = hooks
603620 # we need to find tables for each plugin so that they can be unloaded from the global metadata when the
604621 # plugin is reloaded
605622 self .tables = find_tables (code )
@@ -803,9 +820,6 @@ def __init__(self, plugin, sieve_hook):
803820 :type plugin: Plugin
804821 :type sieve_hook: cloudbot.util.hook._SieveHook
805822 """
806-
807- self .priority = sieve_hook .kwargs .pop ("priority" , 100 )
808- # We don't want to thread sieves by default - this is retaining old behavior for compatibility
809823 super ().__init__ ("sieve" , plugin , sieve_hook )
810824
811825 def __repr__ (self ):
@@ -891,8 +905,6 @@ def __init__(self, plugin, sieve_hook):
891905 :type plugin: Plugin
892906 :type sieve_hook: cloudbot.util.hook._Hook
893907 """
894-
895- self .priority = sieve_hook .kwargs .pop ("priority" , 100 )
896908 super ().__init__ ("on_connect" , plugin , sieve_hook )
897909
898910 def __repr__ (self ):
@@ -913,6 +925,17 @@ def __str__(self):
913925 return "irc_out {} from {}" .format (self .function_name , self .plugin .file_name )
914926
915927
928+ class PostHookHook (Hook ):
929+ def __init__ (self , plugin , out_hook ):
930+ super ().__init__ ("post_hook" , plugin , out_hook )
931+
932+ def __repr__ (self ):
933+ return "Post_hook[{}]" .format (Hook .__repr__ (self ))
934+
935+ def __str__ (self ):
936+ return "post_hook {} from {}" .format (self .function_name , self .plugin .file_name )
937+
938+
916939_hook_name_to_plugin = {
917940 "command" : CommandHook ,
918941 "regex" : RegexHook ,
@@ -926,4 +949,5 @@ def __str__(self):
926949 "on_cap_ack" : OnCapAckHook ,
927950 "on_connect" : OnConnectHook ,
928951 "irc_out" : IrcOutHook ,
952+ "post_hook" : PostHookHook ,
929953}
0 commit comments