mmrelay 1.2.2__py3-none-any.whl → 1.2.3__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 +21 -20
- mmrelay/config.py +12 -12
- mmrelay/e2ee_utils.py +7 -2
- mmrelay/main.py +2 -2
- mmrelay/matrix_utils.py +58 -58
- mmrelay/meshtastic_utils.py +20 -21
- mmrelay/message_queue.py +11 -11
- mmrelay/plugins/mesh_relay_plugin.py +6 -5
- mmrelay/setup_utils.py +3 -3
- mmrelay/windows_utils.py +1 -1
- {mmrelay-1.2.2.dist-info → mmrelay-1.2.3.dist-info}/METADATA +1 -1
- {mmrelay-1.2.2.dist-info → mmrelay-1.2.3.dist-info}/RECORD +17 -17
- {mmrelay-1.2.2.dist-info → mmrelay-1.2.3.dist-info}/WHEEL +0 -0
- {mmrelay-1.2.2.dist-info → mmrelay-1.2.3.dist-info}/entry_points.txt +0 -0
- {mmrelay-1.2.2.dist-info → mmrelay-1.2.3.dist-info}/licenses/LICENSE +0 -0
- {mmrelay-1.2.2.dist-info → mmrelay-1.2.3.dist-info}/top_level.txt +0 -0
mmrelay/__init__.py
CHANGED
mmrelay/cli.py
CHANGED
|
@@ -238,11 +238,11 @@ def print_version():
|
|
|
238
238
|
def _validate_e2ee_dependencies():
|
|
239
239
|
"""
|
|
240
240
|
Check whether end-to-end encryption (E2EE) is usable on the current platform.
|
|
241
|
-
|
|
241
|
+
|
|
242
242
|
Returns:
|
|
243
243
|
bool: True if the platform is supported and required E2EE libraries can be imported;
|
|
244
244
|
False otherwise.
|
|
245
|
-
|
|
245
|
+
|
|
246
246
|
Notes:
|
|
247
247
|
- This function performs only local checks (platform and importability) and does not perform
|
|
248
248
|
network I/O.
|
|
@@ -1499,13 +1499,13 @@ def handle_auth_logout(args):
|
|
|
1499
1499
|
def handle_service_command(args):
|
|
1500
1500
|
"""
|
|
1501
1501
|
Dispatch service-related subcommands.
|
|
1502
|
-
|
|
1502
|
+
|
|
1503
1503
|
Currently supports the "install" subcommand which imports and runs mmrelay.setup_utils.install_service().
|
|
1504
1504
|
Returns 0 on successful installation, 1 on failure or for unknown subcommands.
|
|
1505
|
-
|
|
1505
|
+
|
|
1506
1506
|
Parameters:
|
|
1507
1507
|
args: argparse.Namespace with a `service_command` attribute indicating the requested action.
|
|
1508
|
-
|
|
1508
|
+
|
|
1509
1509
|
Returns:
|
|
1510
1510
|
int: Exit code (0 on success, 1 on error).
|
|
1511
1511
|
"""
|
|
@@ -1526,13 +1526,13 @@ def _diagnose_config_paths(args):
|
|
|
1526
1526
|
"""
|
|
1527
1527
|
Print a diagnostic summary of resolved configuration file search paths and their directory accessibility.
|
|
1528
1528
|
|
|
1529
|
-
|
|
1529
|
+
Computes the ordered list of candidate config file locations via get_config_paths(args) and prints each path with a short directory status icon:
|
|
1530
1530
|
- ✅ directory exists and is writable
|
|
1531
1531
|
- ⚠️ directory exists but is not writable
|
|
1532
1532
|
- ❌ directory does not exist
|
|
1533
1533
|
|
|
1534
1534
|
Parameters:
|
|
1535
|
-
args (argparse.Namespace):
|
|
1535
|
+
args (argparse.Namespace): CLI arguments used to determine the config search order (passed to get_config_paths).
|
|
1536
1536
|
"""
|
|
1537
1537
|
print("1. Testing configuration paths...")
|
|
1538
1538
|
from mmrelay.config import get_config_paths
|
|
@@ -1551,11 +1551,11 @@ def _diagnose_config_paths(args):
|
|
|
1551
1551
|
def _diagnose_sample_config_accessibility():
|
|
1552
1552
|
"""
|
|
1553
1553
|
Check availability of the bundled sample configuration and print a short diagnostic.
|
|
1554
|
-
|
|
1554
|
+
|
|
1555
1555
|
Performs two non-destructive checks and prints human-readable results:
|
|
1556
1556
|
1) Verifies whether the sample config file exists at the path returned by mmrelay.tools.get_sample_config_path().
|
|
1557
1557
|
2) Attempts to read the embedded resource "sample_config.yaml" from the mmrelay.tools package via importlib.resources and reports success and the content length.
|
|
1558
|
-
|
|
1558
|
+
|
|
1559
1559
|
Returns:
|
|
1560
1560
|
bool: True if a filesystem sample config exists at the resolved path; False otherwise.
|
|
1561
1561
|
"""
|
|
@@ -1586,18 +1586,19 @@ def _diagnose_sample_config_accessibility():
|
|
|
1586
1586
|
|
|
1587
1587
|
def _diagnose_platform_specific(args):
|
|
1588
1588
|
"""
|
|
1589
|
-
Run platform-specific diagnostic checks.
|
|
1589
|
+
Run platform-specific diagnostic checks and report results.
|
|
1590
1590
|
|
|
1591
|
-
On Windows,
|
|
1592
|
-
test
|
|
1593
|
-
platforms this reports that platform-specific tests are not
|
|
1591
|
+
On Windows, attempts to import and run Windows-specific requirement checks and a
|
|
1592
|
+
configuration-generation test, printing per-component statuses and any warnings.
|
|
1593
|
+
On non-Windows platforms this reports that platform-specific tests are not
|
|
1594
|
+
required.
|
|
1594
1595
|
|
|
1595
1596
|
Parameters:
|
|
1596
|
-
args (argparse.Namespace):
|
|
1597
|
-
|
|
1597
|
+
args (argparse.Namespace): CLI arguments passed through to the Windows
|
|
1598
|
+
configuration-generation test (only used when running on Windows).
|
|
1598
1599
|
|
|
1599
1600
|
Returns:
|
|
1600
|
-
bool: True if
|
|
1601
|
+
bool: True if Windows checks were executed (running on Windows), False otherwise.
|
|
1601
1602
|
"""
|
|
1602
1603
|
print("3. Platform-specific diagnostics...")
|
|
1603
1604
|
import sys
|
|
@@ -1704,7 +1705,7 @@ logging:
|
|
|
1704
1705
|
def _diagnose_minimal_config_template():
|
|
1705
1706
|
"""
|
|
1706
1707
|
Validate the built-in minimal YAML configuration template and print a concise pass/fail status.
|
|
1707
|
-
|
|
1708
|
+
|
|
1708
1709
|
Attempts to parse the string returned by _get_minimal_config_template() using yaml.safe_load. Prints a single-line result showing a ✅ with the template character length when the template is valid YAML, or a ❌ with the YAML parsing error when invalid. This is a non-destructive diagnostic helper that prints output and does not return a value.
|
|
1709
1710
|
"""
|
|
1710
1711
|
print("4. Testing minimal config template fallback...")
|
|
@@ -1722,13 +1723,13 @@ def handle_config_diagnose(args):
|
|
|
1722
1723
|
"""
|
|
1723
1724
|
Run non-destructive diagnostics for the MMRelay configuration subsystem and print a human-readable report.
|
|
1724
1725
|
|
|
1725
|
-
Performs four checks: resolves candidate
|
|
1726
|
+
Performs four checks without modifying user files: (1) resolves and reports candidate configuration file paths and their directory accessibility, (2) verifies availability of the packaged sample configuration, (3) runs platform-specific diagnostics (Windows checks when applicable), and (4) validates the built-in minimal YAML configuration template. Results and actionable guidance are written to stdout/stderr.
|
|
1726
1727
|
|
|
1727
1728
|
Parameters:
|
|
1728
|
-
args (argparse.Namespace): Parsed CLI arguments used to resolve
|
|
1729
|
+
args (argparse.Namespace): Parsed CLI arguments used to resolve configuration search paths and to control platform-specific checks.
|
|
1729
1730
|
|
|
1730
1731
|
Returns:
|
|
1731
|
-
int: Exit code (0 on success, 1 on failure).
|
|
1732
|
+
int: Exit code (0 on success, 1 on failure). On failure an error summary is printed to stderr.
|
|
1732
1733
|
"""
|
|
1733
1734
|
print("MMRelay Configuration System Diagnostics")
|
|
1734
1735
|
print("=" * 40)
|
mmrelay/config.py
CHANGED
|
@@ -196,14 +196,14 @@ def get_log_dir():
|
|
|
196
196
|
|
|
197
197
|
def get_e2ee_store_dir():
|
|
198
198
|
"""
|
|
199
|
-
Return the absolute path to the E2EE data store directory, creating it if missing.
|
|
199
|
+
Return the absolute path to the application's end-to-end-encryption (E2EE) data store directory, creating it if missing.
|
|
200
200
|
|
|
201
201
|
On Linux and macOS this is "<base_dir>/store" where base_dir is returned by get_base_dir().
|
|
202
|
-
On Windows this is "<custom_data_dir>/store" when module-level custom_data_dir is set; otherwise it
|
|
202
|
+
On Windows this is "<custom_data_dir>/store" when module-level custom_data_dir is set; otherwise it is
|
|
203
203
|
platformdirs.user_data_dir(APP_NAME, APP_AUTHOR)/store.
|
|
204
204
|
|
|
205
205
|
Returns:
|
|
206
|
-
str: Absolute path to the ensured store directory.
|
|
206
|
+
str: Absolute path to the ensured store directory.
|
|
207
207
|
"""
|
|
208
208
|
if sys.platform in ["linux", "darwin"]:
|
|
209
209
|
# Use ~/.mmrelay/store/ for Linux and Mac
|
|
@@ -388,12 +388,12 @@ def is_e2ee_enabled(config):
|
|
|
388
388
|
def check_e2ee_enabled_silently(args=None):
|
|
389
389
|
"""
|
|
390
390
|
Check silently whether End-to-End Encryption (E2EE) is enabled in the first readable configuration file.
|
|
391
|
-
|
|
391
|
+
|
|
392
392
|
Searches candidate configuration paths returned by get_config_paths(args) in priority order, loads the first readable YAML file, and returns True if that configuration enables E2EE (via is_e2ee_enabled). I/O and YAML parsing errors are ignored and the function continues to the next candidate. On Windows this always returns False.
|
|
393
|
-
|
|
393
|
+
|
|
394
394
|
Parameters:
|
|
395
395
|
args (optional): Parsed command-line arguments that can influence config search order.
|
|
396
|
-
|
|
396
|
+
|
|
397
397
|
Returns:
|
|
398
398
|
bool: True if E2EE is enabled in the first valid configuration file found; otherwise False.
|
|
399
399
|
"""
|
|
@@ -493,16 +493,16 @@ def load_credentials():
|
|
|
493
493
|
def save_credentials(credentials):
|
|
494
494
|
"""
|
|
495
495
|
Persist a JSON-serializable credentials mapping to <base_dir>/credentials.json.
|
|
496
|
-
|
|
496
|
+
|
|
497
497
|
Writes the provided credentials (a JSON-serializable mapping) to the application's
|
|
498
498
|
base configuration directory as credentials.json, creating the base directory if
|
|
499
499
|
necessary. On Unix-like systems the file permissions are adjusted to be
|
|
500
500
|
restrictive (0o600) when possible. I/O and permission errors are caught and
|
|
501
501
|
logged; the function does not raise them.
|
|
502
|
-
|
|
502
|
+
|
|
503
503
|
Parameters:
|
|
504
504
|
credentials (dict): JSON-serializable mapping of credentials to persist.
|
|
505
|
-
|
|
505
|
+
|
|
506
506
|
Returns:
|
|
507
507
|
None
|
|
508
508
|
"""
|
|
@@ -822,18 +822,18 @@ def load_config(config_file=None, args=None):
|
|
|
822
822
|
def validate_yaml_syntax(config_content, config_path):
|
|
823
823
|
"""
|
|
824
824
|
Validate YAML text for syntax and common style issues, parse it with PyYAML, and return results.
|
|
825
|
-
|
|
825
|
+
|
|
826
826
|
Performs lightweight line-based checks for frequent mistakes (using '=' instead of ':'
|
|
827
827
|
for mappings and non-standard boolean words like 'yes'/'no' or 'on'/'off') and then
|
|
828
828
|
attempts to parse the content with yaml.safe_load. If only style warnings are found,
|
|
829
829
|
parsing is considered successful and warnings are returned; if parsing fails or true
|
|
830
830
|
syntax errors are detected, a detailed error message is returned that references
|
|
831
831
|
config_path to identify the source.
|
|
832
|
-
|
|
832
|
+
|
|
833
833
|
Parameters:
|
|
834
834
|
config_content (str): Raw YAML text to validate.
|
|
835
835
|
config_path (str): Path or label used in error messages to identify the source of the content.
|
|
836
|
-
|
|
836
|
+
|
|
837
837
|
Returns:
|
|
838
838
|
tuple:
|
|
839
839
|
is_valid (bool): True if YAML parsed successfully (style warnings allowed), False on syntax/parsing error.
|
mmrelay/e2ee_utils.py
CHANGED
|
@@ -80,8 +80,13 @@ def get_e2ee_status(
|
|
|
80
80
|
importlib.import_module("olm")
|
|
81
81
|
|
|
82
82
|
if os.getenv("MMRELAY_TESTING") != "1":
|
|
83
|
-
importlib.import_module("nio.crypto")
|
|
84
|
-
|
|
83
|
+
nio_crypto = importlib.import_module("nio.crypto")
|
|
84
|
+
if not hasattr(nio_crypto, "OlmDevice"):
|
|
85
|
+
raise ImportError("nio.crypto.OlmDevice is unavailable")
|
|
86
|
+
|
|
87
|
+
nio_store = importlib.import_module("nio.store")
|
|
88
|
+
if not hasattr(nio_store, "SqliteStore"):
|
|
89
|
+
raise ImportError("nio.store.SqliteStore is unavailable")
|
|
85
90
|
|
|
86
91
|
status["dependencies_installed"] = True
|
|
87
92
|
except ImportError:
|
mmrelay/main.py
CHANGED
|
@@ -154,9 +154,9 @@ async def main(config):
|
|
|
154
154
|
|
|
155
155
|
async def shutdown():
|
|
156
156
|
"""
|
|
157
|
-
Signal the application to
|
|
157
|
+
Signal the application to begin shutdown.
|
|
158
158
|
|
|
159
|
-
Sets the Meshtastic shutdown flag and triggers the local shutdown event so
|
|
159
|
+
Sets the Meshtastic shutdown flag and triggers the local shutdown event so any coroutines waiting on that event can start their cleanup. This coroutine only signals shutdown; it does not perform client shutdown or resource cleanup itself.
|
|
160
160
|
"""
|
|
161
161
|
matrix_logger.info("Shutdown signal received. Closing down...")
|
|
162
162
|
meshtastic_utils.shutting_down = True # Set the shutting_down flag
|
mmrelay/matrix_utils.py
CHANGED
|
@@ -113,15 +113,15 @@ def _is_room_alias(value: Any) -> bool:
|
|
|
113
113
|
def _iter_room_alias_entries(mapping):
|
|
114
114
|
"""
|
|
115
115
|
Yield (alias_or_id, setter) pairs for entries in a Matrix room mapping.
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
Each yielded tuple contains:
|
|
118
118
|
- alias_or_id (str): the room alias or room ID found in the entry (may be an alias starting with '#' or a canonical room ID). If a dict entry has no `"id"` key, an empty string is yielded.
|
|
119
119
|
- setter (callable): a function accepting a single argument `new_id` which updates the underlying mapping in-place to replace the alias with the resolved room ID.
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
Supports two mapping shapes:
|
|
122
122
|
- list: items may be strings (alias/ID) or dicts with an `"id"` key.
|
|
123
123
|
- dict: values may be strings (alias/ID) or dicts with an `"id"` key.
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
The setter updates the original collection (list element or dict value) so callers can resolve aliases and persist resolved IDs back into the provided mapping.
|
|
126
126
|
"""
|
|
127
127
|
|
|
@@ -150,13 +150,13 @@ def _iter_room_alias_entries(mapping):
|
|
|
150
150
|
async def _resolve_aliases_in_mapping(mapping, resolver):
|
|
151
151
|
"""
|
|
152
152
|
Resolve Matrix room alias entries found in a mapping (list or dict) by calling an async resolver and replacing aliases with resolved room IDs in-place.
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
This function iterates entries produced by _iter_room_alias_entries(mapping). For each entry whose key/value looks like a Matrix room alias (a string starting with '#'), it awaits the provided resolver coroutine with the alias; if the resolver returns a truthy room ID, the corresponding entry in the original mapping is updated via the entry's setter. If mapping is not a list or dict, the function logs a warning and returns without modifying anything.
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
Parameters:
|
|
157
157
|
mapping (list|dict): A mapping of Matrix rooms where some entries may be aliases (e.g., "#room:example.org").
|
|
158
158
|
resolver (Callable[[str], Awaitable[Optional[str]]]): Async callable that accepts an alias and returns a resolved room ID (or falsy on failure).
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
Returns:
|
|
161
161
|
None
|
|
162
162
|
"""
|
|
@@ -178,12 +178,12 @@ async def _resolve_aliases_in_mapping(mapping, resolver):
|
|
|
178
178
|
def _update_room_id_in_mapping(mapping, alias, resolved_id) -> bool:
|
|
179
179
|
"""
|
|
180
180
|
Replace a room alias with its resolved room ID in a mapping.
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
Parameters:
|
|
183
183
|
mapping (list|dict): A matrix_rooms mapping represented as a list of aliases or a dict of entries; only list and dict types are supported.
|
|
184
184
|
alias (str): The room alias to replace (e.g., "#room:server").
|
|
185
185
|
resolved_id (str): The canonical room ID to substitute for the alias (e.g., "!abcdef:server").
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
Returns:
|
|
188
188
|
bool: True if the alias was found and replaced with resolved_id; False if the mapping type is unsupported or the alias was not present.
|
|
189
189
|
"""
|
|
@@ -789,29 +789,18 @@ def bot_command(command, event):
|
|
|
789
789
|
|
|
790
790
|
async def connect_matrix(passed_config=None):
|
|
791
791
|
"""
|
|
792
|
-
Create and initialize a matrix-nio AsyncClient connected to the configured Matrix homeserver
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
2. Automatic login using username/password from the provided config (saved to credentials.json on success).
|
|
797
|
-
3. Direct token-based values from the config matrix section.
|
|
798
|
-
|
|
799
|
-
Behavior summary:
|
|
800
|
-
- Validates presence of a top-level "matrix_rooms" configuration and raises ValueError if missing.
|
|
801
|
-
- Builds an AsyncClient with a certifi-backed SSL context when available.
|
|
802
|
-
- When E2EE is enabled in configuration and dependencies are present, prepares the encryption store, restores session state, and uploads device keys if needed.
|
|
803
|
-
- Performs an initial full_state sync and resolves room aliases configured as aliases to room IDs.
|
|
804
|
-
- Populates module-level globals used elsewhere (e.g., matrix_client, bot_user_name, matrix_homeserver, matrix_rooms, matrix_access_token, bot_user_id).
|
|
805
|
-
- Returns the initialized AsyncClient instance ready for use.
|
|
806
|
-
|
|
792
|
+
Create and initialize a matrix-nio AsyncClient connected to the configured Matrix homeserver.
|
|
793
|
+
|
|
794
|
+
Attempts credential selection in this order: credentials.json (preferred), automatic login using username/password from config (saved to credentials.json), then direct token values from config. Optionally enables End-to-End Encryption (E2EE) when configured and dependencies are available. Performs an initial full-state sync, resolves configured room aliases to room IDs, sets module-level connection state (matrix_client, bot_user_name, matrix_homeserver, matrix_rooms, matrix_access_token, bot_user_id) and returns the ready AsyncClient.
|
|
795
|
+
|
|
807
796
|
Parameters:
|
|
808
|
-
passed_config (dict | None): Optional configuration to use for this connection attempt
|
|
809
|
-
|
|
797
|
+
passed_config (dict | None): Optional configuration to use for this connection attempt; if provided, it overrides the module-level config for this call.
|
|
798
|
+
|
|
810
799
|
Returns:
|
|
811
|
-
AsyncClient | None: An initialized matrix-nio AsyncClient on success, or None if connection
|
|
812
|
-
|
|
800
|
+
AsyncClient | None: An initialized matrix-nio AsyncClient on success, or None if connection/credentials are unavailable.
|
|
801
|
+
|
|
813
802
|
Raises:
|
|
814
|
-
ValueError: If the top-level "matrix_rooms" configuration is missing
|
|
803
|
+
ValueError: If the required top-level "matrix_rooms" configuration is missing.
|
|
815
804
|
ConnectionError: If the initial Matrix sync fails or times out.
|
|
816
805
|
"""
|
|
817
806
|
global matrix_client, bot_user_name, matrix_homeserver, matrix_rooms, matrix_access_token, bot_user_id, config
|
|
@@ -1010,8 +999,15 @@ async def connect_matrix(passed_config=None):
|
|
|
1010
999
|
# Also check for other required E2EE dependencies unless tests skip them
|
|
1011
1000
|
if os.getenv("MMRELAY_TESTING") != "1":
|
|
1012
1001
|
try:
|
|
1013
|
-
|
|
1014
|
-
|
|
1002
|
+
nio_crypto = importlib.import_module("nio.crypto")
|
|
1003
|
+
if not hasattr(nio_crypto, "OlmDevice"):
|
|
1004
|
+
raise ImportError("nio.crypto.OlmDevice is unavailable")
|
|
1005
|
+
|
|
1006
|
+
nio_store = importlib.import_module("nio.store")
|
|
1007
|
+
if not hasattr(nio_store, "SqliteStore"):
|
|
1008
|
+
raise ImportError(
|
|
1009
|
+
"nio.store.SqliteStore is unavailable"
|
|
1010
|
+
)
|
|
1015
1011
|
|
|
1016
1012
|
logger.debug("All E2EE dependencies are available")
|
|
1017
1013
|
except ImportError:
|
|
@@ -1034,6 +1030,10 @@ async def connect_matrix(passed_config=None):
|
|
|
1034
1030
|
"Skipping additional E2EE dependency imports in test mode"
|
|
1035
1031
|
)
|
|
1036
1032
|
|
|
1033
|
+
if e2ee_enabled:
|
|
1034
|
+
# Ensure nio still receives a store path even when dependency
|
|
1035
|
+
# checks are skipped (e.g. production runs without MMRELAY_TESTING);
|
|
1036
|
+
# without this the client will not load encryption state.
|
|
1037
1037
|
# Get store path from config or use default
|
|
1038
1038
|
if (
|
|
1039
1039
|
"encryption" in config["matrix"]
|
|
@@ -1050,8 +1050,6 @@ async def connect_matrix(passed_config=None):
|
|
|
1050
1050
|
config["matrix"]["e2ee"]["store_path"]
|
|
1051
1051
|
)
|
|
1052
1052
|
else:
|
|
1053
|
-
from mmrelay.config import get_e2ee_store_dir
|
|
1054
|
-
|
|
1055
1053
|
e2ee_store_path = get_e2ee_store_dir()
|
|
1056
1054
|
|
|
1057
1055
|
# Create store directory if it doesn't exist
|
|
@@ -1215,7 +1213,7 @@ async def connect_matrix(passed_config=None):
|
|
|
1215
1213
|
async def _resolve_alias(alias: str) -> Optional[str]:
|
|
1216
1214
|
"""
|
|
1217
1215
|
Resolve a Matrix room alias to its canonical room ID.
|
|
1218
|
-
|
|
1216
|
+
|
|
1219
1217
|
Attempts to resolve the provided room alias using the module's Matrix client. Returns the resolved room ID string on success; returns None if the alias cannot be resolved or if an error/timeout occurs (errors from the underlying nio client are caught and handled internally).
|
|
1220
1218
|
"""
|
|
1221
1219
|
logger.debug(f"Resolving alias from config: {alias}")
|
|
@@ -1778,17 +1776,17 @@ async def login_matrix_bot(
|
|
|
1778
1776
|
async def join_matrix_room(matrix_client, room_id_or_alias: str) -> None:
|
|
1779
1777
|
"""
|
|
1780
1778
|
Join the bot to a Matrix room by ID or alias.
|
|
1781
|
-
|
|
1779
|
+
|
|
1782
1780
|
Resolves a room alias (e.g. "#room:server") to its canonical room ID, updates the in-memory
|
|
1783
1781
|
matrix_rooms mapping with the resolved ID (if available), and attempts to join the resolved
|
|
1784
1782
|
room ID. No-op if the client is already joined to the room. Errors during alias resolution
|
|
1785
1783
|
or join are caught and logged; the function does not raise exceptions.
|
|
1786
|
-
|
|
1784
|
+
|
|
1787
1785
|
Parameters documented only where meaning is not obvious:
|
|
1788
1786
|
room_id_or_alias (str): A Matrix room identifier, either a canonical room ID (e.g. "!abc:server")
|
|
1789
1787
|
or a room alias (starts with '#'). When an alias is provided, it will be resolved and
|
|
1790
1788
|
the resolved room ID will be used for joining and recorded in the module's matrix_rooms mapping.
|
|
1791
|
-
|
|
1789
|
+
|
|
1792
1790
|
Returns:
|
|
1793
1791
|
None
|
|
1794
1792
|
"""
|
|
@@ -2221,12 +2219,12 @@ def strip_quoted_lines(text: str) -> str:
|
|
|
2221
2219
|
async def get_user_display_name(room, event):
|
|
2222
2220
|
"""
|
|
2223
2221
|
Return the display name for the event sender, preferring a room-specific name.
|
|
2224
|
-
|
|
2222
|
+
|
|
2225
2223
|
If the room provides a per-room display name for the sender, that name is returned.
|
|
2226
2224
|
Otherwise the function performs an asynchronous lookup against the homeserver for the
|
|
2227
2225
|
user's global display name and returns it if present. If no display name is available,
|
|
2228
2226
|
the sender's Matrix ID (MXID) is returned.
|
|
2229
|
-
|
|
2227
|
+
|
|
2230
2228
|
Returns:
|
|
2231
2229
|
str: A human-readable display name or the sender's MXID.
|
|
2232
2230
|
"""
|
|
@@ -2320,15 +2318,17 @@ async def send_reply_to_meshtastic(
|
|
|
2320
2318
|
reply_id=None,
|
|
2321
2319
|
):
|
|
2322
2320
|
"""
|
|
2323
|
-
Enqueue a Matrix reply
|
|
2321
|
+
Enqueue a Matrix reply to be sent over Meshtastic, either as a structured reply targeting an existing Meshtastic message or as a regular broadcast.
|
|
2324
2322
|
|
|
2325
|
-
If Meshtastic broadcasting is disabled
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2323
|
+
If Meshtastic broadcasting is disabled this is a no-op. When storage_enabled is True the function will create a mapping entry (using event.event_id and room.room_id) and attach it to the queued message for later reply/reaction correlation. Failures are logged; the function does not raise exceptions.
|
|
2324
|
+
|
|
2325
|
+
Parameters:
|
|
2326
|
+
room_config (dict): Room-specific configuration — must include "meshtastic_channel" (integer channel index).
|
|
2327
|
+
room: Matrix room object; room.room_id is used to build mapping metadata.
|
|
2328
|
+
event: Matrix event object; event.event_id is used to build mapping metadata.
|
|
2329
|
+
storage_enabled (bool): If True, create and attach a message-mapping record to the queued Meshtastic message.
|
|
2330
|
+
reply_id (int | None): If provided, send as a structured Meshtastic reply targeting this message ID; otherwise send a regular text broadcast.
|
|
2331
|
+
local_meshnet_name (str | None): Name of the local meshnet used in mapping metadata.
|
|
2332
2332
|
"""
|
|
2333
2333
|
loop = asyncio.get_running_loop()
|
|
2334
2334
|
meshtastic_interface = await loop.run_in_executor(None, connect_meshtastic)
|
|
@@ -2433,19 +2433,19 @@ async def handle_matrix_reply(
|
|
|
2433
2433
|
meshnet_name=None,
|
|
2434
2434
|
):
|
|
2435
2435
|
"""
|
|
2436
|
-
|
|
2436
|
+
Handle a Matrix reply by forwarding it to Meshtastic when the replied-to Matrix event maps to a Meshtastic message.
|
|
2437
2437
|
|
|
2438
|
-
If the
|
|
2438
|
+
If the Matrix event identified by reply_to_event_id has an associated Meshtastic mapping, this function formats a Meshtastic reply that preserves sender attribution and enqueues it referencing the original Meshtastic message ID. If no mapping exists, it returns False so normal Matrix processing can continue.
|
|
2439
2439
|
|
|
2440
2440
|
Parameters:
|
|
2441
|
-
reply_to_event_id (str): Matrix event ID being replied to; used to locate the
|
|
2442
|
-
storage_enabled (bool):
|
|
2443
|
-
local_meshnet_name (str): Local meshnet name used to determine
|
|
2441
|
+
reply_to_event_id (str): Matrix event ID being replied to; used to locate the Meshtastic mapping.
|
|
2442
|
+
storage_enabled (bool): Whether message mappings/storage are enabled (affects created mapping behavior when sending).
|
|
2443
|
+
local_meshnet_name (str): Local meshnet name used to determine formatting when replying across meshnets.
|
|
2444
2444
|
config (dict): Relay configuration passed to formatting routines.
|
|
2445
|
-
mesh_text_override (str | None): Optional
|
|
2446
|
-
longname (str | None): Sender long display name used for prefixing
|
|
2447
|
-
shortname (str | None): Sender short display name used for prefixing
|
|
2448
|
-
meshnet_name (str | None): Remote meshnet name
|
|
2445
|
+
mesh_text_override (str | None): Optional text to send instead of derived text.
|
|
2446
|
+
longname (str | None): Sender long display name used for prefixing.
|
|
2447
|
+
shortname (str | None): Sender short display name used for prefixing.
|
|
2448
|
+
meshnet_name (str | None): Remote meshnet name associated with the original mapping, if any.
|
|
2449
2449
|
|
|
2450
2450
|
Returns:
|
|
2451
2451
|
bool: True if a mapping was found and the reply was queued to Meshtastic; False if no mapping existed and nothing was sent.
|
|
@@ -2546,7 +2546,7 @@ async def on_room_message(
|
|
|
2546
2546
|
) -> None:
|
|
2547
2547
|
"""
|
|
2548
2548
|
Handle an incoming Matrix room event and, when applicable, relay it to Meshtastic.
|
|
2549
|
-
|
|
2549
|
+
|
|
2550
2550
|
Processes text, notice, emote, and reaction events (including replies and messages relayed from other meshnets) for configured rooms. Behavior highlights:
|
|
2551
2551
|
- Ignores events from before the bot started and events sent by the bot itself.
|
|
2552
2552
|
- Uses per-room configuration and global interaction settings to decide whether to process or ignore the event.
|
|
@@ -2554,12 +2554,12 @@ async def on_room_message(
|
|
|
2554
2554
|
- Bridges Matrix replies to Meshtastic replies when a corresponding Meshtastic mapping is found and replies are enabled.
|
|
2555
2555
|
- Relays regular Matrix messages to Meshtastic using configured prefix/truncation rules; handles special detection-sensor port forwarding.
|
|
2556
2556
|
- Integrates with the plugin system; plugins may consume or modify messages. Messages identified as bot commands are not relayed to Meshtastic.
|
|
2557
|
-
|
|
2557
|
+
|
|
2558
2558
|
Side effects:
|
|
2559
2559
|
- May enqueue Meshtastic send operations (text or data) via the internal queue.
|
|
2560
2560
|
- May read/write persistent message mappings to support reaction/reply bridging.
|
|
2561
2561
|
- May call Matrix APIs (e.g., to fetch display names).
|
|
2562
|
-
|
|
2562
|
+
|
|
2563
2563
|
Returns:
|
|
2564
2564
|
- None
|
|
2565
2565
|
"""
|
mmrelay/meshtastic_utils.py
CHANGED
|
@@ -44,8 +44,17 @@ from mmrelay.constants.network import (
|
|
|
44
44
|
CONNECTION_TYPE_TCP,
|
|
45
45
|
DEFAULT_BACKOFF_TIME,
|
|
46
46
|
ERRNO_BAD_FILE_DESCRIPTOR,
|
|
47
|
-
INFINITE_RETRIES,
|
|
47
|
+
INFINITE_RETRIES,
|
|
48
48
|
)
|
|
49
|
+
from mmrelay.db_utils import (
|
|
50
|
+
get_longname,
|
|
51
|
+
get_message_map_by_meshtastic_id,
|
|
52
|
+
get_shortname,
|
|
53
|
+
save_longname,
|
|
54
|
+
save_shortname,
|
|
55
|
+
)
|
|
56
|
+
from mmrelay.log_utils import get_logger
|
|
57
|
+
from mmrelay.runtime_utils import is_running_as_service
|
|
49
58
|
|
|
50
59
|
# Maximum number of timeout retries when retries are configured as infinite.
|
|
51
60
|
MAX_TIMEOUT_RETRIES_INFINITE = 5
|
|
@@ -62,16 +71,6 @@ except ImportError:
|
|
|
62
71
|
pass
|
|
63
72
|
|
|
64
73
|
|
|
65
|
-
from mmrelay.db_utils import (
|
|
66
|
-
get_longname,
|
|
67
|
-
get_message_map_by_meshtastic_id,
|
|
68
|
-
get_shortname,
|
|
69
|
-
save_longname,
|
|
70
|
-
save_shortname,
|
|
71
|
-
)
|
|
72
|
-
from mmrelay.log_utils import get_logger
|
|
73
|
-
from mmrelay.runtime_utils import is_running_as_service
|
|
74
|
-
|
|
75
74
|
# Global config variable that will be set from config.py
|
|
76
75
|
config = None
|
|
77
76
|
|
|
@@ -153,15 +152,15 @@ def _submit_coro(coro, loop=None):
|
|
|
153
152
|
def _resolve_plugin_timeout(cfg: dict | None, default: float = 5.0) -> float:
|
|
154
153
|
"""
|
|
155
154
|
Resolve and return a positive plugin timeout value from the given configuration.
|
|
156
|
-
|
|
155
|
+
|
|
157
156
|
Attempts to read meshtastic.plugin_timeout from cfg and convert it to a positive float.
|
|
158
157
|
If the value is missing, non-numeric, or not greater than 0, the provided default is returned.
|
|
159
158
|
Invalid or non-positive values will cause a warning to be logged.
|
|
160
|
-
|
|
159
|
+
|
|
161
160
|
Parameters:
|
|
162
161
|
cfg (dict | None): Configuration mapping that may contain a "meshtastic" section.
|
|
163
162
|
default (float): Fallback timeout (seconds) used when cfg does not provide a valid value.
|
|
164
|
-
|
|
163
|
+
|
|
165
164
|
Returns:
|
|
166
165
|
float: A positive timeout in seconds.
|
|
167
166
|
"""
|
|
@@ -270,13 +269,13 @@ def serial_port_exists(port_name):
|
|
|
270
269
|
def connect_meshtastic(passed_config=None, force_connect=False):
|
|
271
270
|
"""
|
|
272
271
|
Establish and return a Meshtastic client connection (serial, BLE, or TCP), with configurable retries, exponential backoff, and single-time event subscription.
|
|
273
|
-
|
|
272
|
+
|
|
274
273
|
Attempts to (re)connect using the module configuration and updates module-level state on success (meshtastic_client, matrix_rooms, and event subscriptions). Supports the legacy "network" alias for TCP, verifies serial port presence before connecting, and honors a retry limit (or infinite retries when unspecified). On successful connection the client's node info and firmware metadata are probed and message/connection-lost handlers are subscribed once for the process lifetime.
|
|
275
|
-
|
|
274
|
+
|
|
276
275
|
Parameters:
|
|
277
276
|
passed_config (dict, optional): If provided, replaces the module-level configuration (and may update matrix_rooms).
|
|
278
277
|
force_connect (bool, optional): When True, forces creating a new connection even if one already exists.
|
|
279
|
-
|
|
278
|
+
|
|
280
279
|
Returns:
|
|
281
280
|
The connected Meshtastic client instance on success, or None if connection cannot be established or shutdown is in progress.
|
|
282
281
|
"""
|
|
@@ -464,7 +463,7 @@ def connect_meshtastic(passed_config=None, force_connect=False):
|
|
|
464
463
|
subscribed_to_connection_lost = True
|
|
465
464
|
logger.debug("Subscribed to meshtastic.connection.lost")
|
|
466
465
|
|
|
467
|
-
except (ConnectionRefusedError, MemoryError)
|
|
466
|
+
except (ConnectionRefusedError, MemoryError):
|
|
468
467
|
# Handle critical errors that should not be retried
|
|
469
468
|
logger.exception("Critical connection error")
|
|
470
469
|
return None
|
|
@@ -1142,17 +1141,17 @@ def sendTextReply(
|
|
|
1142
1141
|
):
|
|
1143
1142
|
"""
|
|
1144
1143
|
Send a Meshtastic text reply that references a previous Meshtastic message.
|
|
1145
|
-
|
|
1144
|
+
|
|
1146
1145
|
Builds a Data payload containing `text` and `reply_id`, wraps it in a MeshPacket on `channelIndex`,
|
|
1147
1146
|
and sends it using the provided Meshtastic interface.
|
|
1148
|
-
|
|
1147
|
+
|
|
1149
1148
|
Parameters:
|
|
1150
1149
|
text (str): UTF-8 text to send.
|
|
1151
1150
|
reply_id (int): ID of the Meshtastic message being replied to.
|
|
1152
1151
|
destinationId (int | str, optional): Recipient address or node ID (defaults to broadcast).
|
|
1153
1152
|
wantAck (bool, optional): If True, request an acknowledgement for the packet.
|
|
1154
1153
|
channelIndex (int, optional): Channel index to send the packet on.
|
|
1155
|
-
|
|
1154
|
+
|
|
1156
1155
|
Returns:
|
|
1157
1156
|
The result returned by the interface's _sendPacket call (typically the sent MeshPacket), or
|
|
1158
1157
|
None if the interface is not available or sending fails.
|
mmrelay/message_queue.py
CHANGED
|
@@ -473,11 +473,11 @@ class MessageQueue:
|
|
|
473
473
|
|
|
474
474
|
def _should_send_message(self) -> bool:
|
|
475
475
|
"""
|
|
476
|
-
Return True
|
|
476
|
+
Return True when it is safe to send a Meshtastic message; otherwise False.
|
|
477
477
|
|
|
478
|
-
Performs runtime checks:
|
|
478
|
+
Performs runtime checks: verifies the module-level `reconnecting` flag is False, a Meshtastic client object exists, and — if the client exposes `is_connected` (callable or boolean) — that it reports connected. If any check fails the method returns False.
|
|
479
479
|
|
|
480
|
-
If importing the Meshtastic utilities raises ImportError, the
|
|
480
|
+
If importing the Meshtastic utilities raises ImportError, the queue will be stopped asynchronously and the method returns False.
|
|
481
481
|
"""
|
|
482
482
|
# Import here to avoid circular imports
|
|
483
483
|
try:
|
|
@@ -517,18 +517,18 @@ class MessageQueue:
|
|
|
517
517
|
|
|
518
518
|
def _handle_message_mapping(self, result, mapping_info):
|
|
519
519
|
"""
|
|
520
|
-
Persist a sent
|
|
520
|
+
Persist a sent mesh-to-Matrix message mapping and optionally prune old mappings.
|
|
521
521
|
|
|
522
|
-
If mapping_info contains 'matrix_event_id', 'room_id', and 'text', stores a mapping using result.id as the mesh message id. If 'msgs_to_keep' is present and > 0 it prunes older mappings to retain that many entries; otherwise DEFAULT_MSGS_TO_KEEP is used.
|
|
522
|
+
If mapping_info contains 'matrix_event_id', 'room_id', and 'text', this stores a mapping using result.id as the mesh message id. If 'msgs_to_keep' is present and > 0 it prunes older mappings to retain that many entries; otherwise DEFAULT_MSGS_TO_KEEP is used.
|
|
523
523
|
|
|
524
524
|
Parameters:
|
|
525
|
-
result:
|
|
525
|
+
result: An object returned by the send function with an `id` attribute (the mesh message id).
|
|
526
526
|
mapping_info (dict): Mapping details. Relevant keys:
|
|
527
|
-
- matrix_event_id (str)
|
|
528
|
-
- room_id (str)
|
|
529
|
-
- text (str)
|
|
530
|
-
- meshnet (optional): passed to
|
|
531
|
-
- msgs_to_keep (optional, int):
|
|
527
|
+
- matrix_event_id (str): Matrix event ID to map to.
|
|
528
|
+
- room_id (str): Matrix room ID where the event was sent.
|
|
529
|
+
- text (str): Message text to associate with the mapping.
|
|
530
|
+
- meshnet (optional): Mesh network identifier passed through to storage.
|
|
531
|
+
- msgs_to_keep (optional, int): Number of mappings to retain when pruning.
|
|
532
532
|
"""
|
|
533
533
|
try:
|
|
534
534
|
# Import here to avoid circular imports
|
|
@@ -139,16 +139,17 @@ class Plugin(BasePlugin):
|
|
|
139
139
|
|
|
140
140
|
def matches(self, event):
|
|
141
141
|
"""
|
|
142
|
-
Return True if the Matrix event's content body
|
|
142
|
+
Return True if the Matrix event's content body matches the bridged-packet marker.
|
|
143
143
|
|
|
144
|
-
Checks event.source["content"]["body"] (when a string) against the anchored
|
|
145
|
-
"Processed
|
|
144
|
+
Checks event.source["content"]["body"] (when it is a string) against the anchored
|
|
145
|
+
pattern "^Processed (.+) radio packet$". Returns True when the body matches this
|
|
146
|
+
pattern, otherwise False.
|
|
146
147
|
|
|
147
148
|
Parameters:
|
|
148
|
-
event: Matrix event object
|
|
149
|
+
event: Matrix event object whose .source mapping is expected to contain a "content" dict.
|
|
149
150
|
|
|
150
151
|
Returns:
|
|
151
|
-
bool: True if the body matches the relayed-packet pattern, False otherwise.
|
|
152
|
+
bool: True if the content body matches the relayed-packet pattern, False otherwise.
|
|
152
153
|
"""
|
|
153
154
|
# Check for the presence of necessary keys in the event
|
|
154
155
|
content = event.source.get("content", {})
|
mmrelay/setup_utils.py
CHANGED
|
@@ -360,10 +360,10 @@ def create_service_file():
|
|
|
360
360
|
"""
|
|
361
361
|
Create or update the per-user systemd unit file for MMRelay.
|
|
362
362
|
|
|
363
|
-
Ensures the user systemd directory (~/.config/systemd/user) and the MMRelay logs directory (~/.mmrelay/logs) exist, obtains a service unit template
|
|
363
|
+
Ensures the user systemd directory (~/.config/systemd/user) and the MMRelay logs directory (~/.mmrelay/logs) exist, obtains a service unit template using the module's template-loading fallbacks, substitutes known placeholders (working directory, packaged launcher, and config path), normalizes the Unit's ExecStart to the resolved MMRelay invocation (an mmrelay executable on PATH or a Python `-m mmrelay` fallback) while preserving any trailing arguments, and writes the resulting unit to ~/.config/systemd/user/mmrelay.service.
|
|
364
364
|
|
|
365
365
|
Returns:
|
|
366
|
-
bool: True if the service file was written successfully; False if
|
|
366
|
+
bool: True if the service file was written successfully; False if a template could not be obtained or writing the file failed.
|
|
367
367
|
"""
|
|
368
368
|
# Get executable paths once to avoid duplicate calls and output
|
|
369
369
|
executable_path = get_executable_path()
|
|
@@ -581,7 +581,7 @@ def check_lingering_enabled():
|
|
|
581
581
|
def enable_lingering():
|
|
582
582
|
"""
|
|
583
583
|
Enable systemd "lingering" for the current user by running `sudo loginctl enable-linger <user>`.
|
|
584
|
-
|
|
584
|
+
|
|
585
585
|
Determines the username from environment variables or getpass.getuser(), invokes the privileged `loginctl` command to enable lingering, and returns True if the command exits successfully. On failure (non-zero exit, missing username, or subprocess/OSError), returns False and prints an error message to stderr.
|
|
586
586
|
"""
|
|
587
587
|
try:
|
mmrelay/windows_utils.py
CHANGED
|
@@ -28,7 +28,7 @@ def is_windows() -> bool:
|
|
|
28
28
|
def setup_windows_console() -> None:
|
|
29
29
|
"""
|
|
30
30
|
Configure the Windows console for UTF-8 output and ANSI (VT100) color support.
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
Best-effort operation: on Windows this attempts to set stdout/stderr encoding to UTF-8
|
|
33
33
|
(if supported) and enable Virtual Terminal Processing so ANSI color sequences and
|
|
34
34
|
Unicode render correctly. No-op on non-Windows platforms; failures are silently ignored.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mmrelay
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Bridge between Meshtastic mesh networks and Matrix chat rooms
|
|
5
5
|
Home-page: https://github.com/jeremiah-k/meshtastic-matrix-relay
|
|
6
6
|
Author: Geoff Whittington, Jeremiah K., and contributors
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
mmrelay/__init__.py,sha256=
|
|
1
|
+
mmrelay/__init__.py,sha256=SQOj1vq5IynIEPwAMNWFQIlhKBzos14QQ5WYg2Fx87g,120
|
|
2
2
|
mmrelay/__main__.py,sha256=Z839i5jxZmhdoPvR1-reWoQL4Xpmty81p8TsTKsJLC8,814
|
|
3
|
-
mmrelay/cli.py,sha256=
|
|
3
|
+
mmrelay/cli.py,sha256=GVQKaJRIJN2wOSWvSRSb3d04RFHm4ZTaXdSLqpFAupE,79506
|
|
4
4
|
mmrelay/cli_utils.py,sha256=h0JKhU_sToO2_Orf9ddO8x07_L1k201V61yXydfU2es,28887
|
|
5
|
-
mmrelay/config.py,sha256=
|
|
5
|
+
mmrelay/config.py,sha256=QF95n5i_XzRkBOPAVLfCD709Mcc17hsXJhJFY1tUv1A,38283
|
|
6
6
|
mmrelay/db_utils.py,sha256=utt254MipaIRTaGqSAe7N9L_S7eIPSnQsOQj6Gk2E3U,22502
|
|
7
|
-
mmrelay/e2ee_utils.py,sha256=
|
|
7
|
+
mmrelay/e2ee_utils.py,sha256=7cSb3F-FYqFcibSmb7eJzQJoRdq4Xk0_L5oppza3PHE,16914
|
|
8
8
|
mmrelay/log_utils.py,sha256=psTzfRUTp1aVIE8U-7bzS7qc1ulU9EavqAEbDt8hUQA,9126
|
|
9
|
-
mmrelay/main.py,sha256=
|
|
10
|
-
mmrelay/matrix_utils.py,sha256=
|
|
11
|
-
mmrelay/meshtastic_utils.py,sha256=
|
|
12
|
-
mmrelay/message_queue.py,sha256=
|
|
9
|
+
mmrelay/main.py,sha256=2Zu1cGopppJ3qKQZ8KgvuOYqQJyjU_-LBP3EF6_w_og,16807
|
|
10
|
+
mmrelay/matrix_utils.py,sha256=iz5aKB3ZCd1vrdRjpTosMemyrxoM249AaQ9kVtDhLCI,134485
|
|
11
|
+
mmrelay/meshtastic_utils.py,sha256=wbVa8HDjbYNOTDLttVMGeRwTsYt8zJilUv_63FnmHoo,48783
|
|
12
|
+
mmrelay/message_queue.py,sha256=esoXr6AyaWnFnJsWTx-QsqVtkXI17Xn1wU8gy0hXuDo,28156
|
|
13
13
|
mmrelay/plugin_loader.py,sha256=iXUpLGyjir7sfxtnL1REHdg5i5ZGWaHO_6sRQqf_JCE,59052
|
|
14
14
|
mmrelay/runtime_utils.py,sha256=u8DtpfI-U7Ip3SAwjn214WfMTzc-xr5wffjN0i7WBZc,1261
|
|
15
|
-
mmrelay/setup_utils.py,sha256=
|
|
16
|
-
mmrelay/windows_utils.py,sha256=
|
|
15
|
+
mmrelay/setup_utils.py,sha256=yh8UTgnm7Bb_oxfFFYrLICRItBx4wRgj0sUOoo3F6ac,32735
|
|
16
|
+
mmrelay/windows_utils.py,sha256=FqjjsrCWhYxvEH5TcfVBuONCpA19VX9kcx7jHKsRWbM,13060
|
|
17
17
|
mmrelay/constants/__init__.py,sha256=M8AXeIcS1JuS8OwmfTmhcCOkAz5XmWlNQ53GBxYOx94,1494
|
|
18
18
|
mmrelay/constants/app.py,sha256=iNKqQMp5WZSfzsuRXYZ00znVm_ZyfwqiWMQgAblG6rY,725
|
|
19
19
|
mmrelay/constants/config.py,sha256=B1A_OZLLNXHM9rHOwc01ZaJBvFugR2eXlhYYl4nUxlY,2479
|
|
@@ -29,7 +29,7 @@ mmrelay/plugins/drop_plugin.py,sha256=x4S-e0Muun2Dy1H2qwRMTBB1ptLmy7ZZJhgPu-KefG
|
|
|
29
29
|
mmrelay/plugins/health_plugin.py,sha256=svV_GfpAVL0QhiVzi3PVZ1mNpsOL1NHSmkRF-Mn_ExE,2250
|
|
30
30
|
mmrelay/plugins/help_plugin.py,sha256=S7nBhsANK46Zv9wPHOVegPGcuYGMErBsxAnrRlSSCwg,2149
|
|
31
31
|
mmrelay/plugins/map_plugin.py,sha256=eHV_t3TFcypBD4xT_OQx0hD6_iGkLJOADjwYVny0PvE,11292
|
|
32
|
-
mmrelay/plugins/mesh_relay_plugin.py,sha256=
|
|
32
|
+
mmrelay/plugins/mesh_relay_plugin.py,sha256=2dcLjHdVXS42BglgUhXHKFqN-Hiy1GxZXZdu3Ae-wBU,8512
|
|
33
33
|
mmrelay/plugins/nodes_plugin.py,sha256=RDabzyG5hKG5aYWecsRUcLSjMCCv6Pngmq2Qpld1A1U,2903
|
|
34
34
|
mmrelay/plugins/ping_plugin.py,sha256=8uFnT3qfO3RBaTUOx348voIfKpzXB3zTfcT6Gtfc8kM,4070
|
|
35
35
|
mmrelay/plugins/telemetry_plugin.py,sha256=8SxWv4BLXMUTbiVaD3MjlMMdQyS7S_1OfLlVNAUMSO0,6306
|
|
@@ -40,9 +40,9 @@ mmrelay/tools/sample-docker-compose-prebuilt.yaml,sha256=RT3IWA4DuLfIB5C7fUT1cDa
|
|
|
40
40
|
mmrelay/tools/sample-docker-compose.yaml,sha256=pbE9nXSA9DcEbdwhCQ0mZzzmhYRi3L9y8crx3a_YrTE,940
|
|
41
41
|
mmrelay/tools/sample.env,sha256=RP-o3rX3jnEIrVG2rqCZq31O1yRXou4HcGrXWLVbKKw,311
|
|
42
42
|
mmrelay/tools/sample_config.yaml,sha256=odAeiU-wesWuwnZGyXzVjxaXVYc26p8QI1HOEA-Tvco,6396
|
|
43
|
-
mmrelay-1.2.
|
|
44
|
-
mmrelay-1.2.
|
|
45
|
-
mmrelay-1.2.
|
|
46
|
-
mmrelay-1.2.
|
|
47
|
-
mmrelay-1.2.
|
|
48
|
-
mmrelay-1.2.
|
|
43
|
+
mmrelay-1.2.3.dist-info/licenses/LICENSE,sha256=aB_07MhnK-bL5WLI1ucXLUSdW_yBVoepPRYB0kaAOl8,35204
|
|
44
|
+
mmrelay-1.2.3.dist-info/METADATA,sha256=kmjNvTq2IaKoT7lQ0kfYK7AfD2x4ox__8haxHPHtLWg,6167
|
|
45
|
+
mmrelay-1.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
46
|
+
mmrelay-1.2.3.dist-info/entry_points.txt,sha256=SJZwGUOEpQ-qx4H8UL4xKFnKeInGUaZNW1I0ddjK7Ws,45
|
|
47
|
+
mmrelay-1.2.3.dist-info/top_level.txt,sha256=B_ZLCRm7NYAmI3PipRUyHGymP-C-q16LSeMGzmqJfo4,8
|
|
48
|
+
mmrelay-1.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|