mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
dx command
This commit is contained in:
@@ -299,6 +299,7 @@ time =
|
||||
# using Hamlib rig control will monitor and alert on channel use
|
||||
enabled = False
|
||||
rigControlServerAddress = localhost:4532
|
||||
dxspotter_enabled = True
|
||||
# device interface to send the message to
|
||||
sigWatchBroadcastInterface = 1
|
||||
# broadcast channel can also be a comma separated list of channels
|
||||
|
||||
@@ -50,6 +50,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
"cqcq": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
|
||||
"cqcqcq": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
|
||||
"dopewars": lambda: handleDopeWars(message, message_from_id, deviceID),
|
||||
"dx": lambda: handledxcluster(message, message_from_id, deviceID),
|
||||
"ea": lambda: handle_emergency_alerts(message, message_from_id, deviceID),
|
||||
"echo": lambda: handle_echo(message, message_from_id, deviceID, isDM, channel_number),
|
||||
"ealert": lambda: handle_emergency_alerts(message, message_from_id, deviceID),
|
||||
|
||||
@@ -875,4 +875,62 @@ bbslink_enabled = True
|
||||
bbslink_whitelist = # list of whitelisted nodes numbers ex: 2813308004,4258675309 empty list allows all
|
||||
```
|
||||
|
||||
# DX Spotter Module
|
||||
|
||||
The DX Spotter module allows you to fetch and display recent DX cluster spots from [spothole.app](https://spothole.app) directly in your mesh-bot.
|
||||
|
||||
## Command
|
||||
|
||||
| Command | Description |
|
||||
|---------|------------------------------|
|
||||
| `dx` | Show recent DX cluster spots |
|
||||
|
||||
## Usage
|
||||
|
||||
Send a message to the bot containing the `dx` command. You can add filters to narrow down the results:
|
||||
|
||||
- **Basic usage:**
|
||||
```
|
||||
dx
|
||||
```
|
||||
Returns the latest DX spots.
|
||||
|
||||
- **With filters:**
|
||||
```
|
||||
dx band=20m mode=SSB
|
||||
dx xota=WWFF
|
||||
dx by=K7MHI
|
||||
```
|
||||
- `band=`: Filter by band (e.g., 20m, 40m)
|
||||
- `mode=`: Filter by mode (e.g., SSB, CW, FT8)
|
||||
- `xota=`: Filter by source/group (e.g., WWFF, POTA, SOTA)
|
||||
- `by=`: Filter by callsign of the spotter
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
K7ABC @14.074 MHz FT8 WWFF KFF-1234 by:N0CALL CN87 Some comment
|
||||
W1XYZ @7.030 MHz CW SOTA W7W/WE-001 by:K7MHI CN88
|
||||
```
|
||||
|
||||
- Each line shows:
|
||||
`DX_CALL @FREQUENCY MODE GROUP GROUP_REF by:SPOTTER_CALL SPOTTER_GRID COMMENT`
|
||||
|
||||
## Notes
|
||||
|
||||
- Returns up to 4 of the most recent spots matching your filters.
|
||||
- Data is fetched from [spothole.app](https://spothole.app/).
|
||||
- If no spots are found, you’ll see:
|
||||
`No DX spots found.`
|
||||
|
||||
## Configuration
|
||||
|
||||
No additional configuration is required. The module is enabled if present in your `modules/` directory.
|
||||
|
||||
---
|
||||
|
||||
**Written for Meshtastic mesh-bot by K7MHI Kelly Keeton 2025**
|
||||
|
||||
|
||||
|
||||
Happy meshing!
|
||||
112
modules/dxspot.py
Normal file
112
modules/dxspot.py
Normal file
@@ -0,0 +1,112 @@
|
||||
#meshing-around modules/dxspot.py
|
||||
import requests
|
||||
import datetime
|
||||
from modules.log import logger
|
||||
|
||||
trap_list_dxspotter = ["dx"]
|
||||
|
||||
def handledxcluster(message, nodeID, deviceID):
|
||||
from modules.dxspot import get_spothole_spots
|
||||
if "DX" in message.upper():
|
||||
logger.debug(f"System: DXSpotter: Device:{deviceID} Handler: DX Spot Request Received from Node {nodeID}")
|
||||
band = None
|
||||
mode = None
|
||||
source = None
|
||||
dx_call = None
|
||||
parts = message.split()
|
||||
for part in parts:
|
||||
if part.lower().startswith("band="):
|
||||
band = part.split("=")[1]
|
||||
elif part.lower().startswith("mode="):
|
||||
mode = part.split("=")[1]
|
||||
elif part.lower().startswith("xota="):
|
||||
source = part.split("=")[1]
|
||||
elif part.lower().startswith("by="):
|
||||
dx_call = part.split("=")[1]
|
||||
# Build params dict
|
||||
params = {}
|
||||
if source:
|
||||
params["source"] = source.upper()
|
||||
if band:
|
||||
params["band"] = band.lower()
|
||||
if mode:
|
||||
params["mode"] = mode.upper()
|
||||
if dx_call:
|
||||
params["dx_call"] = dx_call.upper()
|
||||
|
||||
# Fetch spots
|
||||
spots = get_spothole_spots(**params)
|
||||
if spots:
|
||||
response_lines = []
|
||||
for spot in spots[:5]:
|
||||
callsign = spot.get('dx_call', spot.get('callsign', 'N/A'))
|
||||
freq_hz = spot.get('freq', spot.get('frequency', None))
|
||||
frequency = f"{float(freq_hz)/1e6:.3f} MHz" if freq_hz else "N/A"
|
||||
mode_val = spot.get('mode', 'N/A')
|
||||
comment = spot.get('comment', '')
|
||||
sig = spot.get('sig', '')
|
||||
de_grid = spot.get('de_grid', '')
|
||||
de_call = spot.get('de_call', '')
|
||||
sig_ref_name = spot.get('sig_refs_names', [''])[0] if spot.get('sig_refs_names') else ''
|
||||
line = f"{callsign} @{frequency} {mode_val} {sig} {sig_ref_name} by:{de_call} {de_grid} {comment}"
|
||||
response_lines.append(line)
|
||||
response = "\n".join(response_lines)
|
||||
else:
|
||||
response = "No DX spots found."
|
||||
return response
|
||||
return "Error: No DX command found."
|
||||
|
||||
def get_spothole_spots(source=None, band=None, mode=None, date=None, dx_call=None, de_continent=None, de_location=None):
|
||||
"""
|
||||
Fetches spots from https://spothole.app/api/v1/spots with optional filters.
|
||||
Returns a list of spot dicts.
|
||||
"""
|
||||
url = "https://spothole.app/api/v1/spots"
|
||||
params = {}
|
||||
|
||||
|
||||
# Add administrative filters if provided
|
||||
qrt = False # Always fetch active spots
|
||||
needs_sig = False # Always need spots wth a group ike Xota
|
||||
limit = 4
|
||||
dedupe = True
|
||||
|
||||
params["dedupe"] = str(dedupe).lower()
|
||||
params["limit"] = limit
|
||||
params["qrt"] = str(qrt).lower()
|
||||
params["needs_sig"] = str(needs_sig).lower()
|
||||
params["needs_sig_ref"] = 'true'
|
||||
# Only get spots from last 9 hours
|
||||
received_since_dt = datetime.datetime.utcnow() - datetime.timedelta(hours=9)
|
||||
received_since = int(received_since_dt.timestamp())
|
||||
params["received_since"] = received_since
|
||||
|
||||
# Add spot filters if provided
|
||||
if de_continent:
|
||||
params["de_continent"] = de_continent
|
||||
if de_location:
|
||||
params["de_location"] = de_location
|
||||
if source:
|
||||
params["source"] = source
|
||||
if band:
|
||||
params["band"] = band
|
||||
if mode:
|
||||
params["mode"] = mode
|
||||
if dx_call:
|
||||
params["dx_call"] = dx_call
|
||||
if date:
|
||||
# date should be a string in YYYY-MM-DD or datetime.date
|
||||
if isinstance(date, datetime.date):
|
||||
params["date"] = date.isoformat()
|
||||
else:
|
||||
params["date"] = date
|
||||
|
||||
try:
|
||||
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko"}
|
||||
response = requests.get(url, params=params, headers=headers)
|
||||
response.raise_for_status()
|
||||
spots = response.json()
|
||||
except Exception as e:
|
||||
logger.debug(f"Error fetching spots: {e}")
|
||||
spots = []
|
||||
return spots
|
||||
@@ -385,6 +385,7 @@ try:
|
||||
|
||||
# radio monitoring
|
||||
radio_detection_enabled = config['radioMon'].getboolean('enabled', False)
|
||||
dxspotter_enabled = config['radioMon'].getboolean('dxspotter_enabled', True) # default True
|
||||
rigControlServerAddress = config['radioMon'].get('rigControlServerAddress', 'localhost:4532') # default localhost:4532
|
||||
sigWatchBroadcastCh = config['radioMon'].get('sigWatchBroadcastCh', '2').split(',') # default Channel 2
|
||||
sigWatchBroadcastInterface = config['radioMon'].getint('sigWatchBroadcastInterface', 1) # default interface 1
|
||||
|
||||
@@ -141,6 +141,11 @@ if dad_jokes_enabled:
|
||||
trap_list = trap_list + ("joke",)
|
||||
help_message = help_message + ", joke"
|
||||
|
||||
if dxspotter_enabled:
|
||||
from modules.dxspot import handledxcluster
|
||||
trap_list = trap_list + ("dx",)
|
||||
help_message = help_message + ", dx"
|
||||
|
||||
# Wikipedia Search Configuration
|
||||
if wikipedia_enabled:
|
||||
from modules.wiki import * # from the spudgunman/meshing-around repo
|
||||
|
||||
Reference in New Issue
Block a user