fix formatting and replies
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
maubot: 0.3.0
|
||||
id: cloud.catgirl.ntfy
|
||||
version: 0.1.0
|
||||
version: 0.1.1
|
||||
license: AGPL-3.0-or-later
|
||||
modules:
|
||||
- ntfy
|
||||
|
||||
128
ntfy/bot.py
128
ntfy/bot.py
@@ -6,8 +6,7 @@ from typing import Any, Dict, Tuple
|
||||
from aiohttp import ClientTimeout
|
||||
from maubot import MessageEvent, Plugin
|
||||
from maubot.handlers import command
|
||||
from mautrix.types import (EventType, Format, MessageType,
|
||||
TextMessageEventContent)
|
||||
from mautrix.types import EventType, Format, MessageType, TextMessageEventContent
|
||||
from mautrix.util.async_db import UpgradeTable
|
||||
from mautrix.util.config import BaseProxyConfig
|
||||
from mautrix.util.formatter import parse_html
|
||||
@@ -27,8 +26,7 @@ class NtfyBot(Plugin):
|
||||
self.config.load_and_update()
|
||||
self.db = DB(self.database, self.log)
|
||||
if EMOJI_FALLBACK:
|
||||
self.log.warn(
|
||||
"Please install the `emoji` package for full emoji support")
|
||||
self.log.warn("Please install the `emoji` package for full emoji support")
|
||||
await self.resubscribe()
|
||||
|
||||
async def stop(self) -> None:
|
||||
@@ -63,19 +61,33 @@ class NtfyBot(Plugin):
|
||||
async def can_use_command(self, evt: MessageEvent) -> bool:
|
||||
if evt.sender in self.config["admins"]:
|
||||
return True
|
||||
levels = await self.client.get_state_event(evt.room_id, EventType.ROOM_POWER_LEVELS)
|
||||
levels = await self.client.get_state_event(
|
||||
evt.room_id, EventType.ROOM_POWER_LEVELS
|
||||
)
|
||||
user_level = levels.get_user_level(evt.sender)
|
||||
if user_level < 50:
|
||||
await evt.reply("You don't have the permission to manage ntfy subscriptions in this room.")
|
||||
await evt.reply(
|
||||
"You don't have the permission to manage ntfy subscriptions in this room."
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
@command.new(name=lambda self: self.config["command_prefix"], help="Manage ntfy subscriptions.", require_subcommand=True)
|
||||
@command.new(
|
||||
name=lambda self: self.config["command_prefix"],
|
||||
help="Manage ntfy subscriptions.",
|
||||
require_subcommand=True,
|
||||
)
|
||||
async def ntfy(self) -> None:
|
||||
pass
|
||||
|
||||
@ntfy.subcommand("subscribe", aliases=("sub",), help="Subscribe this room to a ntfy topic.")
|
||||
@command.argument("topic", "topic URL", matches="(([a-zA-Z0-9-]{1,63}\\.)+[a-zA-Z]{2,6}/[a-zA-Z0-9_-]{1,64})")
|
||||
@ntfy.subcommand(
|
||||
"subscribe", aliases=("sub",), help="Subscribe this room to a ntfy topic."
|
||||
)
|
||||
@command.argument(
|
||||
"topic",
|
||||
"topic URL",
|
||||
matches="(([a-zA-Z0-9-]{1,63}\\.)+[a-zA-Z]{2,6}/[a-zA-Z0-9_-]{1,64})",
|
||||
)
|
||||
async def subscribe(self, evt: MessageEvent, topic: Tuple[str, Any]) -> None:
|
||||
# see https://github.com/binwiederhier/ntfy/blob/82df434d19e3ef45ada9c00dfe9fc0f8dfba15e6/server/server.go#L61 for the valid topic regex
|
||||
if not await self.can_use_command(evt):
|
||||
@@ -83,20 +95,30 @@ class NtfyBot(Plugin):
|
||||
server, topic = topic[0].split("/")
|
||||
db_topic = await self.db.get_topic(server, topic)
|
||||
if not db_topic:
|
||||
db_topic = await self.db.create_topic(Topic(id=-1, server=server, topic=topic, last_event_id=None))
|
||||
db_topic = await self.db.create_topic(
|
||||
Topic(id=-1, server=server, topic=topic, last_event_id=None)
|
||||
)
|
||||
existing_subscriptions = await self.db.get_subscriptions(db_topic.id)
|
||||
sub, _ = await self.db.get_subscription(db_topic.id, evt.room_id)
|
||||
if sub:
|
||||
await evt.reply("This room is already subscribed to %s/%s", server, topic)
|
||||
await evt.reply(f"This room is already subscribed to {server}/{topic}")
|
||||
else:
|
||||
await self.db.add_subscription(db_topic.id, evt.room_id)
|
||||
await evt.reply("Subscribed this room to %s/%s", server, topic)
|
||||
await evt.reply(f"Subscribed this room to {server}/{topic}")
|
||||
await evt.react(WHITE_CHECK_MARK)
|
||||
if not existing_subscriptions:
|
||||
self.subscribe_to_topic(db_topic)
|
||||
|
||||
@ntfy.subcommand("unsubscribe", aliases=("unsub",), help="Unsubscribe this room from a ntfy topic.")
|
||||
@command.argument("topic", "topic URL", matches="(([a-zA-Z0-9-]{1,63}\\.)+[a-zA-Z]{2,6}/[a-zA-Z0-9_-]{1,64})")
|
||||
@ntfy.subcommand(
|
||||
"unsubscribe",
|
||||
aliases=("unsub",),
|
||||
help="Unsubscribe this room from a ntfy topic.",
|
||||
)
|
||||
@command.argument(
|
||||
"topic",
|
||||
"topic URL",
|
||||
matches="(([a-zA-Z0-9-]{1,63}\\.)+[a-zA-Z]{2,6}/[a-zA-Z0-9_-]{1,64})",
|
||||
)
|
||||
async def unsubscribe(self, evt: MessageEvent, topic: Tuple[str, Any]) -> None:
|
||||
# see https://github.com/binwiederhier/ntfy/blob/82df434d19e3ef45ada9c00dfe9fc0f8dfba15e6/server/server.go#L61 for the valid topic regex
|
||||
if not await self.can_use_command(evt):
|
||||
@@ -104,17 +126,17 @@ class NtfyBot(Plugin):
|
||||
server, topic = topic[0].split("/")
|
||||
db_topic = await self.db.get_topic(server, topic)
|
||||
if not db_topic:
|
||||
await evt.reply("This room is not subscribed to %s/%s", server, topic)
|
||||
await evt.reply(f"This room is not subscribed to {server}/{topic}")
|
||||
return
|
||||
sub, _ = await self.db.get_subscription(db_topic.id, evt.room_id)
|
||||
if not sub:
|
||||
await evt.reply("This room is not subscribed to %s/%s", server, topic)
|
||||
await evt.reply(f"This room is not subscribed to {server}/{topic}")
|
||||
return
|
||||
await self.db.remove_subscription(db_topic.id, evt.room_id)
|
||||
if not await self.db.get_subscriptions(db_topic.id):
|
||||
self.tasks[db_topic.id].cancel()
|
||||
await self.db.clear_topic_id(db_topic.id)
|
||||
await evt.reply("Unsubscribed this room from %s/%s", server, topic)
|
||||
await evt.reply(f"Unsubscribed this room from {server}/{topic}")
|
||||
await evt.react(WHITE_CHECK_MARK)
|
||||
|
||||
async def subscribe_to_topics(self) -> None:
|
||||
@@ -130,22 +152,22 @@ class NtfyBot(Plugin):
|
||||
if task.done() and not task.cancelled():
|
||||
exc = task.exception()
|
||||
self.log.exception(
|
||||
"Subscription task errored, resubscribing", exc_info=exc)
|
||||
self.tasks[topic.id] = self.loop.create_task(
|
||||
asyncio.sleep(10.0))
|
||||
"Subscription task errored, resubscribing", exc_info=exc
|
||||
)
|
||||
self.tasks[topic.id] = self.loop.create_task(asyncio.sleep(10.0))
|
||||
self.tasks[topic.id].add_done_callback(
|
||||
lambda _: self.subscribe_to_topic(topic))
|
||||
lambda _: self.subscribe_to_topic(topic)
|
||||
)
|
||||
|
||||
self.log.info("Subscribing to %s/%s", topic.server, topic.topic)
|
||||
url = "%s/%s/json" % (topic.server, topic.topic)
|
||||
self.log.info(f"Subscribing to {topic.server}/{topic.topic}")
|
||||
url = f"{topic.server}/{topic.topic}/json"
|
||||
if not url.startswith(("http://", "https://")):
|
||||
url = "https://" + url
|
||||
if topic.last_event_id:
|
||||
url += "?since=%s" % topic.last_event_id
|
||||
url += f"?since={topic.last_event_id}"
|
||||
|
||||
self.log.debug("Subscribing to URL %s", url)
|
||||
task = self.loop.create_task(
|
||||
self.run_topic_subscription(topic, url))
|
||||
self.log.debug(f"Subscribing to URL {url}")
|
||||
task = self.loop.create_task(self.run_topic_subscription(topic, url))
|
||||
self.tasks[topic.id] = task
|
||||
task.add_done_callback(log_task_exc)
|
||||
|
||||
@@ -155,17 +177,16 @@ class NtfyBot(Plugin):
|
||||
line = await resp.content.readline()
|
||||
# convert to string and remove trailing newline
|
||||
line = line.decode("utf-8").strip()
|
||||
self.log.trace("Received notification: %s", line)
|
||||
self.log.trace(f"Received notification: {line}")
|
||||
message = json.loads(line)
|
||||
if message["event"] != "message":
|
||||
continue
|
||||
self.log.debug("Received message event: %s", line)
|
||||
self.log.debug(f"Received message event: {line}")
|
||||
# persist the received message id
|
||||
await self.db.update_topic_id(topic.id, message["id"])
|
||||
|
||||
# build matrix message
|
||||
html_content = self.build_message_content(
|
||||
topic.server, message)
|
||||
html_content = self.build_message_content(topic.server, message)
|
||||
text_content = await parse_html(html_content.strip())
|
||||
|
||||
content = TextMessageEventContent(
|
||||
@@ -181,7 +202,8 @@ class NtfyBot(Plugin):
|
||||
await self.client.send_message(sub.room_id, content)
|
||||
except Exception as exc:
|
||||
self.log.exception(
|
||||
"Failed to send matrix message!", exc_info=exc)
|
||||
"Failed to send matrix message!", exc_info=exc
|
||||
)
|
||||
|
||||
def build_message_content(self, server: str, message) -> str:
|
||||
topic = message["topic"]
|
||||
@@ -190,7 +212,9 @@ class NtfyBot(Plugin):
|
||||
tags = message.get("tags", None)
|
||||
click = message.get("click", None)
|
||||
attachment = message.get("attachment", None)
|
||||
view_actions = [a for a in message.get("actions", []) if a.get("action", "") == "view"]
|
||||
view_actions = [
|
||||
a for a in message.get("actions", []) if a.get("action", "") == "view"
|
||||
]
|
||||
|
||||
if tags:
|
||||
(emoji, non_emoji) = parse_tags(self.log, tags)
|
||||
@@ -199,40 +223,50 @@ class NtfyBot(Plugin):
|
||||
else:
|
||||
emoji = tags = ""
|
||||
|
||||
html_content = "<span>Ntfy message in topic <code>%s/%s</code></span><blockquote>" % (
|
||||
html.escape(server), html.escape(topic))
|
||||
html_content = "<span>Ntfy message in topic <code>{server}/{topic}</code></span><blockquote>".format(
|
||||
server=html.escape(server), topic=html.escape(topic)
|
||||
)
|
||||
# build title
|
||||
if title and click:
|
||||
html_content += "<p><h4>%s<a href=\"%s\">%s</a></h4></p>" % (
|
||||
emoji, html.escape(click), html.escape(title))
|
||||
html_content += (
|
||||
'<p><h4>{emoji}<a href="{click}">{title}</a></h4></p>'.format(
|
||||
emoji=emoji, click=html.escape(click), title=html.escape(title)
|
||||
)
|
||||
)
|
||||
emoji = ""
|
||||
elif title:
|
||||
html_content += "<p><h4>%s%s</h4></p>" % (emoji, html.escape(title))
|
||||
html_content += "<p><h4>{emoji}{title}</h4></p>".format(
|
||||
emoji=emoji, title=html.escape(title)
|
||||
)
|
||||
emoji = ""
|
||||
|
||||
# build body
|
||||
if click and not title:
|
||||
html_content += "<p>%s<a href=\"%s\">%s</a></p>" % (
|
||||
emoji, html.escape(click), html.escape(body).replace("\n", "<br />"))
|
||||
html_content += '<p>{emoji}<a href="{click}">{body}</a></p>'.format(
|
||||
emoji=emoji,
|
||||
click=html.escape(click),
|
||||
body=html.escape(body).replace("\n", "<br />"),
|
||||
)
|
||||
else:
|
||||
html_content += "<p>%s%s</p>" % (emoji, html.escape(body).replace("\n", "<br />"))
|
||||
html_content += "<p>{emoji}{body}</p>".format(
|
||||
emoji=emoji, body=html.escape(body).replace("\n", "<br />")
|
||||
)
|
||||
|
||||
# add non-emoji tags
|
||||
if tags:
|
||||
html_content += "<p><small>Tags: <code>%s</code></small></p>" % html.escape(
|
||||
tags)
|
||||
|
||||
html_content += (
|
||||
f"<p><small>Tags: <code>{html.escape(tags)}</code></small></p>"
|
||||
)
|
||||
# add actions
|
||||
if len(view_actions) > 0:
|
||||
html_content += "<p>"
|
||||
for action in view_actions:
|
||||
html_content += "<a href=\"%s\">%s</a>" % (html.escape(action["url"]), html.escape(action["label"]))
|
||||
html_content += f"<a href=\"{html.escape(action['url'])}\">{html.escape(action['label'])}</a>"
|
||||
html_content += "</p>"
|
||||
|
||||
# build attachment
|
||||
if attachment:
|
||||
html_content += "<p><a href=\"%s\">View %s</a></p>" % (html.escape(
|
||||
attachment["url"]), html.escape(attachment["name"]))
|
||||
html_content += f"<p><a href=\"{html.escape(attachment['url'])}\">View {html.escape(attachment['name'])}</a></p>"
|
||||
html_content += "</blockquote>"
|
||||
|
||||
return html_content
|
||||
|
||||
Reference in New Issue
Block a user