From 06466187b6a7a5a077f0f6df7b0fe2fc899ca0e3 Mon Sep 17 00:00:00 2001 From: dngfx Date: Fri, 31 Aug 2018 18:23:46 +0100 Subject: Introduce .killers for ducks.py --- modules/ducks.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/modules/ducks.py b/modules/ducks.py index 54b5bc61..72aca2f8 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -24,8 +24,8 @@ class Module(object): events.on("received.command.friends").hook(self.duck_friends, help="See who the friendliest people to ducks are!") - # events.on("received.command.killers").hook(self.duck_killers, - # help="See who shoots the most amount of ducks.") + events.on("received.command.killers").hook(self.duck_enemies, + help="See who shoots the most amount of ducks.") # events.on("received.command.ducks").hook(self.duck_list, # help="Shows a list of the most popular duck superstars.") @@ -98,6 +98,38 @@ class Module(object): def decoy_time(self): return random.randint(10, 20) + def duck_enemies(self, event): + the_enemy = event["server"].find_all_user_channel_settings( + "ducks-shot") + + notorious = {} + enemy_nicks = [] + enemy_ducks = [] + + for i in the_enemy: + if i[1] in notorious.keys(): + notorious[i[1]] += i[2] + else: + notorious[i[1]] = i[2] + + for user, enemies in sorted(notorious.items(), key=itemgetter(1), + reverse=True): + enemy_nicks.append(user) + enemy_ducks.append(enemies) + + sentence = "Most Notorious Users -- " + + length = len(enemy_nicks) if len(enemy_nicks) < 11 else 11 + + for i in range(0, length): + sentence += enemy_nicks[i] + " (" + str(enemy_ducks[i]) + ")" + if i < 10: + sentence += ", " + + sentence = sentence[0:-2] + + event["stdout"].write(sentence) + def duck_friends(self, event): friends = event["server"].find_all_user_channel_settings( "ducks-befriended") -- cgit v1.3.1-10-gc9f91 From 4a9d823d54ce2341cccb5947c7b16f612fd7251c Mon Sep 17 00:00:00 2001 From: dngfx Date: Fri, 31 Aug 2018 18:31:46 +0100 Subject: Adjust timing for production. --- modules/ducks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ducks.py b/modules/ducks.py index 72aca2f8..b79b6d97 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -30,7 +30,7 @@ class Module(object): # help="Shows a list of the most popular duck superstars.") now = datetime.datetime.now() - next_duck_time = random.randint(30, 40) + next_duck_time = random.randint(120, 1200) self.duck_times = {} self.decoys = {} @@ -96,7 +96,7 @@ class Module(object): return random.randint(self.duck_times[min], self.duck_times[max]) def decoy_time(self): - return random.randint(10, 20) + return random.randint(60, 180) def duck_enemies(self, event): the_enemy = event["server"].find_all_user_channel_settings( -- cgit v1.3.1-10-gc9f91 From cc21f1d9a3b9798e4a095813467b8c6b6535f878 Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 01:56:07 +0100 Subject: Set a timer for sending out ducks randomly, and change some messages to stdout. --- modules/ducks.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/modules/ducks.py b/modules/ducks.py index b79b6d97..3fe00c17 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -1,6 +1,8 @@ from operator import itemgetter +from threading import Timer import datetime import random +import time import IRCLogging @@ -14,6 +16,7 @@ class Module(object): self.events = events self.active_duck = 0 self.decoy_hooked = 0 + self.duck_timer = None events.on("received.command.bef").hook(self.duck_bef, help="Befriend a duck!") @@ -30,8 +33,9 @@ class Module(object): # help="Shows a list of the most popular duck superstars.") now = datetime.datetime.now() - next_duck_time = random.randint(120, 1200) + next_duck_time = self.get_random_duck_time() + self.duck_timer = None self.duck_times = {} self.decoys = {} @@ -50,11 +54,16 @@ class Module(object): "channelset").assure_call(setting="max-duck-time", help="Max seconds before a duck is summoned") - events.on("timer").on("duck-appear").hook(self.show_duck) - bot.add_timer("duck-appear", next_duck_time, persist=False) - events.on("received.numeric.366").hook(self.bootstrap) + events.on("raw").on("376").hook(self.duck_loop_entry) + + def duck_loop_entry(self, event): + wait = self.get_random_duck_time() + self.timer = Timer(wait, self.show_duck, [event]) + print(" Sending out animal in %s seconds" % wait) + self.timer.start() + def bootstrap(self, event): for server in self.bot.servers.values(): for channel in server.channels.values(): @@ -65,8 +74,8 @@ class Module(object): min_time = "min-duck-time-%s" % channel.name max_time = "max-duck-time-%s" % channel.name - min_duck_time = channel.get_setting("min-duck-time", 240) - max_duck_time = channel.get_setting("max-duck-time", 1200) + min_duck_time = channel.get_setting("min-duck-time", 20) + max_duck_time = channel.get_setting("max-duck-time", 30) min_duck_time = int(min_duck_time) if isinstance(min_duck_time, str) else min_duck_time @@ -196,8 +205,7 @@ class Module(object): befriended_ducks + 1) + " duck" + grammar + " in " + event[ "target"].name) - next_duck_time = self.duck_time(event) - self.bot.add_timer("duck-appear", next_duck_time, persist=False) + self.duck_loop_entry(event) def duck_bang(self, event): user = event["user"] @@ -225,8 +233,10 @@ class Module(object): shot_ducks + 1) + " duck" + grammar + " in " + event[ "target"].name) - next_duck_time = self.duck_time(event) - self.bot.add_timer("duck-appear", next_duck_time, persist=False) + self.duck_loop_entry(event) + + def get_random_duck_time(self): + return random.randint(120, 1200) def show_duck(self, event): for server in self.bot.servers.values(): @@ -254,7 +264,7 @@ class Module(object): "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" ] - channel.send_message(random.choice(ducks)) + event["stdout"].write(random.choice(ducks)) channel.set_setting("active-duck", 1) @@ -264,10 +274,6 @@ class Module(object): else: channel.set_setting("active-duck", 0) - next_duck_time = self.duck_time(channel.name) - self.bot.add_timer("duck-appear", next_duck_time, - persist=False) - def duck_decoy(self, event): ducks = [ "・゜゜・。。・゜ ​ ゜\_O​< q​uack!", @@ -279,7 +285,7 @@ class Module(object): "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" ] - event["channel"].send_message(random.choice(ducks)) + event["stdout"].write(random.choice(ducks)) def set_decoy(self, event): channel = event["target"] -- cgit v1.3.1-10-gc9f91 From e5d298af60171151d3844bdff5208eb4830947ac Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 02:21:57 +0100 Subject: Fix this back to channel.send_message(), stdout just no. --- modules/ducks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ducks.py b/modules/ducks.py index 3fe00c17..7e1dba33 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -264,7 +264,7 @@ class Module(object): "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" ] - event["stdout"].write(random.choice(ducks)) + channel.send_message(random.choice(ducks)) channel.set_setting("active-duck", 1) -- cgit v1.3.1-10-gc9f91 -- cgit v1.3.1-10-gc9f91 From 3d3cb9a598f09a5d7bf31288dd207b4df78a0d59 Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 09:09:02 +0100 Subject: Switch print() to log.info() for next-duck-wave information. --- modules/ducks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/ducks.py b/modules/ducks.py index 7e1dba33..0680fd4d 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -61,7 +61,8 @@ class Module(object): def duck_loop_entry(self, event): wait = self.get_random_duck_time() self.timer = Timer(wait, self.show_duck, [event]) - print(" Sending out animal in %s seconds" % wait) + self.bot.log.info("Sending out a wave of ducks in %s seconds", + [wait]) self.timer.start() def bootstrap(self, event): @@ -98,9 +99,9 @@ class Module(object): max = "max-duck-time-%s" % (channel_name) self.bot.log.debug("Attempting to set %s to %s", - [str(min), str(self.duck_times[min])]); + [str(min), str(self.duck_times[min])]) self.bot.log.debug("Attempting to set %s to %s", - [str(max), str(self.duck_times[max])]); + [str(max), str(self.duck_times[max])]) return random.randint(self.duck_times[min], self.duck_times[max]) -- cgit v1.3.1-10-gc9f91 From 3f66940e772702068288d072c96eb93020bd4873 Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 10:22:44 +0100 Subject: Remove superfluous code from ducks.py and introduce dice.py (DND rolling function .roll 1d20) --- modules/dice.py | 43 +++++++++++++++++++++++++++++++++++++++++++ modules/ducks.py | 49 ++++++++++++------------------------------------- 2 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 modules/dice.py diff --git a/modules/dice.py b/modules/dice.py new file mode 100644 index 00000000..9862b172 --- /dev/null +++ b/modules/dice.py @@ -0,0 +1,43 @@ +import random + + +class Module(object): + def __init__(self, bot, events): + events.on("received.command.roll").hook( + self.roll_dice, + min_args=1, + help="Roll some dice, DND style!", + usage="[1-5]d[1-20]" + ) + + self.err_msg = "Incorrectly formatted dice! Format must be [number]d[number], for example, 1d20" + + def roll_dice(self, event): + raw_input = event["args_split"][0] + roll = raw_input.split("d") + results = [] + + if len(roll) is not 2: + event["stderr"].write(self.err_msg) + return + + if roll[0].isdigit() is False or roll[1].isdigit() is False: + event["stderr"].write(self.err_msg) + return + + roll = [int(roll[0]), int(roll[1])] + + num_of_die = 5 if roll[0] > 5 else roll[0] + sides_of_die = 20 if roll[1] > 20 else roll[1] + + str_roll = str(num_of_die) + "d" + str(sides_of_die) + + for i in range(0, num_of_die): + results.append(random.randint(1, sides_of_die)) + + total = sum(results) + results = ', '.join(map(str, results)) + + event["stdout"].write("Rolled " + str_roll + " for a total " + + "of " + str(total) + + ": [" + results + "]") diff --git a/modules/ducks.py b/modules/ducks.py index 0680fd4d..fac698f1 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -2,22 +2,25 @@ from operator import itemgetter from threading import Timer import datetime import random -import time - -import IRCLogging - -DUCK_LAST_SEEN = datetime.datetime.now() class Module(object): def __init__(self, bot, events): - self.log = IRCLogging.Log self.bot = bot self.events = events - self.active_duck = 0 self.decoy_hooked = 0 self.duck_timer = None + self.duck_list = [ + "・゜゜・。。・゜ ​ ゜\_O​< q​uack!", + "・゜゜・。。・゜ ​ ゜\_o< QUACK!", + "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack!", + "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack quack!", + "・゜゜ 。 ​ 。・゜ \​_ó< bawk!", + "・゜゜ 。 ​ 。・゜゜\​_ó< squawk!", + "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" + ] + events.on("received.command.bef").hook(self.duck_bef, help="Befriend a duck!") events.on("received.command.bang").hook(self.duck_bang, @@ -32,15 +35,7 @@ class Module(object): # events.on("received.command.ducks").hook(self.duck_list, # help="Shows a list of the most popular duck superstars.") - now = datetime.datetime.now() - next_duck_time = self.get_random_duck_time() - - self.duck_timer = None self.duck_times = {} - self.decoys = {} - - tricky = next_duck_time - now.second - tricky += ((next_duck_time - (now.minute + 1)) * 2) events.on("postboot").on("configure").on( "channelset").assure_call(setting="ducks-enabled", @@ -255,17 +250,7 @@ class Module(object): str) else active_duck if ducks_enabled == 1 and active_duck == 0: - ducks = [ - "・゜゜・。。・゜ ​ ゜\_O​< q​uack!", - "・゜゜・。。・゜ ​ ゜\_o< QUACK!", - "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack!", - "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack quack!", - "・゜゜ 。 ​ 。・゜ \​_ó< bawk!", - "・゜゜ 。 ​ 。・゜゜\​_ó< squawk!", - "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" - ] - - channel.send_message(random.choice(ducks)) + channel.send_message(random.choice(self.duck_list)) channel.set_setting("active-duck", 1) @@ -276,17 +261,7 @@ class Module(object): channel.set_setting("active-duck", 0) def duck_decoy(self, event): - ducks = [ - "・゜゜・。。・゜ ​ ゜\_O​< q​uack!", - "・゜゜・。。・゜ ​ ゜\_o< QUACK!", - "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack!", - "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack quack!", - "・゜゜ 。 ​ 。・゜ \​_ó< bawk!", - "・゜゜ 。 ​ 。・゜゜\​_ó< squawk!", - "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" - ] - - event["stdout"].write(random.choice(ducks)) + event["stdout"].write(random.choice(self.duck_list)) def set_decoy(self, event): channel = event["target"] -- cgit v1.3.1-10-gc9f91 From c284608cbdcbd4f402e21d3650c160a4116e46a6 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 1 Sep 2018 10:37:09 +0100 Subject: Formatting request (#14) * Add Database.UserChannelSettings.find_all_by_setting * Turns out we didn't need find_all_by_setting * Actually, we do need find_all_by_setting * Table name typo * Add Utils.bold and Utils.underline --- Utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Utils.py b/Utils.py index 773e3150..15d8e2be 100644 --- a/Utils.py +++ b/Utils.py @@ -123,6 +123,12 @@ def color(foreground, background=None): return "%s%s%s" % (FONT_COLOR, foreground, "" if not background else ",%s" % background) +def bold(s): + return "%s%s%s" % (FONT_BOLD, s, FONT_BOLD) + +def underline(s): + return "%s%s%s" % (FONT_UNDERLINE, s, FONT_UNDERLINE) + TIME_SECOND = 1 TIME_MINUTE = TIME_SECOND*60 TIME_HOUR = TIME_MINUTE*60 -- cgit v1.3.1-10-gc9f91 From 0a96a79077d82de51533d914296b7a51ca19eb3b Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 10:52:04 +0100 Subject: Cosmetic enhancement for ducks and dice. --- modules/dice.py | 8 ++++---- modules/ducks.py | 16 +++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/dice.py b/modules/dice.py index 9862b172..2829cf48 100644 --- a/modules/dice.py +++ b/modules/dice.py @@ -1,5 +1,5 @@ import random - +import Utils class Module(object): def __init__(self, bot, events): @@ -38,6 +38,6 @@ class Module(object): total = sum(results) results = ', '.join(map(str, results)) - event["stdout"].write("Rolled " + str_roll + " for a total " - + "of " + str(total) - + ": [" + results + "]") + event["stdout"].write("Rolled " + Utils.bold(str_roll) + " for a total " + + "of " + Utils.bold(str(total)) + + ": " + results) diff --git a/modules/ducks.py b/modules/ducks.py index fac698f1..e50cc055 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -1,6 +1,6 @@ from operator import itemgetter from threading import Timer -import datetime +import Utils import random @@ -197,9 +197,10 @@ class Module(object): grammar = "" if befriended_ducks == 0 else "s" event["stdout"].write( - target + ", you've befriended " + str( - befriended_ducks + 1) + " duck" + grammar + " in " + event[ - "target"].name) + target + ", you've befriended " + Utils.bold(str( + befriended_ducks + 1)) + " duck" + grammar + " in " + + Utils.bold(event[ + "target"].name)) self.duck_loop_entry(event) @@ -225,9 +226,10 @@ class Module(object): grammar = "" if shot_ducks == 0 else "s" event["stdout"].write( - target + ", you've shot " + str( - shot_ducks + 1) + " duck" + grammar + " in " + event[ - "target"].name) + target + ", you've shot " + + Utils.bold(str(shot_ducks + 1)) + " duck" + + grammar + " in " + + Utils.bold(event["target"].name)) self.duck_loop_entry(event) -- cgit v1.3.1-10-gc9f91 From 80cbf2d66eed9199635503968618d56db7c679ca Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 11:04:51 +0100 Subject: Add 8ball module --- modules/8all.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 modules/8all.py diff --git a/modules/8all.py b/modules/8all.py new file mode 100644 index 00000000..db5e4d3f --- /dev/null +++ b/modules/8all.py @@ -0,0 +1,30 @@ +import random + + +class Module(object): + def __init__(self, bot, events): + events.on("received.command.8ball").hook( + self.decide, + min_args=1, + help="Ask the mystic 8ball a question!", + usage="" + ) + + def decide(selfs, event): + choices = [ + "Definitely", + "Yes", + "Probably", + "Maybe", + "Probably not", + "No", + "Definitely not", + "I don't know", + "Ask again later", + "The answer is unclear", + "Absolutely", + "Dubious at best", + "I'm on a break, ask again later" + ] + + event["stdout"].write(random.choice(choices)) -- cgit v1.3.1-10-gc9f91 From 7db9a8ea2bee07a76a15cb5de51ab37ddc8dc707 Mon Sep 17 00:00:00 2001 From: dngfx Date: Sat, 1 Sep 2018 11:40:08 +0100 Subject: Move lists out of scope --- modules/8all.py | 33 ++++++++++++++++----------------- modules/ducks.py | 23 +++++++++++------------ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/modules/8all.py b/modules/8all.py index db5e4d3f..57be2da4 100644 --- a/modules/8all.py +++ b/modules/8all.py @@ -1,5 +1,20 @@ import random +CHOICES = [ + "Definitely", + "Yes", + "Probably", + "Maybe", + "Probably not", + "No", + "Definitely not", + "I don't know", + "Ask again later", + "The answer is unclear", + "Absolutely", + "Dubious at best", + "I'm on a break, ask again later" +] class Module(object): def __init__(self, bot, events): @@ -11,20 +26,4 @@ class Module(object): ) def decide(selfs, event): - choices = [ - "Definitely", - "Yes", - "Probably", - "Maybe", - "Probably not", - "No", - "Definitely not", - "I don't know", - "Ask again later", - "The answer is unclear", - "Absolutely", - "Dubious at best", - "I'm on a break, ask again later" - ] - - event["stdout"].write(random.choice(choices)) + event["stdout"].write(random.choice(CHOICES)) diff --git a/modules/ducks.py b/modules/ducks.py index e50cc055..44b03d00 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -3,6 +3,15 @@ from threading import Timer import Utils import random +DUCK_LIST = [ + "・゜゜・。。・゜ ​ ゜\_O​< q​uack!", + "・゜゜・。。・゜ ​ ゜\_o< QUACK!", + "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack!", + "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack quack!", + "・゜゜ 。 ​ 。・゜ \​_ó< bawk!", + "・゜゜ 。 ​ 。・゜゜\​_ó< squawk!", + "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" +] class Module(object): def __init__(self, bot, events): @@ -11,16 +20,6 @@ class Module(object): self.decoy_hooked = 0 self.duck_timer = None - self.duck_list = [ - "・゜゜・。。・゜ ​ ゜\_O​< q​uack!", - "・゜゜・。。・゜ ​ ゜\_o< QUACK!", - "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack!", - "・゜゜・。 ​ 。・゜゜\​_ó< qu​ack quack!", - "・゜゜ 。 ​ 。・゜ \​_ó< bawk!", - "・゜゜ 。 ​ 。・゜゜\​_ó< squawk!", - "・ ゜・。 ​ 。・゜゜ \​_ó< beep beep!" - ] - events.on("received.command.bef").hook(self.duck_bef, help="Befriend a duck!") events.on("received.command.bang").hook(self.duck_bang, @@ -252,7 +251,7 @@ class Module(object): str) else active_duck if ducks_enabled == 1 and active_duck == 0: - channel.send_message(random.choice(self.duck_list)) + channel.send_message(random.choice(DUCK_LIST)) channel.set_setting("active-duck", 1) @@ -263,7 +262,7 @@ class Module(object): channel.set_setting("active-duck", 0) def duck_decoy(self, event): - event["stdout"].write(random.choice(self.duck_list)) + event["stdout"].write(random.choice(DUCK_LIST)) def set_decoy(self, event): channel = event["target"] -- cgit v1.3.1-10-gc9f91 From ba065ad646748719aa4ca986193e3d40601fc91e Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 1 Sep 2018 11:40:57 +0100 Subject: omg it's the future (#15) * Add Database.UserChannelSettings.find_all_by_setting * Turns out we didn't need find_all_by_setting * Actually, we do need find_all_by_setting * Table name typo * Add Utils.bold and Utils.underline * Added functionality to load, unload and reload modules from a command! --- EventManager.py | 15 ++++++++++----- ModuleManager.py | 49 +++++++++++++++++++++++++++++++------------------ modules/modules.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 modules/modules.py diff --git a/EventManager.py b/EventManager.py index 4ca5837c..326192ad 100644 --- a/EventManager.py +++ b/EventManager.py @@ -179,7 +179,7 @@ class EventHook(object): returns.append(hook.call(event)) except Exception as e: traceback.print_exc() - self.bot.log.error("failed to call event \"%s", [ + self.bot.log.error("failed to call event \"%s\"", [ event_path], exc_info=True) called += 1 @@ -202,11 +202,9 @@ class EventHook(object): child_name_lower = child_name.lower() if child_name_lower in self._children: del self._children[child_name_lower] - def get_children(self): - return self._children.keys() def check_purge(self): - if len(self.get_hooks()) == 0 and len(self._children + if len(self.get_hooks()) == 0 and len(self.get_children() ) == 0 and not self.parent == None: self.parent.remove_child(self.name) self.parent.check_purge() @@ -218,9 +216,16 @@ class EventHook(object): def purge_context(self, context): if self.has_context(context): self.remove_context(context) - for child in self.get_children(): + for child_name in self.get_children()[:]: + child = self.get_child(child_name) child.purge_context(context) + if child.is_empty(): + self.remove_child(child_name) def get_hooks(self): return sorted(self._hooks + list(itertools.chain.from_iterable( self._context_hooks.values())), key=lambda e: e.priority) + def get_children(self): + return list(self._children.keys()) + def is_empty(self): + return len(self.get_hooks() + self.get_children()) == 0 diff --git a/ModuleManager.py b/ModuleManager.py index 812edb71..404502e8 100644 --- a/ModuleManager.py +++ b/ModuleManager.py @@ -10,13 +10,15 @@ class ModuleManager(object): def list_modules(self): return sorted(glob.glob(os.path.join(self.directory, "*.py"))) - def module_name(self, filename): - return os.path.basename(filename).rsplit(".py", 1)[0].lower() + def _module_name(self, path): + return os.path.basename(path).rsplit(".py", 1)[0].lower() + def _module_path(self, name): + return os.path.join(self.directory, "%s.py" % name) - def _load_module(self, filename): - name = self.module_name(filename) + def _load_module(self, name): + path = self._module_path(name) - with open(filename) as module_file: + with open(path) as module_file: while True: line = module_file.readline().strip() line_split = line.split(" ") @@ -36,11 +38,11 @@ class ModuleManager(object): if not "bitbot_%s" % line_split[1].lower() in sys.modules: if not line_split[1].lower() in self.waiting_requirement: self.waiting_requirement[line_split[1].lower()] = set([]) - self.waiting_requirement[line_split[1].lower()].add(filename) + self.waiting_requirement[line_split[1].lower()].add(path) return None else: break - module = imp.load_source(name, filename) + module = imp.load_source(name, path) if not hasattr(module, "Module"): raise ImportError("module '%s' doesn't have a Module class.") @@ -60,23 +62,34 @@ class ModuleManager(object): "module name '%s' attempted to be used twice.") return module_object - def load_module(self, filename): - name = self.module_name(filename) + def load_module(self, name): try: - module = self._load_module(filename) + module = self._load_module(name) except ImportError as e: - sys.stderr.write("module '%s' not loaded: Could not resolve import.\n" % filename) + self.bot.log.error("failed to load module \"%s\": %s", + [name, e.msg]) return if module: self.modules[module._import_name] = module if name in self.waiting_requirement: - for filename in self.waiting_requirement: - self.load_module(filename) - sys.stderr.write("module '%s' loaded.\n" % filename) + for requirement_name in self.waiting_requirement: + self.load_module(requirement_name) + self.bot.log.info("Module '%s' loaded", [name]) else: - sys.stderr.write("module '%s' not loaded.\n" % filename) + self.bot.log.error("Module '%s' not loaded", [name]) def load_modules(self, whitelist=None): - for filename in self.list_modules(): - if whitelist == None or filename in whitelist: - self.load_module(filename) + for path in self.list_modules(): + name = self._module_name(path) + if whitelist == None or name in whitelist: + self.load_module(name) + + def unload_module(self, name): + module = self.modules[name] + del self.modules[name] + + event_context = module._event_context + self.events.purge_context(event_context) + + del sys.modules[name] + del module diff --git a/modules/modules.py b/modules/modules.py new file mode 100644 index 00000000..0138be1b --- /dev/null +++ b/modules/modules.py @@ -0,0 +1,39 @@ + + +class Module(object): + def __init__(self, bot, events): + self.bot = bot + events.on("received.command.loadmodule").hook(self.load, + min_args=1, permission="load-module", help="Load a module", + usage="") + events.on("received.command.unloadmodule").hook(self.unload, + min_args=1, permission="unload-module", help="Unload a module", + usage="") + events.on("received.command.reloadmodule").hook(self.reload, + min_args=1, permission="reload-module", help="Reload a module", + usage="") + + def load(self, event): + name = event["args_split"][0].lower() + if name in self.bot.modules.modules: + event["stderr"].write("Module '%s' is already loaded" % name) + return + self.bot.modules.load_module(name) + event["stdout"].write("Loaded '%s'" % name) + + def unload(self, event): + name = event["args_split"][0].lower() + if not name in self.bot.modules.modules: + event["stderr"].write("Module '%s' isn't loaded" % name) + return + self.bot.modules.unload_module(name) + event["stdout"].write("Unloaded '%s'" % name) + + def reload(self, event): + name = event["args_split"][0].lower() + if not name in self.bot.modules.modules: + event["stderr"].write("Module '%s' isn't loaded" % name) + return + self.bot.modules.unload_module(name) + self.bot.modules.load_module(name) + event["stdout"].write("Reloaded '%s'" % name) -- cgit v1.3.1-10-gc9f91