mmrelay 1.0.6__py3-none-any.whl → 1.0.8__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 +54 -16
- mmrelay/main.py +4 -1
- mmrelay/matrix_utils.py +52 -4
- mmrelay/plugins/base_plugin.py +21 -16
- mmrelay/plugins/drop_plugin.py +2 -3
- mmrelay/plugins/help_plugin.py +2 -3
- mmrelay/plugins/map_plugin.py +2 -3
- mmrelay/plugins/ping_plugin.py +2 -3
- mmrelay/plugins/weather_plugin.py +2 -3
- mmrelay/setup_utils.py +137 -8
- mmrelay/tools/__init__.py +28 -0
- mmrelay/tools/mmrelay.service +18 -0
- mmrelay/tools/sample_config.yaml +64 -0
- {mmrelay-1.0.6.dist-info → mmrelay-1.0.8.dist-info}/METADATA +24 -48
- mmrelay-1.0.8.dist-info/RECORD +32 -0
- {mmrelay-1.0.6.dist-info → mmrelay-1.0.8.dist-info}/WHEEL +1 -1
- mmrelay-1.0.6.dist-info/RECORD +0 -29
- {mmrelay-1.0.6.dist-info → mmrelay-1.0.8.dist-info}/entry_points.txt +0 -0
- {mmrelay-1.0.6.dist-info → mmrelay-1.0.8.dist-info}/licenses/LICENSE +0 -0
- {mmrelay-1.0.6.dist-info → mmrelay-1.0.8.dist-info}/top_level.txt +0 -0
mmrelay/__init__.py
CHANGED
mmrelay/cli.py
CHANGED
|
@@ -3,6 +3,7 @@ Command-line interface handling for the Meshtastic Matrix Relay.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import importlib.resources
|
|
6
7
|
import os
|
|
7
8
|
import sys
|
|
8
9
|
|
|
@@ -11,6 +12,7 @@ from yaml.loader import SafeLoader
|
|
|
11
12
|
|
|
12
13
|
# Import version from package
|
|
13
14
|
from mmrelay import __version__
|
|
15
|
+
from mmrelay.tools import get_sample_config_path
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def parse_arguments():
|
|
@@ -363,29 +365,65 @@ def generate_sample_config():
|
|
|
363
365
|
# Ensure the directory exists
|
|
364
366
|
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
|
365
367
|
|
|
366
|
-
#
|
|
367
|
-
|
|
368
|
-
package_dir = os.path.dirname(__file__)
|
|
369
|
-
sample_config_path = os.path.join(
|
|
370
|
-
os.path.dirname(os.path.dirname(package_dir)), "sample_config.yaml"
|
|
371
|
-
)
|
|
368
|
+
# Use the helper function to get the sample config path
|
|
369
|
+
sample_config_path = get_sample_config_path()
|
|
372
370
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
sample_config_path = os.path.join(repo_root, "sample_config.yaml")
|
|
371
|
+
if os.path.exists(sample_config_path):
|
|
372
|
+
# Copy the sample config file to the target path
|
|
373
|
+
import shutil
|
|
377
374
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
375
|
+
shutil.copy2(sample_config_path, target_path)
|
|
376
|
+
print(f"Generated sample config file at: {target_path}")
|
|
377
|
+
print(
|
|
378
|
+
"\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
|
|
379
|
+
)
|
|
380
|
+
return True
|
|
381
|
+
|
|
382
|
+
# If the helper function failed, try using importlib.resources directly
|
|
383
|
+
try:
|
|
384
|
+
# Try to get the sample config from the package resources
|
|
385
|
+
sample_config_content = (
|
|
386
|
+
importlib.resources.files("mmrelay.tools")
|
|
387
|
+
.joinpath("sample_config.yaml")
|
|
388
|
+
.read_text()
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
# Write the sample config to the target path
|
|
392
|
+
with open(target_path, "w") as f:
|
|
393
|
+
f.write(sample_config_content)
|
|
381
394
|
|
|
382
|
-
if os.path.exists(sample_config_path):
|
|
383
|
-
shutil.copy(sample_config_path, target_path)
|
|
384
395
|
print(f"Generated sample config file at: {target_path}")
|
|
385
396
|
print(
|
|
386
397
|
"\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
|
|
387
398
|
)
|
|
388
399
|
return True
|
|
389
|
-
|
|
400
|
+
except (FileNotFoundError, ImportError, OSError) as e:
|
|
401
|
+
print(f"Error accessing sample_config.yaml: {e}")
|
|
402
|
+
|
|
403
|
+
# Fallback to traditional file paths if importlib.resources fails
|
|
404
|
+
# First, check in the package directory
|
|
405
|
+
package_dir = os.path.dirname(__file__)
|
|
406
|
+
sample_config_paths = [
|
|
407
|
+
# Check in the tools subdirectory of the package
|
|
408
|
+
os.path.join(package_dir, "tools", "sample_config.yaml"),
|
|
409
|
+
# Check in the package directory
|
|
410
|
+
os.path.join(package_dir, "sample_config.yaml"),
|
|
411
|
+
# Check in the repository root
|
|
412
|
+
os.path.join(
|
|
413
|
+
os.path.dirname(os.path.dirname(package_dir)), "sample_config.yaml"
|
|
414
|
+
),
|
|
415
|
+
# Check in the current directory
|
|
416
|
+
os.path.join(os.getcwd(), "sample_config.yaml"),
|
|
417
|
+
]
|
|
418
|
+
|
|
419
|
+
for path in sample_config_paths:
|
|
420
|
+
if os.path.exists(path):
|
|
421
|
+
shutil.copy(path, target_path)
|
|
422
|
+
print(f"Generated sample config file at: {target_path}")
|
|
423
|
+
print(
|
|
424
|
+
"\nEdit this file with your Matrix and Meshtastic settings before running mmrelay."
|
|
425
|
+
)
|
|
426
|
+
return True
|
|
427
|
+
|
|
390
428
|
print("Error: Could not find sample_config.yaml")
|
|
391
429
|
return False
|
mmrelay/main.py
CHANGED
|
@@ -9,6 +9,7 @@ import signal
|
|
|
9
9
|
import sys
|
|
10
10
|
|
|
11
11
|
from nio import ReactionEvent, RoomMessageEmote, RoomMessageNotice, RoomMessageText
|
|
12
|
+
from nio.events.room_events import RoomMemberEvent
|
|
12
13
|
|
|
13
14
|
# Import version from package
|
|
14
15
|
# Import meshtastic_utils as a module to set event_loop
|
|
@@ -22,7 +23,7 @@ from mmrelay.db_utils import (
|
|
|
22
23
|
from mmrelay.log_utils import get_logger
|
|
23
24
|
from mmrelay.matrix_utils import connect_matrix, join_matrix_room
|
|
24
25
|
from mmrelay.matrix_utils import logger as matrix_logger
|
|
25
|
-
from mmrelay.matrix_utils import on_room_message
|
|
26
|
+
from mmrelay.matrix_utils import on_room_message, on_room_member
|
|
26
27
|
from mmrelay.meshtastic_utils import connect_meshtastic
|
|
27
28
|
from mmrelay.meshtastic_utils import logger as meshtastic_logger
|
|
28
29
|
from mmrelay.plugin_loader import load_plugins
|
|
@@ -109,6 +110,8 @@ async def main(config):
|
|
|
109
110
|
)
|
|
110
111
|
# Add ReactionEvent callback so we can handle matrix reactions
|
|
111
112
|
matrix_client.add_event_callback(on_room_message, ReactionEvent)
|
|
113
|
+
# Add RoomMemberEvent callback to track room-specific display name changes
|
|
114
|
+
matrix_client.add_event_callback(on_room_member, RoomMemberEvent)
|
|
112
115
|
|
|
113
116
|
# Set up shutdown event
|
|
114
117
|
shutdown_event = asyncio.Event()
|
mmrelay/matrix_utils.py
CHANGED
|
@@ -18,6 +18,7 @@ from nio import (
|
|
|
18
18
|
UploadResponse,
|
|
19
19
|
WhoamiError,
|
|
20
20
|
)
|
|
21
|
+
from nio.events.room_events import RoomMemberEvent
|
|
21
22
|
from PIL import Image
|
|
22
23
|
|
|
23
24
|
from mmrelay.db_utils import (
|
|
@@ -517,8 +518,14 @@ async def on_room_message(
|
|
|
517
518
|
meshtastic_id, matrix_room_id, meshtastic_text_db, meshtastic_meshnet_db = (
|
|
518
519
|
orig
|
|
519
520
|
)
|
|
520
|
-
|
|
521
|
-
|
|
521
|
+
# Get room-specific display name if available, fallback to global display name
|
|
522
|
+
room_display_name = room.user_name(event.sender)
|
|
523
|
+
if room_display_name:
|
|
524
|
+
full_display_name = room_display_name
|
|
525
|
+
else:
|
|
526
|
+
# Fallback to global display name if room-specific name is not available
|
|
527
|
+
display_name_response = await matrix_client.get_displayname(event.sender)
|
|
528
|
+
full_display_name = display_name_response.displayname or event.sender
|
|
522
529
|
|
|
523
530
|
# If not from a remote meshnet, proceed as normal to relay back to the originating meshnet
|
|
524
531
|
short_display_name = full_display_name[:5]
|
|
@@ -578,8 +585,14 @@ async def on_room_message(
|
|
|
578
585
|
return
|
|
579
586
|
else:
|
|
580
587
|
# Normal Matrix message from a Matrix user
|
|
581
|
-
|
|
582
|
-
|
|
588
|
+
# Get room-specific display name if available, fallback to global display name
|
|
589
|
+
room_display_name = room.user_name(event.sender)
|
|
590
|
+
if room_display_name:
|
|
591
|
+
full_display_name = room_display_name
|
|
592
|
+
else:
|
|
593
|
+
# Fallback to global display name if room-specific name is not available
|
|
594
|
+
display_name_response = await matrix_client.get_displayname(event.sender)
|
|
595
|
+
full_display_name = display_name_response.displayname or event.sender
|
|
583
596
|
short_display_name = full_display_name[:5]
|
|
584
597
|
prefix = f"{short_display_name}[M]: "
|
|
585
598
|
logger.debug(f"Processing matrix message from [{full_display_name}]: {text}")
|
|
@@ -752,3 +765,38 @@ async def send_room_image(
|
|
|
752
765
|
message_type="m.room.message",
|
|
753
766
|
content={"msgtype": "m.image", "url": upload_response.content_uri, "body": ""},
|
|
754
767
|
)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
async def on_room_member(room: MatrixRoom, event: RoomMemberEvent) -> None:
|
|
771
|
+
"""
|
|
772
|
+
Callback to handle room member events, specifically tracking room-specific display name changes.
|
|
773
|
+
This ensures we detect when users update their display names in specific rooms.
|
|
774
|
+
"""
|
|
775
|
+
# Only track updates from active members
|
|
776
|
+
if event.membership != "join":
|
|
777
|
+
return
|
|
778
|
+
|
|
779
|
+
new_displayname = event.content.get("displayname")
|
|
780
|
+
old_displayname = event.prev_content.get("displayname") if event.prev_content else None
|
|
781
|
+
user_id = event.state_key
|
|
782
|
+
room_id = room.room_id
|
|
783
|
+
|
|
784
|
+
# Log display name changes for debugging
|
|
785
|
+
if new_displayname != old_displayname:
|
|
786
|
+
if new_displayname and old_displayname:
|
|
787
|
+
logger.info(
|
|
788
|
+
f"[Matrix] {user_id} updated room display name in {room_id}: "
|
|
789
|
+
f"'{old_displayname}' → '{new_displayname}'"
|
|
790
|
+
)
|
|
791
|
+
elif new_displayname and not old_displayname:
|
|
792
|
+
logger.info(
|
|
793
|
+
f"[Matrix] {user_id} set room display name in {room_id}: '{new_displayname}'"
|
|
794
|
+
)
|
|
795
|
+
elif not new_displayname and old_displayname:
|
|
796
|
+
logger.info(
|
|
797
|
+
f"[Matrix] {user_id} removed room display name in {room_id}: '{old_displayname}' → (global name)"
|
|
798
|
+
)
|
|
799
|
+
|
|
800
|
+
# Note: We don't need to maintain a separate cache here since matrix-nio
|
|
801
|
+
# automatically updates the room state and room.user_name() will return
|
|
802
|
+
# the updated room-specific display name immediately after this event.
|
mmrelay/plugins/base_plugin.py
CHANGED
|
@@ -29,25 +29,28 @@ class BasePlugin(ABC):
|
|
|
29
29
|
def description(self):
|
|
30
30
|
return ""
|
|
31
31
|
|
|
32
|
-
def __init__(self) -> None:
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# self.plugin_name in your __init__ method BEFORE calling super().__init__()
|
|
36
|
-
# Example:
|
|
37
|
-
# def __init__(self):
|
|
38
|
-
# self.plugin_name = "your_plugin_name" # Set this FIRST
|
|
39
|
-
# super().__init__() # Then call parent
|
|
40
|
-
#
|
|
41
|
-
# Failure to do this will cause command recognition issues and other problems.
|
|
42
|
-
|
|
32
|
+
def __init__(self, plugin_name=None) -> None:
|
|
33
|
+
# Allow plugin_name to be passed as a parameter for simpler initialization
|
|
34
|
+
# This maintains backward compatibility while providing a cleaner API
|
|
43
35
|
super().__init__()
|
|
44
36
|
|
|
45
|
-
#
|
|
37
|
+
# If plugin_name is provided as a parameter, use it
|
|
38
|
+
if plugin_name is not None:
|
|
39
|
+
self.plugin_name = plugin_name
|
|
40
|
+
|
|
41
|
+
# For backward compatibility: if plugin_name is not provided as a parameter,
|
|
42
|
+
# check if it's set as an instance attribute (old way) or use the class attribute
|
|
46
43
|
if not hasattr(self, "plugin_name") or self.plugin_name is None:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
# Try to get the class-level plugin_name
|
|
45
|
+
class_plugin_name = getattr(self.__class__, "plugin_name", None)
|
|
46
|
+
if class_plugin_name is not None:
|
|
47
|
+
self.plugin_name = class_plugin_name
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"{self.__class__.__name__} is missing plugin_name definition. "
|
|
51
|
+
f"Either set class.plugin_name, pass plugin_name to __init__, "
|
|
52
|
+
f"or set self.plugin_name before calling super().__init__()"
|
|
53
|
+
)
|
|
51
54
|
|
|
52
55
|
self.logger = get_logger(f"Plugin:{self.plugin_name}")
|
|
53
56
|
self.config = {"active": False}
|
|
@@ -66,6 +69,8 @@ class BasePlugin(ABC):
|
|
|
66
69
|
room.get("meshtastic_channel")
|
|
67
70
|
for room in config.get("matrix_rooms", [])
|
|
68
71
|
]
|
|
72
|
+
else:
|
|
73
|
+
self.mapped_channels = []
|
|
69
74
|
|
|
70
75
|
# Get the channels specified for this plugin, or default to all mapped channels
|
|
71
76
|
self.channels = self.config.get("channels", self.mapped_channels)
|
mmrelay/plugins/drop_plugin.py
CHANGED
|
@@ -10,9 +10,8 @@ class Plugin(BasePlugin):
|
|
|
10
10
|
plugin_name = "drop"
|
|
11
11
|
special_node = "!NODE_MSGS!"
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
super().__init__()
|
|
13
|
+
# No __init__ method needed with the simplified plugin system
|
|
14
|
+
# The BasePlugin will automatically use the class-level plugin_name
|
|
16
15
|
|
|
17
16
|
def get_position(self, meshtastic_client, node_id):
|
|
18
17
|
for _node, info in meshtastic_client.nodes.items():
|
mmrelay/plugins/help_plugin.py
CHANGED
|
@@ -7,9 +7,8 @@ from mmrelay.plugins.base_plugin import BasePlugin
|
|
|
7
7
|
class Plugin(BasePlugin):
|
|
8
8
|
plugin_name = "help"
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
super().__init__()
|
|
10
|
+
# No __init__ method needed with the simplified plugin system
|
|
11
|
+
# The BasePlugin will automatically use the class-level plugin_name
|
|
13
12
|
|
|
14
13
|
@property
|
|
15
14
|
def description(self):
|
mmrelay/plugins/map_plugin.py
CHANGED
|
@@ -235,9 +235,8 @@ async def send_image(client: AsyncClient, room_id: str, image: Image.Image):
|
|
|
235
235
|
class Plugin(BasePlugin):
|
|
236
236
|
plugin_name = "map"
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
super().__init__()
|
|
238
|
+
# No __init__ method needed with the simplified plugin system
|
|
239
|
+
# The BasePlugin will automatically use the class-level plugin_name
|
|
241
240
|
|
|
242
241
|
@property
|
|
243
242
|
def description(self):
|
mmrelay/plugins/ping_plugin.py
CHANGED
|
@@ -17,9 +17,8 @@ class Plugin(BasePlugin):
|
|
|
17
17
|
plugin_name = "ping"
|
|
18
18
|
punctuation = string.punctuation
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
super().__init__()
|
|
20
|
+
# No __init__ method needed with the simplified plugin system
|
|
21
|
+
# The BasePlugin will automatically use the class-level plugin_name
|
|
23
22
|
|
|
24
23
|
@property
|
|
25
24
|
def description(self):
|
|
@@ -9,9 +9,8 @@ from mmrelay.plugins.base_plugin import BasePlugin
|
|
|
9
9
|
class Plugin(BasePlugin):
|
|
10
10
|
plugin_name = "weather"
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
super().__init__()
|
|
12
|
+
# No __init__ method needed with the simplified plugin system
|
|
13
|
+
# The BasePlugin will automatically use the class-level plugin_name
|
|
15
14
|
|
|
16
15
|
@property
|
|
17
16
|
def description(self):
|
mmrelay/setup_utils.py
CHANGED
|
@@ -5,6 +5,8 @@ This module provides simple functions for managing the systemd user service
|
|
|
5
5
|
and generating configuration files.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import importlib.resources
|
|
9
|
+
|
|
8
10
|
# Import version from package
|
|
9
11
|
import os
|
|
10
12
|
import shutil
|
|
@@ -12,6 +14,8 @@ import subprocess
|
|
|
12
14
|
import sys
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
|
|
17
|
+
from mmrelay.tools import get_service_template_path
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
def get_executable_path():
|
|
17
21
|
"""Get the full path to the mmrelay executable.
|
|
@@ -103,16 +107,31 @@ def get_template_service_path():
|
|
|
103
107
|
os.path.join(sys.prefix, "share", "mmrelay", "mmrelay.service"),
|
|
104
108
|
os.path.join(sys.prefix, "share", "mmrelay", "tools", "mmrelay.service"),
|
|
105
109
|
# Check in the user site-packages location
|
|
106
|
-
os.path.join(
|
|
107
|
-
|
|
110
|
+
os.path.join(
|
|
111
|
+
os.path.expanduser("~"), ".local", "share", "mmrelay", "mmrelay.service"
|
|
112
|
+
),
|
|
113
|
+
os.path.join(
|
|
114
|
+
os.path.expanduser("~"),
|
|
115
|
+
".local",
|
|
116
|
+
"share",
|
|
117
|
+
"mmrelay",
|
|
118
|
+
"tools",
|
|
119
|
+
"mmrelay.service",
|
|
120
|
+
),
|
|
108
121
|
# Check one level up from the package directory
|
|
109
122
|
os.path.join(os.path.dirname(package_dir), "tools", "mmrelay.service"),
|
|
110
123
|
# Check two levels up from the package directory (for development)
|
|
111
|
-
os.path.join(
|
|
124
|
+
os.path.join(
|
|
125
|
+
os.path.dirname(os.path.dirname(package_dir)), "tools", "mmrelay.service"
|
|
126
|
+
),
|
|
112
127
|
# Check in the repository root (for development)
|
|
113
|
-
os.path.join(
|
|
128
|
+
os.path.join(
|
|
129
|
+
os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
|
|
130
|
+
"tools",
|
|
131
|
+
"mmrelay.service",
|
|
132
|
+
),
|
|
114
133
|
# Check in the current directory (fallback)
|
|
115
|
-
os.path.join(os.getcwd(), "tools", "mmrelay.service")
|
|
134
|
+
os.path.join(os.getcwd(), "tools", "mmrelay.service"),
|
|
116
135
|
]
|
|
117
136
|
|
|
118
137
|
# Try each path until we find one that exists
|
|
@@ -136,8 +155,10 @@ def get_template_service_content():
|
|
|
136
155
|
Returns:
|
|
137
156
|
str: The content of the template service file, or a default template if not found.
|
|
138
157
|
"""
|
|
139
|
-
|
|
140
|
-
|
|
158
|
+
# Use the helper function to get the service template path
|
|
159
|
+
template_path = get_service_template_path()
|
|
160
|
+
|
|
161
|
+
if template_path and os.path.exists(template_path):
|
|
141
162
|
# Read the template from file
|
|
142
163
|
try:
|
|
143
164
|
with open(template_path, "r") as f:
|
|
@@ -146,6 +167,28 @@ def get_template_service_content():
|
|
|
146
167
|
except Exception as e:
|
|
147
168
|
print(f"Error reading service template file: {e}")
|
|
148
169
|
|
|
170
|
+
# If the helper function failed, try using importlib.resources directly
|
|
171
|
+
try:
|
|
172
|
+
service_template = (
|
|
173
|
+
importlib.resources.files("mmrelay.tools")
|
|
174
|
+
.joinpath("mmrelay.service")
|
|
175
|
+
.read_text()
|
|
176
|
+
)
|
|
177
|
+
return service_template
|
|
178
|
+
except (FileNotFoundError, ImportError, OSError) as e:
|
|
179
|
+
print(f"Error accessing mmrelay.service via importlib.resources: {e}")
|
|
180
|
+
|
|
181
|
+
# Fall back to the file path method
|
|
182
|
+
template_path = get_template_service_path()
|
|
183
|
+
if template_path:
|
|
184
|
+
# Read the template from file
|
|
185
|
+
try:
|
|
186
|
+
with open(template_path, "r") as f:
|
|
187
|
+
service_template = f.read()
|
|
188
|
+
return service_template
|
|
189
|
+
except Exception as e:
|
|
190
|
+
print(f"Error reading service template file: {e}")
|
|
191
|
+
|
|
149
192
|
# If we couldn't find or read the template file, use a default template
|
|
150
193
|
print("Using default service template")
|
|
151
194
|
return """[Unit]
|
|
@@ -154,7 +197,7 @@ After=network-online.target
|
|
|
154
197
|
Wants=network-online.target
|
|
155
198
|
|
|
156
199
|
[Service]
|
|
157
|
-
Type=
|
|
200
|
+
Type=simple
|
|
158
201
|
# The mmrelay binary can be installed via pipx or pip
|
|
159
202
|
ExecStart=%h/.local/bin/mmrelay --config %h/.mmrelay/config.yaml --logfile %h/.mmrelay/logs/mmrelay.log
|
|
160
203
|
WorkingDirectory=%h/.mmrelay
|
|
@@ -310,6 +353,69 @@ def service_needs_update():
|
|
|
310
353
|
return False, "Service file is up to date"
|
|
311
354
|
|
|
312
355
|
|
|
356
|
+
def check_loginctl_available():
|
|
357
|
+
"""Check if loginctl is available on the system.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
bool: True if loginctl is available, False otherwise.
|
|
361
|
+
"""
|
|
362
|
+
try:
|
|
363
|
+
result = subprocess.run(
|
|
364
|
+
["which", "loginctl"],
|
|
365
|
+
check=False,
|
|
366
|
+
capture_output=True,
|
|
367
|
+
text=True,
|
|
368
|
+
)
|
|
369
|
+
return result.returncode == 0
|
|
370
|
+
except Exception:
|
|
371
|
+
return False
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def check_lingering_enabled():
|
|
375
|
+
"""Check if user lingering is enabled.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
bool: True if lingering is enabled, False otherwise.
|
|
379
|
+
"""
|
|
380
|
+
try:
|
|
381
|
+
username = os.environ.get("USER", os.environ.get("USERNAME"))
|
|
382
|
+
result = subprocess.run(
|
|
383
|
+
["loginctl", "show-user", username, "--property=Linger"],
|
|
384
|
+
check=False,
|
|
385
|
+
capture_output=True,
|
|
386
|
+
text=True,
|
|
387
|
+
)
|
|
388
|
+
return result.returncode == 0 and "Linger=yes" in result.stdout
|
|
389
|
+
except Exception:
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def enable_lingering():
|
|
394
|
+
"""Enable user lingering using sudo.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
bool: True if lingering was enabled successfully, False otherwise.
|
|
398
|
+
"""
|
|
399
|
+
try:
|
|
400
|
+
username = os.environ.get("USER", os.environ.get("USERNAME"))
|
|
401
|
+
print(f"Enabling lingering for user {username}...")
|
|
402
|
+
result = subprocess.run(
|
|
403
|
+
["sudo", "loginctl", "enable-linger", username],
|
|
404
|
+
check=False,
|
|
405
|
+
capture_output=True,
|
|
406
|
+
text=True,
|
|
407
|
+
)
|
|
408
|
+
if result.returncode == 0:
|
|
409
|
+
print("Lingering enabled successfully")
|
|
410
|
+
return True
|
|
411
|
+
else:
|
|
412
|
+
print(f"Error enabling lingering: {result.stderr}")
|
|
413
|
+
return False
|
|
414
|
+
except Exception as e:
|
|
415
|
+
print(f"Error enabling lingering: {e}")
|
|
416
|
+
return False
|
|
417
|
+
|
|
418
|
+
|
|
313
419
|
def install_service():
|
|
314
420
|
"""Install or update the MMRelay user service."""
|
|
315
421
|
# Check if service already exists
|
|
@@ -355,6 +461,27 @@ def install_service():
|
|
|
355
461
|
|
|
356
462
|
# We don't need to validate the config here as it will be validated when the service starts
|
|
357
463
|
|
|
464
|
+
# Check if loginctl is available
|
|
465
|
+
loginctl_available = check_loginctl_available()
|
|
466
|
+
if loginctl_available:
|
|
467
|
+
# Check if user lingering is enabled
|
|
468
|
+
lingering_enabled = check_lingering_enabled()
|
|
469
|
+
if not lingering_enabled:
|
|
470
|
+
print(
|
|
471
|
+
"\nUser lingering is not enabled. This is required for the service to start automatically at boot."
|
|
472
|
+
)
|
|
473
|
+
print(
|
|
474
|
+
"Lingering allows user services to run even when you're not logged in."
|
|
475
|
+
)
|
|
476
|
+
if (
|
|
477
|
+
input(
|
|
478
|
+
"Do you want to enable lingering for your user? (requires sudo) (y/n): "
|
|
479
|
+
)
|
|
480
|
+
.lower()
|
|
481
|
+
.startswith("y")
|
|
482
|
+
):
|
|
483
|
+
enable_lingering()
|
|
484
|
+
|
|
358
485
|
# Check if the service is already enabled
|
|
359
486
|
service_enabled = is_service_enabled()
|
|
360
487
|
if service_enabled:
|
|
@@ -417,6 +544,8 @@ def install_service():
|
|
|
417
544
|
print("\nService Status Summary:")
|
|
418
545
|
print(f" Service File: {service_path}")
|
|
419
546
|
print(f" Enabled at Boot: {'Yes' if service_enabled else 'No'}")
|
|
547
|
+
if loginctl_available:
|
|
548
|
+
print(f" User Lingering: {'Yes' if check_lingering_enabled() else 'No'}")
|
|
420
549
|
print(f" Currently Running: {'Yes' if is_service_active() else 'No'}")
|
|
421
550
|
print("\nService Management Commands:")
|
|
422
551
|
print_service_commands()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Tools and resources for MMRelay."""
|
|
2
|
+
|
|
3
|
+
import importlib.resources
|
|
4
|
+
import pathlib
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_sample_config_path():
|
|
8
|
+
"""Get the path to the sample config file."""
|
|
9
|
+
try:
|
|
10
|
+
# For Python 3.9+
|
|
11
|
+
return str(
|
|
12
|
+
importlib.resources.files("mmrelay.tools").joinpath("sample_config.yaml")
|
|
13
|
+
)
|
|
14
|
+
except AttributeError:
|
|
15
|
+
# Fallback for older Python versions
|
|
16
|
+
return str(pathlib.Path(__file__).parent / "sample_config.yaml")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_service_template_path():
|
|
20
|
+
"""Get the path to the service template file."""
|
|
21
|
+
try:
|
|
22
|
+
# For Python 3.9+
|
|
23
|
+
return str(
|
|
24
|
+
importlib.resources.files("mmrelay.tools").joinpath("mmrelay.service")
|
|
25
|
+
)
|
|
26
|
+
except AttributeError:
|
|
27
|
+
# Fallback for older Python versions
|
|
28
|
+
return str(pathlib.Path(__file__).parent / "mmrelay.service")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=A Meshtastic <=> Matrix Relay
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=simple
|
|
8
|
+
# The mmrelay binary can be installed via pipx or pip
|
|
9
|
+
ExecStart=%h/.local/bin/mmrelay --config %h/.mmrelay/config.yaml --logfile %h/.mmrelay/logs/mmrelay.log
|
|
10
|
+
WorkingDirectory=%h/.mmrelay
|
|
11
|
+
Restart=on-failure
|
|
12
|
+
RestartSec=10
|
|
13
|
+
Environment=PYTHONUNBUFFERED=1
|
|
14
|
+
# Ensure both pipx and pip environments are properly loaded
|
|
15
|
+
Environment=PATH=%h/.local/bin:%h/.local/pipx/venvs/mmrelay/bin:/usr/local/bin:/usr/bin:/bin
|
|
16
|
+
|
|
17
|
+
[Install]
|
|
18
|
+
WantedBy=default.target
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
matrix:
|
|
2
|
+
homeserver: https://example.matrix.org
|
|
3
|
+
access_token: reaalllllyloooooongsecretttttcodeeeeeeforrrrbot # See: https://t2bot.io/docs/access_tokens/
|
|
4
|
+
bot_user_id: "@botuser:example.matrix.org"
|
|
5
|
+
|
|
6
|
+
matrix_rooms: # Needs at least 1 room & channel, but supports all Meshtastic channels
|
|
7
|
+
- id: "#someroomalias:example.matrix.org" # Matrix room aliases & IDs supported
|
|
8
|
+
meshtastic_channel: 0
|
|
9
|
+
- id: "!someroomid:example.matrix.org"
|
|
10
|
+
meshtastic_channel: 2
|
|
11
|
+
|
|
12
|
+
meshtastic:
|
|
13
|
+
connection_type: serial # Choose either "tcp", "serial", or "ble"
|
|
14
|
+
serial_port: /dev/ttyUSB0 # Only used when connection is "serial"
|
|
15
|
+
host: meshtastic.local # Only used when connection is "tcp"
|
|
16
|
+
ble_address: AA:BB:CC:DD:EE:FF # Only used when connection is "ble" - Uses either an address or name from a `meshtastic --ble-scan`
|
|
17
|
+
meshnet_name: Your Meshnet Name # This is displayed in full on Matrix, but is truncated when sent to a Meshnet
|
|
18
|
+
broadcast_enabled: true # Must be set to true to enable Matrix to Meshtastic messages
|
|
19
|
+
detection_sensor: true # Must be set to true to forward messages of Meshtastic's detection sensor module
|
|
20
|
+
plugin_response_delay: 3 # Default response delay in seconds for plugins that respond on the mesh;
|
|
21
|
+
relay_reactions: true # Defaults to false, set to true to enable relay reactions between platforms
|
|
22
|
+
|
|
23
|
+
logging:
|
|
24
|
+
level: info
|
|
25
|
+
#log_to_file: true # Set to true to enable file logging
|
|
26
|
+
#filename: ~/.mmrelay/logs/mmrelay.log # Default location if log_to_file is true
|
|
27
|
+
#max_log_size: 10485760 # 10 MB default if omitted
|
|
28
|
+
#backup_count: 1 # Keeps 1 backup as the default if omitted
|
|
29
|
+
#color_enabled: true # Set to false to disable colored console output
|
|
30
|
+
|
|
31
|
+
#database:
|
|
32
|
+
# path: ~/.mmrelay/data/meshtastic.sqlite # Default location
|
|
33
|
+
# msg_map: # The message map is necessary for the relay_reactions functionality. If `relay_reactions` is set to false, nothing will be saved to the message map.
|
|
34
|
+
# msgs_to_keep: 500 # If set to 0, it will not delete any messages; Defaults to 500
|
|
35
|
+
# wipe_on_restart: true # Clears out the message map when the relay is restarted; Defaults to False
|
|
36
|
+
|
|
37
|
+
# These are core Plugins - Note: Some plugins are experimental and some need maintenance.
|
|
38
|
+
plugins:
|
|
39
|
+
ping:
|
|
40
|
+
active: true
|
|
41
|
+
#channels: [2,3,5] # List of channels the plugin will respond to; DMs are always processed if the plugin is active
|
|
42
|
+
weather:
|
|
43
|
+
active: true
|
|
44
|
+
units: imperial # Options: metric, imperial - Default is metric
|
|
45
|
+
#channels: [] # Empty list, will only respond to DMs
|
|
46
|
+
nodes:
|
|
47
|
+
active: true
|
|
48
|
+
# Does not need to specify channels, as it's a Matrix-only plugin
|
|
49
|
+
|
|
50
|
+
#community-plugins:
|
|
51
|
+
# sample_plugin:
|
|
52
|
+
# active: true
|
|
53
|
+
# repository: https://github.com/username/sample_plugin.git
|
|
54
|
+
# tag: master
|
|
55
|
+
# advanced_plugin:
|
|
56
|
+
# active: false
|
|
57
|
+
# repository: https://github.com/username/advanced_plugin.git
|
|
58
|
+
# tag: v1.2.0
|
|
59
|
+
|
|
60
|
+
#custom-plugins:
|
|
61
|
+
# my_custom_plugin:
|
|
62
|
+
# active: true
|
|
63
|
+
# another_custom_plugin:
|
|
64
|
+
# active: false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mmrelay
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: Bridge between Meshtastic mesh networks and Matrix chat rooms
|
|
5
5
|
Home-page: https://github.com/geoffwhittington/meshtastic-matrix-relay
|
|
6
6
|
Author: Geoff Whittington, Jeremiah K., and contributors
|
|
@@ -22,72 +22,45 @@ Requires-Dist: requests==2.32.3
|
|
|
22
22
|
Requires-Dist: markdown==3.8
|
|
23
23
|
Requires-Dist: haversine==2.9.0
|
|
24
24
|
Requires-Dist: schedule==1.2.2
|
|
25
|
-
Requires-Dist: platformdirs==4.3.
|
|
25
|
+
Requires-Dist: platformdirs==4.3.8
|
|
26
26
|
Requires-Dist: py-staticmaps>=0.4.0
|
|
27
27
|
Requires-Dist: rich==14.0.0
|
|
28
|
-
Requires-Dist: setuptools==80.
|
|
28
|
+
Requires-Dist: setuptools==80.8.0
|
|
29
29
|
Dynamic: license-file
|
|
30
30
|
|
|
31
31
|
# M<>M Relay
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## (Meshtastic <=> Matrix Relay)
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
A powerful and easy-to-use relay between Meshtastic devices and Matrix chat rooms, allowing seamless communication across platforms. This opens the door for bridging Meshtastic devices to [many other platforms](https://matrix.org/bridges/).
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
## Documentation
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
2. Move your configuration to the new standard location (`~/.mmrelay/config.yaml`)
|
|
41
|
-
3. See [ANNOUNCEMENT.md](ANNOUNCEMENT.md) for all the exciting new features
|
|
39
|
+
Visit our [Wiki](https://github.com/geoffwhittington/meshtastic-matrix-relay/wiki) for comprehensive guides and information.
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
A powerful and easy-to-use relay between Meshtastic devices and Matrix chat rooms, allowing seamless communication across platforms. This opens the door for bridging Meshtastic devices to [many other platforms](https://matrix.org/bridges/).
|
|
41
|
+
- [Installation Instructions](docs/INSTRUCTIONS.md) - Setup and configuration guide
|
|
42
|
+
- [v1.0 Release Announcement](docs/ANNOUNCEMENT.md) - New changes in v1.0
|
|
43
|
+
- [Upgrade Guide](docs/UPGRADE_TO_V1.md) - Migration guidance for existing users
|
|
48
44
|
|
|
49
45
|
---
|
|
50
46
|
|
|
51
|
-
##
|
|
47
|
+
## Quick Start
|
|
52
48
|
|
|
53
49
|
MMRelay runs on Linux, macOS, and Windows.
|
|
54
50
|
|
|
55
|
-
### Quick Installation
|
|
56
|
-
|
|
57
51
|
```bash
|
|
58
52
|
# Install using pipx for isolated installation (recommended)
|
|
59
53
|
pipx install mmrelay
|
|
60
54
|
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
For pipx installation instructions, see: [pipx installation guide](https://pipx.pypa.io/stable/installation/#on-linux)
|
|
55
|
+
# Generate a sample configuration file & then edit it
|
|
56
|
+
mmrelay --generate-config
|
|
66
57
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- **New Users**: See [INSTRUCTIONS.md](INSTRUCTIONS.md) for setup and configuration
|
|
70
|
-
- **Existing Users**: See [UPGRADE_TO_V1.md](UPGRADE_TO_V1.md) for migration guidance
|
|
71
|
-
- **Configuration**: Review [sample_config.yaml](sample_config.yaml) for examples
|
|
72
|
-
|
|
73
|
-
### Command-Line Options
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
usage: mmrelay [-h] [--config CONFIG] [--data-dir DATA_DIR] [--log-level {error,warning,info,debug}] [--logfile LOGFILE] [--version] [--generate-config] [--install-service] [--check-config]
|
|
77
|
-
|
|
78
|
-
Options:
|
|
79
|
-
-h, --help Show this help message and exit
|
|
80
|
-
--config CONFIG Path to config file
|
|
81
|
-
--data-dir DATA_DIR Base directory for all data (logs, database, plugins)
|
|
82
|
-
--log-level {error,warning,info,debug}
|
|
83
|
-
Set logging level
|
|
84
|
-
--logfile LOGFILE Path to log file (can be overridden by --data-dir)
|
|
85
|
-
--version Show version and exit
|
|
86
|
-
--generate-config Generate a sample config.yaml file
|
|
87
|
-
--install-service Install or update the systemd user service
|
|
88
|
-
--check-config Check if the configuration file is valid
|
|
58
|
+
# Start the relay (without --install-service to run manually)
|
|
59
|
+
mmrelay --install-service
|
|
89
60
|
```
|
|
90
61
|
|
|
62
|
+
For detailed installation and configuration instructions, see the [Installation Guide](docs/INSTRUCTIONS.md).
|
|
63
|
+
|
|
91
64
|
---
|
|
92
65
|
|
|
93
66
|
## Features
|
|
@@ -133,7 +106,12 @@ See the full list of core plugins [here](https://github.com/geoffwhittington/mes
|
|
|
133
106
|
|
|
134
107
|
### Community & Custom Plugins
|
|
135
108
|
|
|
136
|
-
|
|
109
|
+
MMRelay's plugin system allows you to extend functionality in two ways:
|
|
110
|
+
|
|
111
|
+
- **Custom Plugins**: Create personal plugins for your own use, stored in `~/.mmrelay/plugins/custom/`
|
|
112
|
+
- **Community Plugins**: Share your creations with others or use plugins developed by the community
|
|
113
|
+
|
|
114
|
+
Check the [Community Plugins Development Guide](https://github.com/geoffwhittington/meshtastic-matrix-relay/wiki/Community-Plugin-Development-Guide) in our wiki to get started.
|
|
137
115
|
|
|
138
116
|
✨️ Visit the [Community Plugins List](https://github.com/geoffwhittington/meshtastic-matrix-relay/wiki/Community-Plugin-List)!
|
|
139
117
|
|
|
@@ -151,14 +129,12 @@ community-plugins:
|
|
|
151
129
|
|
|
152
130
|
### Plugin System
|
|
153
131
|
|
|
154
|
-
MMRelay features a powerful plugin system with standardized locations:
|
|
132
|
+
Plugins make it easy to extend functionality without modifying the core program. MMRelay features a powerful plugin system with standardized locations:
|
|
155
133
|
|
|
156
134
|
- **Core Plugins**: Pre-installed with the package
|
|
157
135
|
- **Custom Plugins**: Your own plugins in `~/.mmrelay/plugins/custom/`
|
|
158
136
|
- **Community Plugins**: Third-party plugins in `~/.mmrelay/plugins/community/`
|
|
159
137
|
|
|
160
|
-
Plugins make it easy to extend functionality without modifying the core code.
|
|
161
|
-
|
|
162
138
|
---
|
|
163
139
|
|
|
164
140
|
## Getting Started with Matrix
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
mmrelay/__init__.py,sha256=m8l2zIk6h1Ba54Ru2fsB0Ujsi_4btG1sZ40XjtYK0Mg,588
|
|
2
|
+
mmrelay/cli.py,sha256=hdPTlcGsXTJC9GEUiScG7b3IFp02B3lwhqgwFpU3NsM,13835
|
|
3
|
+
mmrelay/config.py,sha256=5VZag8iSc5yLQgvwI76bbpizbtqag74cHnfXCrWHNyA,7910
|
|
4
|
+
mmrelay/config_checker.py,sha256=UnoHVTXzfdTfFkbmXv9r_Si76v-sxXLb5FOaQSOM45E,4909
|
|
5
|
+
mmrelay/db_utils.py,sha256=DP2YuKBZtV771wo9X-Z7Ww5txfaIR0inWh1K_oVZ7cA,11430
|
|
6
|
+
mmrelay/log_utils.py,sha256=FXhaq4WSDHwlqiG3k1BbSxiOl5be4P4Kyr1gsIThOBw,4572
|
|
7
|
+
mmrelay/main.py,sha256=4Cgv5bLwX9JLMAYmY4TePRY8XDMEKcj3ghYicBwednA,11301
|
|
8
|
+
mmrelay/matrix_utils.py,sha256=7rjjHtm8JNXi2fgZ33L3VRvxomA9gQw3dJD6mqZi_lY,32770
|
|
9
|
+
mmrelay/meshtastic_utils.py,sha256=Pd0j7mz008ncBDIjRGyHOIb0U3vKq06uXWoJP-csHcQ,23381
|
|
10
|
+
mmrelay/plugin_loader.py,sha256=Gg8E_TKqndMQHiVJwtmlIC-jCgcty-5EsX_5JJ6Onvo,36590
|
|
11
|
+
mmrelay/setup_utils.py,sha256=N6qdScHKHEMFKDmT1l7dcLDPNTusZXPkyxrLXjFLhRI,19910
|
|
12
|
+
mmrelay/plugins/__init__.py,sha256=KVMQIXRhe0wlGj4O3IZ0vOIQRKFkfPYejHXhJL17qrc,51
|
|
13
|
+
mmrelay/plugins/base_plugin.py,sha256=AlC7Y0jMh42jouXpJNtpvo4vMbS0KedOUFuJvWmZggY,8962
|
|
14
|
+
mmrelay/plugins/debug_plugin.py,sha256=Jziht9Nj_bRO6Rmy7TjfBXaYo5eM3XsenbWFxPpyUs4,443
|
|
15
|
+
mmrelay/plugins/drop_plugin.py,sha256=0GOz0dgLnFST1HTgqrMayrjwmYlnu02QxfnTZtdPZ3U,4671
|
|
16
|
+
mmrelay/plugins/health_plugin.py,sha256=svV_GfpAVL0QhiVzi3PVZ1mNpsOL1NHSmkRF-Mn_ExE,2250
|
|
17
|
+
mmrelay/plugins/help_plugin.py,sha256=GP7i1iWNiJxb_Pbuod0ctveIQYL1FNN8ctUvy24yRNM,1506
|
|
18
|
+
mmrelay/plugins/map_plugin.py,sha256=xqQvjuY-VVuuhVQyDJHnj9uZKYaXV0qSaBTbVwfVOfo,10078
|
|
19
|
+
mmrelay/plugins/mesh_relay_plugin.py,sha256=E04JU4uKr7KTScahS0VKBJ1xwvq1ULJaeFVYEtceMvA,4273
|
|
20
|
+
mmrelay/plugins/nodes_plugin.py,sha256=RDabzyG5hKG5aYWecsRUcLSjMCCv6Pngmq2Qpld1A1U,2903
|
|
21
|
+
mmrelay/plugins/ping_plugin.py,sha256=8uFnT3qfO3RBaTUOx348voIfKpzXB3zTfcT6Gtfc8kM,4070
|
|
22
|
+
mmrelay/plugins/telemetry_plugin.py,sha256=8SxWv4BLXMUTbiVaD3MjlMMdQyS7S_1OfLlVNAUMSO0,6306
|
|
23
|
+
mmrelay/plugins/weather_plugin.py,sha256=1bQhmiX-enNphzGoFVprU0LcZQX9BvGxWAJAG8Wekg0,8596
|
|
24
|
+
mmrelay/tools/__init__.py,sha256=WFjDQjdevgg19_zT6iEoL29rvb1JPqYSd8708Jn5D7A,838
|
|
25
|
+
mmrelay/tools/mmrelay.service,sha256=3vqK1VbfXvVftkTrTEOan77aTHeOT36hIAL7HqJsmTg,567
|
|
26
|
+
mmrelay/tools/sample_config.yaml,sha256=iGOoVxELUEJwfmpFxyeWXXS0RWNsErnf8-6LXyIwH2k,3052
|
|
27
|
+
mmrelay-1.0.8.dist-info/licenses/LICENSE,sha256=yceWauM1c0-FHxVplsD7W1-AbSeRaUNlmqT4UO1msBU,1073
|
|
28
|
+
mmrelay-1.0.8.dist-info/METADATA,sha256=orw_9ScvoB4pOpJMkQ5WJvfLEZphl7N5OD0oRHpyIC8,5916
|
|
29
|
+
mmrelay-1.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
mmrelay-1.0.8.dist-info/entry_points.txt,sha256=SJZwGUOEpQ-qx4H8UL4xKFnKeInGUaZNW1I0ddjK7Ws,45
|
|
31
|
+
mmrelay-1.0.8.dist-info/top_level.txt,sha256=B_ZLCRm7NYAmI3PipRUyHGymP-C-q16LSeMGzmqJfo4,8
|
|
32
|
+
mmrelay-1.0.8.dist-info/RECORD,,
|
mmrelay-1.0.6.dist-info/RECORD
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
mmrelay/__init__.py,sha256=8fghAZAeSUb8uIaiit0FGNUWI0ZsBR__QbXNwqNbGtw,588
|
|
2
|
-
mmrelay/cli.py,sha256=yBYOkCwGYDJ2gRKPoPltxB_PydX1CPNUerVJHcNuHTo,12355
|
|
3
|
-
mmrelay/config.py,sha256=5VZag8iSc5yLQgvwI76bbpizbtqag74cHnfXCrWHNyA,7910
|
|
4
|
-
mmrelay/config_checker.py,sha256=UnoHVTXzfdTfFkbmXv9r_Si76v-sxXLb5FOaQSOM45E,4909
|
|
5
|
-
mmrelay/db_utils.py,sha256=DP2YuKBZtV771wo9X-Z7Ww5txfaIR0inWh1K_oVZ7cA,11430
|
|
6
|
-
mmrelay/log_utils.py,sha256=FXhaq4WSDHwlqiG3k1BbSxiOl5be4P4Kyr1gsIThOBw,4572
|
|
7
|
-
mmrelay/main.py,sha256=acgBF-DnL0Rs8MmYAfbNHZ9xjqM3Qc0_oKtATN4iOEE,11085
|
|
8
|
-
mmrelay/matrix_utils.py,sha256=GkIVj2bbPHtx1emFMwhEhc1SWHcv4UvkuyZYdb-Wnwo,30511
|
|
9
|
-
mmrelay/meshtastic_utils.py,sha256=Pd0j7mz008ncBDIjRGyHOIb0U3vKq06uXWoJP-csHcQ,23381
|
|
10
|
-
mmrelay/plugin_loader.py,sha256=Gg8E_TKqndMQHiVJwtmlIC-jCgcty-5EsX_5JJ6Onvo,36590
|
|
11
|
-
mmrelay/setup_utils.py,sha256=UZLX944GyiUQPUGNGNDwLocgShA8SzOJtvfntEu821A,16060
|
|
12
|
-
mmrelay/plugins/__init__.py,sha256=KVMQIXRhe0wlGj4O3IZ0vOIQRKFkfPYejHXhJL17qrc,51
|
|
13
|
-
mmrelay/plugins/base_plugin.py,sha256=hv21tSEYG-AB36aLAFdW9DDKm0NOTRNPpGIO5F3i1ts,8633
|
|
14
|
-
mmrelay/plugins/debug_plugin.py,sha256=Jziht9Nj_bRO6Rmy7TjfBXaYo5eM3XsenbWFxPpyUs4,443
|
|
15
|
-
mmrelay/plugins/drop_plugin.py,sha256=ACchX6GfEldqTyvsZVg-5jwbe2-CjENQVVZFnw8SaEM,4618
|
|
16
|
-
mmrelay/plugins/health_plugin.py,sha256=svV_GfpAVL0QhiVzi3PVZ1mNpsOL1NHSmkRF-Mn_ExE,2250
|
|
17
|
-
mmrelay/plugins/help_plugin.py,sha256=-C4M1PPi1m-fPRvNAlcMyHDNiEEIF6013z2vzrepa1Q,1453
|
|
18
|
-
mmrelay/plugins/map_plugin.py,sha256=tWzNvErSoIjqpWtdR-uhbnqdX1SQZl1XQn3Qrp195Kk,10024
|
|
19
|
-
mmrelay/plugins/mesh_relay_plugin.py,sha256=E04JU4uKr7KTScahS0VKBJ1xwvq1ULJaeFVYEtceMvA,4273
|
|
20
|
-
mmrelay/plugins/nodes_plugin.py,sha256=RDabzyG5hKG5aYWecsRUcLSjMCCv6Pngmq2Qpld1A1U,2903
|
|
21
|
-
mmrelay/plugins/ping_plugin.py,sha256=RTRdgDQUSO33lreDTmWsTlI0L1C3FJrXE0KYqfEWYO0,4017
|
|
22
|
-
mmrelay/plugins/telemetry_plugin.py,sha256=8SxWv4BLXMUTbiVaD3MjlMMdQyS7S_1OfLlVNAUMSO0,6306
|
|
23
|
-
mmrelay/plugins/weather_plugin.py,sha256=yoKA_HdFqFEhgYdXqLhvXatLphCyLJFuGUKCR7fILv0,8546
|
|
24
|
-
mmrelay-1.0.6.dist-info/licenses/LICENSE,sha256=yceWauM1c0-FHxVplsD7W1-AbSeRaUNlmqT4UO1msBU,1073
|
|
25
|
-
mmrelay-1.0.6.dist-info/METADATA,sha256=V_JcF794O_pNB39Bm3K1TxVpB3C5f1lWYGQu4s3j3Dc,6987
|
|
26
|
-
mmrelay-1.0.6.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
|
27
|
-
mmrelay-1.0.6.dist-info/entry_points.txt,sha256=SJZwGUOEpQ-qx4H8UL4xKFnKeInGUaZNW1I0ddjK7Ws,45
|
|
28
|
-
mmrelay-1.0.6.dist-info/top_level.txt,sha256=B_ZLCRm7NYAmI3PipRUyHGymP-C-q16LSeMGzmqJfo4,8
|
|
29
|
-
mmrelay-1.0.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|