Skip to content

feat: handle tapbacks and threaded replies for TEXT messages#17

Merged
Skordy merged 3 commits into
ArizonaMeshtasticCommunity:mainfrom
tylerpieper:main
May 15, 2026
Merged

feat: handle tapbacks and threaded replies for TEXT messages#17
Skordy merged 3 commits into
ArizonaMeshtasticCommunity:mainfrom
tylerpieper:main

Conversation

@tylerpieper

Copy link
Copy Markdown
Contributor

Reactions and Threaded Replies Implementation

The mqttbridge cog has been updated to better handle Meshtastic's threaded replies and emoji tapback reactions in Discord.

Changes Made

1. Packet Processing Updates

In mqtt.py, the process_mqtt_message function was updated to evaluate two fields in the TEXT_MESSAGE_APP payload:

  • reply_id: The ID of the message being replied to or reacted to.
  • emoji: A flag that indicates the packet is an emoji tapback rather than a standard text message.

2. Threaded Replies

If a text packet contains a reply_id but no emoji flag, it is treated as a threaded reply.

  • We perform a lookup in the local SQLite message_history table to find the corresponding discord_message_id.
  • The send_to_discord method now accepts a reply_to_discord_id argument.
  • We utilize Discord's native reference parameter when sending the message (await channel.send(..., reference=msg)), visually linking the reply to the original message in the Discord client.

3. Emoji Tapback Reactions

If a text packet contains both a reply_id and the emoji flag, it is treated as a tapback reaction.

  • A new method, append_reaction_to_discord, was added to handle these.
  • Instead of creating a new message, we fetch the original Discord message embed and modify it.
  • We add (or append to) a Reactions: field in the embed, detailing the emoji and the specific node that reacted (e.g., 👍 - Bob (!abcd1234)).
  • We still save the packet to the message_history table to ensure that deduplication works, but we skip sending a separate Discord message to reduce channel noise.

Validation

To test these changes:

  1. Have a node send a normal broadcast text message.
  2. Have another node "reply" to that message in the app. Verify it posts as a Discord reply.
  3. Have a node send an emoji tapback reaction to the message. Verify the bot edits the original Discord message's embed with the reaction, rather than posting a new message.

@tylerpieper

Copy link
Copy Markdown
Contributor Author

I can't see what is actually coming in via mqtt so it may be worthwhile to check if the reply to and emoji fields are actually there lol

Comment thread mqttbridge/mqtt.py Outdated

if reply_id > 0 and emoji_flag != 0 and discord_msg_id:
# Emoji Reaction (Tapback)
await self.append_reaction_to_discord(mp, se, message_text, discord_msg_id)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

se isn't used in the method, let's remove it from the parameters.

Comment thread mqttbridge/mqtt.py Outdated
import traceback
print(traceback.format_exc())

async def append_reaction_to_discord(self, mp, se, message_text, discord_msg_id):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

se isn't used in the method, let's remove it from the parameters. Same a line #404

Comment thread mqttbridge/mqtt.py Outdated
Comment on lines +817 to +820
if reactions_idx >= 0:
# Append to existing
current_value = embed.fields[reactions_idx].value
embed.set_field_at(reactions_idx, name="Reactions", value=f"{current_value}\n{reaction_str}", inline=False)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about doing something like this instead? Restrict the limit since Discord's API enforces a hard 1,024-character limit on embed field values.


if reactions_idx >= 0:
    current_value = embed.fields[reactions_idx].value
    new_value = f"{current_value}\n{reaction_str}"
    if len(new_value) > REACTIONS_FIELD_LIMIT:
        new_value = new_value[:REACTIONS_FIELD_LIMIT - 3] + "..."
    embed.set_field_at(reactions_idx, name="Reactions", value=new_value, inline=False)```

Comment thread mqttbridge/mqtt.py Outdated
if reply_to_discord_id:
try:
reference = await self.messages_channel.fetch_message(reply_to_discord_id)
except discord.NotFound:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except (discord.NotFound, discord.HTTPException):

@Skordy
Skordy merged commit df21316 into ArizonaMeshtasticCommunity:main May 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants