1
0
Fork 0
nemubot/modules/ddg.py

139 lines
3.7 KiB
Python

"""Search around DuckDuckGo search engine"""
# PYTHON STUFFS #######################################################
from urllib.parse import quote
from nemubot.exception import IMException
from nemubot.hooks import hook
from nemubot.tools import web
from nemubot.module.more import Response
# MODULE CORE #########################################################
def do_search(terms):
if "!safeoff" in terms:
terms.remove("!safeoff")
safeoff = True
else:
safeoff = False
sterm = " ".join(terms)
return DDGResult(sterm, web.getJSON(
"https://api.duckduckgo.com/?q=%s&format=json&no_redirect=1%s" %
(quote(sterm), "&kp=-1" if safeoff else "")))
class DDGResult:
def __init__(self, terms, res):
if res is None:
raise IMException("An error occurs during search")
self.terms = terms
self.ddgres = res
@property
def type(self):
if not self.ddgres or "Type" not in self.ddgres:
return ""
return self.ddgres["Type"]
@property
def definition(self):
if "Definition" not in self.ddgres or not self.ddgres["Definition"]:
return None
return self.ddgres["Definition"] + " <" + self.ddgres["DefinitionURL"] + "> from " + self.ddgres["DefinitionSource"]
@property
def relatedTopics(self):
if "RelatedTopics" in self.ddgres:
for rt in self.ddgres["RelatedTopics"]:
if "Text" in rt:
yield rt["Text"] + " <" + rt["FirstURL"] + ">"
elif "Topics" in rt:
yield rt["Name"] + ": " + "; ".join([srt["Text"] + " <" + srt["FirstURL"] + ">" for srt in rt["Topics"]])
@property
def redirect(self):
if "Redirect" not in self.ddgres or not self.ddgres["Redirect"]:
return None
return self.ddgres["Redirect"]
@property
def entity(self):
if "Entity" not in self.ddgres or not self.ddgres["Entity"]:
return None
return self.ddgres["Entity"]
@property
def heading(self):
if "Heading" not in self.ddgres or not self.ddgres["Heading"]:
return " ".join(self.terms)
return self.ddgres["Heading"]
@property
def result(self):
if "Results" in self.ddgres:
for res in self.ddgres["Results"]:
yield res["Text"] + " <" + res["FirstURL"] + ">"
@property
def answer(self):
if "Answer" not in self.ddgres or not self.ddgres["Answer"]:
return None
return web.striphtml(self.ddgres["Answer"])
@property
def abstract(self):
if "Abstract" not in self.ddgres or not self.ddgres["Abstract"]:
return None
return self.ddgres["AbstractText"] + " <" + self.ddgres["AbstractURL"] + "> from " + self.ddgres["AbstractSource"]
# MODULE INTERFACE ####################################################
@hook.command("define")
def define(msg):
if not len(msg.args):
raise IMException("Indicate a term to define")
s = do_search(msg.args)
if not s.definition:
raise IMException("no definition found for '%s'." % " ".join(msg.args))
return Response(s.definition, channel=msg.channel)
@hook.command("search")
def search(msg):
if not len(msg.args):
raise IMException("Indicate a term to search")
s = do_search(msg.args)
res = Response(channel=msg.channel, nomore="No more results",
count=" (%d more results)")
res.append_message(s.redirect)
res.append_message(s.answer)
res.append_message(s.abstract)
res.append_message([r for r in s.result])
for rt in s.relatedTopics:
res.append_message(rt)
res.append_message(s.definition)
return res