Compare commits

..

13 Commits

Author SHA1 Message Date
SpudGunMan 00280e351c Update mesh_bot.py 2025-10-07 14:04:07 -07:00
SpudGunMan 0e8bb197a9 Update mesh_bot.py 2025-10-07 13:59:49 -07:00
SpudGunMan d825c0fa15 Update mesh_bot.py
what happened here? I forget now but sheesh!
2025-10-07 13:57:00 -07:00
SpudGunMan 6abe73c1bc Update mesh_bot.py
ack
2025-10-07 13:54:32 -07:00
SpudGunMan b8e9adb223 fixMessagesCommand
thanks @mesb1  https://github.com/SpudGunMan/meshing-around/issues/200
2025-10-07 13:48:23 -07:00
SpudGunMan e621016e9a nom
nom
2025-10-07 06:06:21 -07:00
SpudGunMan cfaf652852 Update mesh_bot.py 2025-10-06 20:02:36 -07:00
SpudGunMan 6c27b5d5de xoxo
enhance
2025-10-06 18:03:22 -07:00
SpudGunMan a31fa90942 Update system.py 2025-10-06 14:57:40 -07:00
SpudGunMan 3cd347dff3 Update tictactoe.py 2025-10-06 14:46:24 -07:00
SpudGunMan ea4ac1f9c1 whichonelooksbetter 2025-10-06 14:42:50 -07:00
SpudGunMan a9da8336cc enhance 2025-10-06 14:40:08 -07:00
SpudGunMan 4ba60ed276 correctLogLevel 2025-10-06 14:25:13 -07:00
7 changed files with 94 additions and 27 deletions
+1 -1
View File
@@ -137,7 +137,7 @@ git clone https://github.com/spudgunman/meshing-around
| Command | Description | |
|---------|-------------|-
| `askai` and `ask:` | Ask Ollama LLM AI for a response. Example: `askai what temp do I cook chicken` | ✅ |
| `messages` | Replays the last messages heard, like Store and Forward | ✅ |
| `messages` | Replays the last messages heard on device, like Store and Forward, returns the PublicChannel and Current | ✅ |
| `readnews` | returns the contents of a file (news.txt, by default) via the chunker on air | ✅ |
| `satpass` | returns the pass info from API for defined NORAD ID in config or Example: `satpass 25544,33591`| |
| `wiki:` | Searches Wikipedia and returns the first few sentences of the first result if a match. Example: `wiki: lora radio` |
+1
View File
@@ -323,6 +323,7 @@ IMAP_FOLDER = inbox
[games]
# if hop limit for the user exceeds this value, the message will be dropped
game_hop_limit = 5
disable_emojis = False
# enable or disable the games module(s)
dopeWars = True
lemonade = True
+20 -8
View File
@@ -43,6 +43,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
"checkin": lambda: handle_checklist(message, message_from_id, deviceID),
"checklist": lambda: handle_checklist(message, message_from_id, deviceID),
"checkout": lambda: handle_checklist(message, message_from_id, deviceID),
"chess": lambda: handle_gTnW(chess=True),
"clearsms": lambda: handle_sms(message_from_id, message),
"cmd": lambda: handle_cmd(message, message_from_id, deviceID),
"cq": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
@@ -88,6 +89,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
"test": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
"testing": lambda: handle_ping(message_from_id, deviceID, message, hop, snr, rssi, isDM, channel_number),
"tictactoe": lambda: handleTicTacToe(message, message_from_id, deviceID),
"tic-tac-toe": lambda: handleTicTacToe(message, message_from_id, deviceID),
"tide": lambda: handle_tide(message_from_id, deviceID, channel_number),
"valert": lambda: get_volcano_usgs(),
"videopoker": lambda: handleVideoPoker(message, message_from_id, deviceID),
@@ -543,12 +545,17 @@ def handleDopeWars(message, nodeID, rxNode):
time.sleep(responseDelay + 1)
return msg
def handle_gTnW():
def handle_gTnW(chess = False):
chess = ["How about a nice game of chess?", "Shall we play a game of chess?", "Would you like to play a game of chess?", "f3, to e5, g4??"]
response = ["The only winning move is not to play.", "What are you doing, Dave?",\
"Greetings, Professor Falken.", "Shall we play a game?", "How about a nice game of chess?",\
"You are a hard man to reach. Could not find you in Seattle and no terminal is in operation at your classified address.",\
"I should reach Defcon 1 and release my missiles in 28 hours.","T-minus thirty","Malfunction 54: Treatment pause;dose input 2", "reticulating splines"]
length = len(response)
chess_length = len(chess)
if chess:
response = chess
length = chess_length
indices = list(range(length))
# Shuffle the indices using a convoluted method
for i in range(length):
@@ -832,7 +839,7 @@ def handleTicTacToe(message, nodeID, deviceID):
"nodeID": nodeID,
"last_played": time.time()
})
msg = "🎯Tic-Tac-Toe🤖 '(e)nd' to Quit\n"
msg = "🎯Tic-Tac-Toe🤖 '(e)nd'\n"
msg += tictactoe.play(nodeID, message)
@@ -954,14 +961,19 @@ def handle_messages(message, deviceID, channel_number, msg_history, publicChanne
return message.split("?")[0].title() + " command returns the last " + str(storeFlimit) + " messages sent on a channel."
else:
response = ""
for msgH in msg_history:
# Reverse the message history to show most recent first
for msgH in reversed(msg_history):
# number of messages to return +1 for the header line
if len(response.split("\n")) >= storeFlimit + 1:
break
# if the message is for this deviceID and channel or publicChannel
if msgH[4] == deviceID:
if msgH[2] == channel_number or msgH[2] == publicChannel:
response += f"\n{msgH[0]}: {msgH[1]}"
if len(response) > 0:
return "Message History:" + response
return "📨Messages:" + response
else:
return "No messages in history"
return "No 📭messages in history"
def handle_sun(message_from_id, deviceID, channel_number):
location = get_node_location(message_from_id, deviceID, channel_number)
@@ -1544,7 +1556,7 @@ async def start_rx():
if radio_detection_enabled:
logger.debug(f"System: Radio Detection Enabled using rigctld at {rigControlServerAddress} brodcasting to channels: {sigWatchBroadcastCh} for {get_freq_common_name(get_hamlib('f'))}")
if file_monitor_enabled:
logger.debug(f"System: File Monitor Enabled for {file_monitor_file_path}, broadcasting to channels: {file_monitor_broadcastCh}")
logger.warning(f"System: File Monitor Enabled for {file_monitor_file_path}, broadcasting to channels: {file_monitor_broadcastCh}")
if enable_runShellCmd:
logger.debug(f"System: Shell Command monitor enabled")
if allowXcmd and enable_runShellCmd:
@@ -1681,7 +1693,7 @@ async def main():
if radio_detection_enabled:
tasks.append(asyncio.create_task(handleSignalWatcher(), name="hamlib"))
logger.info(f"System: Starting {len(tasks)} async tasks")
logger.debug(f"System: Starting {len(tasks)} async tasks")
# Wait for all tasks with proper exception handling
results = await asyncio.gather(*tasks, return_exceptions=True)
@@ -1695,7 +1707,7 @@ async def main():
logger.error(f"Main loop error: {e}")
finally:
# Cleanup tasks
logger.info("System: Cleaning up async tasks")
logger.debug("System: Cleaning up async tasks")
for task in tasks:
if not task.done():
task.cancel()
+48 -13
View File
@@ -1,25 +1,45 @@
# Tic-Tac-Toe game for Meshtastic mesh-bot
# Board positions chosen by numbers 1-9
# 2025
from modules.log import *
import random
# to molly and jake, I miss you both so much.
if disable_emojis_in_games:
X = "X"
O = "O"
else:
X = ""
O = "⭕️"
class TicTacToe:
def __init__(self):
self.game = {}
def new_game(self, id):
positiveThoughts = ["🚀I need to call NATO",
"🏅Going for the gold!",
"Mastering ❌TTT⭕️",]
sorryNotGoinWell = ["😭Not your day, huh?",
"📉Results here dont define you.",
"🤖WOPR would be proud."]
"""Start a new game"""
games = won = 0
ret = ""
if id in self.game:
games = self.game[id]["games"]
won = self.game[id]["won"]
ret += f"Games:{games} Won:{won}\n"
if games > 0:
if won / games >= 3.14159265358979323846: # win rate > pi
ret += random.choice(positiveThoughts) + "\n"
else:
ret += random.choice(sorryNotGoinWell) + "\n"
# Retain stats
ret += f"Games:{games} 🥇❌:{won}\n"
self.game[id] = {
"board": [" "] * 9, # 3x3 board as flat list
"player": "X", # Human is X, bot is O
"player": X, # Human is X, bot is O
"games": games + 1,
"won": won,
"turn": "human" # whose turn it is
@@ -39,13 +59,17 @@ class TicTacToe:
row = ""
for j in range(3):
pos = i * 3 + j
cell = b[pos] if b[pos] != " " else str(pos + 1)
if disable_emojis_in_games:
cell = b[pos] if b[pos] != " " else str(pos + 1)
else:
cell = b[pos] if b[pos] != " " else f" {str(pos + 1)} "
row += cell
if j < 2:
row += "|"
row += " | "
board_str += row
if i < 2:
board_str += "\n-+-+-\n"
#board_str += "\n-+-+-\n"
board_str += "\n"
return board_str + "\n"
@@ -62,7 +86,7 @@ class TicTacToe:
return False
# Make human move
g["board"][pos] = "X"
g["board"][pos] = X
return True
def bot_move(self, id):
@@ -70,14 +94,14 @@ class TicTacToe:
g = self.game[id]
# Simple AI: Try to win, block, or pick random
move = self.find_winning_move(id, "O") # Try to win
move = self.find_winning_move(id, O) # Try to win
if move == -1:
move = self.find_winning_move(id, "X") # Block player
move = self.find_winning_move(id, X) # Block player
if move == -1:
move = self.find_random_move(id) # Random move
if move != -1:
g["board"][move] = "O"
g["board"][move] = O
return move
def find_winning_move(self, id, player):
@@ -129,13 +153,13 @@ class TicTacToe:
g = self.game[id]
winner = self.check_winner(id)
if winner == "X":
if winner == X:
g["won"] += 1
return "🎉You won! (n)ew (e)nd"
elif winner == "O":
elif winner == X:
return "🤖Bot wins! (n)ew (e)nd"
else:
return "🤝Tie game! (n)ew (e)nd"
return "🤝Tie, The only winning move! (n)ew (e)nd"
def play(self, id, input_msg):
"""Main game play function"""
@@ -143,7 +167,7 @@ class TicTacToe:
return self.new_game(id)
# If input is just "tictactoe", show current board
if input_msg.lower().strip() == "tictactoe":
if input_msg.lower().strip() == ("tictactoe" or "tic-tac-toe"):
return self.show_board(id) + "Your turn! Pick 1-9:"
g = self.game[id]
@@ -201,8 +225,19 @@ class TicTacToe:
def end_game(self, id):
"""Clean up finished game but keep stats"""
if id in self.game:
games = self.game[id]["games"]
won = self.game[id]["won"]
# Remove game but we'll create new one on next play
del self.game[id]
# Preserve stats for next game
self.game[id] = {
"board": [" "] * 9,
"player": X,
"games": games,
"won": won,
"turn": "human"
}
def end(self, id):
"""End game completely (called by 'end' command)"""
+2 -1
View File
@@ -362,7 +362,8 @@ try:
allowXcmd = config['fileMon'].getboolean('allowXcmd', False) # default False
# games
game_hop_limit = config['messagingSettings'].getint('game_hop_limit', 5) # default 3 hops
game_hop_limit = config['games'].getint('game_hop_limit', 5) # default 5 hops
disable_emojis_in_games = config['games'].getboolean('disable_emojis', False) # default False
dopewars_enabled = config['games'].getboolean('dopeWars', True)
lemonade_enabled = config['games'].getboolean('lemonade', True)
blackjack_enabled = config['games'].getboolean('blackjack', True)
+20 -2
View File
@@ -262,7 +262,7 @@ if hamtest_enabled:
if tictactoe_enabled:
from modules.games.tictactoe import * # from the spudgunman/meshing-around repo
trap_list = trap_list + ("tictactoe",)
trap_list = trap_list + ("tictactoe","tic-tac-toe",)
games_enabled = True
# Games Configuration
@@ -276,7 +276,8 @@ if games_enabled is True:
if lemonade_enabled:
gamesCmdList += "lemonStand, "
if gTnW_enabled:
trap_list = trap_list + ("globalthermonuclearwar",)
trap_list = trap_list + ("globalthermonuclearwar","chess")
gamesCmdList += "chess, "
if blackjack_enabled:
gamesCmdList += "blackJack, "
if videoPoker_enabled:
@@ -1233,6 +1234,23 @@ def consumeMetadata(packet, rxNode=0, channel=-1):
logger.info(f"System: Remote Hardware Data from Device: {rxNode} Channel: {channel} NodeID:{nodeID} Info:{hardware_info}")
except Exception as e:
logger.debug(f"System: REMOTE_HARDWARE_APP decode error: Device: {rxNode} Channel: {channel} {e} packet {packet}")
# ADMIN_APP
# IP_TUNNEL_APP
# SERIAL_APP
# STORE_FOWARD_APP
# RANGE_TEST_APP
# COMPRESSED_TEXT_APP
# AUDIO_APP
# SIMULATOR_APP
return True
def noisyTelemetryCheck():
global positionMetadata
+2 -2
View File
@@ -481,7 +481,7 @@ async def main():
if file_monitor_enabled:
tasks.append(asyncio.create_task(handleFileWatcher(), name="file_monitor"))
logger.info(f"System: Starting {len(tasks)} async tasks")
logger.debug(f"System: Starting {len(tasks)} async tasks")
# Wait for all tasks with proper exception handling
results = await asyncio.gather(*tasks, return_exceptions=True)
@@ -495,7 +495,7 @@ async def main():
logger.error(f"Main loop error: {e}")
finally:
# Cleanup tasks
logger.info("System: Cleaning up async tasks")
logger.debug("System: Cleaning up async tasks")
for task in tasks:
if not task.done():
task.cancel()