mmrelay 1.2.0__py3-none-any.whl → 1.2.2__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/__main__.py +29 -0
- mmrelay/cli.py +735 -135
- mmrelay/cli_utils.py +59 -9
- mmrelay/config.py +198 -71
- mmrelay/constants/app.py +2 -2
- mmrelay/db_utils.py +73 -26
- mmrelay/e2ee_utils.py +6 -3
- mmrelay/log_utils.py +16 -5
- mmrelay/main.py +41 -38
- mmrelay/matrix_utils.py +1069 -293
- mmrelay/meshtastic_utils.py +350 -206
- mmrelay/message_queue.py +212 -62
- mmrelay/plugin_loader.py +634 -205
- mmrelay/plugins/mesh_relay_plugin.py +43 -38
- mmrelay/plugins/weather_plugin.py +11 -12
- mmrelay/runtime_utils.py +35 -0
- mmrelay/setup_utils.py +324 -129
- mmrelay/tools/mmrelay.service +2 -1
- mmrelay/tools/sample-docker-compose-prebuilt.yaml +11 -72
- mmrelay/tools/sample-docker-compose.yaml +12 -58
- mmrelay/tools/sample_config.yaml +14 -13
- mmrelay/windows_utils.py +349 -0
- {mmrelay-1.2.0.dist-info → mmrelay-1.2.2.dist-info}/METADATA +11 -11
- mmrelay-1.2.2.dist-info/RECORD +48 -0
- mmrelay-1.2.0.dist-info/RECORD +0 -45
- {mmrelay-1.2.0.dist-info → mmrelay-1.2.2.dist-info}/WHEEL +0 -0
- {mmrelay-1.2.0.dist-info → mmrelay-1.2.2.dist-info}/entry_points.txt +0 -0
- {mmrelay-1.2.0.dist-info → mmrelay-1.2.2.dist-info}/licenses/LICENSE +0 -0
- {mmrelay-1.2.0.dist-info → mmrelay-1.2.2.dist-info}/top_level.txt +0 -0
mmrelay/tools/mmrelay.service
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[Unit]
|
|
2
|
-
Description=
|
|
2
|
+
Description=MMRelay - Meshtastic <=> Matrix Relay
|
|
3
3
|
After=network-online.target
|
|
4
4
|
Wants=network-online.target
|
|
5
5
|
|
|
@@ -11,6 +11,7 @@ WorkingDirectory=%h/.mmrelay
|
|
|
11
11
|
Restart=on-failure
|
|
12
12
|
RestartSec=10
|
|
13
13
|
Environment=PYTHONUNBUFFERED=1
|
|
14
|
+
Environment=LANG=C.UTF-8
|
|
14
15
|
# Ensure both pipx and pip environments are properly loaded
|
|
15
16
|
Environment=PATH=%h/.local/bin:%h/.local/pipx/venvs/mmrelay/bin:/usr/local/bin:/usr/bin:/bin
|
|
16
17
|
|
|
@@ -3,78 +3,17 @@ services:
|
|
|
3
3
|
image: ghcr.io/jeremiah-k/mmrelay:latest
|
|
4
4
|
container_name: meshtastic-matrix-relay
|
|
5
5
|
restart: unless-stopped
|
|
6
|
+
stop_grace_period: 30s
|
|
6
7
|
user: "${UID:-1000}:${GID:-1000}"
|
|
7
|
-
|
|
8
8
|
environment:
|
|
9
|
-
- TZ=UTC
|
|
10
|
-
- PYTHONUNBUFFERED=1
|
|
11
|
-
- MPLCONFIGDIR=/tmp/matplotlib
|
|
12
|
-
|
|
13
|
-
# Matrix Authentication: Use 'mmrelay auth login' on host system to create credentials.json
|
|
14
|
-
# The credentials file will be automatically loaded from the volume mount below
|
|
15
|
-
|
|
16
|
-
# Meshtastic Connection Settings - Uncomment and configure as needed
|
|
17
|
-
# TCP Connection (most common)
|
|
18
|
-
# - MMRELAY_MESHTASTIC_CONNECTION_TYPE=tcp
|
|
19
|
-
# - MMRELAY_MESHTASTIC_HOST=192.168.1.100
|
|
20
|
-
# - MMRELAY_MESHTASTIC_PORT=4403 # Default port
|
|
21
|
-
|
|
22
|
-
# Serial Connection (uncomment for serial)
|
|
23
|
-
# - MMRELAY_MESHTASTIC_CONNECTION_TYPE=serial
|
|
24
|
-
# - MMRELAY_MESHTASTIC_SERIAL_PORT=/dev/ttyUSB0
|
|
25
|
-
|
|
26
|
-
# BLE Connection (uncomment for Bluetooth)
|
|
27
|
-
# - MMRELAY_MESHTASTIC_CONNECTION_TYPE=ble
|
|
28
|
-
# - MMRELAY_MESHTASTIC_BLE_ADDRESS=AA:BB:CC:DD:EE:FF
|
|
29
|
-
|
|
30
|
-
# Meshtastic Operational Settings
|
|
31
|
-
# - MMRELAY_MESHTASTIC_BROADCAST_ENABLED=true
|
|
32
|
-
# - MMRELAY_MESHTASTIC_MESHNET_NAME=My Mesh Network
|
|
33
|
-
# - MMRELAY_MESHTASTIC_MESSAGE_DELAY=2.2 # Minimum 2.0 seconds
|
|
34
|
-
|
|
35
|
-
# System Configuration
|
|
36
|
-
# - MMRELAY_LOGGING_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
37
|
-
# - MMRELAY_LOG_FILE=/app/data/logs/mmrelay.log # Enables file logging
|
|
38
|
-
# - MMRELAY_DATABASE_PATH=/app/data/meshtastic.sqlite
|
|
39
|
-
|
|
40
|
-
# Note: Environment variables for Meshtastic, logging, and database settings take precedence over config.yaml
|
|
41
|
-
|
|
9
|
+
- TZ=UTC # Set timezone (PYTHONUNBUFFERED and MPLCONFIGDIR are set in Dockerfile)
|
|
42
10
|
volumes:
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
- ${MMRELAY_HOME}/.mmrelay:/app/data
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# For serial connections, uncomment the device you need:
|
|
54
|
-
# devices:
|
|
55
|
-
# - /dev/ttyUSB0:/dev/ttyUSB0
|
|
56
|
-
# - /dev/ttyACM0:/dev/ttyACM0
|
|
57
|
-
|
|
58
|
-
# For BLE connections, uncomment these:
|
|
59
|
-
# privileged: true
|
|
60
|
-
# network_mode: host
|
|
61
|
-
# Additional volumes for BLE (add to existing volumes section above):
|
|
62
|
-
# - /var/run/dbus:/var/run/dbus:ro
|
|
63
|
-
# - /sys/bus/usb:/sys/bus/usb:ro
|
|
64
|
-
# - /sys/class/bluetooth:/sys/class/bluetooth:ro
|
|
65
|
-
# - /sys/devices:/sys/devices:ro
|
|
66
|
-
|
|
67
|
-
# Optional: Watchtower for automatic updates
|
|
68
|
-
# Uncomment this service to enable daily checks for new images
|
|
69
|
-
# watchtower:
|
|
70
|
-
# image: containrrr/watchtower:latest
|
|
71
|
-
# container_name: watchtower-mmrelay
|
|
72
|
-
# restart: unless-stopped
|
|
73
|
-
# volumes:
|
|
74
|
-
# - /var/run/docker.sock:/var/run/docker.sock
|
|
75
|
-
# environment:
|
|
76
|
-
# - WATCHTOWER_CLEANUP=true
|
|
77
|
-
# - WATCHTOWER_INCLUDE_STOPPED=true
|
|
78
|
-
# - WATCHTOWER_SCHEDULE=0 0 2 * * * # Daily at 2 AM
|
|
79
|
-
# - WATCHTOWER_TIMEOUT=30s
|
|
80
|
-
# command: meshtastic-matrix-relay
|
|
11
|
+
# Mount your config directory - create ~/.mmrelay/config.yaml first
|
|
12
|
+
# See docs/DOCKER.md for setup instructions
|
|
13
|
+
# For SELinux systems (RHEL/CentOS/Fedora), add :Z flag to prevent permission denied errors
|
|
14
|
+
- ${MMRELAY_HOME:-$HOME}/.mmrelay/config.yaml:/app/config.yaml:ro,Z
|
|
15
|
+
- ${MMRELAY_HOME:-$HOME}/.mmrelay:/app/data:Z
|
|
16
|
+
# For non-SELinux systems, you can use:
|
|
17
|
+
# - ${MMRELAY_HOME:-$HOME}/.mmrelay/config.yaml:/app/config.yaml:ro
|
|
18
|
+
# - ${MMRELAY_HOME:-$HOME}/.mmrelay:/app/data
|
|
19
|
+
# Tip: For correct permissions and paths, ensure UID, GID, and MMRELAY_HOME are set in a .env file or exported
|
|
@@ -1,65 +1,19 @@
|
|
|
1
1
|
services:
|
|
2
2
|
mmrelay:
|
|
3
3
|
build: .
|
|
4
|
-
container_name:
|
|
4
|
+
container_name: mmrelay # Updated for consistent branding
|
|
5
5
|
restart: unless-stopped
|
|
6
|
+
stop_grace_period: 30s
|
|
6
7
|
user: "${UID:-1000}:${GID:-1000}"
|
|
7
|
-
|
|
8
8
|
environment:
|
|
9
|
-
- TZ=UTC
|
|
10
|
-
- PYTHONUNBUFFERED=1
|
|
11
|
-
- MPLCONFIGDIR=/tmp/matplotlib
|
|
12
|
-
|
|
13
|
-
# Matrix Authentication: Use 'mmrelay auth login' on host system to create credentials.json
|
|
14
|
-
# The credentials file will be automatically loaded from the volume mount below
|
|
15
|
-
|
|
16
|
-
# Meshtastic Connection Settings - Uncomment and configure as needed
|
|
17
|
-
# TCP Connection (most common)
|
|
18
|
-
# - MMRELAY_MESHTASTIC_CONNECTION_TYPE=tcp
|
|
19
|
-
# - MMRELAY_MESHTASTIC_HOST=192.168.1.100
|
|
20
|
-
# - MMRELAY_MESHTASTIC_PORT=4403 # Default port
|
|
21
|
-
|
|
22
|
-
# Serial Connection (uncomment for serial)
|
|
23
|
-
# - MMRELAY_MESHTASTIC_CONNECTION_TYPE=serial
|
|
24
|
-
# - MMRELAY_MESHTASTIC_SERIAL_PORT=/dev/ttyUSB0
|
|
25
|
-
|
|
26
|
-
# BLE Connection (uncomment for Bluetooth)
|
|
27
|
-
# - MMRELAY_MESHTASTIC_CONNECTION_TYPE=ble
|
|
28
|
-
# - MMRELAY_MESHTASTIC_BLE_ADDRESS=AA:BB:CC:DD:EE:FF
|
|
29
|
-
|
|
30
|
-
# Meshtastic Operational Settings
|
|
31
|
-
# - MMRELAY_MESHTASTIC_BROADCAST_ENABLED=true
|
|
32
|
-
# - MMRELAY_MESHTASTIC_MESHNET_NAME=My Mesh Network
|
|
33
|
-
# - MMRELAY_MESHTASTIC_MESSAGE_DELAY=2.2 # Minimum 2.0 seconds
|
|
34
|
-
|
|
35
|
-
# System Configuration
|
|
36
|
-
# - MMRELAY_LOGGING_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
37
|
-
# - MMRELAY_LOG_FILE=/app/data/logs/mmrelay.log # Enables file logging
|
|
38
|
-
# - MMRELAY_DATABASE_PATH=/app/data/meshtastic.sqlite
|
|
39
|
-
|
|
40
|
-
# Note: Environment variables for Meshtastic, logging, and database settings take precedence over config.yaml
|
|
41
|
-
|
|
9
|
+
- TZ=UTC # Set timezone (PYTHONUNBUFFERED and MPLCONFIGDIR are set in Dockerfile)
|
|
42
10
|
volumes:
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
- ${MMRELAY_HOME}/.mmrelay:/app/data
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# For serial connections, uncomment the device you need:
|
|
54
|
-
# devices:
|
|
55
|
-
# - /dev/ttyUSB0:/dev/ttyUSB0
|
|
56
|
-
# - /dev/ttyACM0:/dev/ttyACM0
|
|
57
|
-
|
|
58
|
-
# For BLE connections, uncomment these:
|
|
59
|
-
# privileged: true
|
|
60
|
-
# network_mode: host
|
|
61
|
-
# Additional volumes for BLE (add to existing volumes section above):
|
|
62
|
-
# - /var/run/dbus:/var/run/dbus:ro
|
|
63
|
-
# - /sys/bus/usb:/sys/bus/usb:ro
|
|
64
|
-
# - /sys/class/bluetooth:/sys/class/bluetooth:ro
|
|
65
|
-
# - /sys/devices:/sys/devices:ro
|
|
11
|
+
# Mount your config directory - create ~/.mmrelay/config.yaml first
|
|
12
|
+
# Run 'make config' to set up the files
|
|
13
|
+
# For SELinux systems (RHEL/CentOS/Fedora), add :Z flag to prevent permission denied errors
|
|
14
|
+
- ${MMRELAY_HOME:-$HOME}/.mmrelay/config.yaml:/app/config.yaml:ro,Z
|
|
15
|
+
- ${MMRELAY_HOME:-$HOME}/.mmrelay:/app/data:Z
|
|
16
|
+
# For non-SELinux systems, you can use:
|
|
17
|
+
# - ${MMRELAY_HOME:-$HOME}/.mmrelay/config.yaml:/app/config.yaml:ro
|
|
18
|
+
# - ${MMRELAY_HOME:-$HOME}/.mmrelay:/app/data
|
|
19
|
+
# Tip: For correct permissions and paths, ensure UID, GID, and MMRELAY_HOME are set in a .env file or exported
|
mmrelay/tools/sample_config.yaml
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
matrix:
|
|
2
2
|
homeserver: https://example.matrix.org
|
|
3
|
-
|
|
3
|
+
# Modern authentication using password (recommended)
|
|
4
|
+
# MMRelay will automatically create secure credentials.json on startup
|
|
5
|
+
password: your_matrix_password_here # Set your Matrix account password
|
|
6
|
+
# Security: After first successful start, remove this password from the file.
|
|
7
|
+
# Linux/macOS: chmod 600 ~/.mmrelay/config.yaml
|
|
8
|
+
# Windows: restrict file access to your user (e.g., via file Properties → Security)
|
|
9
|
+
# Never commit this file with a password to version control.
|
|
4
10
|
bot_user_id: "@botuser:example.matrix.org"
|
|
5
11
|
|
|
6
|
-
# Alternative: Automatic credentials creation (Docker-friendly)
|
|
7
|
-
# If you provide password instead of access_token, MMRelay will automatically
|
|
8
|
-
# create credentials.json on startup. Useful for Docker deployments.
|
|
9
|
-
#password: your_matrix_password_here # Uncomment and set your Matrix password
|
|
10
|
-
|
|
11
12
|
# End-to-End Encryption (E2EE) configuration
|
|
12
|
-
#
|
|
13
|
+
# E2EE is automatically enabled when using password-based authentication AND E2EE dependencies are installed (Linux/macOS only)
|
|
14
|
+
# NOTE: E2EE is not available on Windows due to dependency limitations
|
|
13
15
|
#
|
|
14
16
|
# SETUP INSTRUCTIONS:
|
|
15
|
-
# 1. Install E2EE dependencies: pipx install 'mmrelay[e2e]'
|
|
16
|
-
# 2.
|
|
17
|
-
# 3.
|
|
18
|
-
# 4.
|
|
19
|
-
# 5. Restart mmrelay - it will use credentials.json and enable E2EE automatically
|
|
17
|
+
# 1. Install E2EE dependencies: pipx install 'mmrelay[e2e]' (Linux/macOS only)
|
|
18
|
+
# 2. Set your password above and run MMRelay
|
|
19
|
+
# 3. MMRelay will automatically create credentials.json with E2EE support
|
|
20
|
+
# 4. For interactive setup, use: mmrelay auth login
|
|
20
21
|
#
|
|
21
22
|
#e2ee:
|
|
22
23
|
# # Optional: When credentials.json is present, MMRelay auto-enables E2EE.
|
|
@@ -58,7 +59,7 @@ meshtastic:
|
|
|
58
59
|
|
|
59
60
|
# Message prefix customization (Matrix → Meshtastic direction)
|
|
60
61
|
#prefix_enabled: true # Enable username prefixes on messages sent to mesh (e.g., "Alice[M]: message")
|
|
61
|
-
#prefix_format: "{display5}[M]: " # Default format. See
|
|
62
|
+
#prefix_format: "{display5}[M]: " # Default format. See ADVANCED_CONFIGURATION.md for all variables.
|
|
62
63
|
|
|
63
64
|
logging:
|
|
64
65
|
level: info
|
mmrelay/windows_utils.py
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Windows-specific utilities for MMRelay.
|
|
3
|
+
|
|
4
|
+
This module provides Windows-specific functionality and workarounds
|
|
5
|
+
for better compatibility and user experience on Windows systems.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from mmrelay.constants.app import WINDOWS_PLATFORM
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_windows() -> bool:
|
|
16
|
+
"""
|
|
17
|
+
Return True if the current process is running on a Windows platform.
|
|
18
|
+
|
|
19
|
+
Checks common platform indicators (os.name == "nt" or sys.platform == WINDOWS_PLATFORM)
|
|
20
|
+
and returns a boolean accordingly.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
bool: True when running on Windows, otherwise False.
|
|
24
|
+
"""
|
|
25
|
+
return os.name == "nt" or sys.platform == WINDOWS_PLATFORM
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def setup_windows_console() -> None:
|
|
29
|
+
"""
|
|
30
|
+
Configure the Windows console for UTF-8 output and ANSI (VT100) color support.
|
|
31
|
+
|
|
32
|
+
Best-effort operation: on Windows this attempts to set stdout/stderr encoding to UTF-8
|
|
33
|
+
(if supported) and enable Virtual Terminal Processing so ANSI color sequences and
|
|
34
|
+
Unicode render correctly. No-op on non-Windows platforms; failures are silently ignored.
|
|
35
|
+
"""
|
|
36
|
+
if not is_windows():
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
# Enable UTF-8 output on Windows
|
|
41
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
42
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
43
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
44
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
45
|
+
|
|
46
|
+
# Enable ANSI color codes on Windows 10+
|
|
47
|
+
import ctypes
|
|
48
|
+
|
|
49
|
+
kernel32 = ctypes.windll.kernel32
|
|
50
|
+
ENABLE_VTP = 0x0004 # ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
51
|
+
for handle in (-11, -12): # STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
|
|
52
|
+
h = kernel32.GetStdHandle(handle)
|
|
53
|
+
if h is not None and h != -1:
|
|
54
|
+
mode = ctypes.c_uint()
|
|
55
|
+
if kernel32.GetConsoleMode(h, ctypes.byref(mode)):
|
|
56
|
+
kernel32.SetConsoleMode(h, mode.value | ENABLE_VTP)
|
|
57
|
+
except (OSError, AttributeError):
|
|
58
|
+
# If console setup fails, continue without it
|
|
59
|
+
# This is expected on non-Windows systems or older Windows versions
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_windows_error_message(error: Exception) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Return a Windows-tailored, user-friendly message for common filesystem and network exceptions.
|
|
66
|
+
|
|
67
|
+
If not running on Windows this returns str(error) unchanged. For Windows, the function recognizes
|
|
68
|
+
permission errors (PermissionError or OSError with EACCES/EPERM), missing-file errors
|
|
69
|
+
(FileNotFoundError or OSError with ENOENT), and common network-related OSErrors
|
|
70
|
+
(EHOSTUNREACH, ENETDOWN, ENETUNREACH, ECONNREFUSED, ETIMEDOUT) and returns guidance specific
|
|
71
|
+
to each case (possible causes and suggested next steps). For other exceptions it returns
|
|
72
|
+
str(error).
|
|
73
|
+
|
|
74
|
+
Parameters:
|
|
75
|
+
error (Exception): The exception to translate into a user-facing message.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
str: A user-oriented error message; either a Windows-specific guidance string or the
|
|
79
|
+
original exception string for unhandled cases or non-Windows platforms.
|
|
80
|
+
"""
|
|
81
|
+
if not is_windows():
|
|
82
|
+
return str(error)
|
|
83
|
+
|
|
84
|
+
import errno as _errno
|
|
85
|
+
|
|
86
|
+
# Use exception types and errno codes for more robust error detection
|
|
87
|
+
if isinstance(error, PermissionError) or (
|
|
88
|
+
isinstance(error, OSError) and error.errno in {_errno.EACCES, _errno.EPERM}
|
|
89
|
+
):
|
|
90
|
+
return (
|
|
91
|
+
f"Permission denied: {error}\n"
|
|
92
|
+
"This may be caused by:\n"
|
|
93
|
+
"• Antivirus software blocking the operation\n"
|
|
94
|
+
"• Windows User Account Control (UAC) restrictions\n"
|
|
95
|
+
"• File being used by another process\n"
|
|
96
|
+
"Try running as administrator or check antivirus settings."
|
|
97
|
+
)
|
|
98
|
+
elif isinstance(error, FileNotFoundError) or (
|
|
99
|
+
isinstance(error, OSError) and error.errno in {_errno.ENOENT}
|
|
100
|
+
):
|
|
101
|
+
return (
|
|
102
|
+
f"File not found: {error}\n"
|
|
103
|
+
"This may be caused by:\n"
|
|
104
|
+
"• Incorrect file path (check for spaces or special characters)\n"
|
|
105
|
+
"• File moved or deleted by antivirus software\n"
|
|
106
|
+
"• Network drive disconnection\n"
|
|
107
|
+
"Verify the file path and check antivirus quarantine."
|
|
108
|
+
)
|
|
109
|
+
elif isinstance(error, ConnectionError) or (
|
|
110
|
+
isinstance(error, OSError)
|
|
111
|
+
and error.errno
|
|
112
|
+
in {
|
|
113
|
+
_errno.EHOSTUNREACH,
|
|
114
|
+
_errno.ENETDOWN,
|
|
115
|
+
_errno.ENETUNREACH,
|
|
116
|
+
_errno.ECONNREFUSED,
|
|
117
|
+
_errno.ETIMEDOUT,
|
|
118
|
+
}
|
|
119
|
+
):
|
|
120
|
+
return (
|
|
121
|
+
f"Network error: {error}\n"
|
|
122
|
+
"This may be caused by:\n"
|
|
123
|
+
"• Windows Firewall blocking the connection\n"
|
|
124
|
+
"• Antivirus software blocking network access\n"
|
|
125
|
+
"• VPN or proxy configuration issues\n"
|
|
126
|
+
"Check firewall settings and antivirus network protection."
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
return str(error)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def check_windows_requirements() -> Optional[str]:
|
|
133
|
+
"""
|
|
134
|
+
Return a multi-line warning string when common Windows environment issues are detected; otherwise None.
|
|
135
|
+
|
|
136
|
+
Performs Windows-only checks:
|
|
137
|
+
- Python version: warns if running on Python < 3.9.
|
|
138
|
+
- Virtual environment: warns if the process does not appear to be inside a venv/pipx.
|
|
139
|
+
- Current working directory length: warns if the cwd path length exceeds 200 characters.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Optional[str]: A human-readable, multi-line warning message when one or more checks trigger;
|
|
143
|
+
returns None if running on a non-Windows platform or no issues are found.
|
|
144
|
+
"""
|
|
145
|
+
if not is_windows():
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
warnings = []
|
|
149
|
+
|
|
150
|
+
# Check Python version for Windows compatibility
|
|
151
|
+
if sys.version_info < (3, 9):
|
|
152
|
+
warnings.append(
|
|
153
|
+
"Python 3.9+ is recommended on Windows for better compatibility"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Check if running in a virtual environment
|
|
157
|
+
if not hasattr(sys, "real_prefix") and not (
|
|
158
|
+
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
|
|
159
|
+
):
|
|
160
|
+
warnings.append(
|
|
161
|
+
"Consider using a virtual environment (venv) or pipx for better isolation"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Check for common Windows path issues
|
|
165
|
+
if len(os.getcwd()) > 200:
|
|
166
|
+
warnings.append(
|
|
167
|
+
"Current directory path is very long - this may cause issues on Windows"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if warnings:
|
|
171
|
+
return "Windows compatibility warnings:\n• " + "\n• ".join(warnings)
|
|
172
|
+
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_config_generation_windows(args=None) -> dict:
|
|
177
|
+
"""
|
|
178
|
+
Run Windows-only diagnostics for MMRelay configuration generation.
|
|
179
|
+
|
|
180
|
+
Performs four checks and returns a dictionary with per-test results and an overall status:
|
|
181
|
+
- sample_config_path: verifies mmrelay.tools.get_sample_config_path() exists.
|
|
182
|
+
- importlib_resources: attempts to read mmrelay.tools/sample_config.yaml via importlib.resources.
|
|
183
|
+
- config_paths: calls mmrelay.config.get_config_paths(args) and reports the returned paths.
|
|
184
|
+
- directory_creation: ensures parent directories for the config paths exist (creates them if missing).
|
|
185
|
+
|
|
186
|
+
Parameters:
|
|
187
|
+
args (optional): Forwarded to mmrelay.config.get_config_paths; typically CLI-style arguments or None.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
dict: Diagnostic results with these keys:
|
|
191
|
+
- sample_config_path, importlib_resources, config_paths, directory_creation:
|
|
192
|
+
dict objects with "status" ("ok" or "error") and "details" (string).
|
|
193
|
+
- overall_status: one of "ok" (no errors), "partial" (1–2 errors), or "error" (3+ errors).
|
|
194
|
+
If called on a non-Windows platform, returns {"error": "This function is only for Windows systems"}.
|
|
195
|
+
|
|
196
|
+
Notes:
|
|
197
|
+
- The function does not raise on expected failures; errors are reported in the returned dict.
|
|
198
|
+
- Directory creation test may create directories on disk when missing.
|
|
199
|
+
"""
|
|
200
|
+
if not is_windows():
|
|
201
|
+
return {"error": "This function is only for Windows systems"}
|
|
202
|
+
|
|
203
|
+
results = {
|
|
204
|
+
"sample_config_path": {"status": "unknown", "details": ""},
|
|
205
|
+
"importlib_resources": {"status": "unknown", "details": ""},
|
|
206
|
+
"config_paths": {"status": "unknown", "details": ""},
|
|
207
|
+
"directory_creation": {"status": "unknown", "details": ""},
|
|
208
|
+
"overall_status": "unknown",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
# Test 1: Sample config path
|
|
213
|
+
try:
|
|
214
|
+
from mmrelay.tools import get_sample_config_path
|
|
215
|
+
|
|
216
|
+
sample_path = get_sample_config_path()
|
|
217
|
+
if os.path.exists(sample_path):
|
|
218
|
+
results["sample_config_path"] = {
|
|
219
|
+
"status": "ok",
|
|
220
|
+
"details": f"Found at: {sample_path}",
|
|
221
|
+
}
|
|
222
|
+
else:
|
|
223
|
+
results["sample_config_path"] = {
|
|
224
|
+
"status": "error",
|
|
225
|
+
"details": f"Not found at: {sample_path}",
|
|
226
|
+
}
|
|
227
|
+
except (
|
|
228
|
+
ImportError,
|
|
229
|
+
OSError,
|
|
230
|
+
FileNotFoundError,
|
|
231
|
+
AttributeError,
|
|
232
|
+
TypeError,
|
|
233
|
+
) as e:
|
|
234
|
+
results["sample_config_path"] = {"status": "error", "details": str(e)}
|
|
235
|
+
|
|
236
|
+
# Test 2: importlib.resources fallback
|
|
237
|
+
try:
|
|
238
|
+
import importlib.resources
|
|
239
|
+
|
|
240
|
+
content = (
|
|
241
|
+
importlib.resources.files("mmrelay.tools")
|
|
242
|
+
.joinpath("sample_config.yaml")
|
|
243
|
+
.read_text()
|
|
244
|
+
)
|
|
245
|
+
results["importlib_resources"] = {
|
|
246
|
+
"status": "ok",
|
|
247
|
+
"details": f"Content length: {len(content)} chars",
|
|
248
|
+
}
|
|
249
|
+
except (ImportError, OSError, FileNotFoundError) as e:
|
|
250
|
+
results["importlib_resources"] = {"status": "error", "details": str(e)}
|
|
251
|
+
|
|
252
|
+
# Test 3: Config paths
|
|
253
|
+
try:
|
|
254
|
+
from mmrelay.config import get_config_paths
|
|
255
|
+
|
|
256
|
+
paths = get_config_paths(args)
|
|
257
|
+
results["config_paths"] = {"status": "ok", "details": f"Paths: {paths}"}
|
|
258
|
+
except (ImportError, OSError) as e:
|
|
259
|
+
results["config_paths"] = {"status": "error", "details": str(e)}
|
|
260
|
+
|
|
261
|
+
# Test 4: Directory creation
|
|
262
|
+
try:
|
|
263
|
+
from mmrelay.config import get_config_paths
|
|
264
|
+
|
|
265
|
+
paths = get_config_paths(args)
|
|
266
|
+
created_dirs = []
|
|
267
|
+
for path in paths:
|
|
268
|
+
dir_path = os.path.dirname(path)
|
|
269
|
+
if not os.path.exists(dir_path):
|
|
270
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
271
|
+
created_dirs.append(dir_path)
|
|
272
|
+
results["directory_creation"] = {
|
|
273
|
+
"status": "ok",
|
|
274
|
+
"details": f"Created: {created_dirs}",
|
|
275
|
+
}
|
|
276
|
+
except OSError as e:
|
|
277
|
+
results["directory_creation"] = {"status": "error", "details": str(e)}
|
|
278
|
+
|
|
279
|
+
# Determine overall status
|
|
280
|
+
error_count = sum(
|
|
281
|
+
1
|
|
282
|
+
for r in results.values()
|
|
283
|
+
if isinstance(r, dict) and r.get("status") == "error"
|
|
284
|
+
)
|
|
285
|
+
if error_count == 0:
|
|
286
|
+
results["overall_status"] = "ok"
|
|
287
|
+
elif error_count < 3: # If at least one fallback works
|
|
288
|
+
results["overall_status"] = "partial"
|
|
289
|
+
else:
|
|
290
|
+
results["overall_status"] = "error"
|
|
291
|
+
|
|
292
|
+
except OSError as e:
|
|
293
|
+
results["overall_status"] = "error"
|
|
294
|
+
results["error"] = str(e)
|
|
295
|
+
|
|
296
|
+
return results
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def get_windows_install_guidance() -> str:
|
|
300
|
+
"""
|
|
301
|
+
Return a Windows-specific installation and troubleshooting guide as a formatted string.
|
|
302
|
+
|
|
303
|
+
The returned text contains recommended installation methods, common Windows-specific problems with actionable remedies, and troubleshooting tips for configuration and runtime issues. It is safe to display directly to end users or include in logs/help output.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
str: Multiline guidance text suited for Windows users.
|
|
307
|
+
"""
|
|
308
|
+
return """
|
|
309
|
+
Windows Installation & Troubleshooting Guide:
|
|
310
|
+
|
|
311
|
+
📦 Recommended Installation:
|
|
312
|
+
pipx install mmrelay
|
|
313
|
+
(pipx provides better isolation and fewer conflicts)
|
|
314
|
+
|
|
315
|
+
🔧 If pipx is not available:
|
|
316
|
+
pip install --user mmrelay
|
|
317
|
+
(installs to user directory, avoiding system conflicts)
|
|
318
|
+
|
|
319
|
+
⚠️ Common Windows Issues:
|
|
320
|
+
|
|
321
|
+
1. "ModuleNotFoundError: No module named 'pkg_resources'"
|
|
322
|
+
Solution: pip install --upgrade setuptools
|
|
323
|
+
Alternative: Use 'python -m mmrelay' instead of 'mmrelay'
|
|
324
|
+
|
|
325
|
+
2. "Access denied" or permission errors
|
|
326
|
+
Solution: Run command prompt as administrator
|
|
327
|
+
Or: Use --user flag with pip
|
|
328
|
+
|
|
329
|
+
3. "SSL certificate verify failed"
|
|
330
|
+
Solution: Update certificates or use --trusted-host flag
|
|
331
|
+
|
|
332
|
+
4. Antivirus blocking installation/execution
|
|
333
|
+
Solution: Add Python and pip to antivirus exclusions
|
|
334
|
+
|
|
335
|
+
5. Long path issues
|
|
336
|
+
Solution: Enable long path support in Windows 10+
|
|
337
|
+
Or: Use shorter installation directory
|
|
338
|
+
|
|
339
|
+
6. Config generation fails
|
|
340
|
+
Solution: Check if sample_config.yaml is accessible
|
|
341
|
+
Alternative: Manually create config file from documentation
|
|
342
|
+
|
|
343
|
+
🆘 Need Help?
|
|
344
|
+
• Check Windows Event Viewer for detailed error logs
|
|
345
|
+
• Temporarily disable antivirus for testing
|
|
346
|
+
• Use Windows PowerShell instead of Command Prompt
|
|
347
|
+
• Consider using Windows Subsystem for Linux (WSL)
|
|
348
|
+
• Test config generation: 'python -m mmrelay config diagnose'
|
|
349
|
+
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mmrelay
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
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
|
|
@@ -22,16 +22,16 @@ Requires-Dist: meshtastic>=2.6.4
|
|
|
22
22
|
Requires-Dist: Pillow==11.3.0
|
|
23
23
|
Requires-Dist: matrix-nio==0.25.2
|
|
24
24
|
Requires-Dist: matplotlib==3.10.1
|
|
25
|
-
Requires-Dist: requests==2.32.
|
|
26
|
-
Requires-Dist: markdown==3.
|
|
25
|
+
Requires-Dist: requests==2.32.5
|
|
26
|
+
Requires-Dist: markdown==3.9
|
|
27
27
|
Requires-Dist: bleach==6.2.0
|
|
28
28
|
Requires-Dist: haversine==2.9.0
|
|
29
29
|
Requires-Dist: schedule==1.2.2
|
|
30
|
-
Requires-Dist: platformdirs==4.
|
|
30
|
+
Requires-Dist: platformdirs==4.4.0
|
|
31
31
|
Requires-Dist: py-staticmaps>=0.4.0
|
|
32
32
|
Requires-Dist: psutil>=5.8.0
|
|
33
33
|
Requires-Dist: rich==14.1.0
|
|
34
|
-
Requires-Dist: setuptools
|
|
34
|
+
Requires-Dist: setuptools>=80.9.0
|
|
35
35
|
Provides-Extra: e2e
|
|
36
36
|
Requires-Dist: matrix-nio[e2e]==0.25.2; extra == "e2e"
|
|
37
37
|
Requires-Dist: python-olm; extra == "e2e"
|
|
@@ -48,7 +48,7 @@ Dynamic: requires-dist
|
|
|
48
48
|
Dynamic: requires-python
|
|
49
49
|
Dynamic: summary
|
|
50
50
|
|
|
51
|
-
#
|
|
51
|
+
# MMRelay
|
|
52
52
|
|
|
53
53
|
## (Meshtastic <=> Matrix Relay)
|
|
54
54
|
|
|
@@ -69,22 +69,22 @@ A powerful and easy-to-use relay between Meshtastic devices and Matrix chat room
|
|
|
69
69
|
- ✨️ _Native Docker support_ ✨️
|
|
70
70
|
- 🔐 **Matrix End-to-End Encryption (E2EE) support** 🔐 **NEW in v1.2!**
|
|
71
71
|
|
|
72
|
-
**MMRelay v1.2** introduces
|
|
72
|
+
**MMRelay v1.2** introduces **Matrix End-to-End Encryption** support for secure communication in encrypted rooms. Messages are automatically encrypted/decrypted when communicating with encrypted Matrix rooms, with simple setup using `mmrelay auth login` or automatic credentials creation from config.yaml.
|
|
73
73
|
|
|
74
74
|
## Documentation
|
|
75
75
|
|
|
76
|
-
Visit our [Wiki](https://github.com/jeremiah-k/meshtastic-matrix-relay/wiki) for comprehensive guides and information.
|
|
77
|
-
|
|
78
76
|
MMRelay supports multiple deployment methods including Docker, pip installation, and standalone executables. For complete setup instructions and all deployment options, see:
|
|
79
77
|
|
|
80
78
|
- [Installation Instructions](docs/INSTRUCTIONS.md) - Setup and configuration guide
|
|
81
79
|
- [Docker Guide](docs/DOCKER.md) - Docker deployment methods
|
|
80
|
+
- [What's New in v1.2](docs/WHATS_NEW_1.2.md) - New features and improvements
|
|
81
|
+
- [E2EE Setup Guide](docs/E2EE.md) - Matrix End-to-End Encryption configuration
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
85
85
|
## Plugins
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
MMRelay supports plugins for extending its functionality, enabling customization and enhancement of the relay to suit specific needs.
|
|
88
88
|
|
|
89
89
|
### Core Plugins
|
|
90
90
|
|
|
@@ -133,7 +133,7 @@ Plugins make it easy to extend functionality without modifying the core program.
|
|
|
133
133
|
|
|
134
134
|
## Getting Started with Matrix
|
|
135
135
|
|
|
136
|
-
See our Wiki page [Getting Started With Matrix &
|
|
136
|
+
See our Wiki page [Getting Started With Matrix & MMRelay](https://github.com/jeremiah-k/meshtastic-matrix-relay/wiki/Getting-Started-With-Matrix-&-MM-Relay).
|
|
137
137
|
|
|
138
138
|
---
|
|
139
139
|
|