From f39a0eac56c13bab7da5e21b33dbb2437ce8898b Mon Sep 17 00:00:00 2001 From: nemunaire Date: Mon, 2 Nov 2015 20:19:12 +0100 Subject: [PATCH] Refactors hooks registration --- modules/alias.py | 14 ++++---- modules/birthday.py | 6 ++-- modules/bonneannee.py | 6 ++-- modules/books.py | 6 ++-- modules/conjugaison.py | 2 +- modules/ctfs.py | 2 +- modules/cve.py | 2 +- modules/ddg.py | 4 +-- modules/events.py | 16 ++++----- modules/github.py | 8 ++--- modules/imdb.py | 4 +-- modules/jsonbot.py | 2 +- modules/man.py | 4 +-- modules/mapquest.py | 2 +- modules/more.py | 6 ++-- modules/networking/__init__.py | 22 ++++++------ modules/networking/whois.py | 6 ++-- modules/news.py | 2 +- modules/nextstop/__init__.py | 4 +-- modules/rnd.py | 4 +-- modules/sap.py | 2 +- modules/sleepytime.py | 2 +- modules/sms.py | 4 +-- modules/spell/__init__.py | 4 +-- modules/suivi.py | 2 +- modules/syno.py | 4 +-- modules/tpb.py | 2 +- modules/translate.py | 2 +- modules/urbandict.py | 2 +- modules/velib.py | 2 +- modules/weather.py | 8 ++--- modules/whois.py | 8 ++--- modules/wolframalpha.py | 2 +- modules/worldcup.py | 4 +-- nemubot/bot.py | 10 +++--- nemubot/hooks/__init__.py | 43 +++++++++++++++++----- nemubot/hooks/abstract.py | 4 +++ nemubot/hooks/command.py | 65 ++++++++++++++++++++++++++++++++++ nemubot/hooks/message.py | 59 +++++------------------------- nemubot/modulecontext.py | 19 ---------- 40 files changed, 202 insertions(+), 168 deletions(-) create mode 100644 nemubot/hooks/command.py diff --git a/modules/alias.py b/modules/alias.py index 871424b..c308608 100644 --- a/modules/alias.py +++ b/modules/alias.py @@ -155,7 +155,7 @@ def replace_variables(cnts, msg=None): ## Variables management -@hook("cmd_hook", "listvars", +@hook.command("listvars", help="list defined variables for substitution in input commands", help_usage={ None: "List all known variables", @@ -178,7 +178,7 @@ def cmd_listvars(msg): 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_usage={"KEY VALUE": "Define the variable named KEY and fill it with VALUE as content"}) def cmd_set(msg): @@ -191,7 +191,7 @@ def cmd_set(msg): ## Alias management -@hook("cmd_hook", "listalias", +@hook.command("listalias", help="List registered aliases", help_usage={ None: "List all registered aliases", @@ -205,7 +205,7 @@ def cmd_listalias(msg): 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") def cmd_alias(msg): if not len(msg.args): @@ -221,7 +221,7 @@ def cmd_alias(msg): return Response(res, channel=msg.channel, nick=msg.nick) -@hook("cmd_hook", "unalias", +@hook.command("unalias", help="Remove a previously created alias") def cmd_unalias(msg): if not len(msg.args): @@ -242,7 +242,7 @@ def cmd_unalias(msg): ## Alias replacement -@hook("pre_Command") +@hook.add("pre_Command") def treat_alias(msg): if msg.cmd in context.data.getNode("aliases").index: txt = context.data.getNode("aliases").index[msg.cmd]["origin"] @@ -263,7 +263,7 @@ def treat_alias(msg): return msg -@hook("ask_default") +@hook.ask() def parseask(msg): 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) diff --git a/modules/birthday.py b/modules/birthday.py index f0870ec..cb850ac 100644 --- a/modules/birthday.py +++ b/modules/birthday.py @@ -46,7 +46,7 @@ def findName(msg): ## Commands -@hook("cmd_hook", "anniv", +@hook.command("anniv", help="gives the remaining time before the anniversary of known people", help_usage={ None: "Calculate the time remaining before your birthday", @@ -80,7 +80,7 @@ def cmd_anniv(msg): msg.channel, msg.nick) -@hook("cmd_hook", "age", +@hook.command("age", help="Calculate age of known people", help_usage={ None: "Calculate your age", @@ -104,7 +104,7 @@ def cmd_age(msg): ## Input parsing -@hook("ask_default") +@hook.ask() 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) if res is not None: diff --git a/modules/bonneannee.py b/modules/bonneannee.py index 18ba637..b3b3934 100644 --- a/modules/bonneannee.py +++ b/modules/bonneannee.py @@ -47,9 +47,9 @@ def load(context): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "newyear", +@hook.command("newyear", 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) def cmd_newyear(msg): return Response(countdown_format(datetime(yrn, 1, 1, 0, 0, 1, 0, @@ -59,7 +59,7 @@ def cmd_newyear(msg): 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") def cmd_timetoyear(msg, cur): yr = int(msg.cmd) diff --git a/modules/books.py b/modules/books.py index a5ea1b3..df48056 100644 --- a/modules/books.py +++ b/modules/books.py @@ -58,7 +58,7 @@ def search_author(name): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "book", +@hook.command("book", help="Get information about a book from its title", help_usage={ "TITLE": "Get information about a book titled TITLE" @@ -77,7 +77,7 @@ def cmd_book(msg): return res -@hook("cmd_hook", "search_books", +@hook.command("search_books", help="Search book's title", help_usage={ "APPROX_TITLE": "Search for a book approximately titled APPROX_TITLE" @@ -97,7 +97,7 @@ def cmd_books(msg): return res -@hook("cmd_hook", "author_books", +@hook.command("author_books", help="Looking for books writen by a given author", help_usage={ "AUTHOR": "Looking for books writen by AUTHOR" diff --git a/modules/conjugaison.py b/modules/conjugaison.py index d4405e2..25fe242 100644 --- a/modules/conjugaison.py +++ b/modules/conjugaison.py @@ -72,7 +72,7 @@ def compute_line(line, stringTens): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "conjugaison", +@hook.command("conjugaison", help_usage={ "TENS VERB": "give the conjugaison for VERB in TENS." }) diff --git a/modules/ctfs.py b/modules/ctfs.py index 3e02ae9..1526cbc 100644 --- a/modules/ctfs.py +++ b/modules/ctfs.py @@ -16,7 +16,7 @@ URL = 'https://ctftime.org/event/list/upcoming' # MODULE INTERFACE #################################################### -@hook("cmd_hook", "ctfs", +@hook.command("ctfs", help="Display the upcoming CTFs") def get_info_yt(msg): soup = BeautifulSoup(getURLContent(URL)) diff --git a/modules/cve.py b/modules/cve.py index fd28181..c5e125d 100644 --- a/modules/cve.py +++ b/modules/cve.py @@ -20,7 +20,7 @@ def get_cve(cve_id): return desc[17].text.replace("\n", " ") + " Moar at " + search_url -@hook("cmd_hook", "cve") +@hook.command("cve") def get_cve_desc(msg): res = Response(channel=msg.channel) diff --git a/modules/ddg.py b/modules/ddg.py index fc70dd6..78b6022 100644 --- a/modules/ddg.py +++ b/modules/ddg.py @@ -103,7 +103,7 @@ class DDGResult: # MODULE INTERFACE #################################################### -@hook("cmd_hook", "define") +@hook.command("define") def define(msg): if not len(msg.args): raise IMException("Indicate a term to define") @@ -115,7 +115,7 @@ def define(msg): return Response(s.definition, channel=msg.channel) -@hook("cmd_hook", "search") +@hook.command("search") def search(msg): if not len(msg.args): raise IMException("Indicate a term to search") diff --git a/modules/events.py b/modules/events.py index 3354ac6..e1d25d0 100644 --- a/modules/events.py +++ b/modules/events.py @@ -38,7 +38,7 @@ def fini(d, strend): context.data.delChild(context.data.index[strend["name"]]) context.save() -@hook("cmd_hook", "goûter") +@hook.command("goûter") def cmd_gouter(msg): ndate = datetime.now(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 :("), channel=msg.channel) -@hook("cmd_hook", "week-end") +@hook.command("week-end") def cmd_we(msg): ndate = datetime.now(timezone.utc) + timedelta(5 - datetime.today().weekday()) 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."), channel=msg.channel) -@hook("cmd_hook", "start") +@hook.command("start") def start_countdown(msg): """!start /something/: launch a timer""" if len(msg.args) < 1: @@ -135,8 +135,8 @@ def start_countdown(msg): msg.date.strftime("%A %d %B %Y à %H:%M:%S")), nick=msg.frm) -@hook("cmd_hook", "end") -@hook("cmd_hook", "forceend") +@hook.command("end") +@hook.command("forceend") def end_countdown(msg): if len(msg.args) < 1: raise IMException("quel événement terminer ?") @@ -154,7 +154,7 @@ def end_countdown(msg): else: 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): """!eventslist: gets list of timer""" if len(msg.args): @@ -169,7 +169,7 @@ def liste(msg): else: return Response("Compteurs connus : %s." % ", ".join(context.data.index.keys()), channel=msg.channel) -@hook("cmd_default") +@hook.command() def parseanswer(msg): if msg.cmd in context.data.index: 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) -@hook("ask_default") +@hook.ask() def parseask(msg): if RGXP_ask.match(msg.text) is not None: name = re.match("^.*!([^ \"'@!]+).*$", msg.text) diff --git a/modules/github.py b/modules/github.py index 19eadf9..1a345cd 100644 --- a/modules/github.py +++ b/modules/github.py @@ -65,7 +65,7 @@ def info_commit(repo, commit=None): quote(fullname)) -@hook("cmd_hook", "github") +@hook.command("github") def cmd_github(msg): if not len(msg.args): raise IMException("indicate a repository name to search") @@ -93,7 +93,7 @@ def cmd_github(msg): return res -@hook("cmd_hook", "github_user") +@hook.command("github_user") def cmd_github_user(msg): if not len(msg.args): raise IMException("indicate a user name to search") @@ -126,7 +126,7 @@ def cmd_github_user(msg): return res -@hook("cmd_hook", "github_issue") +@hook.command("github_issue") def cmd_github_issue(msg): if not len(msg.args): raise IMException("indicate a repository to view its issues") @@ -164,7 +164,7 @@ def cmd_github_issue(msg): return res -@hook("cmd_hook", "github_commit") +@hook.command("github_commit") def cmd_github_commit(msg): if not len(msg.args): raise IMException("indicate a repository to view its commits") diff --git a/modules/imdb.py b/modules/imdb.py index adea1d8..1e6c6e9 100644 --- a/modules/imdb.py +++ b/modules/imdb.py @@ -68,7 +68,7 @@ def find_movies(title): raise IMException("An error occurs during movie search") -@hook("cmd_hook", "imdb") +@hook.command("imdb") def cmd_imdb(msg): """View movie details with !imdb """ if not len(msg.args): @@ -97,7 +97,7 @@ def cmd_imdb(msg): return res -@hook("cmd_hook", "imdbs") +@hook.command("imdbs") def cmd_search(msg): """!imdbs <approximative title> to search a movie title""" if not len(msg.args): diff --git a/modules/jsonbot.py b/modules/jsonbot.py index c69cca2..fe25187 100644 --- a/modules/jsonbot.py +++ b/modules/jsonbot.py @@ -39,7 +39,7 @@ def getJsonKeys(data): else: return data.keys() -@hook("cmd_hook", "json") +@hook.command("json") def get_json_info(msg): if not len(msg.args): raise IMException("Please specify a url and a list of JSON keys.") diff --git a/modules/man.py b/modules/man.py index 7e7b715..997b85b 100644 --- a/modules/man.py +++ b/modules/man.py @@ -19,7 +19,7 @@ def help_full(): RGXP_s = re.compile(b'\x1b\\[[0-9]+m') -@hook("cmd_hook", "MAN") +@hook.command("MAN") def cmd_man(msg): args = ["man"] num = None @@ -52,7 +52,7 @@ def cmd_man(msg): return res -@hook("cmd_hook", "man") +@hook.command("man") def cmd_whatis(msg): args = ["whatis", " ".join(msg.args)] diff --git a/modules/mapquest.py b/modules/mapquest.py index 40bd40f..2c42ad7 100644 --- a/modules/mapquest.py +++ b/modules/mapquest.py @@ -43,7 +43,7 @@ def where(loc): "{adminArea1}".format(**loc)).strip() -@hook("cmd_hook", "geocode") +@hook.command("geocode") def cmd_geocode(msg): if not len(msg.args): raise IMException("indicate a name") diff --git a/modules/more.py b/modules/more.py index c8b80a9..4742dfe 100644 --- a/modules/more.py +++ b/modules/more.py @@ -239,7 +239,7 @@ SERVERS = dict() # MODULE INTERFACE #################################################### -@hook("all_post") +@hook.post() def parseresponse(res): # TODO: handle inter-bot communication NOMORE # TODO: check that the response is not the one already saved @@ -256,7 +256,7 @@ def parseresponse(res): return res -@hook("cmd_hook", "more") +@hook.command("more") def cmd_more(msg): """Display next chunck of the message""" res = list() @@ -272,7 +272,7 @@ def cmd_more(msg): return res -@hook("cmd_hook", "next") +@hook.command("next") def cmd_next(msg): """Display the next information include in the message""" res = list() diff --git a/modules/networking/__init__.py b/modules/networking/__init__.py index 26d6470..f0df094 100644 --- a/modules/networking/__init__.py +++ b/modules/networking/__init__.py @@ -38,7 +38,7 @@ def load(context): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "title", +@hook.command("title", help="Retrieve webpage's title", help_usage={"URL": "Display the title of the given URL"}) 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) -@hook("cmd_hook", "curly", +@hook.command("curly", help="Retrieve webpage's headers", help_usage={"URL": "Display HTTP headers of the given URL"}) 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) -@hook("cmd_hook", "curl", +@hook.command("curl", help="Retrieve webpage's body", help_usage={"URL": "Display raw HTTP body of the given URL"}) def cmd_curl(msg): @@ -80,7 +80,7 @@ def cmd_curl(msg): return res -@hook("cmd_hook", "w3m", +@hook.command("w3m", help="Retrieve and format webpage's content", help_usage={"URL": "Display and format HTTP content of the given URL"}) def cmd_w3m(msg): @@ -92,7 +92,7 @@ def cmd_w3m(msg): return res -@hook("cmd_hook", "traceurl", +@hook.command("traceurl", help="Follow redirections of a given URL and display each step", help_usage={"URL": "Display redirections steps for the given URL"}) def cmd_traceurl(msg): @@ -109,7 +109,7 @@ def cmd_traceurl(msg): return res -@hook("cmd_hook", "isup", +@hook.command("isup", help="Check if a website is up", help_usage={"DOMAIN": "Check if a DOMAIN is up"}) def cmd_isup(msg): @@ -126,7 +126,7 @@ def cmd_isup(msg): return res -@hook("cmd_hook", "w3c", +@hook.command("w3c", help="Perform a w3c HTML validator check", help_usage={"URL": "Do W3C HTML validation on the given URL"}) 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_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_usage={"URL": "Watch the given domain and alert when it availability status changes"}) 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) -@hook("cmd_hook", "listwatch", +@hook.command("listwatch", help="List URL watched for the channel", help_usage={None: "List URL watched for the channel"}) 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) -@hook("cmd_hook", "unwatch", +@hook.command("unwatch", help="Unwatch a previously watched URL", help_usage={"URL": "Unwatch the given URL"}) def cmd_unwatch(msg): diff --git a/modules/networking/whois.py b/modules/networking/whois.py index 0b8eb9f..b185cf8 100644 --- a/modules/networking/whois.py +++ b/modules/networking/whois.py @@ -21,9 +21,9 @@ def load(CONF, add_hook): URL_WHOIS = URL_WHOIS % (urllib.parse.quote(CONF.getNode("whoisxmlapi")["username"]), urllib.parse.quote(CONF.getNode("whoisxmlapi")["password"])) import nemubot.hooks - add_hook("cmd_hook", nemubot.hooks.Message(cmd_whois, "netwhois", - help="Get whois information about given domains", - help_usage={"DOMAIN": "Return whois information on the given DOMAIN"})) + add_hook("in_Command", nemubot.hooks.Command(cmd_whois, "netwhois", + help="Get whois information about given domains", + help_usage={"DOMAIN": "Return whois information on the given DOMAIN"})) def extractdate(str): diff --git a/modules/news.py b/modules/news.py index dccc77e..a8fb8de 100644 --- a/modules/news.py +++ b/modules/news.py @@ -41,7 +41,7 @@ def get_last_news(url): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "news") +@hook.command("news") def cmd_news(msg): if not len(msg.args): raise IMException("Indicate the URL to visit.") diff --git a/modules/nextstop/__init__.py b/modules/nextstop/__init__.py index 9a0e5c7..9530ab8 100644 --- a/modules/nextstop/__init__.py +++ b/modules/nextstop/__init__.py @@ -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." -@hook("cmd_hook", "ratp") +@hook.command("ratp") def ask_ratp(msg): """Hook entry from !ratp""" if len(msg.args) >= 3: @@ -44,7 +44,7 @@ def ask_ratp(msg): else: 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): if len(msg.args) == 2: transport = msg.args[0] diff --git a/modules/rnd.py b/modules/rnd.py index f1f3721..32c2adf 100644 --- a/modules/rnd.py +++ b/modules/rnd.py @@ -15,7 +15,7 @@ from more import Response # MODULE INTERFACE #################################################### -@hook("cmd_hook", "choice") +@hook.command("choice") def cmd_choice(msg): if not len(msg.args): raise IMException("indicate some terms to pick!") @@ -25,7 +25,7 @@ def cmd_choice(msg): nick=msg.nick) -@hook("cmd_hook", "choicecmd") +@hook.command("choicecmd") def cmd_choicecmd(msg): if not len(msg.args): raise IMException("indicate some command to pick!") diff --git a/modules/sap.py b/modules/sap.py index a7d65cf..8691d6a 100644 --- a/modules/sap.py +++ b/modules/sap.py @@ -19,7 +19,7 @@ def help_full(): 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): if not len(msg.args): raise IMException("indicate a transaction code or " diff --git a/modules/sleepytime.py b/modules/sleepytime.py index aef2db3..715b3b9 100644 --- a/modules/sleepytime.py +++ b/modules/sleepytime.py @@ -19,7 +19,7 @@ def help_full(): " hh:mm") -@hook("cmd_hook", "sleepytime") +@hook.command("sleepytime") def cmd_sleep(msg): if len(msg.args) and re.match("[0-9]{1,2}[h':.,-]([0-9]{1,2})?[m'\":.,-]?", msg.args[0]) is not None: diff --git a/modules/sms.py b/modules/sms.py index 103a938..3a9727f 100644 --- a/modules/sms.py +++ b/modules/sms.py @@ -47,7 +47,7 @@ def send_sms(frm, api_usr, api_key, content): return None -@hook("cmd_hook", "sms") +@hook.command("sms") def cmd_sms(msg): if not len(msg.args): 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) 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): if msg.text.find("Free") >= 0 and ( msg.text.find("API") >= 0 or msg.text.find("api") >= 0) and ( diff --git a/modules/spell/__init__.py b/modules/spell/__init__.py index fe5aadd..ca2c834 100644 --- a/modules/spell/__init__.py +++ b/modules/spell/__init__.py @@ -23,7 +23,7 @@ def help_full(): def load(context): context.data.setIndex("name", "score") -@hook("cmd_hook", "spell") +@hook.command("spell") def cmd_spell(msg): if not len(msg.args): 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.save() -@hook("cmd_hook", "spellscore") +@hook.command("spellscore") def cmd_score(msg): res = list() unknown = list() diff --git a/modules/suivi.py b/modules/suivi.py index a0964ac..55c469f 100644 --- a/modules/suivi.py +++ b/modules/suivi.py @@ -154,7 +154,7 @@ TRACKING_HANDLERS = { # HOOKS ############################################################## -@hook("cmd_hook", "track", +@hook.command("track", help="Track postage delivery", help_usage={ "TRACKING_ID [...]": "Track the specified postage IDs on various tracking services." diff --git a/modules/syno.py b/modules/syno.py index 10bb764..650e7e9 100644 --- a/modules/syno.py +++ b/modules/syno.py @@ -72,8 +72,8 @@ def get_english_synos(key, word): lang_binding = { 'fr': get_french_synos } -@hook("cmd_hook", "synonymes", data="synonymes") -@hook("cmd_hook", "antonymes", data="antonymes") +@hook.command("synonymes", data="synonymes") +@hook.command("antonymes", data="antonymes") def go(msg, what): if not len(msg.args): raise IMException("de quel mot veux-tu connaître la liste des synonymes ?") diff --git a/modules/tpb.py b/modules/tpb.py index 7d30ee1..ce98b04 100644 --- a/modules/tpb.py +++ b/modules/tpb.py @@ -22,7 +22,7 @@ def load(context): global URL_TPBAPI URL_TPBAPI = context.config["url"] -@hook("cmd_hook", "tpb") +@hook.command("tpb") def cmd_tpb(msg): if not len(msg.args): raise IMException("indicate an item to search!") diff --git a/modules/translate.py b/modules/translate.py index bbca24a..9d50966 100644 --- a/modules/translate.py +++ b/modules/translate.py @@ -79,7 +79,7 @@ def translate(term, langFrom="en", langTo="fr"): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "translate", +@hook.command("translate", help="Word translation using WordReference.com", help_usage={ "TERM": "Found translation of TERM from/to english to/from <lang>." diff --git a/modules/urbandict.py b/modules/urbandict.py index 135d240..e90c096 100644 --- a/modules/urbandict.py +++ b/modules/urbandict.py @@ -20,7 +20,7 @@ def search(terms): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "urbandictionnary") +@hook.command("urbandictionnary") def udsearch(msg): if not len(msg.args): raise IMException("Indicate a term to search") diff --git a/modules/velib.py b/modules/velib.py index aad5939..8ef6833 100644 --- a/modules/velib.py +++ b/modules/velib.py @@ -66,7 +66,7 @@ def print_station_status(msg, station): # MODULE INTERFACE #################################################### -@hook("cmd_hook", "velib", +@hook.command("velib", help="gives available bikes and slots at the given station", help_usage={ "STATION_ID": "gives available bikes and slots at the station STATION_ID" diff --git a/modules/weather.py b/modules/weather.py index 1d9cf13..34a861a 100644 --- a/modules/weather.py +++ b/modules/weather.py @@ -136,7 +136,7 @@ def get_json_weather(coords): return wth -@hook("cmd_hook", "coordinates") +@hook.command("coordinates") def cmd_coordinates(msg): if len(msg.args) < 1: 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) -@hook("cmd_hook", "alert") +@hook.command("alert") def cmd_alert(msg): loc, coords, specific = treat_coord(msg) wth = get_json_weather(coords) @@ -163,7 +163,7 @@ def cmd_alert(msg): return res -@hook("cmd_hook", "météo") +@hook.command("météo") def cmd_weather(msg): loc, coords, specific = treat_coord(msg) 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) -@hook("ask_default") +@hook.ask() def parseask(msg): res = gps_ask.match(msg.text) if res is not None: diff --git a/modules/whois.py b/modules/whois.py index 4c43500..4a13e9c 100644 --- a/modules/whois.py +++ b/modules/whois.py @@ -30,8 +30,8 @@ def load(context): context.data.getNode("pics").setIndex("login", "pict") import nemubot.hooks - context.add_hook("cmd_hook", - nemubot.hooks.Message(cmd_whois, "whois")) + context.add_hook("in_Command", + nemubot.hooks.Command(cmd_whois, "whois")) class Login: @@ -87,7 +87,7 @@ def cmd_whois(msg): res.append_message("Unknown %s :(" % srch) return res -@hook("cmd_hook", "nicks") +@hook.command("nicks") def cmd_nicks(msg): if len(msg.args) < 1: raise IMException("Provide a login") @@ -106,7 +106,7 @@ def cmd_nicks(msg): else: return Response("%s has no known alias." % nick, channel=msg.channel) -@hook("ask_default") +@hook.ask() 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) if res is not None: diff --git a/modules/wolframalpha.py b/modules/wolframalpha.py index e8421a3..a83b500 100644 --- a/modules/wolframalpha.py +++ b/modules/wolframalpha.py @@ -92,7 +92,7 @@ class WFAResults: # MODULE INTERFACE #################################################### -@hook("cmd_hook", "calculate", +@hook.command("calculate", help="Perform search and calculation using WolframAlpha", help_usage={ "TERM": "Look at the given term on WolframAlpha", diff --git a/modules/worldcup.py b/modules/worldcup.py index 87a182c..512a247 100644 --- a/modules/worldcup.py +++ b/modules/worldcup.py @@ -38,7 +38,7 @@ def start_watch(msg): context.save() raise IMException("This channel is now watching world cup events!") -@hook("cmd_hook", "watch_worldcup") +@hook.command("watch_worldcup") def cmd_watch(msg): # Get current state @@ -177,7 +177,7 @@ def get_matches(url): if is_valid(match): yield match -@hook("cmd_hook", "worldcup") +@hook.command("worldcup") def cmd_worldcup(msg): res = Response(channel=msg.channel, nomore="No more match to display", count=" (%d more matches)") diff --git a/nemubot/bot.py b/nemubot/bot.py index 32a2f22..b3dfbc1 100644 --- a/nemubot/bot.py +++ b/nemubot/bot.py @@ -79,7 +79,7 @@ class Bot(threading.Thread): def in_echo(msg): from nemubot.message import Text 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): """Parse and response to help messages""" @@ -98,7 +98,7 @@ class Bot(threading.Thread): elif msg.args[0][0] == "!": for module in self.modules: 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: 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() @@ -128,7 +128,7 @@ class Bot(threading.Thread): " 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__]) 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 # Messages to be treated @@ -462,9 +462,9 @@ class Bot(threading.Thread): # Register decorated functions 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) - nemubot.hooks.last_registered = [] + nemubot.hooks.hook.last_registered = [] # Launch the module if hasattr(module, "load"): diff --git a/nemubot/hooks/__init__.py b/nemubot/hooks/__init__.py index a9a8a31..9904119 100644 --- a/nemubot/hooks/__init__.py +++ b/nemubot/hooks/__init__.py @@ -14,29 +14,54 @@ # 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/>. +from nemubot.hooks.abstract import Abstract +from nemubot.hooks.command import Command from nemubot.hooks.message import Message -last_registered = [] + +class hook: + + last_registered = [] -def hook(store, *args, **kargs): - """Function used as a decorator for module loading""" - def sec(call): - last_registered.append((store, Message(call, *args, **kargs))) - return call - return sec + def _add(store, h, *args, **kwargs): + """Function used as a decorator for module loading""" + def sec(call): + hook.last_registered.append((store, h(call, *args, **kwargs))) + return call + 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(): - global Message import imp import nemubot.hooks.abstract imp.reload(nemubot.hooks.abstract) + import nemubot.hooks.command + imp.reload(nemubot.hooks.command) + import nemubot.hooks.message imp.reload(nemubot.hooks.message) - Message = nemubot.hooks.message.Message import nemubot.hooks.keywords imp.reload(nemubot.hooks.keywords) diff --git a/nemubot/hooks/abstract.py b/nemubot/hooks/abstract.py index e2dc78b..25efc45 100644 --- a/nemubot/hooks/abstract.py +++ b/nemubot/hooks/abstract.py @@ -87,6 +87,10 @@ class Abstract: return False + def __str__(self): + return "" + + def can_write(self, receivers=list(), server=None): return True diff --git a/nemubot/hooks/command.py b/nemubot/hooks/command.py new file mode 100644 index 0000000..02fdb4d --- /dev/null +++ b/nemubot/hooks/command.py @@ -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)) + ) diff --git a/nemubot/hooks/message.py b/nemubot/hooks/message.py index a14177a..1c245ea 100644 --- a/nemubot/hooks/message.py +++ b/nemubot/hooks/message.py @@ -17,9 +17,6 @@ import re 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 @@ -27,64 +24,26 @@ class Message(Abstract): """Class storing hook information, specialized for a generic Message""" - def __init__(self, call, name=None, regexp=None, channels=list(), - server=None, help=None, help_usage=dict(), keywords=NoKeyword(), - **kargs): - - Abstract.__init__(self, call=call, **kargs) - - if isinstance(keywords, dict): - keywords = DictKeywords(keywords) + def __init__(self, call, regexp=None, help=None, **kwargs): + super().__init__(call=call, **kwargs) 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.server = server - self.channels = channels self.help = help - 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.server) else "", - ": %s" % self.help if self.help is not None else "" - ) + # TODO: find a way to name the feature (like command: help) + return self.help if self.help is not None else super().__str__() 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): - if not isinstance(msg, nemubot.message.abstract.Abstract): - 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: + def match(self, msg): + if not isinstance(msg, nemubot.message.text.Text): 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: - return True - return False + else: + return self.regexp is None or re.match(self.regexp, msg.message) diff --git a/nemubot/modulecontext.py b/nemubot/modulecontext.py index 5b47278..b24d94d 100644 --- a/nemubot/modulecontext.py +++ b/nemubot/modulecontext.py @@ -14,21 +14,6 @@ # 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/>. -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: def __init__(self, context, module): @@ -60,11 +45,9 @@ class ModuleContext: self.data = context.datastore.load(module_name) def add_hook(store, hook): - store = convert_legacy_store(store) self.hooks.append((store, hook)) return context.treater.hm.add_hook(hook, store) def del_hook(store, hook): - store = convert_legacy_store(store) self.hooks.remove((store, hook)) return context.treater.hm.del_hook(hook, store) def call_hook(store, msg): @@ -98,10 +81,8 @@ class ModuleContext: self.data = module_state.ModuleState("nemubotstate") def add_hook(store, hook): - store = convert_legacy_store(store) self.hooks.append((store, hook)) def del_hook(store, hook): - store = convert_legacy_store(store) self.hooks.remove((store, hook)) def call_hook(store, msg): # TODO: what can we do here?