mmrelay 1.1.3__py3-none-any.whl → 1.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mmrelay might be problematic. Click here for more details.
- mmrelay/__init__.py +1 -1
- mmrelay/cli.py +124 -64
- mmrelay/config.py +63 -36
- mmrelay/config_checker.py +41 -12
- mmrelay/constants/__init__.py +54 -0
- mmrelay/constants/app.py +17 -0
- mmrelay/constants/config.py +73 -0
- mmrelay/constants/database.py +22 -0
- mmrelay/constants/formats.py +20 -0
- mmrelay/constants/messages.py +36 -0
- mmrelay/constants/network.py +35 -0
- mmrelay/constants/queue.py +17 -0
- mmrelay/db_utils.py +281 -132
- mmrelay/log_utils.py +38 -14
- mmrelay/main.py +5 -4
- mmrelay/matrix_utils.py +43 -53
- mmrelay/meshtastic_utils.py +203 -99
- mmrelay/message_queue.py +17 -17
- mmrelay/plugin_loader.py +54 -51
- mmrelay/plugins/base_plugin.py +58 -11
- mmrelay/plugins/drop_plugin.py +13 -5
- mmrelay/plugins/mesh_relay_plugin.py +7 -10
- mmrelay/plugins/weather_plugin.py +10 -1
- mmrelay/setup_utils.py +67 -30
- {mmrelay-1.1.3.dist-info → mmrelay-1.1.4.dist-info}/METADATA +3 -3
- mmrelay-1.1.4.dist-info/RECORD +43 -0
- mmrelay-1.1.3.dist-info/RECORD +0 -35
- {mmrelay-1.1.3.dist-info → mmrelay-1.1.4.dist-info}/WHEEL +0 -0
- {mmrelay-1.1.3.dist-info → mmrelay-1.1.4.dist-info}/entry_points.txt +0 -0
- {mmrelay-1.1.3.dist-info → mmrelay-1.1.4.dist-info}/licenses/LICENSE +0 -0
- {mmrelay-1.1.3.dist-info → mmrelay-1.1.4.dist-info}/top_level.txt +0 -0
mmrelay/matrix_utils.py
CHANGED
|
@@ -21,6 +21,18 @@ from nio import (
|
|
|
21
21
|
from nio.events.room_events import RoomMemberEvent
|
|
22
22
|
from PIL import Image
|
|
23
23
|
|
|
24
|
+
from mmrelay.constants.config import (
|
|
25
|
+
CONFIG_KEY_ACCESS_TOKEN,
|
|
26
|
+
CONFIG_KEY_HOMESERVER,
|
|
27
|
+
CONFIG_SECTION_MATRIX,
|
|
28
|
+
)
|
|
29
|
+
from mmrelay.constants.database import DEFAULT_MSGS_TO_KEEP
|
|
30
|
+
from mmrelay.constants.formats import (
|
|
31
|
+
DEFAULT_MATRIX_PREFIX,
|
|
32
|
+
DEFAULT_MESHTASTIC_PREFIX,
|
|
33
|
+
DETECTION_SENSOR_APP,
|
|
34
|
+
)
|
|
35
|
+
from mmrelay.constants.network import MILLISECONDS_PER_SECOND
|
|
24
36
|
from mmrelay.db_utils import (
|
|
25
37
|
get_message_map_by_matrix_event_id,
|
|
26
38
|
prune_message_map,
|
|
@@ -37,14 +49,14 @@ logger = get_logger(name="matrix_utils")
|
|
|
37
49
|
|
|
38
50
|
def _get_msgs_to_keep_config():
|
|
39
51
|
"""
|
|
40
|
-
|
|
52
|
+
Returns the configured number of messages to retain for message mapping, supporting both current and legacy configuration sections.
|
|
41
53
|
|
|
42
54
|
Returns:
|
|
43
|
-
int:
|
|
55
|
+
int: Number of messages to keep for message mapping; defaults to the predefined constant if not set.
|
|
44
56
|
"""
|
|
45
57
|
global config
|
|
46
58
|
if not config:
|
|
47
|
-
return
|
|
59
|
+
return DEFAULT_MSGS_TO_KEEP
|
|
48
60
|
|
|
49
61
|
msg_map_config = config.get("database", {}).get("msg_map", {})
|
|
50
62
|
|
|
@@ -58,26 +70,19 @@ def _get_msgs_to_keep_config():
|
|
|
58
70
|
"Using 'db.msg_map' configuration (legacy). 'database.msg_map' is now the preferred format and 'db.msg_map' will be deprecated in a future version."
|
|
59
71
|
)
|
|
60
72
|
|
|
61
|
-
return msg_map_config.get("msgs_to_keep",
|
|
73
|
+
return msg_map_config.get("msgs_to_keep", DEFAULT_MSGS_TO_KEEP)
|
|
62
74
|
|
|
63
75
|
|
|
64
76
|
def _create_mapping_info(
|
|
65
77
|
matrix_event_id, room_id, text, meshnet=None, msgs_to_keep=None
|
|
66
78
|
):
|
|
67
79
|
"""
|
|
68
|
-
|
|
80
|
+
Create a metadata dictionary linking a Matrix event to a Meshtastic message for message mapping.
|
|
69
81
|
|
|
70
|
-
Removes quoted lines from the message text and includes
|
|
71
|
-
|
|
72
|
-
Parameters:
|
|
73
|
-
matrix_event_id: The Matrix event ID to map.
|
|
74
|
-
room_id: The Matrix room ID where the event occurred.
|
|
75
|
-
text: The message text to be mapped; quoted lines are removed.
|
|
76
|
-
meshnet: Optional name of the target mesh network.
|
|
77
|
-
msgs_to_keep: Optional number of messages to retain for mapping; uses configuration default if not provided.
|
|
82
|
+
Removes quoted lines from the message text and includes identifiers and message retention settings. Returns `None` if any required parameter is missing.
|
|
78
83
|
|
|
79
84
|
Returns:
|
|
80
|
-
dict:
|
|
85
|
+
dict: Mapping information for the message queue, or `None` if required fields are missing.
|
|
81
86
|
"""
|
|
82
87
|
if not matrix_event_id or not room_id or not text:
|
|
83
88
|
return None
|
|
@@ -94,16 +99,11 @@ def _create_mapping_info(
|
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
|
|
97
|
-
# Default prefix format constants
|
|
98
|
-
DEFAULT_MESHTASTIC_PREFIX = "{display5}[M]: "
|
|
99
|
-
DEFAULT_MATRIX_PREFIX = "[{long}/{mesh}]: "
|
|
100
|
-
|
|
101
|
-
|
|
102
102
|
def get_interaction_settings(config):
|
|
103
103
|
"""
|
|
104
|
-
|
|
104
|
+
Determine if message reactions and replies are enabled in the configuration.
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
Checks for both the new `message_interactions` structure and the legacy `relay_reactions` flag for backward compatibility. Returns a dictionary with boolean values for `reactions` and `replies`, defaulting to both disabled if not specified.
|
|
107
107
|
"""
|
|
108
108
|
if config is None:
|
|
109
109
|
return {"reactions": False, "replies": False}
|
|
@@ -244,28 +244,19 @@ def get_meshtastic_prefix(config, display_name, user_id=None):
|
|
|
244
244
|
|
|
245
245
|
def get_matrix_prefix(config, longname, shortname, meshnet_name):
|
|
246
246
|
"""
|
|
247
|
-
|
|
247
|
+
Generates a formatted prefix string for Meshtastic messages relayed to Matrix, based on configuration settings and sender/mesh network names.
|
|
248
|
+
|
|
249
|
+
The prefix format supports variable-length truncation for the sender and mesh network names using template variables (e.g., `{long4}` for the first 4 characters of the sender name). Returns an empty string if prefixing is disabled in the configuration.
|
|
248
250
|
|
|
249
251
|
Parameters:
|
|
250
|
-
config (dict): The application configuration dictionary.
|
|
251
252
|
longname (str): Full Meshtastic sender name.
|
|
252
253
|
shortname (str): Short Meshtastic sender name.
|
|
253
254
|
meshnet_name (str): Name of the mesh network.
|
|
254
255
|
|
|
255
256
|
Returns:
|
|
256
|
-
str: The formatted prefix string
|
|
257
|
-
|
|
258
|
-
Examples:
|
|
259
|
-
Basic usage:
|
|
260
|
-
get_matrix_prefix(config, "Alice", "Ali", "MyMesh")
|
|
261
|
-
# Returns: "[Alice/MyMesh]: " (with default format)
|
|
262
|
-
|
|
263
|
-
Custom format:
|
|
264
|
-
config = {"matrix": {"prefix_format": "({long4}): "}}
|
|
265
|
-
get_matrix_prefix(config, "Alice", "Ali", "MyMesh")
|
|
266
|
-
# Returns: "(Alic): "
|
|
257
|
+
str: The formatted prefix string, or an empty string if prefixing is disabled.
|
|
267
258
|
"""
|
|
268
|
-
matrix_config = config.get(
|
|
259
|
+
matrix_config = config.get(CONFIG_SECTION_MATRIX, {})
|
|
269
260
|
|
|
270
261
|
# Enhanced debug logging for configuration troubleshooting
|
|
271
262
|
logger.debug(
|
|
@@ -327,7 +318,7 @@ matrix_access_token = None
|
|
|
327
318
|
bot_user_id = None
|
|
328
319
|
bot_user_name = None # Detected upon logon
|
|
329
320
|
bot_start_time = int(
|
|
330
|
-
time.time() *
|
|
321
|
+
time.time() * MILLISECONDS_PER_SECOND
|
|
331
322
|
) # Timestamp when the bot starts, used to filter out old messages
|
|
332
323
|
|
|
333
324
|
logger = get_logger(name="Matrix")
|
|
@@ -372,11 +363,9 @@ def bot_command(command, event):
|
|
|
372
363
|
|
|
373
364
|
async def connect_matrix(passed_config=None):
|
|
374
365
|
"""
|
|
375
|
-
|
|
376
|
-
Sets global matrix_client and detects the bot's display name.
|
|
366
|
+
Asynchronously connects to the Matrix homeserver, initializes the Matrix client, and retrieves the bot's device ID and display name.
|
|
377
367
|
|
|
378
|
-
|
|
379
|
-
passed_config: The configuration dictionary to use (will update global config)
|
|
368
|
+
If a configuration dictionary is provided, it updates the global configuration before connecting. Returns the initialized Matrix AsyncClient instance, or `None` if configuration is missing. Raises `ConnectionError` if SSL context creation fails.
|
|
380
369
|
"""
|
|
381
370
|
global matrix_client, bot_user_name, matrix_homeserver, matrix_rooms, matrix_access_token, bot_user_id, config
|
|
382
371
|
|
|
@@ -390,9 +379,9 @@ async def connect_matrix(passed_config=None):
|
|
|
390
379
|
return None
|
|
391
380
|
|
|
392
381
|
# Extract Matrix configuration
|
|
393
|
-
matrix_homeserver = config[
|
|
382
|
+
matrix_homeserver = config[CONFIG_SECTION_MATRIX][CONFIG_KEY_HOMESERVER]
|
|
394
383
|
matrix_rooms = config["matrix_rooms"]
|
|
395
|
-
matrix_access_token = config[
|
|
384
|
+
matrix_access_token = config[CONFIG_SECTION_MATRIX][CONFIG_KEY_ACCESS_TOKEN]
|
|
396
385
|
bot_user_id = config["matrix"]["bot_user_id"]
|
|
397
386
|
|
|
398
387
|
# Check if client already exists
|
|
@@ -400,7 +389,11 @@ async def connect_matrix(passed_config=None):
|
|
|
400
389
|
return matrix_client
|
|
401
390
|
|
|
402
391
|
# Create SSL context using certifi's certificates
|
|
403
|
-
|
|
392
|
+
try:
|
|
393
|
+
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
|
394
|
+
except Exception as e:
|
|
395
|
+
logger.error(f"Failed to create SSL context: {e}")
|
|
396
|
+
raise ConnectionError(f"SSL context creation failed: {e}") from e
|
|
404
397
|
|
|
405
398
|
# Initialize the Matrix client with custom SSL context
|
|
406
399
|
client_config = AsyncClientConfig(encryption_enabled=False)
|
|
@@ -487,9 +480,9 @@ async def matrix_relay(
|
|
|
487
480
|
reply_to_event_id=None,
|
|
488
481
|
):
|
|
489
482
|
"""
|
|
490
|
-
|
|
483
|
+
Relay a message from the Meshtastic network to a Matrix room, supporting replies, emotes, emoji reactions, and message mapping for future interactions.
|
|
491
484
|
|
|
492
|
-
If
|
|
485
|
+
If a reply target is specified, formats the message as a Matrix reply with quoted original content. When message interactions (reactions or replies) are enabled, stores a mapping between the Meshtastic message ID and the resulting Matrix event ID to support cross-network interactions. Prunes old message mappings according to configuration to limit storage.
|
|
493
486
|
|
|
494
487
|
Parameters:
|
|
495
488
|
room_id (str): The Matrix room ID to send the message to.
|
|
@@ -504,9 +497,6 @@ async def matrix_relay(
|
|
|
504
497
|
emote (bool, optional): Whether to send the message as an emote.
|
|
505
498
|
emoji (bool, optional): Whether the message is an emoji reaction.
|
|
506
499
|
reply_to_event_id (str, optional): The Matrix event ID being replied to, if sending a reply.
|
|
507
|
-
|
|
508
|
-
Returns:
|
|
509
|
-
None
|
|
510
500
|
"""
|
|
511
501
|
global config
|
|
512
502
|
|
|
@@ -540,8 +530,8 @@ async def matrix_relay(
|
|
|
540
530
|
"Using 'db.msg_map' configuration (legacy). 'database.msg_map' is now the preferred format and 'db.msg_map' will be deprecated in a future version."
|
|
541
531
|
)
|
|
542
532
|
msgs_to_keep = msg_map_config.get(
|
|
543
|
-
"msgs_to_keep",
|
|
544
|
-
) # Default
|
|
533
|
+
"msgs_to_keep", DEFAULT_MSGS_TO_KEEP
|
|
534
|
+
) # Default from constants
|
|
545
535
|
|
|
546
536
|
try:
|
|
547
537
|
# Always use our own local meshnet_name for outgoing events
|
|
@@ -875,9 +865,9 @@ async def on_room_message(
|
|
|
875
865
|
event: Union[RoomMessageText, RoomMessageNotice, ReactionEvent, RoomMessageEmote],
|
|
876
866
|
) -> None:
|
|
877
867
|
"""
|
|
878
|
-
|
|
868
|
+
Asynchronously handles incoming Matrix room messages, reactions, and replies, relaying them to Meshtastic as appropriate.
|
|
879
869
|
|
|
880
|
-
|
|
870
|
+
Processes Matrix events—including text messages, reactions, and replies—in configured rooms. Relays supported messages to the Meshtastic mesh network if broadcasting is enabled, applying message mapping for cross-referencing when reactions or replies are enabled. Ignores messages from the bot itself, messages sent before the bot started, and reactions to reactions. Integrates with plugins for command and message handling; only messages not handled by plugins or identified as commands are forwarded to Meshtastic, with appropriate formatting and truncation. Handles special cases for relaying messages and reactions from remote mesh networks and detection sensor data.
|
|
881
871
|
"""
|
|
882
872
|
# Importing here to avoid circular imports and to keep logic consistent
|
|
883
873
|
# Note: We do not call store_message_map directly here for inbound matrix->mesh messages.
|
|
@@ -1227,7 +1217,7 @@ async def on_room_message(
|
|
|
1227
1217
|
if not found_matching_plugin:
|
|
1228
1218
|
if config["meshtastic"]["broadcast_enabled"]:
|
|
1229
1219
|
portnum = event.source["content"].get("meshtastic_portnum")
|
|
1230
|
-
if portnum ==
|
|
1220
|
+
if portnum == DETECTION_SENSOR_APP:
|
|
1231
1221
|
# If detection_sensor is enabled, forward this data as detection sensor data
|
|
1232
1222
|
if config["meshtastic"].get("detection_sensor", False):
|
|
1233
1223
|
success = queue_message(
|