From 2793c97469fdf428728531c422e6a9d68e6b8f32 Mon Sep 17 00:00:00 2001 From: BananeChocolat <52428259+BananeChocolat@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:46:23 +0200 Subject: [PATCH 1/3] Modification du menu (osef des allergenes <3) --- src/cogs/restauration.py | 83 +++++++--------------------------------- 1 file changed, 13 insertions(+), 70 deletions(-) diff --git a/src/cogs/restauration.py b/src/cogs/restauration.py index 6eddd70..2caea9f 100644 --- a/src/cogs/restauration.py +++ b/src/cogs/restauration.py @@ -29,6 +29,7 @@ def __init__(self, bot: MP2IBot) -> None: self.already_posted: list[str] = self.read_restauration_file() async def cog_load(self) -> None: + await self.check_menu() self.check_menu.start() async def cog_unload(self) -> None: @@ -61,89 +62,31 @@ def read_restauration_file(self) -> list[str]: with open(RESTAURATION_PATH, "r") as f: return json.load(f) - async def get_menu_imgs(self) -> dict[str, io.BytesIO]: - """ - This function will get the images from the page "restaurant-scolaire". - Something noticeable is that, it doesn't search for each images links in the page, but rather get the whole - page downloaded in a zip, using an export method of mbn. - - The zip structure looks like : - . - ├── xxx/ - │ ├── photo_dddd-ddddd.jpeg - │ ├── photo_dddd-ddddd.jpeg - │ ├── v_photo_dddd-ddddd.jpeg - │ └── v_photo_dddd-ddddd.jpeg - └── Restaurant scolaire_xxx.html - - So, by checking the file name in xxx/, we can check if the image was already sent. - Btw, the v_* files are just the photos in a very very low quality. So we ignore them. - - By doing this, we are able to get all images in one single request. - But we *need* to fetch them all each time, ig. - Something possible is to compare the ID_METATAG, to avoid redundant requests. - Otherwise, we can look at each single photos in the page 1 by 1, to not make extra request if already fetched. - But this implies to multiply the minimal amount of request needed. - """ + + @tasks.loop(minutes=60) + async def check_menu(self) -> None: async with httpx.AsyncClient() as client: - result = await client.get("https://lyc-kleber.monbureaunumerique.fr/l-etablissement/restaurant-scolaire/") + result = await client.get("https://lycee-kleber.com.fr/restauration") page = result.text - scrap = BeautifulSoup(page, "html.parser") - - if not isinstance(tag := scrap.find("input", id="ID_METATAG"), Tag): - raise Exception("Could not find the tag") - if not isinstance(tag_id := tag.get("value"), str): - raise Exception("Could not find the tag id") - - print(tag_id) # temp : just check if the tag change when file changes. - - result = await client.get( - f"https://lyc-kleber.monbureaunumerique.fr/exportContent?&ACTION=EXPORTER&exportType=FICHE&ID_FICHE={tag_id}" - ) - zip_buffer = io.BytesIO(result.read()) - zip_obj = ZipFile(zip_buffer, "r") - - imgs_buffers: dict[str, io.BytesIO] = {} - imgs = [obj for obj in zip_obj.infolist() if obj.filename.endswith("jpeg") and obj.file_size > 50_000] - - for img in imgs: - buffer = io.BytesIO(zip_obj.read(img.filename)) - buffer.seek(0) - imgs_buffers[img.filename] = buffer + scrap = BeautifulSoup(page, "html.parser") + img_link = scrap.body.div.div.div.section[3].div.div.div.div.div.div[3].div.div.h2[2].strong.a.get('href') - return imgs_buffers - async def post_menu(self, imgs: dict[str, io.BytesIO]) -> None: - """ - This function will post the new images on Discord. + if img_link in self.already_posted: + return + else: + self.add_restauration_file(img_link) - It will look for each channel named "menu-cantine" in all guilds the bot have. - Then it will just post all the images in one single message for all of those. - """ channels: list[TextChannel] = [ ch for ch in self.bot.get_all_channels() if isinstance(ch, TextChannel) and ch.name == "menu-cantine" ] + for channel in channels: - files = [File(buffer, filename=filename) for filename, buffer in imgs.items()] try: - await channel.send(files=files) + await channel.send(img_link) except HTTPException: pass - [buffer.seek(0) for buffer in imgs.values()] - - @tasks.loop(minutes=30) - async def check_menu(self) -> None: - try: - menus = await self.get_menu_imgs() - except Exception: - return - - menus = {filename: image_file for filename, image_file in menus.items() if filename not in self.already_posted} - for filename in menus: - self.add_restauration_file(filename) - await self.post_menu(menus) - async def setup(bot: MP2IBot): await bot.add_cog(Restauration(bot)) From 1c51d9181add9ca00838310019e821471df0758a Mon Sep 17 00:00:00 2001 From: "airo.pi_" <47398145+AiroPi@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:28:51 +0200 Subject: [PATCH 2/3] =?UTF-8?q?use=20regex=20to=20find=20menus=20and=20fr:?= =?UTF-8?q?allerg=C3=A8nes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cogs/restauration.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/cogs/restauration.py b/src/cogs/restauration.py index 2caea9f..f172eae 100644 --- a/src/cogs/restauration.py +++ b/src/cogs/restauration.py @@ -4,21 +4,22 @@ from __future__ import annotations -import io import json +import re from os import path from typing import TYPE_CHECKING -from zipfile import ZipFile import httpx -from bs4 import BeautifulSoup, Tag -from discord import File, HTTPException, TextChannel +from bs4 import BeautifulSoup +from discord import HTTPException, TextChannel from discord.ext import tasks from discord.ext.commands import Cog # pyright: ignore[reportMissingTypeStubs] if TYPE_CHECKING: from bot import MP2IBot + +IMAGES_REGEX = re.compile(r"https://lycee-kleber.com.fr/wp-content/uploads/\d{4}/\d{2}/([^.]+).jpg") RESTAURATION_PATH = "./data/restauration.json" @@ -62,20 +63,34 @@ def read_restauration_file(self) -> list[str]: with open(RESTAURATION_PATH, "r") as f: return json.load(f) + async def get_imgs(self) -> tuple[tuple[str, ...], tuple[str, ...]]: + """Scrap the website to get the menu and allergenes images. - @tasks.loop(minutes=60) - async def check_menu(self) -> None: + Returns: + A tuple with fr:MENUs, and a second tuple with fr:ALLERGENES. + """ async with httpx.AsyncClient() as client: result = await client.get("https://lycee-kleber.com.fr/restauration") page = result.text scrap = BeautifulSoup(page, "html.parser") - img_link = scrap.body.div.div.div.section[3].div.div.div.div.div.div[3].div.div.h2[2].strong.a.get('href') + element = scrap.find_all("a", href=lambda r: bool(IMAGES_REGEX.match(r))) + links: list[str] = [e.get("href") for e in element] + menus: tuple[str, ...] = tuple( + l for l in links if (m := IMAGES_REGEX.match(l)) and m.group(1).lower().startswith("menu") + ) + allergenes: tuple[str, ...] = tuple( + l for l in links if (m := IMAGES_REGEX.match(l)) and m.group(1).lower().startswith("allergenes") + ) + return menus, allergenes - if img_link in self.already_posted: - return - else: + @tasks.loop(minutes=60) + async def check_menu(self) -> None: + """Post the menu if there is a new one, checked every hour.""" + menus, _ = await self.get_imgs() + menus = [m for m in menus if m not in self.already_posted] # filter with only new ones. + for img_link in menus: self.add_restauration_file(img_link) channels: list[TextChannel] = [ @@ -84,9 +99,10 @@ async def check_menu(self) -> None: for channel in channels: try: - await channel.send(img_link) + await channel.send("\n".join(menus)) except HTTPException: pass + async def setup(bot: MP2IBot): await bot.add_cog(Restauration(bot)) From a71f2c621a30f68904296ab92d9ac1eba53329e8 Mon Sep 17 00:00:00 2001 From: "airo.pi_" <47398145+AiroPi@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:34:02 +0200 Subject: [PATCH 3/3] Add an allergen command. --- src/cogs/restauration.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cogs/restauration.py b/src/cogs/restauration.py index f172eae..55c3007 100644 --- a/src/cogs/restauration.py +++ b/src/cogs/restauration.py @@ -9,9 +9,10 @@ from os import path from typing import TYPE_CHECKING +import discord import httpx from bs4 import BeautifulSoup -from discord import HTTPException, TextChannel +from discord import HTTPException, TextChannel, app_commands from discord.ext import tasks from discord.ext.commands import Cog # pyright: ignore[reportMissingTypeStubs] @@ -103,6 +104,20 @@ async def check_menu(self) -> None: except HTTPException: pass + @app_commands.command(name="allergenes", description="Affiche les allergènes du menu du jour.") + async def allergen(self, inter: discord.Interaction): + _, allergens = await self.get_imgs() + bn = "\n" + await inter.response.send_message( + ( + "Voici les allergènes du menu du jour :\n" + f"{bn.join(allergens)}" + "\n\nS'ils ne sont pas à jour, c'est que le lycée ne les a pas publié.\n" + "Attention : les allergènes sont susceptibles d'être modifiés, merci de se référer au panneau" + " d'affichage à la restauration scolaire." + ) + ) + async def setup(bot: MP2IBot): await bot.add_cog(Restauration(bot))