feat(wormhole): add 'Jump to replied message' button on relayed replies
When a reply is relayed across servers, each copy now includes a URL button underneath the quoted reply text: - If the referenced message has a relayed copy in the target channel, the button links to that local copy (so users stay in their own server). - Otherwise, it falls back to the original message URL. Works across all relay modes (webhook, embed, compact) and leverages the existing msg_map to resolve per-channel message IDs. Bumps version to 3.4.3.
This commit is contained in:
parent
0413fbcb66
commit
13b28d3800
1 changed files with 47 additions and 5 deletions
|
|
@ -29,6 +29,10 @@ Phase 5.2: Fix empty-prefix bug — get_context() with an empty-string prefix
|
||||||
matched every message, silently blocking all relay traffic. Now
|
matched every message, silently blocking all relay traffic. Now
|
||||||
requires ctx.prefix to be non-empty before filtering. Re-added
|
requires ctx.prefix to be non-empty before filtering. Re-added
|
||||||
empty-prefix warning to wh debug.
|
empty-prefix warning to wh debug.
|
||||||
|
Phase 5.3: Enhanced reply buttons — relayed replies now include a
|
||||||
|
"Jump to replied message" URL button that links to the local
|
||||||
|
copy of the referenced message (if available in the target
|
||||||
|
server) or falls back to the original message URL.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -270,6 +274,18 @@ _DEFAULT_GLOBAL = {
|
||||||
}
|
}
|
||||||
|
|
||||||
_MAP_LIMIT = 2_000
|
_MAP_LIMIT = 2_000
|
||||||
|
|
||||||
|
|
||||||
|
def _reply_jump_view(url: str) -> discord.ui.View:
|
||||||
|
"""Build a View with a single URL button that jumps to the replied message."""
|
||||||
|
view = discord.ui.View()
|
||||||
|
view.add_item(discord.ui.Button(
|
||||||
|
style=discord.ButtonStyle.link,
|
||||||
|
label="↩ Jump to replied message",
|
||||||
|
url=url,
|
||||||
|
))
|
||||||
|
return view
|
||||||
|
|
||||||
_AUDIT_LIMIT = 500
|
_AUDIT_LIMIT = 500
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -378,7 +394,7 @@ class _ReportModal(discord.ui.Modal, title="Report Message"):
|
||||||
class Wormhole(commands.Cog):
|
class Wormhole(commands.Cog):
|
||||||
"""The ultimate cross-server relay: networks, DMs, starboard, auto-mod, invites, portals & more."""
|
"""The ultimate cross-server relay: networks, DMs, starboard, auto-mod, invites, portals & more."""
|
||||||
|
|
||||||
__version__ = "3.4.2"
|
__version__ = "3.4.3"
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
@ -4203,12 +4219,31 @@ class Wormhole(commands.Cog):
|
||||||
user_colour = nd.get("user_colours", {}).get(str(message.author.id))
|
user_colour = nd.get("user_colours", {}).get(str(message.author.id))
|
||||||
|
|
||||||
# Reply context
|
# Reply context
|
||||||
|
reply_jump_urls: Dict[int, str] = {} # {target_ch_id: jump_url}
|
||||||
|
reply_fallback_url: Optional[str] = None
|
||||||
if nd.get("sync_replies") and message.reference and message.reference.message_id:
|
if nd.get("sync_replies") and message.reference and message.reference.message_id:
|
||||||
try:
|
try:
|
||||||
ref = message.reference.cached_message or await message.channel.fetch_message(message.reference.message_id)
|
ref = message.reference.cached_message or await message.channel.fetch_message(message.reference.message_id)
|
||||||
ref_name = self._anon_name(nd, ref.author.id) if is_anon else ref.author.display_name
|
ref_name = self._anon_name(nd, ref.author.id) if is_anon else ref.author.display_name
|
||||||
preview = truncate(ref.content, 100) if ref.content else "*[attachment]*"
|
preview = truncate(ref.content, 100) if ref.content else "*[attachment]*"
|
||||||
content = f"> **↩ {ref_name}:** {preview}\n{content}"
|
content = f"> **↩ {ref_name}:** {preview}\n{content}"
|
||||||
|
|
||||||
|
# Build per-channel "Jump to replied message" URLs
|
||||||
|
if ref.guild:
|
||||||
|
reply_fallback_url = f"https://discord.com/channels/{ref.guild.id}/{ref.channel.id}/{ref.id}"
|
||||||
|
# Look up relayed copies of the referenced message
|
||||||
|
orig_ref_id = self.msg_map.get_original(net_name, ref.id)
|
||||||
|
ref_relayed = (
|
||||||
|
self.msg_map.forward.get(net_name, {}).get(orig_ref_id, {})
|
||||||
|
if orig_ref_id
|
||||||
|
else self.msg_map.forward.get(net_name, {}).get(ref.id, {})
|
||||||
|
)
|
||||||
|
for cid, mid in ref_relayed.items():
|
||||||
|
target_ch = self.bot.get_channel(cid)
|
||||||
|
if target_ch and target_ch.guild:
|
||||||
|
reply_jump_urls[cid] = f"https://discord.com/channels/{target_ch.guild.id}/{cid}/{mid}"
|
||||||
|
# Include the channel where the ref message lives
|
||||||
|
reply_jump_urls.setdefault(ref.channel.id, reply_fallback_url)
|
||||||
except: content = f"> ↩ *[reply]*\n{content}"
|
except: content = f"> ↩ *[reply]*\n{content}"
|
||||||
|
|
||||||
# Stickers
|
# Stickers
|
||||||
|
|
@ -4235,6 +4270,11 @@ class Wormhole(commands.Cog):
|
||||||
ch = self.bot.get_channel(ch_id)
|
ch = self.bot.get_channel(ch_id)
|
||||||
if not ch: continue
|
if not ch: continue
|
||||||
ch_mode = self._get_override(nd, ch_id, "relay_mode") or relay_mode
|
ch_mode = self._get_override(nd, ch_id, "relay_mode") or relay_mode
|
||||||
|
|
||||||
|
# Build reply-jump view for this channel (if replying)
|
||||||
|
jump_url = reply_jump_urls.get(ch_id) or reply_fallback_url
|
||||||
|
reply_view = _reply_jump_view(jump_url) if jump_url else None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sent_msg = None
|
sent_msg = None
|
||||||
if ch_mode == "webhook":
|
if ch_mode == "webhook":
|
||||||
|
|
@ -4253,6 +4293,7 @@ class Wormhole(commands.Cog):
|
||||||
avatar_url=avatar,
|
avatar_url=avatar,
|
||||||
files=files or discord.utils.MISSING,
|
files=files or discord.utils.MISSING,
|
||||||
embeds=extra_embeds or discord.utils.MISSING,
|
embeds=extra_embeds or discord.utils.MISSING,
|
||||||
|
view=reply_view,
|
||||||
wait=True,
|
wait=True,
|
||||||
)
|
)
|
||||||
mapping[ch_id] = sent_msg.id
|
mapping[ch_id] = sent_msg.id
|
||||||
|
|
@ -4272,22 +4313,23 @@ class Wormhole(commands.Cog):
|
||||||
avatar_url=avatar,
|
avatar_url=avatar,
|
||||||
files=files2 or discord.utils.MISSING,
|
files=files2 or discord.utils.MISSING,
|
||||||
embeds=extra_embeds or discord.utils.MISSING,
|
embeds=extra_embeds or discord.utils.MISSING,
|
||||||
|
view=reply_view,
|
||||||
wait=True,
|
wait=True,
|
||||||
)
|
)
|
||||||
mapping[ch_id] = sent_msg.id
|
mapping[ch_id] = sent_msg.id
|
||||||
except Exception:
|
except Exception:
|
||||||
log.warning("Webhook retry failed for %s, falling back to embed", ch_id)
|
log.warning("Webhook retry failed for %s, falling back to embed", ch_id)
|
||||||
em = build_relay_embed(message, nick, nd.get("colour"))
|
em = build_relay_embed(message, nick, nd.get("colour"))
|
||||||
sent_msg = await ch.send(embeds=[em] + extra_embeds[:9])
|
sent_msg = await ch.send(embeds=[em] + extra_embeds[:9], view=reply_view)
|
||||||
mapping[ch_id] = sent_msg.id
|
mapping[ch_id] = sent_msg.id
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
log.warning("No webhook perms in %s, falling back to embed", ch_id)
|
log.warning("No webhook perms in %s, falling back to embed", ch_id)
|
||||||
em = build_relay_embed(message, nick, nd.get("colour"))
|
em = build_relay_embed(message, nick, nd.get("colour"))
|
||||||
sent_msg = await ch.send(embeds=[em] + extra_embeds[:9])
|
sent_msg = await ch.send(embeds=[em] + extra_embeds[:9], view=reply_view)
|
||||||
mapping[ch_id] = sent_msg.id
|
mapping[ch_id] = sent_msg.id
|
||||||
elif ch_mode == "embed":
|
elif ch_mode == "embed":
|
||||||
em = build_relay_embed(message, nick, user_colour or nd.get("colour"))
|
em = build_relay_embed(message, nick, user_colour or nd.get("colour"))
|
||||||
sent_msg = await ch.send(embeds=[em] + extra_embeds[:9])
|
sent_msg = await ch.send(embeds=[em] + extra_embeds[:9], view=reply_view)
|
||||||
mapping[ch_id] = sent_msg.id
|
mapping[ch_id] = sent_msg.id
|
||||||
elif ch_mode == "compact":
|
elif ch_mode == "compact":
|
||||||
g = nick or message.guild.name
|
g = nick or message.guild.name
|
||||||
|
|
@ -4296,7 +4338,7 @@ class Wormhole(commands.Cog):
|
||||||
for a in message.attachments:
|
for a in message.attachments:
|
||||||
try: files.append(await a.to_file())
|
try: files.append(await a.to_file())
|
||||||
except: pass
|
except: pass
|
||||||
sent_msg = await ch.send(content=compact_format(g, display, content), files=files or None)
|
sent_msg = await ch.send(content=compact_format(g, display, content), files=files or None, view=reply_view)
|
||||||
mapping[ch_id] = sent_msg.id
|
mapping[ch_id] = sent_msg.id
|
||||||
|
|
||||||
# Ephemeral deletion
|
# Ephemeral deletion
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue