Refactors hooks registration

This commit is contained in:
nemunaire 2015-11-02 20:19:12 +01:00
parent 49d7e4ced6
commit f39a0eac56
40 changed files with 202 additions and 168 deletions

View File

@ -155,7 +155,7 @@ def replace_variables(cnts, msg=None):
## Variables management ## Variables management
@hook("cmd_hook", "listvars", @hook.command("listvars",
help="list defined variables for substitution in input commands", help="list defined variables for substitution in input commands",
help_usage={ help_usage={
None: "List all known variables", None: "List all known variables",
@ -178,7 +178,7 @@ def cmd_listvars(msg):
return Response("There is currently no variable stored.", channel=msg.channel) return Response("There is currently no variable stored.", channel=msg.channel)
@hook("cmd_hook", "set", @hook.command("set",
help="Create or set variables for substitution in input commands", help="Create or set variables for substitution in input commands",
help_usage={"KEY VALUE": "Define the variable named KEY and fill it with VALUE as content"}) help_usage={"KEY VALUE": "Define the variable named KEY and fill it with VALUE as content"})
def cmd_set(msg): def cmd_set(msg):
@ -191,7 +191,7 @@ def cmd_set(msg):
## Alias management ## Alias management
@hook("cmd_hook", "listalias", @hook.command("listalias",
help="List registered aliases", help="List registered aliases",
help_usage={ help_usage={
None: "List all registered aliases", None: "List all registered aliases",
@ -205,7 +205,7 @@ def cmd_listalias(msg):
return Response("There is no alias currently.", channel=msg.channel) return Response("There is no alias currently.", channel=msg.channel)
@hook("cmd_hook", "alias", @hook.command("alias",
help="Display the replacement command for a given alias") help="Display the replacement command for a given alias")
def cmd_alias(msg): def cmd_alias(msg):
if not len(msg.args): if not len(msg.args):
@ -221,7 +221,7 @@ def cmd_alias(msg):
return Response(res, channel=msg.channel, nick=msg.nick) return Response(res, channel=msg.channel, nick=msg.nick)
@hook("cmd_hook", "unalias", @hook.command("unalias",
help="Remove a previously created alias") help="Remove a previously created alias")
def cmd_unalias(msg): def cmd_unalias(msg):
if not len(msg.args): if not len(msg.args):
@ -242,7 +242,7 @@ def cmd_unalias(msg):
## Alias replacement ## Alias replacement
@hook("pre_Command") @hook.add("pre_Command")
def treat_alias(msg): def treat_alias(msg):
if msg.cmd in context.data.getNode("aliases").index: if msg.cmd in context.data.getNode("aliases").index:
txt = context.data.getNode("aliases").index[msg.cmd]["origin"] txt = context.data.getNode("aliases").index[msg.cmd]["origin"]
@ -263,7 +263,7 @@ def treat_alias(msg):
return msg return msg
@hook("ask_default") @hook.ask()
def parseask(msg): def parseask(msg):
if re.match(".*(register|set|cr[ée]{2}|new|nouvel(le)?) alias.*", msg.text) is not None: if re.match(".*(register|set|cr[ée]{2}|new|nouvel(le)?) alias.*", msg.text) is not None:
result = re.match(".*alias !?([^ ]+) ?(pour|for|=|:) ?(.+)$", msg.text) result = re.match(".*alias !?([^ ]+) ?(pour|for|=|:) ?(.+)$", msg.text)

View File

@ -46,7 +46,7 @@ def findName(msg):
## Commands ## Commands
@hook("cmd_hook", "anniv", @hook.command("anniv",
help="gives the remaining time before the anniversary of known people", help="gives the remaining time before the anniversary of known people",
help_usage={ help_usage={
None: "Calculate the time remaining before your birthday", None: "Calculate the time remaining before your birthday",
@ -80,7 +80,7 @@ def cmd_anniv(msg):
msg.channel, msg.nick) msg.channel, msg.nick)
@hook("cmd_hook", "age", @hook.command("age",
help="Calculate age of known people", help="Calculate age of known people",
help_usage={ help_usage={
None: "Calculate your age", None: "Calculate your age",
@ -104,7 +104,7 @@ def cmd_age(msg):
## Input parsing ## Input parsing
@hook("ask_default") @hook.ask()
def parseask(msg): def parseask(msg):
res = re.match(r"^(\S+)\s*('s|suis|est|is|was|were)?\s+(birthday|geburtstag|née? |nee? le|born on).*$", msg.text, re.I) res = re.match(r"^(\S+)\s*('s|suis|est|is|was|were)?\s+(birthday|geburtstag|née? |nee? le|born on).*$", msg.text, re.I)
if res is not None: if res is not None:

View File

@ -47,9 +47,9 @@ def load(context):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "newyear", @hook.command("newyear",
help="Display the remaining time before the next new year") help="Display the remaining time before the next new year")
@hook("cmd_hook", str(yrn), @hook.command(str(yrn),
help="Display the remaining time before %d" % yrn) help="Display the remaining time before %d" % yrn)
def cmd_newyear(msg): def cmd_newyear(msg):
return Response(countdown_format(datetime(yrn, 1, 1, 0, 0, 1, 0, return Response(countdown_format(datetime(yrn, 1, 1, 0, 0, 1, 0,
@ -59,7 +59,7 @@ def cmd_newyear(msg):
channel=msg.channel) channel=msg.channel)
@hook("cmd_rgxp", data=yrn, regexp="^[0-9]{4}$", @hook.command(data=yrn, regexp="^[0-9]{4}$",
help="Calculate time remaining/passed before/since the requested year") help="Calculate time remaining/passed before/since the requested year")
def cmd_timetoyear(msg, cur): def cmd_timetoyear(msg, cur):
yr = int(msg.cmd) yr = int(msg.cmd)

View File

@ -58,7 +58,7 @@ def search_author(name):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "book", @hook.command("book",
help="Get information about a book from its title", help="Get information about a book from its title",
help_usage={ help_usage={
"TITLE": "Get information about a book titled TITLE" "TITLE": "Get information about a book titled TITLE"
@ -77,7 +77,7 @@ def cmd_book(msg):
return res return res
@hook("cmd_hook", "search_books", @hook.command("search_books",
help="Search book's title", help="Search book's title",
help_usage={ help_usage={
"APPROX_TITLE": "Search for a book approximately titled APPROX_TITLE" "APPROX_TITLE": "Search for a book approximately titled APPROX_TITLE"
@ -97,7 +97,7 @@ def cmd_books(msg):
return res return res
@hook("cmd_hook", "author_books", @hook.command("author_books",
help="Looking for books writen by a given author", help="Looking for books writen by a given author",
help_usage={ help_usage={
"AUTHOR": "Looking for books writen by AUTHOR" "AUTHOR": "Looking for books writen by AUTHOR"

View File

@ -72,7 +72,7 @@ def compute_line(line, stringTens):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "conjugaison", @hook.command("conjugaison",
help_usage={ help_usage={
"TENS VERB": "give the conjugaison for VERB in TENS." "TENS VERB": "give the conjugaison for VERB in TENS."
}) })

View File

@ -16,7 +16,7 @@ URL = 'https://ctftime.org/event/list/upcoming'
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "ctfs", @hook.command("ctfs",
help="Display the upcoming CTFs") help="Display the upcoming CTFs")
def get_info_yt(msg): def get_info_yt(msg):
soup = BeautifulSoup(getURLContent(URL)) soup = BeautifulSoup(getURLContent(URL))

View File

@ -20,7 +20,7 @@ def get_cve(cve_id):
return desc[17].text.replace("\n", " ") + " Moar at " + search_url return desc[17].text.replace("\n", " ") + " Moar at " + search_url
@hook("cmd_hook", "cve") @hook.command("cve")
def get_cve_desc(msg): def get_cve_desc(msg):
res = Response(channel=msg.channel) res = Response(channel=msg.channel)

View File

@ -103,7 +103,7 @@ class DDGResult:
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "define") @hook.command("define")
def define(msg): def define(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("Indicate a term to define") raise IMException("Indicate a term to define")
@ -115,7 +115,7 @@ def define(msg):
return Response(s.definition, channel=msg.channel) return Response(s.definition, channel=msg.channel)
@hook("cmd_hook", "search") @hook.command("search")
def search(msg): def search(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("Indicate a term to search") raise IMException("Indicate a term to search")

View File

@ -38,7 +38,7 @@ def fini(d, strend):
context.data.delChild(context.data.index[strend["name"]]) context.data.delChild(context.data.index[strend["name"]])
context.save() context.save()
@hook("cmd_hook", "goûter") @hook.command("goûter")
def cmd_gouter(msg): def cmd_gouter(msg):
ndate = datetime.now(timezone.utc) ndate = datetime.now(timezone.utc)
ndate = datetime(ndate.year, ndate.month, ndate.day, 16, 42, 0, 0, timezone.utc) ndate = datetime(ndate.year, ndate.month, ndate.day, 16, 42, 0, 0, timezone.utc)
@ -47,7 +47,7 @@ def cmd_gouter(msg):
"Nous avons %s de retard pour le goûter :("), "Nous avons %s de retard pour le goûter :("),
channel=msg.channel) channel=msg.channel)
@hook("cmd_hook", "week-end") @hook.command("week-end")
def cmd_we(msg): def cmd_we(msg):
ndate = datetime.now(timezone.utc) + timedelta(5 - datetime.today().weekday()) ndate = datetime.now(timezone.utc) + timedelta(5 - datetime.today().weekday())
ndate = datetime(ndate.year, ndate.month, ndate.day, 0, 0, 1, 0, timezone.utc) ndate = datetime(ndate.year, ndate.month, ndate.day, 0, 0, 1, 0, timezone.utc)
@ -56,7 +56,7 @@ def cmd_we(msg):
"Youhou, on est en week-end depuis %s."), "Youhou, on est en week-end depuis %s."),
channel=msg.channel) channel=msg.channel)
@hook("cmd_hook", "start") @hook.command("start")
def start_countdown(msg): def start_countdown(msg):
"""!start /something/: launch a timer""" """!start /something/: launch a timer"""
if len(msg.args) < 1: if len(msg.args) < 1:
@ -135,8 +135,8 @@ def start_countdown(msg):
msg.date.strftime("%A %d %B %Y à %H:%M:%S")), msg.date.strftime("%A %d %B %Y à %H:%M:%S")),
nick=msg.frm) nick=msg.frm)
@hook("cmd_hook", "end") @hook.command("end")
@hook("cmd_hook", "forceend") @hook.command("forceend")
def end_countdown(msg): def end_countdown(msg):
if len(msg.args) < 1: if len(msg.args) < 1:
raise IMException("quel événement terminer ?") raise IMException("quel événement terminer ?")
@ -154,7 +154,7 @@ def end_countdown(msg):
else: else:
return Response("%s n'est pas un compteur connu."% (msg.args[0]), channel=msg.channel, nick=msg.nick) return Response("%s n'est pas un compteur connu."% (msg.args[0]), channel=msg.channel, nick=msg.nick)
@hook("cmd_hook", "eventslist") @hook.command("eventslist")
def liste(msg): def liste(msg):
"""!eventslist: gets list of timer""" """!eventslist: gets list of timer"""
if len(msg.args): if len(msg.args):
@ -169,7 +169,7 @@ def liste(msg):
else: else:
return Response("Compteurs connus : %s." % ", ".join(context.data.index.keys()), channel=msg.channel) return Response("Compteurs connus : %s." % ", ".join(context.data.index.keys()), channel=msg.channel)
@hook("cmd_default") @hook.command()
def parseanswer(msg): def parseanswer(msg):
if msg.cmd in context.data.index: if msg.cmd in context.data.index:
res = Response(channel=msg.channel) res = Response(channel=msg.channel)
@ -189,7 +189,7 @@ def parseanswer(msg):
RGXP_ask = re.compile(r"^.*((create|new)\s+(a|an|a\s*new|an\s*other)?\s*(events?|commande?)|(nouvel(le)?|ajoute|cr[ée]{1,3})\s+(un)?\s*([eé]v[ée]nements?|commande?)).*$", re.I) RGXP_ask = re.compile(r"^.*((create|new)\s+(a|an|a\s*new|an\s*other)?\s*(events?|commande?)|(nouvel(le)?|ajoute|cr[ée]{1,3})\s+(un)?\s*([eé]v[ée]nements?|commande?)).*$", re.I)
@hook("ask_default") @hook.ask()
def parseask(msg): def parseask(msg):
if RGXP_ask.match(msg.text) is not None: if RGXP_ask.match(msg.text) is not None:
name = re.match("^.*!([^ \"'@!]+).*$", msg.text) name = re.match("^.*!([^ \"'@!]+).*$", msg.text)

View File

@ -65,7 +65,7 @@ def info_commit(repo, commit=None):
quote(fullname)) quote(fullname))
@hook("cmd_hook", "github") @hook.command("github")
def cmd_github(msg): def cmd_github(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate a repository name to search") raise IMException("indicate a repository name to search")
@ -93,7 +93,7 @@ def cmd_github(msg):
return res return res
@hook("cmd_hook", "github_user") @hook.command("github_user")
def cmd_github_user(msg): def cmd_github_user(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate a user name to search") raise IMException("indicate a user name to search")
@ -126,7 +126,7 @@ def cmd_github_user(msg):
return res return res
@hook("cmd_hook", "github_issue") @hook.command("github_issue")
def cmd_github_issue(msg): def cmd_github_issue(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate a repository to view its issues") raise IMException("indicate a repository to view its issues")
@ -164,7 +164,7 @@ def cmd_github_issue(msg):
return res return res
@hook("cmd_hook", "github_commit") @hook.command("github_commit")
def cmd_github_commit(msg): def cmd_github_commit(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate a repository to view its commits") raise IMException("indicate a repository to view its commits")

View File

@ -68,7 +68,7 @@ def find_movies(title):
raise IMException("An error occurs during movie search") raise IMException("An error occurs during movie search")
@hook("cmd_hook", "imdb") @hook.command("imdb")
def cmd_imdb(msg): def cmd_imdb(msg):
"""View movie details with !imdb <title>""" """View movie details with !imdb <title>"""
if not len(msg.args): if not len(msg.args):
@ -97,7 +97,7 @@ def cmd_imdb(msg):
return res return res
@hook("cmd_hook", "imdbs") @hook.command("imdbs")
def cmd_search(msg): def cmd_search(msg):
"""!imdbs <approximative title> to search a movie title""" """!imdbs <approximative title> to search a movie title"""
if not len(msg.args): if not len(msg.args):

View File

@ -39,7 +39,7 @@ def getJsonKeys(data):
else: else:
return data.keys() return data.keys()
@hook("cmd_hook", "json") @hook.command("json")
def get_json_info(msg): def get_json_info(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("Please specify a url and a list of JSON keys.") raise IMException("Please specify a url and a list of JSON keys.")

View File

@ -19,7 +19,7 @@ def help_full():
RGXP_s = re.compile(b'\x1b\\[[0-9]+m') RGXP_s = re.compile(b'\x1b\\[[0-9]+m')
@hook("cmd_hook", "MAN") @hook.command("MAN")
def cmd_man(msg): def cmd_man(msg):
args = ["man"] args = ["man"]
num = None num = None
@ -52,7 +52,7 @@ def cmd_man(msg):
return res return res
@hook("cmd_hook", "man") @hook.command("man")
def cmd_whatis(msg): def cmd_whatis(msg):
args = ["whatis", " ".join(msg.args)] args = ["whatis", " ".join(msg.args)]

View File

@ -43,7 +43,7 @@ def where(loc):
"{adminArea1}".format(**loc)).strip() "{adminArea1}".format(**loc)).strip()
@hook("cmd_hook", "geocode") @hook.command("geocode")
def cmd_geocode(msg): def cmd_geocode(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate a name") raise IMException("indicate a name")

View File

@ -239,7 +239,7 @@ SERVERS = dict()
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("all_post") @hook.post()
def parseresponse(res): def parseresponse(res):
# TODO: handle inter-bot communication NOMORE # TODO: handle inter-bot communication NOMORE
# TODO: check that the response is not the one already saved # TODO: check that the response is not the one already saved
@ -256,7 +256,7 @@ def parseresponse(res):
return res return res
@hook("cmd_hook", "more") @hook.command("more")
def cmd_more(msg): def cmd_more(msg):
"""Display next chunck of the message""" """Display next chunck of the message"""
res = list() res = list()
@ -272,7 +272,7 @@ def cmd_more(msg):
return res return res
@hook("cmd_hook", "next") @hook.command("next")
def cmd_next(msg): def cmd_next(msg):
"""Display the next information include in the message""" """Display the next information include in the message"""
res = list() res = list()

View File

@ -38,7 +38,7 @@ def load(context):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "title", @hook.command("title",
help="Retrieve webpage's title", help="Retrieve webpage's title",
help_usage={"URL": "Display the title of the given URL"}) help_usage={"URL": "Display the title of the given URL"})
def cmd_title(msg): def cmd_title(msg):
@ -54,7 +54,7 @@ def cmd_title(msg):
return Response("%s: %s" % (url, res.group(1).replace("\n", " ")), channel=msg.channel) return Response("%s: %s" % (url, res.group(1).replace("\n", " ")), channel=msg.channel)
@hook("cmd_hook", "curly", @hook.command("curly",
help="Retrieve webpage's headers", help="Retrieve webpage's headers",
help_usage={"URL": "Display HTTP headers of the given URL"}) help_usage={"URL": "Display HTTP headers of the given URL"})
def cmd_curly(msg): def cmd_curly(msg):
@ -67,7 +67,7 @@ def cmd_curly(msg):
return Response("Entêtes de la page %s : HTTP/%s, statut : %d %s ; headers : %s" % (url, version, status, reason, ", ".join(["\x03\x02" + h + "\x03\x02: " + v for h, v in headers])), channel=msg.channel) return Response("Entêtes de la page %s : HTTP/%s, statut : %d %s ; headers : %s" % (url, version, status, reason, ", ".join(["\x03\x02" + h + "\x03\x02: " + v for h, v in headers])), channel=msg.channel)
@hook("cmd_hook", "curl", @hook.command("curl",
help="Retrieve webpage's body", help="Retrieve webpage's body",
help_usage={"URL": "Display raw HTTP body of the given URL"}) help_usage={"URL": "Display raw HTTP body of the given URL"})
def cmd_curl(msg): def cmd_curl(msg):
@ -80,7 +80,7 @@ def cmd_curl(msg):
return res return res
@hook("cmd_hook", "w3m", @hook.command("w3m",
help="Retrieve and format webpage's content", help="Retrieve and format webpage's content",
help_usage={"URL": "Display and format HTTP content of the given URL"}) help_usage={"URL": "Display and format HTTP content of the given URL"})
def cmd_w3m(msg): def cmd_w3m(msg):
@ -92,7 +92,7 @@ def cmd_w3m(msg):
return res return res
@hook("cmd_hook", "traceurl", @hook.command("traceurl",
help="Follow redirections of a given URL and display each step", help="Follow redirections of a given URL and display each step",
help_usage={"URL": "Display redirections steps for the given URL"}) help_usage={"URL": "Display redirections steps for the given URL"})
def cmd_traceurl(msg): def cmd_traceurl(msg):
@ -109,7 +109,7 @@ def cmd_traceurl(msg):
return res return res
@hook("cmd_hook", "isup", @hook.command("isup",
help="Check if a website is up", help="Check if a website is up",
help_usage={"DOMAIN": "Check if a DOMAIN is up"}) help_usage={"DOMAIN": "Check if a DOMAIN is up"})
def cmd_isup(msg): def cmd_isup(msg):
@ -126,7 +126,7 @@ def cmd_isup(msg):
return res return res
@hook("cmd_hook", "w3c", @hook.command("w3c",
help="Perform a w3c HTML validator check", help="Perform a w3c HTML validator check",
help_usage={"URL": "Do W3C HTML validation on the given URL"}) help_usage={"URL": "Do W3C HTML validation on the given URL"})
def cmd_w3c(msg): def cmd_w3c(msg):
@ -149,10 +149,10 @@ def cmd_w3c(msg):
@hook("cmd_hook", "watch", data="diff", @hook.command("watch", data="diff",
help="Alert on webpage change", help="Alert on webpage change",
help_usage={"URL": "Watch the given URL and alert when it changes"}) help_usage={"URL": "Watch the given URL and alert when it changes"})
@hook("cmd_hook", "updown", data="updown", @hook.command("updown", data="updown",
help="Alert on server availability change", help="Alert on server availability change",
help_usage={"URL": "Watch the given domain and alert when it availability status changes"}) help_usage={"URL": "Watch the given domain and alert when it availability status changes"})
def cmd_watch(msg, diffType="diff"): def cmd_watch(msg, diffType="diff"):
@ -162,7 +162,7 @@ def cmd_watch(msg, diffType="diff"):
return watchWebsite.add_site(msg.args[0], msg.frm, msg.channel, msg.server, diffType) return watchWebsite.add_site(msg.args[0], msg.frm, msg.channel, msg.server, diffType)
@hook("cmd_hook", "listwatch", @hook.command("listwatch",
help="List URL watched for the channel", help="List URL watched for the channel",
help_usage={None: "List URL watched for the channel"}) help_usage={None: "List URL watched for the channel"})
def cmd_listwatch(msg): def cmd_listwatch(msg):
@ -173,7 +173,7 @@ def cmd_listwatch(msg):
return Response("No URL are currently watched. Use !watch URL to watch one.", channel=msg.channel) return Response("No URL are currently watched. Use !watch URL to watch one.", channel=msg.channel)
@hook("cmd_hook", "unwatch", @hook.command("unwatch",
help="Unwatch a previously watched URL", help="Unwatch a previously watched URL",
help_usage={"URL": "Unwatch the given URL"}) help_usage={"URL": "Unwatch the given URL"})
def cmd_unwatch(msg): def cmd_unwatch(msg):

View File

@ -21,7 +21,7 @@ def load(CONF, add_hook):
URL_WHOIS = URL_WHOIS % (urllib.parse.quote(CONF.getNode("whoisxmlapi")["username"]), urllib.parse.quote(CONF.getNode("whoisxmlapi")["password"])) URL_WHOIS = URL_WHOIS % (urllib.parse.quote(CONF.getNode("whoisxmlapi")["username"]), urllib.parse.quote(CONF.getNode("whoisxmlapi")["password"]))
import nemubot.hooks import nemubot.hooks
add_hook("cmd_hook", nemubot.hooks.Message(cmd_whois, "netwhois", add_hook("in_Command", nemubot.hooks.Command(cmd_whois, "netwhois",
help="Get whois information about given domains", help="Get whois information about given domains",
help_usage={"DOMAIN": "Return whois information on the given DOMAIN"})) help_usage={"DOMAIN": "Return whois information on the given DOMAIN"}))

View File

@ -41,7 +41,7 @@ def get_last_news(url):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "news") @hook.command("news")
def cmd_news(msg): def cmd_news(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("Indicate the URL to visit.") raise IMException("Indicate the URL to visit.")

View File

@ -14,7 +14,7 @@ def help_full ():
return "!ratp transport line [station]: Donne des informations sur les prochains passages du transport en commun séléctionné à l'arrêt désiré. Si aucune station n'est précisée, les liste toutes." return "!ratp transport line [station]: Donne des informations sur les prochains passages du transport en commun séléctionné à l'arrêt désiré. Si aucune station n'est précisée, les liste toutes."
@hook("cmd_hook", "ratp") @hook.command("ratp")
def ask_ratp(msg): def ask_ratp(msg):
"""Hook entry from !ratp""" """Hook entry from !ratp"""
if len(msg.args) >= 3: if len(msg.args) >= 3:
@ -44,7 +44,7 @@ def ask_ratp(msg):
else: else:
raise IMException("Mauvais usage, merci de spécifier un type de transport et une ligne, ou de consulter l'aide du module.") raise IMException("Mauvais usage, merci de spécifier un type de transport et une ligne, ou de consulter l'aide du module.")
@hook("cmd_hook", "ratp_alert") @hook.command("ratp_alert")
def ratp_alert(msg): def ratp_alert(msg):
if len(msg.args) == 2: if len(msg.args) == 2:
transport = msg.args[0] transport = msg.args[0]

View File

@ -15,7 +15,7 @@ from more import Response
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "choice") @hook.command("choice")
def cmd_choice(msg): def cmd_choice(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate some terms to pick!") raise IMException("indicate some terms to pick!")
@ -25,7 +25,7 @@ def cmd_choice(msg):
nick=msg.nick) nick=msg.nick)
@hook("cmd_hook", "choicecmd") @hook.command("choicecmd")
def cmd_choicecmd(msg): def cmd_choicecmd(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate some command to pick!") raise IMException("indicate some command to pick!")

View File

@ -19,7 +19,7 @@ def help_full():
return "Retrieve SAP transaction codes and details using tcodes or keywords: !tcode <transaction code|keywords>" return "Retrieve SAP transaction codes and details using tcodes or keywords: !tcode <transaction code|keywords>"
@hook("cmd_hook", "tcode") @hook.command("tcode")
def cmd_tcode(msg): def cmd_tcode(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate a transaction code or " raise IMException("indicate a transaction code or "

View File

@ -19,7 +19,7 @@ def help_full():
" hh:mm") " hh:mm")
@hook("cmd_hook", "sleepytime") @hook.command("sleepytime")
def cmd_sleep(msg): def cmd_sleep(msg):
if len(msg.args) and re.match("[0-9]{1,2}[h':.,-]([0-9]{1,2})?[m'\":.,-]?", if len(msg.args) and re.match("[0-9]{1,2}[h':.,-]([0-9]{1,2})?[m'\":.,-]?",
msg.args[0]) is not None: msg.args[0]) is not None:

View File

@ -47,7 +47,7 @@ def send_sms(frm, api_usr, api_key, content):
return None return None
@hook("cmd_hook", "sms") @hook.command("sms")
def cmd_sms(msg): def cmd_sms(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("À qui veux-tu envoyer ce SMS ?") raise IMException("À qui veux-tu envoyer ce SMS ?")
@ -80,7 +80,7 @@ def cmd_sms(msg):
apiuser_ask = re.compile(r"(utilisateur|user|numéro|numero|compte|abonne|abone|abonné|account)\s+(est|is)\s+(?P<user>[0-9]{7,})", re.IGNORECASE) apiuser_ask = re.compile(r"(utilisateur|user|numéro|numero|compte|abonne|abone|abonné|account)\s+(est|is)\s+(?P<user>[0-9]{7,})", re.IGNORECASE)
apikey_ask = re.compile(r"(clef|key|password|mot de passe?)\s+(?:est|is)?\s+(?P<key>[a-zA-Z0-9]{10,})", re.IGNORECASE) apikey_ask = re.compile(r"(clef|key|password|mot de passe?)\s+(?:est|is)?\s+(?P<key>[a-zA-Z0-9]{10,})", re.IGNORECASE)
@hook("ask_default") @hook.ask()
def parseask(msg): def parseask(msg):
if msg.text.find("Free") >= 0 and ( if msg.text.find("Free") >= 0 and (
msg.text.find("API") >= 0 or msg.text.find("api") >= 0) and ( msg.text.find("API") >= 0 or msg.text.find("api") >= 0) and (

View File

@ -23,7 +23,7 @@ def help_full():
def load(context): def load(context):
context.data.setIndex("name", "score") context.data.setIndex("name", "score")
@hook("cmd_hook", "spell") @hook.command("spell")
def cmd_spell(msg): def cmd_spell(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indique une orthographe approximative du mot dont tu veux vérifier l'orthographe.") raise IMException("indique une orthographe approximative du mot dont tu veux vérifier l'orthographe.")
@ -61,7 +61,7 @@ def add_score(nick, t):
context.data.index[nick][t] = 1 context.data.index[nick][t] = 1
context.save() context.save()
@hook("cmd_hook", "spellscore") @hook.command("spellscore")
def cmd_score(msg): def cmd_score(msg):
res = list() res = list()
unknown = list() unknown = list()

View File

@ -154,7 +154,7 @@ TRACKING_HANDLERS = {
# HOOKS ############################################################## # HOOKS ##############################################################
@hook("cmd_hook", "track", @hook.command("track",
help="Track postage delivery", help="Track postage delivery",
help_usage={ help_usage={
"TRACKING_ID [...]": "Track the specified postage IDs on various tracking services." "TRACKING_ID [...]": "Track the specified postage IDs on various tracking services."

View File

@ -72,8 +72,8 @@ def get_english_synos(key, word):
lang_binding = { 'fr': get_french_synos } lang_binding = { 'fr': get_french_synos }
@hook("cmd_hook", "synonymes", data="synonymes") @hook.command("synonymes", data="synonymes")
@hook("cmd_hook", "antonymes", data="antonymes") @hook.command("antonymes", data="antonymes")
def go(msg, what): def go(msg, what):
if not len(msg.args): if not len(msg.args):
raise IMException("de quel mot veux-tu connaître la liste des synonymes ?") raise IMException("de quel mot veux-tu connaître la liste des synonymes ?")

View File

@ -22,7 +22,7 @@ def load(context):
global URL_TPBAPI global URL_TPBAPI
URL_TPBAPI = context.config["url"] URL_TPBAPI = context.config["url"]
@hook("cmd_hook", "tpb") @hook.command("tpb")
def cmd_tpb(msg): def cmd_tpb(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("indicate an item to search!") raise IMException("indicate an item to search!")

View File

@ -79,7 +79,7 @@ def translate(term, langFrom="en", langTo="fr"):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "translate", @hook.command("translate",
help="Word translation using WordReference.com", help="Word translation using WordReference.com",
help_usage={ help_usage={
"TERM": "Found translation of TERM from/to english to/from <lang>." "TERM": "Found translation of TERM from/to english to/from <lang>."

View File

@ -20,7 +20,7 @@ def search(terms):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "urbandictionnary") @hook.command("urbandictionnary")
def udsearch(msg): def udsearch(msg):
if not len(msg.args): if not len(msg.args):
raise IMException("Indicate a term to search") raise IMException("Indicate a term to search")

View File

@ -66,7 +66,7 @@ def print_station_status(msg, station):
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "velib", @hook.command("velib",
help="gives available bikes and slots at the given station", help="gives available bikes and slots at the given station",
help_usage={ help_usage={
"STATION_ID": "gives available bikes and slots at the station STATION_ID" "STATION_ID": "gives available bikes and slots at the station STATION_ID"

View File

@ -136,7 +136,7 @@ def get_json_weather(coords):
return wth return wth
@hook("cmd_hook", "coordinates") @hook.command("coordinates")
def cmd_coordinates(msg): def cmd_coordinates(msg):
if len(msg.args) < 1: if len(msg.args) < 1:
raise IMException("indique-moi un nom de ville.") raise IMException("indique-moi un nom de ville.")
@ -149,7 +149,7 @@ def cmd_coordinates(msg):
return Response("Les coordonnées de %s sont %s,%s" % (msg.args[0], coords["lat"], coords["long"]), channel=msg.channel) return Response("Les coordonnées de %s sont %s,%s" % (msg.args[0], coords["lat"], coords["long"]), channel=msg.channel)
@hook("cmd_hook", "alert") @hook.command("alert")
def cmd_alert(msg): def cmd_alert(msg):
loc, coords, specific = treat_coord(msg) loc, coords, specific = treat_coord(msg)
wth = get_json_weather(coords) wth = get_json_weather(coords)
@ -163,7 +163,7 @@ def cmd_alert(msg):
return res return res
@hook("cmd_hook", "météo") @hook.command("météo")
def cmd_weather(msg): def cmd_weather(msg):
loc, coords, specific = treat_coord(msg) loc, coords, specific = treat_coord(msg)
wth = get_json_weather(coords) wth = get_json_weather(coords)
@ -217,7 +217,7 @@ def cmd_weather(msg):
gps_ask = re.compile(r"^\s*(?P<city>.*\w)\s*(?:(?:se|est)\s+(?:trouve|situ[ée]*)\s+[aà])\s*(?P<lat>-?[0-9]+(?:[,.][0-9]+))[^0-9.](?P<long>-?[0-9]+(?:[,.][0-9]+))\s*$", re.IGNORECASE) gps_ask = re.compile(r"^\s*(?P<city>.*\w)\s*(?:(?:se|est)\s+(?:trouve|situ[ée]*)\s+[aà])\s*(?P<lat>-?[0-9]+(?:[,.][0-9]+))[^0-9.](?P<long>-?[0-9]+(?:[,.][0-9]+))\s*$", re.IGNORECASE)
@hook("ask_default") @hook.ask()
def parseask(msg): def parseask(msg):
res = gps_ask.match(msg.text) res = gps_ask.match(msg.text)
if res is not None: if res is not None:

View File

@ -30,8 +30,8 @@ def load(context):
context.data.getNode("pics").setIndex("login", "pict") context.data.getNode("pics").setIndex("login", "pict")
import nemubot.hooks import nemubot.hooks
context.add_hook("cmd_hook", context.add_hook("in_Command",
nemubot.hooks.Message(cmd_whois, "whois")) nemubot.hooks.Command(cmd_whois, "whois"))
class Login: class Login:
@ -87,7 +87,7 @@ def cmd_whois(msg):
res.append_message("Unknown %s :(" % srch) res.append_message("Unknown %s :(" % srch)
return res return res
@hook("cmd_hook", "nicks") @hook.command("nicks")
def cmd_nicks(msg): def cmd_nicks(msg):
if len(msg.args) < 1: if len(msg.args) < 1:
raise IMException("Provide a login") raise IMException("Provide a login")
@ -106,7 +106,7 @@ def cmd_nicks(msg):
else: else:
return Response("%s has no known alias." % nick, channel=msg.channel) return Response("%s has no known alias." % nick, channel=msg.channel)
@hook("ask_default") @hook.ask()
def parseask(msg): def parseask(msg):
res = re.match(r"^(\S+)\s*('s|suis|est|is|was|were)\s+([a-zA-Z0-9_-]{3,8})$", msg.text, re.I) res = re.match(r"^(\S+)\s*('s|suis|est|is|was|were)\s+([a-zA-Z0-9_-]{3,8})$", msg.text, re.I)
if res is not None: if res is not None:

View File

@ -92,7 +92,7 @@ class WFAResults:
# MODULE INTERFACE #################################################### # MODULE INTERFACE ####################################################
@hook("cmd_hook", "calculate", @hook.command("calculate",
help="Perform search and calculation using WolframAlpha", help="Perform search and calculation using WolframAlpha",
help_usage={ help_usage={
"TERM": "Look at the given term on WolframAlpha", "TERM": "Look at the given term on WolframAlpha",

View File

@ -38,7 +38,7 @@ def start_watch(msg):
context.save() context.save()
raise IMException("This channel is now watching world cup events!") raise IMException("This channel is now watching world cup events!")
@hook("cmd_hook", "watch_worldcup") @hook.command("watch_worldcup")
def cmd_watch(msg): def cmd_watch(msg):
# Get current state # Get current state
@ -177,7 +177,7 @@ def get_matches(url):
if is_valid(match): if is_valid(match):
yield match yield match
@hook("cmd_hook", "worldcup") @hook.command("worldcup")
def cmd_worldcup(msg): def cmd_worldcup(msg):
res = Response(channel=msg.channel, nomore="No more match to display", count=" (%d more matches)") res = Response(channel=msg.channel, nomore="No more match to display", count=" (%d more matches)")

View File

@ -79,7 +79,7 @@ class Bot(threading.Thread):
def in_echo(msg): def in_echo(msg):
from nemubot.message import Text from nemubot.message import Text
return Text(msg.nick + ": " + " ".join(msg.args), to=msg.to_response) return Text(msg.nick + ": " + " ".join(msg.args), to=msg.to_response)
self.treater.hm.add_hook(nemubot.hooks.Message(in_echo, "echo"), "in", "Command") self.treater.hm.add_hook(nemubot.hooks.Command(in_echo, "echo"), "in", "Command")
def _help_msg(msg): def _help_msg(msg):
"""Parse and response to help messages""" """Parse and response to help messages"""
@ -98,7 +98,7 @@ class Bot(threading.Thread):
elif msg.args[0][0] == "!": elif msg.args[0][0] == "!":
for module in self.modules: for module in self.modules:
for (s, h) in self.modules[module].__nemubot_context__.hooks: for (s, h) in self.modules[module].__nemubot_context__.hooks:
if s == "in_Command" and (h.name is not None or h.regexp is not None) and h.is_matching(msg.args[0][1:]): if s == "in_Command" and (h.name is not None or h.regexp is not None) and ((h.name is not None and msg.args[0][1:] == h.name) or (h.regexp is not None and re.match(h.regexp, msg.args[0][1:]))):
if h.help_usage: if h.help_usage:
lp = ["\x03\x02%s%s\x03\x02: %s" % (msg.args[0], (" " + k if k is not None else ""), h.help_usage[k]) for k in h.help_usage] lp = ["\x03\x02%s%s\x03\x02: %s" % (msg.args[0], (" " + k if k is not None else ""), h.help_usage[k]) for k in h.help_usage]
jp = h.keywords.help() jp = h.keywords.help()
@ -128,7 +128,7 @@ class Bot(threading.Thread):
" de tous les modules disponibles localement", " de tous les modules disponibles localement",
message=["\x03\x02%s\x03\x02 (%s)" % (im, self.modules[im].__doc__) for im in self.modules if self.modules[im].__doc__]) message=["\x03\x02%s\x03\x02 (%s)" % (im, self.modules[im].__doc__) for im in self.modules if self.modules[im].__doc__])
return res return res
self.treater.hm.add_hook(nemubot.hooks.Message(_help_msg, "help"), "in", "Command") self.treater.hm.add_hook(nemubot.hooks.Command(_help_msg, "help"), "in", "Command")
from queue import Queue from queue import Queue
# Messages to be treated # Messages to be treated
@ -462,9 +462,9 @@ class Bot(threading.Thread):
# Register decorated functions # Register decorated functions
import nemubot.hooks import nemubot.hooks
for s, h in nemubot.hooks.last_registered: for s, h in nemubot.hooks.hook.last_registered:
module.__nemubot_context__.add_hook(s, h) module.__nemubot_context__.add_hook(s, h)
nemubot.hooks.last_registered = [] nemubot.hooks.hook.last_registered = []
# Launch the module # Launch the module
if hasattr(module, "load"): if hasattr(module, "load"):

View File

@ -14,29 +14,54 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from nemubot.hooks.abstract import Abstract
from nemubot.hooks.command import Command
from nemubot.hooks.message import Message from nemubot.hooks.message import Message
last_registered = []
class hook:
last_registered = []
def hook(store, *args, **kargs): def _add(store, h, *args, **kwargs):
"""Function used as a decorator for module loading""" """Function used as a decorator for module loading"""
def sec(call): def sec(call):
last_registered.append((store, Message(call, *args, **kargs))) hook.last_registered.append((store, h(call, *args, **kwargs)))
return call return call
return sec return sec
def add(store, *args, **kwargs):
return hook._add(store, Abstract, *args, **kwargs)
def ask(*args, store="in_DirectAsk", **kwargs):
return hook._add(store, Message, *args, **kwargs)
def command(*args, store="in_Command", **kwargs):
return hook._add(store, Command, *args, **kwargs)
def message(*args, store="in_Text", **kwargs):
return hook._add(store, Message, *args, **kwargs)
def post(*args, store="post", **kwargs):
return hook._add(store, Abstract, *args, **kwargs)
def pre(*args, store="pre", **kwargs):
return hook._add(store, Abstract, *args, **kwargs)
def reload(): def reload():
global Message
import imp import imp
import nemubot.hooks.abstract import nemubot.hooks.abstract
imp.reload(nemubot.hooks.abstract) imp.reload(nemubot.hooks.abstract)
import nemubot.hooks.command
imp.reload(nemubot.hooks.command)
import nemubot.hooks.message import nemubot.hooks.message
imp.reload(nemubot.hooks.message) imp.reload(nemubot.hooks.message)
Message = nemubot.hooks.message.Message
import nemubot.hooks.keywords import nemubot.hooks.keywords
imp.reload(nemubot.hooks.keywords) imp.reload(nemubot.hooks.keywords)

View File

@ -87,6 +87,10 @@ class Abstract:
return False return False
def __str__(self):
return ""
def can_write(self, receivers=list(), server=None): def can_write(self, receivers=list(), server=None):
return True return True

65
nemubot/hooks/command.py Normal file
View File

@ -0,0 +1,65 @@
# Nemubot is a smart and modulable IM bot.
# Copyright (C) 2012-2015 Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from nemubot.hooks.message import Message
from nemubot.hooks.keywords import NoKeyword
from nemubot.hooks.keywords.abstract import Abstract as AbstractKeywords
from nemubot.hooks.keywords.dict import Dict as DictKeywords
import nemubot.message
class Command(Message):
"""Class storing hook information, specialized for Command messages"""
def __init__(self, call, name=None, help_usage=dict(), keywords=NoKeyword(),
**kargs):
super().__init__(call=call, **kargs)
if isinstance(keywords, dict):
keywords = DictKeywords(keywords)
assert type(help_usage) is dict, help_usage
assert isinstance(keywords, AbstractKeywords), keywords
self.name = str(name) if name is not None else None
self.help_usage = help_usage
self.keywords = keywords
def __str__(self):
return "\x03\x02%s\x03\x02%s%s" % (
self.name if self.name is not None else "\x03\x1f" + self.regexp + "\x03\x1f" if self.regexp is not None else "",
" (restricted to %:%s)" % ((",".join(self.servers) if self.server else "*") + (",".join(self.channels) if self.channels else "*")) if len(self.channels) or len(self.servers) else "",
": %s" % self.help if self.help is not None else ""
)
def check(self, msg):
return self.keywords.check(msg.kwargs) and super().check(msg)
def match(self, msg):
if not isinstance(msg, nemubot.message.command.Command):
return False
else:
return (
(self.name is None or msg.cmd == self.name) and
(self.regexp is None or re.match(self.regexp, msg.cmd))
)

View File

@ -17,9 +17,6 @@
import re import re
from nemubot.hooks.abstract import Abstract from nemubot.hooks.abstract import Abstract
from nemubot.hooks.keywords import NoKeyword
from nemubot.hooks.keywords.abstract import Abstract as AbstractKeywords
from nemubot.hooks.keywords.dict import Dict as DictKeywords
import nemubot.message import nemubot.message
@ -27,64 +24,26 @@ class Message(Abstract):
"""Class storing hook information, specialized for a generic Message""" """Class storing hook information, specialized for a generic Message"""
def __init__(self, call, name=None, regexp=None, channels=list(), def __init__(self, call, regexp=None, help=None, **kwargs):
server=None, help=None, help_usage=dict(), keywords=NoKeyword(), super().__init__(call=call, **kwargs)
**kargs):
Abstract.__init__(self, call=call, **kargs)
if isinstance(keywords, dict):
keywords = DictKeywords(keywords)
assert regexp is None or type(regexp) is str, regexp assert regexp is None or type(regexp) is str, regexp
assert channels is None or type(channels) is list, channels
assert server is None or type(server) is str, server
assert type(help_usage) is dict, help_usage
assert isinstance(keywords, AbstractKeywords), keywords
self.name = str(name) if name is not None else None
self.regexp = regexp self.regexp = regexp
self.server = server
self.channels = channels
self.help = help self.help = help
self.help_usage = help_usage
self.keywords = keywords
def __str__(self): def __str__(self):
return "\x03\x02%s\x03\x02%s%s" % ( # TODO: find a way to name the feature (like command: help)
self.name if self.name is not None else "\x03\x1f" + self.regexp + "\x03\x1f" if self.regexp is not None else "", return self.help if self.help is not None else super().__str__()
" (restricted to %:%s)" % ((",".join(self.servers) if self.server else "*") + (",".join(self.channels) if self.channels else "*")) if len(self.channels) or len(self.server) else "",
": %s" % self.help if self.help is not None else ""
)
def check(self, msg): def check(self, msg):
return not hasattr(msg, "kwargs") or self.keywords.check(msg.kwargs) return super().check(msg)
def match(self, msg, server=None): def match(self, msg):
if not isinstance(msg, nemubot.message.abstract.Abstract): if not isinstance(msg, nemubot.message.text.Text):
return True
elif isinstance(msg, nemubot.message.Command):
return self.is_matching(msg.cmd, msg.to, server)
elif isinstance(msg, nemubot.message.Text):
return self.is_matching(msg.message, msg.to, server)
else:
return False return False
def is_matching(self, strcmp, receivers=list(), server=None):
"""Test if the current hook correspond to the message"""
if ((server is None or self.server is None or self.server == server)
and ((self.name is None or strcmp == self.name) and (
self.regexp is None or re.match(self.regexp, strcmp)))):
if receivers and self.channels:
for receiver in receivers:
if receiver in self.channels:
return True
else: else:
return True return self.regexp is None or re.match(self.regexp, msg.message)
return False

View File

@ -14,21 +14,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
def convert_legacy_store(old):
if old == "cmd_hook" or old == "cmd_rgxp" or old == "cmd_default":
return "in_Command"
elif old == "ask_hook" or old == "ask_rgxp" or old == "ask_default":
return "in_DirectAsk"
elif old == "msg_hook" or old == "msg_rgxp" or old == "msg_default":
return "in_Text"
elif old == "all_post":
return "post"
elif old == "all_pre":
return "pre"
else:
return old
class ModuleContext: class ModuleContext:
def __init__(self, context, module): def __init__(self, context, module):
@ -60,11 +45,9 @@ class ModuleContext:
self.data = context.datastore.load(module_name) self.data = context.datastore.load(module_name)
def add_hook(store, hook): def add_hook(store, hook):
store = convert_legacy_store(store)
self.hooks.append((store, hook)) self.hooks.append((store, hook))
return context.treater.hm.add_hook(hook, store) return context.treater.hm.add_hook(hook, store)
def del_hook(store, hook): def del_hook(store, hook):
store = convert_legacy_store(store)
self.hooks.remove((store, hook)) self.hooks.remove((store, hook))
return context.treater.hm.del_hook(hook, store) return context.treater.hm.del_hook(hook, store)
def call_hook(store, msg): def call_hook(store, msg):
@ -98,10 +81,8 @@ class ModuleContext:
self.data = module_state.ModuleState("nemubotstate") self.data = module_state.ModuleState("nemubotstate")
def add_hook(store, hook): def add_hook(store, hook):
store = convert_legacy_store(store)
self.hooks.append((store, hook)) self.hooks.append((store, hook))
def del_hook(store, hook): def del_hook(store, hook):
store = convert_legacy_store(store)
self.hooks.remove((store, hook)) self.hooks.remove((store, hook))
def call_hook(store, msg): def call_hook(store, msg):
# TODO: what can we do here? # TODO: what can we do here?