diff --git a/base-config.yaml b/base-config.yaml
index 64c2165..3408a8b 100644
--- a/base-config.yaml
+++ b/base-config.yaml
@@ -1,3 +1,4 @@
hoarder_url: "http://hoarder.local"
hoarder_api_key: "votre_cle_api"
+hoarder_list_name: "list_name"
command_prefix: hoarder
\ No newline at end of file
diff --git a/hoarder.py b/hoarder.py
index f9571e1..26b76e2 100644
--- a/hoarder.py
+++ b/hoarder.py
@@ -4,13 +4,13 @@ from typing import Type
from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper
from maubot import Plugin, MessageEvent
from maubot.handlers import event, command
-from datetime import datetime
# Setup config file
class Config(BaseProxyConfig):
def do_update(self, helper: ConfigUpdateHelper) -> None:
helper.copy("hoarder_url")
helper.copy("hoarder_api_key")
+ helper.copy("hoarder_list_name")
helper.copy("command_prefix")
class HoarderForwarder(Plugin):
@@ -22,35 +22,54 @@ class HoarderForwarder(Plugin):
def get_config_class(cls) -> Type[BaseProxyConfig]:
return Config
- async def get_page_title(self, url: str) -> str:
- """Récupère le titre de la page web à partir de l'URL sans utiliser BeautifulSoup."""
- try:
- async with aiohttp.ClientSession() as session:
- async with session.get(url) as response:
- if response.status == 200:
- html = await response.text()
- # Rechercher la balise
- match = re.search(r"(.*?)", html, re.IGNORECASE | re.DOTALL)
- if match:
- return match.group(1).strip()
- else:
- return "Sans titre"
- else:
- return "Titre introuvable"
- except Exception as e:
- print(f"Erreur lors de la récupération du titre: {e}")
- return "Erreur"
-
- @command.new(
- "hoarder",
- )
+ async def send_message(evt: MessageEvent, message: str, formatted: bool = False, in_thread: bool = False):
+ """
+ Envoie un message en réponse à un événement Matrix, avec support des threads.
+
+ Args:
+ evt (MessageEvent): L'événement de type message auquel répondre.
+ message (str): Le texte du message à envoyer.
+ formatted (bool): Si True, le message sera envoyé en tant que texte formaté (HTML).
+ in_thread (bool): Si True, le message sera envoyé en tant que réponse dans un thread.
+ """
+ content = {
+ "body": message,
+ "msgtype": "m.text" if not formatted else "m.notice",
+ }
+
+ if formatted:
+ content["formatted_body"] = message
+ content["format"] = "org.matrix.custom.html"
+
+ if in_thread:
+ # Ajouter des métadonnées pour créer un thread
+ content["m.relates_to"] = {
+ "rel_type": "m.thread",
+ "event_id": evt.event_id # ID de l'événement original pour le thread
+ }
+
+ # Envoyer le message
+ await evt.client.send_message(
+ room_id=evt.room_id, # Envoyer dans la même salle
+ content=content
+ )
+
+ @command.new("hoarder", require_subcommand=True, help="Commandes pour gérer Hoarder.")
+ async def base_command(self, evt: MessageEvent):
+ """Commande principale pour Hoarder (placeholder)."""
+ #await evt.reply("Utilisez `!hoarder help` pour voir les commandes disponibles.")
+ pass
+ @base_command.subcommand("bookmark", help="Ajoute un bookmark à Hoarder.")
@command.argument("url", pass_raw=True, required=True)
- async def send_to_hoarder(self, evt: MessageEvent, url: str) -> None:
+ async def bookmark(self, evt: MessageEvent, url: str) -> None:
await evt.mark_read()
- hoarder_url = self.config["hoarder_url"] + '/api/trpc/bookmarks.createBookmark'
+ hoarder_list = await self.ensure_list_exists(evt, self.config["hoarder_list_name"])
+
+ hoarder_url = self.config["hoarder_url"]
api_key = self.config["hoarder_api_key"]
+ create_endpoint = f"{hoarder_url}/api/trpc/bookmarks.createBookmark"
self.log.info(hoarder_url)
#title = await self.get_page_title(url) # Récupère le titre de la page
@@ -73,10 +92,141 @@ class HoarderForwarder(Plugin):
try:
async with aiohttp.ClientSession() as session:
- async with session.post(hoarder_url, json=payload, headers=headers) as response:
- if response.status == 201:
- self.log.info(f"URL envoyée avec succès à Hoarder : {url}")
+ async with session.post(create_endpoint, json=payload, headers=headers) as response:
+ if response.status == 200 or response.status == 201 or response.status == 204:
+ data = await response.json()
+ bookmark_id = data[0]["result"]["data"]["json"]["id"]
+ await self.send_message(evt, f"Bookmark créé avec succès : {bookmark_id}", in_thread=True)
+ self.add_bookmark_to_list(bookmark_id, hoarder_list)
+ return bookmark_id
else:
- self.log.error(f"Échec de l'envoi de l'URL à Hoarder : {response.status}")
+ await self.send_message(evt, f"Erreur lors de la création du bookmark : {response.status}", in_thread=True)
+ print(await response.text())
except Exception as e:
- self.log.exception(f"Erreur lors de l'envoi de l'URL à Hoarder : {e}")
+ await self.send_message(evt, f"Erreur lors de la création du bookmark : {e}", in_thread=True)
+ return None
+
+ async def get_all_lists(self, evt: MessageEvent):
+ """Récupère la liste des listes sur Hoarder."""
+ hoarder_url = self.config["hoarder_url"]
+ api_key = self.config["hoarder_api_key"]
+ list_endpoint = f"{hoarder_url}/api/v1/lists"
+
+ headers = {
+ "Authorization": f"Bearer {api_key}",
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.get(list_endpoint, headers=headers) as response:
+ if response.status == 200:
+ data = await response.json()
+ return data.get("lists", [])
+ else:
+ await self.send_message(evt, f"Erreur lors de la récupération des listes : {response.status}", in_thread=True)
+ return []
+ except Exception as e:
+ await self.send_message(evt, f"Erreur lors de la récupération des listes : {e}", in_thread=True)
+ return []
+
+ async def find_list_by_name(self, evt: MessageEvent, name: str) -> str:
+ """Vérifie si une liste existe et retourne son ID si elle est trouvée."""
+ lists = await self.get_all_lists(evt)
+ for lst in lists:
+ if lst["name"] == name:
+ await self.send_message(evt, f"Liste trouvée : {name} (ID : {lst['id']})", in_thread=True)
+ return lst["id"]
+ await self.send_message(evt, f"Aucune liste trouvée avec le nom : {name}", in_thread=True)
+ return None
+
+ async def create_list(self, evt: MessageEvent, name: str) -> str:
+ """Crée une nouvelle liste avec le nom donné et retourne son ID."""
+ hoarder_url = self.config["hoarder_url"]
+ api_key = self.config["hoarder_api_key"]
+ create_endpoint = f"{hoarder_url}/api/trpc/lists.createList"
+
+ headers = {
+ "Authorization": f"Bearer {api_key}",
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ payload = {
+ "json": {
+ "icon": "💬", # Icône par défaut
+ "name": name
+ }
+ }
+
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.post(create_endpoint, json=payload, headers=headers) as response:
+ if response.status == 200 or response.status == 201 or response.status == 204:
+ data = await response.json()
+ list_id = data["result"]["data"]["json"]["id"]
+ await self.send_message(evt, f"Liste créée avec succès : {name} (ID : {list_id})", in_thread=True)
+ return list_id
+ else:
+ await self.send_message(evt, f"Erreur lors de la création de la liste : {response.status}", in_thread=True)
+ return None
+ except Exception as e:
+ await self.send_message(evt, f"Erreur lors de la création de la liste : {e}", in_thread=True)
+ return None
+
+ async def ensure_list_exists(self, evt: MessageEvent, name: str) -> str:
+ """Vérifie si une liste existe, sinon la crée."""
+ list_id = await self.find_list_by_name(evt, name)
+ if list_id is None:
+ await self.send_message(evt, f"La liste '{name}' n\'existe pas, création en cours...", in_thread=True)
+ list_id = await self.create_list(evt, name)
+ return list_id
+
+ async def add_bookmark_to_list(self, evt: MessageEvent, bookmark_id: str):
+ """Ajoute un bookmark à une liste donnée."""
+ hoarder_url = self.config["hoarder_url"]
+ api_key = self.config["hoarder_api_key"]
+
+ list_id = await self.ensure_list_exists(evt, self.config["hoarder_list_name"])
+ add_to_list_endpoint = f"{hoarder_url}/api/v1/lists/{list_id}/bookmarks"
+
+ headers = {
+ "Authorization": f"Bearer {api_key}",
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ }
+
+ payload = {
+ "bookmarkId": bookmark_id
+ }
+
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.put(add_to_list_endpoint, headers=headers) as response:
+ if response.status == 200 or response.status == 201 or response.status == 204:
+ await self.send_message(evt, f"Bookmark {bookmark_id} ajouté à la liste {list_id} avec succès.", in_thread=True)
+ return True
+ else:
+ await self.send_message(evt, f"Erreur lors de l'ajout à la liste : {response.status}", in_thread=True)
+ await self.send_message(evt, await response.text(), in_thread=True)
+ return False
+ except Exception as e:
+ await self.send_message(evt, f"Erreur lors de l'ajout du bookmark à la liste : {e}", in_thread=True)
+ return False
+
+ @base_command.subcommand("help", help="Affiche l'aide pour les commandes Hoarder.")
+ async def hoarder_help(self, evt: MessageEvent):
+ """Affiche un message d'aide pour les commandes Hoarder."""
+ help_message = (
+ "💬 **Commandes disponibles pour Hoarder Bot** 💬\n"
+ "- `!hoarder bookmark ` : Ajoute le lien spécifié à votre liste Hoarder.\n"
+ "- `!hoarder help` : Affiche ce message d'aide.\n\n"
+ "⚙️ **Configuration** :\n"
+ "Assurez-vous que l'URL de base de votre installation d'Hoarder est définie dans la configuration sous `hoarder_url`.\n\n"
+ "Assurez-vous que la clef API d'Hoarder est définie dans la configuration sous `hoarder_api_key`.\n\n"
+ "Assurez-vous que la liste cible est définie dans la configuration sous `hoarder_list_name`.\n\n"
+ "📘 **Documentation supplémentaire** :\n"
+ "Consultez les guides sur [Hoarder](https://docs.hoarder.app)."
+ )
+ await evt.reply(help_message)
\ No newline at end of file
diff --git a/maubot.yaml b/maubot.yaml
index a4f8e09..10be18e 100644
--- a/maubot.yaml
+++ b/maubot.yaml
@@ -8,7 +8,7 @@ maubot: 0.1.0
id: fr.mrraph.hoarder-bot
# A PEP 440 compliant version string.
-version: 0.0.2
+version: 0.1.0
# The SPDX license identifier for the plugin. https://spdx.org/licenses/
# Optional, assumes all rights reserved if omitted.