Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding reaction support #113

Merged
merged 30 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
083a7b3
Reapply "Feature/reactions support"
jeremiah-k Dec 7, 2024
22f305e
Add debug logging to db_utils.py
jeremiah-k Dec 8, 2024
132911f
Add debug logging to matrix_utils.py
jeremiah-k Dec 8, 2024
496e27d
Check meshtastic_id from returned sendText() or sendData()
jeremiah-k Dec 9, 2024
bfb09b7
Remove redudant debug logging line
jeremiah-k Dec 9, 2024
88c990b
Set encoding parameter for FileHandler
jeremiah-k Dec 9, 2024
005f6f4
Fix when relay_reactions is set to false. Add comments liberally
jeremiah-k Dec 9, 2024
201a3b8
If emoji is not available, fallback to warning symbol as default in o…
jeremiah-k Dec 9, 2024
49be5b3
Add support for relaying reactions from remote meshnets
jeremiah-k Dec 10, 2024
aaab500
Introduce effective_meshnet_name
jeremiah-k Dec 10, 2024
7dacdf0
Track origin_meshnet in matrix_utils.py
jeremiah-k Dec 10, 2024
df091c2
Treat reactions from remote meshnets as a regular message.
jeremiah-k Dec 10, 2024
d376321
Check reaction_meshnet
jeremiah-k Dec 11, 2024
c380346
Ensure all outgoing messages & reactions contain our own `meshnet_name`
jeremiah-k Dec 11, 2024
3bf8d18
Relay remote meshnet reactions to our local meshnet
jeremiah-k Dec 11, 2024
b914514
Check for presence of meshtastic_replyId and meshtastic_emoji in orde…
jeremiah-k Dec 11, 2024
e4732a9
Use matrix_event_id to lookup original message: Inside the if is_remo…
jeremiah-k Dec 11, 2024
d8a0a94
Corrected remote reaction identification logic
jeremiah-k Dec 11, 2024
73778cb
Refactor remote reaction handling
jeremiah-k Dec 11, 2024
b17aaf3
Another refactor. Could it be the last one? 🙏
jeremiah-k Dec 11, 2024
b2c0019
Commit comments
jeremiah-k Dec 11, 2024
5a265c6
finally, from nio import RoomMessageEmote 🤦‍♂️ 🤦‍♂️ 🤦‍♂️ & refactor
jeremiah-k Dec 11, 2024
d69fae5
This is it 🙏
jeremiah-k Dec 12, 2024
296727b
Minor bugfix
jeremiah-k Dec 12, 2024
bd70c3c
use meshtastic_replyId to look up the original message for remote rea…
jeremiah-k Dec 12, 2024
ae2e598
Finally got my AI tools to understand what I was telling it.
jeremiah-k Dec 12, 2024
e5a1615
Simplify conditions
jeremiah-k Dec 12, 2024
1d669e1
Add RoomMessageEmote to callback in main.py...
jeremiah-k Dec 12, 2024
8ae2d81
Final refactor I hope. Abbreviated message should be present in m.em…
jeremiah-k Dec 12, 2024
6e0cdb7
Don't relay reactions to reactions in any cases
jeremiah-k Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 74 additions & 10 deletions db_utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import json
import sqlite3

from log_utils import get_logger

logger = get_logger(name="db_utils")

# Initialize SQLite database
def initialize_database():
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
# Updated table schema: matrix_event_id is now PRIMARY KEY, meshtastic_id is not necessarily unique
cursor.execute(
"CREATE TABLE IF NOT EXISTS longnames (meshtastic_id TEXT PRIMARY KEY, longname TEXT)"
)
Expand All @@ -15,8 +19,23 @@ def initialize_database():
cursor.execute(
"CREATE TABLE IF NOT EXISTS plugin_data (plugin_name TEXT, meshtastic_id TEXT, data TEXT, PRIMARY KEY (plugin_name, meshtastic_id))"
)
conn.commit()
# Changed the schema for message_map: matrix_event_id is now primary key
# Added a new column 'meshtastic_meshnet' to store the meshnet origin of the message.
# If table already exists, we try adding the column if it doesn't exist.
cursor.execute(
"CREATE TABLE IF NOT EXISTS message_map (meshtastic_id INTEGER, matrix_event_id TEXT PRIMARY KEY, matrix_room_id TEXT, meshtastic_text TEXT, meshtastic_meshnet TEXT)"
)

# Attempt to add meshtastic_meshnet column if it's missing (for upgrades)
# This is a no-op if the column already exists.
# If user runs fresh, it will already be there from CREATE TABLE IF NOT EXISTS.
try:
cursor.execute("ALTER TABLE message_map ADD COLUMN meshtastic_meshnet TEXT")
except sqlite3.OperationalError:
# Column already exists, or table just created with it
pass

conn.commit()

def store_plugin_data(plugin_name, meshtastic_id, data):
with sqlite3.connect("meshtastic.sqlite") as conn:
Expand All @@ -27,7 +46,6 @@ def store_plugin_data(plugin_name, meshtastic_id, data):
)
conn.commit()


def delete_plugin_data(plugin_name, meshtastic_id):
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
Expand All @@ -37,7 +55,6 @@ def delete_plugin_data(plugin_name, meshtastic_id):
)
conn.commit()


# Get the data for a given plugin and Meshtastic ID
def get_plugin_data_for_node(plugin_name, meshtastic_id):
with sqlite3.connect("meshtastic.sqlite") as conn:
Expand All @@ -52,7 +69,6 @@ def get_plugin_data_for_node(plugin_name, meshtastic_id):
result = cursor.fetchone()
return json.loads(result[0] if result else "[]")


# Get the data for a given plugin
def get_plugin_data(plugin_name):
with sqlite3.connect("meshtastic.sqlite") as conn:
Expand All @@ -63,7 +79,6 @@ def get_plugin_data(plugin_name):
)
return cursor.fetchall()


# Get the longname for a given Meshtastic ID
def get_longname(meshtastic_id):
with sqlite3.connect("meshtastic.sqlite") as conn:
Expand All @@ -74,7 +89,6 @@ def get_longname(meshtastic_id):
result = cursor.fetchone()
return result[0] if result else None


def save_longname(meshtastic_id, longname):
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
Expand All @@ -84,7 +98,6 @@ def save_longname(meshtastic_id, longname):
)
conn.commit()


def update_longnames(nodes):
if nodes:
for node in nodes.values():
Expand All @@ -94,7 +107,6 @@ def update_longnames(nodes):
longname = user.get("longName", "N/A")
save_longname(meshtastic_id, longname)


def get_shortname(meshtastic_id):
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
Expand All @@ -104,7 +116,6 @@ def get_shortname(meshtastic_id):
result = cursor.fetchone()
return result[0] if result else None


def save_shortname(meshtastic_id, shortname):
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
Expand All @@ -114,7 +125,6 @@ def save_shortname(meshtastic_id, shortname):
)
conn.commit()


def update_shortnames(nodes):
if nodes:
for node in nodes.values():
Expand All @@ -123,3 +133,57 @@ def update_shortnames(nodes):
meshtastic_id = user["id"]
shortname = user.get("shortName", "N/A")
save_shortname(meshtastic_id, shortname)

def store_message_map(meshtastic_id, matrix_event_id, matrix_room_id, meshtastic_text, meshtastic_meshnet=None):
"""
Stores a message map in the database.

:param meshtastic_id: The Meshtastic message ID (integer or None)
:param matrix_event_id: The Matrix event ID (string, primary key)
:param matrix_room_id: The Matrix room ID (string)
:param meshtastic_text: The text of the Meshtastic message
:param meshtastic_meshnet: The name of the meshnet this message originated from.
This helps us identify remote vs local mesh origins.
"""
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
logger.debug(
f"Storing message map: meshtastic_id={meshtastic_id}, matrix_event_id={matrix_event_id}, matrix_room_id={matrix_room_id}, meshtastic_text={meshtastic_text}, meshtastic_meshnet={meshtastic_meshnet}"
)
cursor.execute(
"INSERT OR REPLACE INTO message_map (meshtastic_id, matrix_event_id, matrix_room_id, meshtastic_text, meshtastic_meshnet) VALUES (?, ?, ?, ?, ?)",
(meshtastic_id, matrix_event_id, matrix_room_id, meshtastic_text, meshtastic_meshnet),
)
conn.commit()

def get_message_map_by_meshtastic_id(meshtastic_id):
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT matrix_event_id, matrix_room_id, meshtastic_text, meshtastic_meshnet FROM message_map WHERE meshtastic_id=?",
(meshtastic_id,),
)
result = cursor.fetchone()
logger.debug(
f"Retrieved message map by meshtastic_id={meshtastic_id}: {result}"
)
if result:
# result = (matrix_event_id, matrix_room_id, meshtastic_text, meshtastic_meshnet)
return result[0], result[1], result[2], result[3]
return None

def get_message_map_by_matrix_event_id(matrix_event_id):
with sqlite3.connect("meshtastic.sqlite") as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT meshtastic_id, matrix_room_id, meshtastic_text, meshtastic_meshnet FROM message_map WHERE matrix_event_id=?",
(matrix_event_id,),
)
result = cursor.fetchone()
logger.debug(
f"Retrieved message map by matrix_event_id={matrix_event_id}: {result}"
)
if result:
# result = (meshtastic_id, matrix_room_id, meshtastic_text, meshtastic_meshnet)
return result[0], result[1], result[2], result[3]
return None
2 changes: 1 addition & 1 deletion log_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def get_logger(name):
"backup_count", 1
) # Default to 1 backup
file_handler = RotatingFileHandler(
log_file, maxBytes=max_bytes, backupCount=backup_count
log_file, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8'
)

file_handler.setFormatter(
Expand Down
6 changes: 4 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys
from typing import List

from nio import RoomMessageNotice, RoomMessageText
from nio import RoomMessageNotice, RoomMessageText, ReactionEvent, RoomMessageEmote

# Import meshtastic_utils as a module to set event_loop
import meshtastic_utils
Expand Down Expand Up @@ -62,8 +62,10 @@ async def main():
# Register the message callback for Matrix
matrix_logger.info("Listening for inbound Matrix messages...")
matrix_client.add_event_callback(
on_room_message, (RoomMessageText, RoomMessageNotice)
on_room_message, (RoomMessageText, RoomMessageNotice, RoomMessageEmote)
)
# Add ReactionEvent callback so we can handle matrix reactions
matrix_client.add_event_callback(on_room_message, ReactionEvent)

# Set up shutdown event
shutdown_event = asyncio.Event()
Expand Down
Loading