mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-06-03 14:24:03 +00:00
feat(discord-bridge): suppress mention notifications and enhance message formatting
- Implemented suppression of Discord mention notifications for bridged messages by setting `allowed_mentions` to an empty list, ensuring that mentions like `@everyone` and `@here` are displayed as plain text. - Updated documentation to reflect this change and clarify the formatting of mentions in bridged messages. - Adjusted payload structure in the Discord webhook integration to include `allowed_mentions` for better control over message parsing. - Enhanced unit tests to verify the inclusion of `allowed_mentions` in the message payload.
This commit is contained in:
@@ -139,6 +139,8 @@ seriously, there are some people...
|
||||
|
||||
MeshCore @ mentions are cleaned up and bolded: `@[username]` → `**@username**`
|
||||
|
||||
Bridged messages never trigger Discord mention notifications (`@everyone`, `@here`, roles, or users). The webhook sets `allowed_mentions` so mesh highlights display as plain text only.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -28,6 +28,8 @@ except ImportError:
|
||||
REQUESTS_AVAILABLE = False
|
||||
|
||||
DISCORD_WEBHOOK_PREFIX = "https://discord.com/api/webhooks/"
|
||||
# Suppress @everyone/@here/role/user pings when relaying mesh text to Discord.
|
||||
DISCORD_WEBHOOK_ALLOWED_MENTIONS: dict[str, list[str]] = {"parse": []}
|
||||
DISCORD_CONTENT_MAX = 2000
|
||||
TELEGRAM_TEXT_MAX = 4096
|
||||
TELEGRAM_TRUNCATE_AT = 4000
|
||||
@@ -68,6 +70,7 @@ async def post_discord_webhook(
|
||||
payload = {
|
||||
"content": _truncate_discord_content(content),
|
||||
"username": username[:80] if username else "MeshCore",
|
||||
"allowed_mentions": DISCORD_WEBHOOK_ALLOWED_MENTIONS,
|
||||
}
|
||||
|
||||
if AIOHTTP_AVAILABLE:
|
||||
|
||||
@@ -35,6 +35,7 @@ except ImportError:
|
||||
# Import base service
|
||||
import contextlib
|
||||
|
||||
from ..bridge_outbound import DISCORD_WEBHOOK_ALLOWED_MENTIONS
|
||||
from ..profanity_filter import censor, contains_profanity
|
||||
from ..security_utils import sanitize_name
|
||||
from .base_service import BaseServicePlugin
|
||||
@@ -44,7 +45,7 @@ from .base_service import BaseServicePlugin
|
||||
class QueuedMessage:
|
||||
"""Represents a message queued for Discord posting."""
|
||||
webhook_url: str
|
||||
payload: dict[str, str]
|
||||
payload: dict[str, Any]
|
||||
channel_name: str
|
||||
retry_count: int = 0
|
||||
first_queued: float = 0.0 # Timestamp when first queued
|
||||
@@ -450,7 +451,8 @@ class DiscordBridgeService(BaseServicePlugin):
|
||||
|
||||
payload = {
|
||||
"content": message,
|
||||
"username": username
|
||||
"username": username,
|
||||
"allowed_mentions": DISCORD_WEBHOOK_ALLOWED_MENTIONS,
|
||||
}
|
||||
|
||||
if avatar_url:
|
||||
@@ -575,7 +577,7 @@ class DiscordBridgeService(BaseServicePlugin):
|
||||
self.logger.error(f"Error in message queue processor: {e}", exc_info=True)
|
||||
await asyncio.sleep(1.0) # Wait a bit before retrying on error
|
||||
|
||||
async def _post_to_webhook(self, webhook_url: str, payload: dict[str, str], channel_name: str, queued_msg: Optional[QueuedMessage] = None) -> bool:
|
||||
async def _post_to_webhook(self, webhook_url: str, payload: dict[str, Any], channel_name: str, queued_msg: Optional[QueuedMessage] = None) -> bool:
|
||||
"""Post message to Discord webhook.
|
||||
|
||||
Args:
|
||||
@@ -601,7 +603,7 @@ class DiscordBridgeService(BaseServicePlugin):
|
||||
self.logger.error(f"Failed to post to Discord webhook [{channel_name}]: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
async def _post_async(self, webhook_url: str, payload: dict[str, str], channel_name: str, queued_msg: Optional[QueuedMessage] = None) -> bool:
|
||||
async def _post_async(self, webhook_url: str, payload: dict[str, Any], channel_name: str, queued_msg: Optional[QueuedMessage] = None) -> bool:
|
||||
"""Post to webhook using aiohttp (async).
|
||||
|
||||
Args:
|
||||
@@ -653,7 +655,7 @@ class DiscordBridgeService(BaseServicePlugin):
|
||||
self.logger.error(f"Error posting to Discord webhook [{channel_name}]: {e}")
|
||||
return False
|
||||
|
||||
async def _post_sync(self, webhook_url: str, payload: dict[str, str], channel_name: str, queued_msg: Optional[QueuedMessage] = None) -> bool:
|
||||
async def _post_sync(self, webhook_url: str, payload: dict[str, Any], channel_name: str, queued_msg: Optional[QueuedMessage] = None) -> bool:
|
||||
"""Post to webhook using requests library (sync fallback).
|
||||
|
||||
Args:
|
||||
|
||||
@@ -35,6 +35,8 @@ async def test_post_discord_webhook_async_success():
|
||||
logger=MagicMock(),
|
||||
)
|
||||
assert ok is True
|
||||
_, kwargs = mock_session.post.call_args
|
||||
assert kwargs["json"]["allowed_mentions"] == bridge_outbound.DISCORD_WEBHOOK_ALLOWED_MENTIONS
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -85,3 +87,4 @@ async def test_post_discord_requests_fallback():
|
||||
)
|
||||
assert ok is True
|
||||
p.assert_called_once()
|
||||
assert p.call_args.kwargs["json"]["allowed_mentions"] == bridge_outbound.DISCORD_WEBHOOK_ALLOWED_MENTIONS
|
||||
|
||||
@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from modules.bridge_outbound import DISCORD_WEBHOOK_ALLOWED_MENTIONS
|
||||
from modules.service_plugins.discord_bridge_service import DiscordBridgeService
|
||||
|
||||
|
||||
@@ -62,3 +63,22 @@ async def test_multiple_webhooks_parsed_and_queued(multi_webhook_bot):
|
||||
assert mock_queue.await_count == 2
|
||||
called_urls = {call.args[0] for call in mock_queue.await_args_list}
|
||||
assert called_urls == {url1, url2}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_message_includes_allowed_mentions(multi_webhook_bot):
|
||||
"""Webhook payload disables Discord mention parsing (no @everyone pings)."""
|
||||
url = "https://discord.com/api/webhooks/123/abc"
|
||||
multi_webhook_bot.config.set("DiscordBridge", "enabled", "true")
|
||||
multi_webhook_bot.config.set("DiscordBridge", "bridge.Public", url)
|
||||
|
||||
service = DiscordBridgeService(multi_webhook_bot)
|
||||
await service._queue_message(url, "**@everyone** alert", "Public", "Alice")
|
||||
|
||||
queued = service.message_queues[url][0]
|
||||
assert queued.payload["allowed_mentions"] == DISCORD_WEBHOOK_ALLOWED_MENTIONS
|
||||
|
||||
|
||||
def test_format_mentions_bolds_mesh_highlight(multi_webhook_bot):
|
||||
service = DiscordBridgeService(multi_webhook_bot)
|
||||
assert service._format_mentions("@[everyone] hi") == "**@everyone** hi"
|
||||
|
||||
Reference in New Issue
Block a user