Compare commits

..

12 Commits

Author SHA1 Message Date
975c3a4131 Actualiser .gitea/workflows/build-and-release.yml
Some checks failed
Build and Release Maubot Plugin / Build Plugin (push) Failing after 26s
2024-12-03 16:20:51 +01:00
5b9d8920de Actualiser .gitea/workflows/build-and-release.yml 2024-12-03 16:20:26 +01:00
MrRaph_
1a58e61d36 .
Some checks failed
Build and Release Maubot Plugin / Build Plugin (push) Failing after 39s
2024-12-03 16:14:28 +01:00
41902ad080 Actualiser .gitea/workflows/build-and-release.yml
Some checks failed
Build and Release Maubot Plugin / Build Plugin (push) Successful in 37s
Build and Release Maubot Plugin / Release Plugin (push) Failing after 22s
2024-12-03 14:51:24 +01:00
9f8c08e561 Actualiser .gitea/workflows/build-and-release.yml
Some checks failed
Build and Release Maubot Plugin / Build Plugin (push) Successful in 38s
Build and Release Maubot Plugin / Release Plugin (push) Failing after 21s
2024-12-03 14:40:19 +01:00
6126bc3bb1 Actualiser .gitea/workflows/build-and-release.yml 2024-12-03 14:36:52 +01:00
98c84ff171 Actualiser .gitea/workflows/build-and-release.yml
Some checks failed
Build and Release Maubot Plugin / Build Plugin (push) Successful in 1m12s
Build and Release Maubot Plugin / Release Plugin (push) Failing after 34s
2024-12-03 14:32:12 +01:00
MrRaph_
9525d8dc9d feat: added CI
Some checks failed
Build and Release Maubot Plugin / Build Plugin (push) Failing after 2m44s
Build and Release Maubot Plugin / Release Plugin (push) Has been skipped
2024-12-03 14:26:49 +01:00
MrRaph_
f91f0d229a feat: ajout du lien dans la liste 2024-12-03 14:21:18 +01:00
MrRaph_
2abb14e7c0 feat: list creation/search if exists 2024-12-03 13:53:02 +01:00
MrRaph_
a270bc3296 v0.1.0 2024-12-03 11:44:43 +01:00
MrRaph_
9688aa5957 ignore releases 2024-12-03 09:44:51 +01:00
5 changed files with 266 additions and 42 deletions

View File

@@ -0,0 +1,73 @@
name: Build and Release Maubot Plugin
on:
push:
tags:
- '*'
jobs:
build:
name: Build Plugin
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
env:
GITEA_TOKEN: ${{ secrets.TOKEN }}
- name: Set up Python
uses: actions/setup-python@v4
env:
GITEA_TOKEN: ${{ secrets.TOKEN }}
with:
python-version: 3.9
- name: Install Dependencies
env:
GITEA_TOKEN: ${{ secrets.TOKEN }}
run: |
python -m pip install --upgrade pip
pip install maubot
- name: Build Plugin
run: |
mbc build .
env:
MB_PLUGIN_DIR: ${{ github.workspace }}
GITEA_TOKEN: ${{ secrets.TOKEN }}
- name: Upload Build Artifact
env:
GITEA_TOKEN: ${{ secrets.TOKEN }}
uses: actions/upload-artifact@v4
with:
name: plugin
path: fr.mrraph.hoarder-bot-*.mbp
- name: Create Release
uses: gitea/create-release@v1
env:
GITEA_TOKEN: ${{ secrets.TOKEN }}
with:
gitea_url: https://git.mrraph.fr # Remplacez par l'URL de votre Gitea
token: ${{ secrets.TOKEN }}
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
tag: ${{ github.ref_name }}
name: "Release ${{ github.ref_name }}"
body: |
## Description
Plugin Maubot compilé à partir du tag `${{ github.ref_name }}`.
draft: false
prerelease: false
- name: Upload Release Asset
env:
GITEA_TOKEN: ${{ secrets.TOKEN }}
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: fr.mrraph.hoarder-bot-*.mbp
asset_name: r.mrraph.hoarder-bot-${{ github.ref_name }}.mbp
asset_content_type: application/octet-stream

2
.gitignore vendored
View File

@@ -160,4 +160,4 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
fr.mrraph.hoarder-bot-v0.0.2.mbp *.mbp

View File

@@ -1,3 +1,4 @@
hoarder_url: "http://hoarder.local" hoarder_url: "http://hoarder.local"
hoarder_api_key: "votre_cle_api" hoarder_api_key: "votre_cle_api"
hoarder_list_name: "list_name"
command_prefix: hoarder command_prefix: hoarder

View File

@@ -4,13 +4,13 @@ from typing import Type
from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper
from maubot import Plugin, MessageEvent from maubot import Plugin, MessageEvent
from maubot.handlers import event, command from maubot.handlers import event, command
from datetime import datetime
# Setup config file # Setup config file
class Config(BaseProxyConfig): class Config(BaseProxyConfig):
def do_update(self, helper: ConfigUpdateHelper) -> None: def do_update(self, helper: ConfigUpdateHelper) -> None:
helper.copy("hoarder_url") helper.copy("hoarder_url")
helper.copy("hoarder_api_key") helper.copy("hoarder_api_key")
helper.copy("hoarder_list_name")
helper.copy("command_prefix") helper.copy("command_prefix")
class HoarderForwarder(Plugin): class HoarderForwarder(Plugin):
@@ -22,42 +22,63 @@ class HoarderForwarder(Plugin):
def get_config_class(cls) -> Type[BaseProxyConfig]: def get_config_class(cls) -> Type[BaseProxyConfig]:
return Config return Config
async def get_page_title(self, url: str) -> str: async def send_message(self, evt: MessageEvent, message: str, formatted: bool = False, in_thread: bool = False):
"""Récupère le titre de la page web à partir de l'URL sans utiliser BeautifulSoup.""" """
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:
# Vérifier que evt contient event_id
if hasattr(evt, "event_id"):
content["m.relates_to"] = {
"rel_type": "m.thread",
"event_id": evt.event_id # Utiliser evt.event_id pour le thread
}
else:
self.log.error("Impossible de créer un thread : event_id manquant.")
await evt.reply("Erreur interne : impossible de créer un thread.")
return
# Envoyer le message
try: try:
async with aiohttp.ClientSession() as session: await evt.client.send_message(
async with session.get(url) as response: room_id=evt.room_id, # Envoyer dans la même salle
if response.status == 200: content=content
html = await response.text()
# Rechercher la balise <title>
match = re.search(r"<title>(.*?)</title>", 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",
) )
except Exception as e:
self.log.exception(f"Erreur lors de l'envoi du message : {e}")
@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) @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() 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"] api_key = self.config["hoarder_api_key"]
create_endpoint = f"{hoarder_url}/api/v1/bookmarks"
self.log.info(hoarder_url)
#title = await self.get_page_title(url) # Récupère le titre de la page
if not hoarder_url or not api_key:
self.log.warning("L'URL ou la clé API de Hoarder n'est pas configurée.")
return
headers = { headers = {
"Authorization": f"Bearer {api_key}", "Authorization": f"Bearer {api_key}",
@@ -65,18 +86,147 @@ class HoarderForwarder(Plugin):
"Accept": "application/json", "Accept": "application/json",
} }
payload = { payload = {
"json": {
"type": "link", "type": "link",
"url": url "url": url
} }
}
try: try:
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.post(hoarder_url, json=payload, headers=headers) as response: async with session.post(create_endpoint, json=payload, headers=headers) as response:
if response.status == 201: if response.status == 200 or response.status == 201 or response.status == 204:
self.log.info(f"URL envoyée avec succès à Hoarder : {url}") data = await response.json()
bookmark_id = data["id"]
await self.send_message(evt, f"Bookmark créé avec succès : {bookmark_id}", in_thread=False)
await self.add_bookmark_to_list(evt, bookmark_id)
return bookmark_id
else: 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=False)
print(await response.text())
except Exception as e: 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=False)
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=False)
return []
except Exception as e:
await self.send_message(evt, f"Erreur lors de la récupération des listes : {e}", in_thread=False)
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=False)
return lst["id"]
await self.send_message(evt, f"Aucune liste trouvée avec le nom : {name}", in_thread=False)
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/v1/lists"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
}
payload = {
"name": f"{name}",
"icon": "💬"
}
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()
self.log.info(response.json())
list_id = data["id"]
self.log.info("STATUS OK")
self.log.info(data)
await self.send_message(evt, f"Liste créée avec succès : {name} (ID : {list_id})", in_thread=False)
return list_id
else:
self.log.info("STATUS NOK")
await self.send_message(evt, f"Erreur lors de la création de la liste : {response.status}", in_thread=False)
return None
except Exception as e:
self.log.error("EXCEPT - create_list")
self.log.error(f"EXCEPT - {e}")
await self.send_message(evt, f"Erreur lors de la création de la liste : {e}", in_thread=False)
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=False)
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/{bookmark_id}"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
}
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=False)
return True
else:
await self.send_message(evt, f"Erreur lors de l'ajout à la liste : {response.status}", in_thread=False)
await self.send_message(evt, await response.text(), in_thread=False)
return False
except Exception as e:
await self.send_message(evt, f"Erreur lors de l'ajout du bookmark à la liste : {e}", in_thread=False)
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 <url>` : 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)

View File

@@ -8,7 +8,7 @@ maubot: 0.1.0
id: fr.mrraph.hoarder-bot id: fr.mrraph.hoarder-bot
# A PEP 440 compliant version string. # 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/ # The SPDX license identifier for the plugin. https://spdx.org/licenses/
# Optional, assumes all rights reserved if omitted. # Optional, assumes all rights reserved if omitted.