mmrelay 1.0.4__py3-none-any.whl → 1.0.6__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/plugin_loader.py CHANGED
@@ -56,16 +56,23 @@ def get_community_plugin_dirs():
56
56
  return dirs
57
57
 
58
58
 
59
- def clone_or_update_repo(repo_url, tag, plugins_dir):
59
+ def clone_or_update_repo(repo_url, ref, plugins_dir):
60
60
  # Extract the repository name from the URL
61
61
  repo_name = os.path.splitext(os.path.basename(repo_url.rstrip("/")))[0]
62
62
  repo_path = os.path.join(plugins_dir, repo_name)
63
63
 
64
- # Default branch names to try if tag is not specified
64
+ # Default branch names to try if ref is not specified
65
65
  default_branches = ["main", "master"]
66
66
 
67
- # If tag is one of the default branches, we'll handle it as a branch
68
- is_default_branch = tag in default_branches
67
+ # Get the ref type and value
68
+ ref_type = ref["type"] # "tag" or "branch"
69
+ ref_value = ref["value"]
70
+
71
+ # Log what we're trying to do
72
+ logger.info(f"Using {ref_type} '{ref_value}' for repository {repo_name}")
73
+
74
+ # If it's a branch and one of the default branches, we'll handle it specially
75
+ is_default_branch = ref_type == "branch" and ref_value in default_branches
69
76
 
70
77
  if os.path.isdir(repo_path):
71
78
  try:
@@ -85,32 +92,39 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
85
92
  universal_newlines=True,
86
93
  ).strip()
87
94
 
88
- if current_branch == tag:
95
+ if current_branch == ref_value:
89
96
  # We're on the right branch, just pull
90
97
  try:
91
98
  subprocess.check_call(
92
- ["git", "-C", repo_path, "pull", "origin", tag]
99
+ ["git", "-C", repo_path, "pull", "origin", ref_value]
100
+ )
101
+ logger.info(
102
+ f"Updated repository {repo_name} branch {ref_value}"
93
103
  )
94
- logger.info(f"Updated repository {repo_name} branch {tag}")
95
104
  return True
96
105
  except subprocess.CalledProcessError as e:
97
- logger.warning(f"Error pulling branch {tag}: {e}")
106
+ logger.warning(f"Error pulling branch {ref_value}: {e}")
98
107
  # Continue anyway, we'll use what we have
99
108
  return True
100
109
  else:
101
110
  # Switch to the right branch
102
- subprocess.check_call(["git", "-C", repo_path, "checkout", tag])
103
111
  subprocess.check_call(
104
- ["git", "-C", repo_path, "pull", "origin", tag]
112
+ ["git", "-C", repo_path, "checkout", ref_value]
113
+ )
114
+ subprocess.check_call(
115
+ ["git", "-C", repo_path, "pull", "origin", ref_value]
105
116
  )
106
- logger.info(f"Switched to and updated branch {tag}")
117
+ if ref_type == "branch":
118
+ logger.info(f"Switched to and updated branch {ref_value}")
119
+ else:
120
+ logger.info(f"Switched to and updated tag {ref_value}")
107
121
  return True
108
122
  except subprocess.CalledProcessError:
109
123
  # If we can't checkout the specified branch, try the other default branch
110
- other_default = "main" if tag == "master" else "master"
124
+ other_default = "main" if ref_value == "master" else "master"
111
125
  try:
112
126
  logger.warning(
113
- f"Branch {tag} not found, trying {other_default}"
127
+ f"Branch {ref_value} not found, trying {other_default}"
114
128
  )
115
129
  subprocess.check_call(
116
130
  ["git", "-C", repo_path, "checkout", other_default]
@@ -118,7 +132,9 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
118
132
  subprocess.check_call(
119
133
  ["git", "-C", repo_path, "pull", "origin", other_default]
120
134
  )
121
- logger.info(f"Using {other_default} branch instead of {tag}")
135
+ logger.info(
136
+ f"Using {other_default} branch instead of {ref_value}"
137
+ )
122
138
  return True
123
139
  except subprocess.CalledProcessError:
124
140
  # If that fails too, just use whatever branch we're on
@@ -140,7 +156,7 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
140
156
  tag_commit = None
141
157
  try:
142
158
  tag_commit = subprocess.check_output(
143
- ["git", "-C", repo_path, "rev-parse", tag],
159
+ ["git", "-C", repo_path, "rev-parse", ref_value],
144
160
  universal_newlines=True,
145
161
  ).strip()
146
162
  except subprocess.CalledProcessError:
@@ -149,24 +165,35 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
149
165
 
150
166
  # If we're already at the tag's commit, we're done
151
167
  if tag_commit and current_commit == tag_commit:
152
- logger.info(f"Repository {repo_name} is already at tag {tag}")
168
+ logger.info(
169
+ f"Repository {repo_name} is already at tag {ref_value}"
170
+ )
153
171
  return True
154
172
 
155
173
  # Otherwise, try to checkout the tag
156
- subprocess.check_call(["git", "-C", repo_path, "checkout", tag])
157
- logger.info(f"Updated repository {repo_name} to tag {tag}")
174
+ subprocess.check_call(
175
+ ["git", "-C", repo_path, "checkout", ref_value]
176
+ )
177
+ if ref_type == "branch":
178
+ logger.info(
179
+ f"Updated repository {repo_name} to branch {ref_value}"
180
+ )
181
+ else:
182
+ logger.info(
183
+ f"Updated repository {repo_name} to tag {ref_value}"
184
+ )
158
185
  return True
159
186
  except subprocess.CalledProcessError:
160
187
  # If tag checkout fails, try to fetch it specifically
161
188
  logger.warning(
162
- f"Tag {tag} not found locally, trying to fetch it specifically"
189
+ f"Tag {ref_value} not found locally, trying to fetch it specifically"
163
190
  )
164
191
  try:
165
192
  # Try to fetch the specific tag, but first remove any existing tag with the same name
166
193
  try:
167
194
  # Delete the local tag if it exists to avoid conflicts
168
195
  subprocess.check_call(
169
- ["git", "-C", repo_path, "tag", "-d", tag]
196
+ ["git", "-C", repo_path, "tag", "-d", ref_value]
170
197
  )
171
198
  except subprocess.CalledProcessError:
172
199
  # Tag doesn't exist locally, which is fine
@@ -182,7 +209,7 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
182
209
  repo_path,
183
210
  "fetch",
184
211
  "origin",
185
- f"refs/tags/{tag}",
212
+ f"refs/tags/{ref_value}",
186
213
  ]
187
214
  )
188
215
  except subprocess.CalledProcessError:
@@ -194,34 +221,45 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
194
221
  repo_path,
195
222
  "fetch",
196
223
  "origin",
197
- f"refs/tags/{tag}:refs/tags/{tag}",
224
+ f"refs/tags/{ref_value}:refs/tags/{ref_value}",
198
225
  ]
199
226
  )
200
227
 
201
- subprocess.check_call(["git", "-C", repo_path, "checkout", tag])
202
- logger.info(f"Successfully fetched and checked out tag {tag}")
228
+ subprocess.check_call(
229
+ ["git", "-C", repo_path, "checkout", ref_value]
230
+ )
231
+ if ref_type == "branch":
232
+ logger.info(
233
+ f"Successfully fetched and checked out branch {ref_value}"
234
+ )
235
+ else:
236
+ logger.info(
237
+ f"Successfully fetched and checked out tag {ref_value}"
238
+ )
203
239
  return True
204
240
  except subprocess.CalledProcessError:
205
241
  # If that fails too, try as a branch
206
- logger.warning(f"Could not fetch tag {tag}, trying as a branch")
242
+ logger.warning(
243
+ f"Could not fetch tag {ref_value}, trying as a branch"
244
+ )
207
245
  try:
208
246
  subprocess.check_call(
209
- ["git", "-C", repo_path, "fetch", "origin", tag]
247
+ ["git", "-C", repo_path, "fetch", "origin", ref_value]
210
248
  )
211
249
  subprocess.check_call(
212
- ["git", "-C", repo_path, "checkout", tag]
250
+ ["git", "-C", repo_path, "checkout", ref_value]
213
251
  )
214
252
  subprocess.check_call(
215
- ["git", "-C", repo_path, "pull", "origin", tag]
253
+ ["git", "-C", repo_path, "pull", "origin", ref_value]
216
254
  )
217
255
  logger.info(
218
- f"Updated repository {repo_name} to branch {tag}"
256
+ f"Updated repository {repo_name} to branch {ref_value}"
219
257
  )
220
258
  return True
221
259
  except subprocess.CalledProcessError:
222
260
  # If all else fails, just use a default branch
223
261
  logger.warning(
224
- f"Could not checkout {tag} as tag or branch, trying default branches"
262
+ f"Could not checkout {ref_value} as tag or branch, trying default branches"
225
263
  )
226
264
  for default_branch in default_branches:
227
265
  try:
@@ -245,7 +283,7 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
245
283
  ]
246
284
  )
247
285
  logger.info(
248
- f"Using {default_branch} instead of {tag}"
286
+ f"Using {default_branch} instead of {ref_value}"
249
287
  )
250
288
  return True
251
289
  except subprocess.CalledProcessError:
@@ -272,18 +310,24 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
272
310
  try:
273
311
  # Try to clone with the specified branch
274
312
  subprocess.check_call(
275
- ["git", "clone", "--branch", tag, repo_url], cwd=plugins_dir
276
- )
277
- logger.info(
278
- f"Cloned repository {repo_name} from {repo_url} at branch {tag}"
313
+ ["git", "clone", "--branch", ref_value, repo_url],
314
+ cwd=plugins_dir,
279
315
  )
316
+ if ref_type == "branch":
317
+ logger.info(
318
+ f"Cloned repository {repo_name} from {repo_url} at branch {ref_value}"
319
+ )
320
+ else:
321
+ logger.info(
322
+ f"Cloned repository {repo_name} from {repo_url} at tag {ref_value}"
323
+ )
280
324
  return True
281
325
  except subprocess.CalledProcessError:
282
326
  # If that fails, try the other default branch
283
- other_default = "main" if tag == "master" else "master"
327
+ other_default = "main" if ref_value == "master" else "master"
284
328
  try:
285
329
  logger.warning(
286
- f"Could not clone with branch {tag}, trying {other_default}"
330
+ f"Could not clone with branch {ref_value}, trying {other_default}"
287
331
  )
288
332
  subprocess.check_call(
289
333
  ["git", "clone", "--branch", other_default, repo_url],
@@ -310,16 +354,22 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
310
354
  try:
311
355
  # Try to clone with the specified tag
312
356
  subprocess.check_call(
313
- ["git", "clone", "--branch", tag, repo_url], cwd=plugins_dir
314
- )
315
- logger.info(
316
- f"Cloned repository {repo_name} from {repo_url} at tag {tag}"
357
+ ["git", "clone", "--branch", ref_value, repo_url],
358
+ cwd=plugins_dir,
317
359
  )
360
+ if ref_type == "branch":
361
+ logger.info(
362
+ f"Cloned repository {repo_name} from {repo_url} at branch {ref_value}"
363
+ )
364
+ else:
365
+ logger.info(
366
+ f"Cloned repository {repo_name} from {repo_url} at tag {ref_value}"
367
+ )
318
368
  return True
319
369
  except subprocess.CalledProcessError:
320
370
  # If that fails, clone without specifying a tag
321
371
  logger.warning(
322
- f"Could not clone with tag {tag}, cloning default branch"
372
+ f"Could not clone with tag {ref_value}, cloning default branch"
323
373
  )
324
374
  subprocess.check_call(["git", "clone", repo_url], cwd=plugins_dir)
325
375
 
@@ -334,7 +384,7 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
334
384
  repo_path,
335
385
  "fetch",
336
386
  "origin",
337
- f"refs/tags/{tag}",
387
+ f"refs/tags/{ref_value}",
338
388
  ]
339
389
  )
340
390
  except subprocess.CalledProcessError:
@@ -346,35 +396,42 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
346
396
  repo_path,
347
397
  "fetch",
348
398
  "origin",
349
- f"refs/tags/{tag}:refs/tags/{tag}",
399
+ f"refs/tags/{ref_value}:refs/tags/{ref_value}",
350
400
  ]
351
401
  )
352
402
 
353
403
  # Now checkout the tag
354
- subprocess.check_call(["git", "-C", repo_path, "checkout", tag])
355
- logger.info(
356
- f"Cloned repository {repo_name} and checked out tag {tag}"
404
+ subprocess.check_call(
405
+ ["git", "-C", repo_path, "checkout", ref_value]
357
406
  )
407
+ if ref_type == "branch":
408
+ logger.info(
409
+ f"Cloned repository {repo_name} and checked out branch {ref_value}"
410
+ )
411
+ else:
412
+ logger.info(
413
+ f"Cloned repository {repo_name} and checked out tag {ref_value}"
414
+ )
358
415
  return True
359
416
  except subprocess.CalledProcessError:
360
417
  # If that fails, try as a branch
361
418
  try:
362
419
  logger.warning(
363
- f"Could not checkout {tag} as a tag, trying as a branch"
420
+ f"Could not checkout {ref_value} as a tag, trying as a branch"
364
421
  )
365
422
  subprocess.check_call(
366
- ["git", "-C", repo_path, "fetch", "origin", tag]
423
+ ["git", "-C", repo_path, "fetch", "origin", ref_value]
367
424
  )
368
425
  subprocess.check_call(
369
- ["git", "-C", repo_path, "checkout", tag]
426
+ ["git", "-C", repo_path, "checkout", ref_value]
370
427
  )
371
428
  logger.info(
372
- f"Cloned repository {repo_name} and checked out branch {tag}"
429
+ f"Cloned repository {repo_name} and checked out branch {ref_value}"
373
430
  )
374
431
  return True
375
432
  except subprocess.CalledProcessError:
376
433
  logger.warning(
377
- f"Could not checkout {tag}, using default branch"
434
+ f"Could not checkout {ref_value}, using default branch"
378
435
  )
379
436
  logger.info(
380
437
  f"Cloned repository {repo_name} from {repo_url} (default branch)"
@@ -390,17 +447,53 @@ def clone_or_update_repo(repo_url, tag, plugins_dir):
390
447
  requirements_path = os.path.join(repo_path, "requirements.txt")
391
448
  if os.path.isfile(requirements_path):
392
449
  try:
393
- # Use pip to install the requirements.txt
394
- subprocess.check_call(
395
- [sys.executable, "-m", "pip", "install", "-r", requirements_path]
396
- )
397
- logger.info(f"Installed requirements for plugin {repo_name}")
450
+ # Check if we're running in a pipx environment
451
+ in_pipx = "PIPX_HOME" in os.environ or "PIPX_LOCAL_VENVS" in os.environ
452
+
453
+ # Read requirements from file
454
+ with open(requirements_path, "r") as f:
455
+ requirements = [
456
+ line.strip()
457
+ for line in f
458
+ if line.strip() and not line.startswith("#")
459
+ ]
460
+
461
+ if requirements:
462
+ if in_pipx:
463
+ # Use pipx inject for each requirement
464
+ logger.info(
465
+ f"Installing requirements for plugin {repo_name} with pipx inject"
466
+ )
467
+ for req in requirements:
468
+ logger.info(f"Installing {req}")
469
+ subprocess.check_call(["pipx", "inject", "mmrelay", req])
470
+ else:
471
+ # Use pip to install the requirements.txt
472
+ logger.info(
473
+ f"Installing requirements for plugin {repo_name} with pip"
474
+ )
475
+ subprocess.check_call(
476
+ [
477
+ sys.executable,
478
+ "-m",
479
+ "pip",
480
+ "install",
481
+ "-r",
482
+ requirements_path,
483
+ ]
484
+ )
485
+ logger.info(
486
+ f"Successfully installed requirements for plugin {repo_name}"
487
+ )
398
488
  except subprocess.CalledProcessError as e:
399
489
  logger.error(f"Error installing requirements for plugin {repo_name}: {e}")
400
490
  logger.error(
401
491
  f"Please manually install the requirements from {requirements_path}"
402
492
  )
403
- sys.exit(1)
493
+ # Don't exit, just continue with a warning
494
+ logger.warning(
495
+ f"Plugin {repo_name} may not work correctly without its dependencies"
496
+ )
404
497
 
405
498
 
406
499
  def load_plugins_from_directory(directory, recursive=False):
@@ -418,14 +511,101 @@ def load_plugins_from_directory(directory, recursive=False):
418
511
  module_name, plugin_path
419
512
  )
420
513
  plugin_module = importlib.util.module_from_spec(spec)
514
+
515
+ # Create a compatibility layer for plugins
516
+ # This allows plugins to import from 'plugins' or 'mmrelay.plugins'
517
+ if "mmrelay.plugins" not in sys.modules:
518
+ import mmrelay.plugins
519
+
520
+ sys.modules["mmrelay.plugins"] = mmrelay.plugins
521
+
522
+ # For backward compatibility with older plugins
523
+ if "plugins" not in sys.modules:
524
+ import mmrelay.plugins
525
+
526
+ sys.modules["plugins"] = mmrelay.plugins
527
+
421
528
  try:
529
+ # Add the plugin's directory to sys.path temporarily
530
+ plugin_dir = os.path.dirname(plugin_path)
531
+ sys.path.insert(0, plugin_dir)
532
+
533
+ # Execute the module
422
534
  spec.loader.exec_module(plugin_module)
535
+
536
+ # Remove the plugin directory from sys.path
537
+ if plugin_dir in sys.path:
538
+ sys.path.remove(plugin_dir)
539
+
423
540
  if hasattr(plugin_module, "Plugin"):
424
541
  plugins.append(plugin_module.Plugin())
425
542
  else:
426
543
  logger.warning(
427
544
  f"{plugin_path} does not define a Plugin class."
428
545
  )
546
+ except ModuleNotFoundError as e:
547
+ missing_module = str(e).split()[-1].strip("'")
548
+ logger.warning(
549
+ f"Missing dependency for plugin {plugin_path}: {missing_module}"
550
+ )
551
+
552
+ # Try to automatically install the missing dependency
553
+ try:
554
+ # Check if we're running in a pipx environment
555
+ in_pipx = (
556
+ "PIPX_HOME" in os.environ
557
+ or "PIPX_LOCAL_VENVS" in os.environ
558
+ )
559
+
560
+ if in_pipx:
561
+ logger.info(
562
+ f"Attempting to install missing dependency with pipx inject: {missing_module}"
563
+ )
564
+ subprocess.check_call(
565
+ ["pipx", "inject", "mmrelay", missing_module]
566
+ )
567
+ else:
568
+ logger.info(
569
+ f"Attempting to install missing dependency with pip: {missing_module}"
570
+ )
571
+ subprocess.check_call(
572
+ [
573
+ sys.executable,
574
+ "-m",
575
+ "pip",
576
+ "install",
577
+ missing_module,
578
+ ]
579
+ )
580
+
581
+ logger.info(
582
+ f"Successfully installed {missing_module}, retrying plugin load"
583
+ )
584
+
585
+ # Try to load the module again
586
+ spec.loader.exec_module(plugin_module)
587
+
588
+ if hasattr(plugin_module, "Plugin"):
589
+ plugins.append(plugin_module.Plugin())
590
+ else:
591
+ logger.warning(
592
+ f"{plugin_path} does not define a Plugin class."
593
+ )
594
+
595
+ except subprocess.CalledProcessError:
596
+ logger.error(
597
+ f"Failed to automatically install {missing_module}"
598
+ )
599
+ logger.error("Please install it manually:")
600
+ logger.error(
601
+ f"pipx inject mmrelay {missing_module} # if using pipx"
602
+ )
603
+ logger.error(
604
+ f"pip install {missing_module} # if using pip"
605
+ )
606
+ logger.error(
607
+ f"Plugin directory: {os.path.dirname(plugin_path)}"
608
+ )
429
609
  except Exception as e:
430
610
  logger.error(f"Error loading plugin {plugin_path}: {e}")
431
611
  if not recursive:
@@ -524,7 +704,7 @@ def load_plugins(passed_config=None):
524
704
 
525
705
  # Get the first directory for cloning (prefer user directory)
526
706
  community_plugins_dir = community_plugin_dirs[
527
- -1
707
+ 0
528
708
  ] # Use the user directory for new clones
529
709
 
530
710
  # Create community plugins directory if needed
@@ -553,10 +733,28 @@ def load_plugins(passed_config=None):
553
733
  continue
554
734
 
555
735
  repo_url = plugin_info.get("repository")
556
- tag = plugin_info.get("tag", "master")
736
+
737
+ # Support both tag and branch parameters
738
+ tag = plugin_info.get("tag")
739
+ branch = plugin_info.get("branch")
740
+
741
+ # Determine what to use (tag, branch, or default)
742
+ if tag and branch:
743
+ logger.warning(
744
+ f"Both tag and branch specified for plugin {plugin_name}, using tag"
745
+ )
746
+ ref = {"type": "tag", "value": tag}
747
+ elif tag:
748
+ ref = {"type": "tag", "value": tag}
749
+ elif branch:
750
+ ref = {"type": "branch", "value": branch}
751
+ else:
752
+ # Default to main branch if neither is specified
753
+ ref = {"type": "branch", "value": "main"}
754
+
557
755
  if repo_url:
558
756
  # Clone to the user directory by default
559
- success = clone_or_update_repo(repo_url, tag, community_plugins_dir)
757
+ success = clone_or_update_repo(repo_url, ref, community_plugins_dir)
560
758
  if not success:
561
759
  logger.warning(
562
760
  f"Failed to clone/update plugin {plugin_name}, skipping"
@@ -580,7 +778,7 @@ def load_plugins(passed_config=None):
580
778
  for dir_path in community_plugin_dirs:
581
779
  plugin_path = os.path.join(dir_path, repo_name)
582
780
  if os.path.exists(plugin_path):
583
- logger.debug(f"Loading community plugin from: {plugin_path}")
781
+ logger.info(f"Loading community plugin from: {plugin_path}")
584
782
  plugins.extend(
585
783
  load_plugins_from_directory(plugin_path, recursive=True)
586
784
  )
mmrelay/setup_utils.py CHANGED
@@ -91,42 +91,82 @@ def get_template_service_path():
91
91
  str: The path to the template service file, or None if not found.
92
92
  """
93
93
  # Try to find the service template file
94
- # First, check in the package directory (where it should be after installation)
95
94
  package_dir = os.path.dirname(__file__)
96
- template_path = os.path.join(
97
- os.path.dirname(os.path.dirname(package_dir)), "tools", "mmrelay.service"
98
- )
99
-
100
- # If not found, try the repository root (for development)
101
- if not os.path.exists(template_path):
102
- repo_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
103
- template_path = os.path.join(repo_root, "tools", "mmrelay.service")
104
-
105
- # If still not found, try the current directory (fallback)
106
- if not os.path.exists(template_path):
107
- template_path = os.path.join(os.getcwd(), "tools", "mmrelay.service")
108
95
 
109
- if not os.path.exists(template_path):
110
- return None
111
-
112
- return template_path
96
+ # Try to find the service template file in various locations
97
+ template_paths = [
98
+ # Check in the package directory (where it should be after installation)
99
+ os.path.join(package_dir, "mmrelay.service"),
100
+ # Check in a tools subdirectory of the package
101
+ os.path.join(package_dir, "tools", "mmrelay.service"),
102
+ # Check in the data files location (where it should be after installation)
103
+ os.path.join(sys.prefix, "share", "mmrelay", "mmrelay.service"),
104
+ os.path.join(sys.prefix, "share", "mmrelay", "tools", "mmrelay.service"),
105
+ # Check in the user site-packages location
106
+ os.path.join(os.path.expanduser("~"), ".local", "share", "mmrelay", "mmrelay.service"),
107
+ os.path.join(os.path.expanduser("~"), ".local", "share", "mmrelay", "tools", "mmrelay.service"),
108
+ # Check one level up from the package directory
109
+ os.path.join(os.path.dirname(package_dir), "tools", "mmrelay.service"),
110
+ # Check two levels up from the package directory (for development)
111
+ os.path.join(os.path.dirname(os.path.dirname(package_dir)), "tools", "mmrelay.service"),
112
+ # Check in the repository root (for development)
113
+ os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "tools", "mmrelay.service"),
114
+ # Check in the current directory (fallback)
115
+ os.path.join(os.getcwd(), "tools", "mmrelay.service")
116
+ ]
117
+
118
+ # Try each path until we find one that exists
119
+ for path in template_paths:
120
+ if os.path.exists(path):
121
+ return path
122
+
123
+ # If we get here, we couldn't find the template
124
+ # Debug output to help diagnose issues
125
+ print("Debug: Could not find mmrelay.service in any of these locations:")
126
+ for path in template_paths:
127
+ print(f" - {path}")
128
+
129
+ # If we get here, we couldn't find the template
130
+ return None
113
131
 
114
132
 
115
133
  def get_template_service_content():
116
134
  """Get the content of the template service file.
117
135
 
118
136
  Returns:
119
- str: The content of the template service file, or None if not found.
137
+ str: The content of the template service file, or a default template if not found.
120
138
  """
121
139
  template_path = get_template_service_path()
122
- if not template_path:
123
- return None
124
-
125
- # Read the template
126
- with open(template_path, "r") as f:
127
- service_template = f.read()
128
-
129
- return service_template
140
+ if template_path:
141
+ # Read the template from file
142
+ try:
143
+ with open(template_path, "r") as f:
144
+ service_template = f.read()
145
+ return service_template
146
+ except Exception as e:
147
+ print(f"Error reading service template file: {e}")
148
+
149
+ # If we couldn't find or read the template file, use a default template
150
+ print("Using default service template")
151
+ return """[Unit]
152
+ Description=A Meshtastic <=> Matrix Relay
153
+ After=network-online.target
154
+ Wants=network-online.target
155
+
156
+ [Service]
157
+ Type=idle
158
+ # The mmrelay binary can be installed via pipx or pip
159
+ ExecStart=%h/.local/bin/mmrelay --config %h/.mmrelay/config.yaml --logfile %h/.mmrelay/logs/mmrelay.log
160
+ WorkingDirectory=%h/.mmrelay
161
+ Restart=on-failure
162
+ RestartSec=10
163
+ Environment=PYTHONUNBUFFERED=1
164
+ # Ensure both pipx and pip environments are properly loaded
165
+ Environment=PATH=%h/.local/bin:%h/.local/pipx/venvs/mmrelay/bin:/usr/local/bin:/usr/bin:/bin
166
+
167
+ [Install]
168
+ WantedBy=default.target
169
+ """
130
170
 
131
171
 
132
172
  def is_service_enabled():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mmrelay
3
- Version: 1.0.4
3
+ Version: 1.0.6
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
@@ -25,6 +25,7 @@ Requires-Dist: schedule==1.2.2
25
25
  Requires-Dist: platformdirs==4.3.7
26
26
  Requires-Dist: py-staticmaps>=0.4.0
27
27
  Requires-Dist: rich==14.0.0
28
+ Requires-Dist: setuptools==80.3.1
28
29
  Dynamic: license-file
29
30
 
30
31
  # M<>M Relay
@@ -7,8 +7,8 @@ mmrelay/log_utils.py,sha256=FXhaq4WSDHwlqiG3k1BbSxiOl5be4P4Kyr1gsIThOBw,4572
7
7
  mmrelay/main.py,sha256=acgBF-DnL0Rs8MmYAfbNHZ9xjqM3Qc0_oKtATN4iOEE,11085
8
8
  mmrelay/matrix_utils.py,sha256=GkIVj2bbPHtx1emFMwhEhc1SWHcv4UvkuyZYdb-Wnwo,30511
9
9
  mmrelay/meshtastic_utils.py,sha256=Pd0j7mz008ncBDIjRGyHOIb0U3vKq06uXWoJP-csHcQ,23381
10
- mmrelay/plugin_loader.py,sha256=na1fso1jaN_APto3LqtXctTciDEOWMyghzAO6TqH4J8,27839
11
- mmrelay/setup_utils.py,sha256=GHttNLoqPwOpjSly0osaCnotPxCq0hfyopsZ2qq313A,14091
10
+ mmrelay/plugin_loader.py,sha256=Gg8E_TKqndMQHiVJwtmlIC-jCgcty-5EsX_5JJ6Onvo,36590
11
+ mmrelay/setup_utils.py,sha256=UZLX944GyiUQPUGNGNDwLocgShA8SzOJtvfntEu821A,16060
12
12
  mmrelay/plugins/__init__.py,sha256=KVMQIXRhe0wlGj4O3IZ0vOIQRKFkfPYejHXhJL17qrc,51
13
13
  mmrelay/plugins/base_plugin.py,sha256=hv21tSEYG-AB36aLAFdW9DDKm0NOTRNPpGIO5F3i1ts,8633
14
14
  mmrelay/plugins/debug_plugin.py,sha256=Jziht9Nj_bRO6Rmy7TjfBXaYo5eM3XsenbWFxPpyUs4,443
@@ -21,9 +21,9 @@ mmrelay/plugins/nodes_plugin.py,sha256=RDabzyG5hKG5aYWecsRUcLSjMCCv6Pngmq2Qpld1A
21
21
  mmrelay/plugins/ping_plugin.py,sha256=RTRdgDQUSO33lreDTmWsTlI0L1C3FJrXE0KYqfEWYO0,4017
22
22
  mmrelay/plugins/telemetry_plugin.py,sha256=8SxWv4BLXMUTbiVaD3MjlMMdQyS7S_1OfLlVNAUMSO0,6306
23
23
  mmrelay/plugins/weather_plugin.py,sha256=yoKA_HdFqFEhgYdXqLhvXatLphCyLJFuGUKCR7fILv0,8546
24
- mmrelay-1.0.4.dist-info/licenses/LICENSE,sha256=yceWauM1c0-FHxVplsD7W1-AbSeRaUNlmqT4UO1msBU,1073
25
- mmrelay-1.0.4.dist-info/METADATA,sha256=fZZsYxyfKn-KANTa3l4CIB7aSwkbg7mKAxRB3UL76FA,6953
26
- mmrelay-1.0.4.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
27
- mmrelay-1.0.4.dist-info/entry_points.txt,sha256=SJZwGUOEpQ-qx4H8UL4xKFnKeInGUaZNW1I0ddjK7Ws,45
28
- mmrelay-1.0.4.dist-info/top_level.txt,sha256=B_ZLCRm7NYAmI3PipRUyHGymP-C-q16LSeMGzmqJfo4,8
29
- mmrelay-1.0.4.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5