Compare commits
10 Commits
cf37cd951d
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a58e61d36 | ||
| 41902ad080 | |||
| 9f8c08e561 | |||
| 6126bc3bb1 | |||
| 98c84ff171 | |||
|
|
9525d8dc9d | ||
|
|
f91f0d229a | ||
|
|
2abb14e7c0 | ||
|
|
a270bc3296 | ||
|
|
9688aa5957 |
62
.gitea/workflows/build-and-release.yml
Normal file
62
.gitea/workflows/build-and-release.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
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
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install maubot
|
||||
|
||||
- name: Build Plugin
|
||||
run: |
|
||||
mbc build .
|
||||
env:
|
||||
MB_PLUGIN_DIR: ${{ github.workspace }}
|
||||
|
||||
- name: Upload Build Artifact
|
||||
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
|
||||
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
2
.gitignore
vendored
@@ -160,4 +160,4 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
fr.mrraph.hoarder-bot-v0.0.2.mbp
|
||||
*.mbp
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
hoarder_url: "http://hoarder.local"
|
||||
hoarder_api_key: "votre_cle_api"
|
||||
hoarder_list_name: "list_name"
|
||||
command_prefix: hoarder
|
||||
224
hoarder.py
224
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,42 +22,63 @@ 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."""
|
||||
async def send_message(self, 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:
|
||||
# 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:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
if response.status == 200:
|
||||
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",
|
||||
await evt.client.send_message(
|
||||
room_id=evt.room_id, # Envoyer dans la même salle
|
||||
content=content
|
||||
)
|
||||
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)
|
||||
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"]
|
||||
|
||||
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
|
||||
create_endpoint = f"{hoarder_url}/api/v1/bookmarks"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
@@ -65,18 +86,147 @@ class HoarderForwarder(Plugin):
|
||||
"Accept": "application/json",
|
||||
}
|
||||
payload = {
|
||||
"json": {
|
||||
"type": "link",
|
||||
"url": url
|
||||
}
|
||||
}
|
||||
|
||||
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["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:
|
||||
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:
|
||||
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)
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user