diff --git a/.gitignore b/.gitignore index fdc50ab..a0dcfb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode __pycache__ -*.json \ No newline at end of file +*.json +*.html \ No newline at end of file diff --git a/cogs/kanji.py b/cogs/kanji.py new file mode 100644 index 0000000..801e3d2 --- /dev/null +++ b/cogs/kanji.py @@ -0,0 +1,91 @@ +import discord +from discord.ext import commands +from utils import jisho + +class JishoKanjiObject(): + def __init__(self, query, owner): + self.response = jisho.JishoKanji(query) + self.total_pages = self.response.entries + self.page = 0 + self.owner = owner + + def prev(self): + self.page -= 1 + if self.page < 0: + self.page = self.total_pages - 1 + + def next(self): + self.page += 1 + if self.page >= self.total_pages: + self.page = 0 + + +class Kanji(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.activeObject = None + self.latestMessage = 0 + + async def createEmbed(self): + response = self.activeObject.response + node = response.nodes[self.activeObject.page] + + embed = discord.Embed( + title = node.kanji, + url = node.url, + description = node.meaning, + colour = 0x56d926 + ) + + if node.kun: + embed.add_field(name="Kun", value="、 ".join(node.kun), inline=False) + if node.on: + embed.add_field(name="On", value="、 ".join(node.on), inline=False) + + embed.add_field(name=f"Radical: {node.radical[0]}", value=node.radical[1], inline=False) + + embed.set_footer(text=f"Jōyō kanji (Grade {node.grade}) | JLPT level {node.jlpt}\t\t{self.activeObject.page + 1}/{self.activeObject.total_pages}") + + return embed + + @commands.Cog.listener() + async def on_reaction_add(self, reaction, user): + message = reaction.message + if message.id != self.latestMessage: + return + + if user == self.bot.user: + return + + if user.id != self.activeObject.owner: + return + + if reaction.me: + if reaction.emoji == "⬅️": + self.activeObject.prev() + await reaction.remove(user) + + if reaction.emoji == "➡️": + self.activeObject.next() + await reaction.remove(user) + + embed = await self.createEmbed() + await message.edit(embed=embed) + + @commands.command(name="kanji", description="Performs a Kanji search", usage="", aliases=["k"]) + @commands.cooldown(1, 5) + async def kanji(self, ctx, *, kanji: str = None): + if kanji is None: + return + + self.activeObject = JishoKanjiObject(kanji, ctx.author.id) + embed = await self.createEmbed() + message = await ctx.send(embed=embed) + self.latestMessage = message.id + + if self.activeObject.total_pages > 1: + await message.add_reaction("⬅️") + await message.add_reaction("➡️") + +def setup(bot): + bot.add_cog(Kanji(bot)) \ No newline at end of file diff --git a/cogs/search.py b/cogs/search.py index b4853a7..a1bc958 100644 --- a/cogs/search.py +++ b/cogs/search.py @@ -3,10 +3,11 @@ from discord.ext import commands from utils import jisho class JishoObject(): - def __init__(self, query): + def __init__(self, query, owner): self.response = jisho.JishoResponse(query) self.total_pages = self.response.entries self.page = 0 + self.owner = owner def prev(self): self.page -= 1 @@ -71,18 +72,21 @@ class Search(commands.Cog): if user == self.bot.user: return + if user.id != self.activeObject.owner: + return + if reaction.me: if reaction.emoji == "⬅️": self.activeObject.prev() + await reaction.remove(user) if reaction.emoji == "➡️": self.activeObject.next() + await reaction.remove(user) embed = await self.createEmbed() await message.edit(embed=embed) - await reaction.remove(user) - @commands.command(name="search", description="Searches Jisho", usage="", aliases=["s"]) @commands.cooldown(1, 5) @@ -90,7 +94,7 @@ class Search(commands.Cog): if query == None: return - self.activeObject = JishoObject(query) + self.activeObject = JishoObject(query, ctx.author.id) embed = await self.createEmbed() message = await ctx.send(embed=embed) self.latestMessage = message.id diff --git a/utils/jisho.py b/utils/jisho.py index 801d6be..ac2dd88 100644 --- a/utils/jisho.py +++ b/utils/jisho.py @@ -1,14 +1,17 @@ import requests import urllib.parse import json +import re +from bs4 import BeautifulSoup TEMPLATE_URL = "https://jisho.org/api/v1/search/words?keyword={0}" +TEMPLATE_KANJI_URL = "https://jisho.org/search/{0}" HEADER = { "User-Agent": "Jisho Bot", "From": "https://github.com/Lauchmelder23/JishoBot" } -class JishoSenses(): +class JishoSense(): def __init__(self, sense): self.english_definitions = sense["english_definitions"] self.fenglish_definitions = "; ".join(self.english_definitions) @@ -52,7 +55,7 @@ class JishoNode(): self.senses = [] for sense in node["senses"]: - self.senses.append(JishoSenses(sense)) + self.senses.append(JishoSense(sense)) class JishoResponse(): def __init__(self, query: str): @@ -63,7 +66,7 @@ class JishoResponse(): def query(self): url = TEMPLATE_URL.format(urllib.parse.quote_plus(self.query_string)) - r = requests.get(url) + r = requests.get(url, headers=HEADER) if r.status_code != 200: print(f"ERROR: Failed to access Jisho API... {r.status_code}") @@ -78,3 +81,78 @@ class JishoResponse(): for node in self.raw["data"]: self.nodes.append(JishoNode(node)) + + +class JishoKanjiNode(): + def __init__(self): + # Information about the Kanji + self.kanji = "" + self.url = "https://jisho.org/search/" + self.meaning = "" + self.kun = [] + self.on = [] + self.radical = [] + self.grade = "" + self.jlpt = "" + + +class JishoKanji(): + def __init__(self, query): + self.query_string = query + + # List of JishoKanjiNodes + self.nodes = [] + self.entries = 0 + + self.query() + + def query(self): + self.url = TEMPLATE_KANJI_URL.format(urllib.parse.quote_plus(self.query_string + "#kanji")) + r = requests.get(self.url, headers=HEADER) + + if r.status_code != 200: + print(f"ERROR: Failed to access Jisho API... {r.status_code}") + return None + + body = BeautifulSoup(r.text, features="html.parser") + + info_blocks = body.find_all("div", {"class": "kanji details"}) + + for info in info_blocks: + self.entries += 1 + self.nodes.append(JishoKanjiNode()) + + self.nodes[-1].kanji = info.findChild("h1").string + self.nodes[-1].url += urllib.parse.quote_plus(self.nodes[-1].kanji + "#kanji") + + # Meanings + self.nodes[-1].meaning = info.findChild("div", {"class": "kanji-details__main-meanings"}, recursive=True).string + + readings_block = info.findChild("div", {"class": "kanji-details__main-readings"}, recursive=True) + + # Kun Yomi + kun_block = readings_block.findChild("dl", {"class": "dictionary_entry kun_yomi"}, recursive=True) + if kun_block != None: + readings = kun_block.findChildren("a", recursive=True) + for reading in readings: + self.nodes[-1].kun.append(reading.string) + + # On Yomi + on_block = readings_block.findChild("dl", {"class": "dictionary_entry on_yomi"}, recursive=True) + if on_block != None: + readings = on_block.findChildren("a", recursive=True) + for reading in readings: + self.nodes[-1].on.append(reading.string) + + # Radical + radical_block = info.findChild("div", {"class": "radicals"}, recursive=True).findChild("span") + self.nodes[-1].radical.append(re.sub(r'[ \n"]', "", radical_block.contents[2].string)) + self.nodes[-1].radical.append(re.sub(r'[ \n"]', "", radical_block.contents[1].string)) + + # JLPT/Grade info + grade_block = info.findChild("div", {"class": "grade"}, recursive=True) + if grade_block != None: + self.nodes[-1].grade = grade_block.findChild("strong").string[-1] + + self.nodes[-1].jlpt = info.findChild("div", {"class": "jlpt"}, recursive=True).findChild("strong").string +