forked from iarv/meshing-around
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abeb28c9cc | ||
|
|
2325f74581 | ||
|
|
e75ef3e44d | ||
|
|
6431b45769 | ||
|
|
7dfcbb619f | ||
|
|
051b58ca6f | ||
|
|
5777b39c22 | ||
|
|
eb4f135698 | ||
|
|
3bcb04ece7 | ||
|
|
e42aca875f | ||
|
|
28915ab848 | ||
|
|
0fe491871d | ||
|
|
6fc6a483a8 | ||
|
|
13236880b5 | ||
|
|
9461719039 | ||
|
|
63163cc4c1 | ||
|
|
5cdf159bc1 | ||
|
|
47a9981fdf | ||
|
|
f85bdb7d02 | ||
|
|
7796d03e21 | ||
|
|
90094c082a | ||
|
|
2b695e2f2e | ||
|
|
2e05f3ef64 | ||
|
|
316f1efd08 | ||
|
|
4678a63955 | ||
|
|
8584454d5d | ||
|
|
2c6cf76a10 | ||
|
|
cd3226df21 | ||
|
|
4bcc6ef1f2 | ||
|
|
77e56c25ae | ||
|
|
e7b363612a | ||
|
|
a217c61ba1 | ||
|
|
e7b4fe44c8 | ||
|
|
f8cc580b99 | ||
|
|
03057b3263 | ||
|
|
452b9b7c67 | ||
|
|
5ae16d0adc | ||
|
|
5ed135c023 | ||
|
|
d425298cd9 | ||
|
|
786815d073 | ||
|
|
54cad92a3f | ||
|
|
54e21f4644 | ||
|
|
3c76f177cd | ||
|
|
aa05c62d94 | ||
|
|
3f16158e27 | ||
|
|
6f2824512d | ||
|
|
723b67f886 | ||
|
|
008d55e63b | ||
|
|
79885454ab | ||
|
|
ba21723bdc | ||
|
|
c36c4918a8 | ||
|
|
853147518d | ||
|
|
2f19d86c95 | ||
|
|
39bdabffcb | ||
|
|
a7bdaedfe1 | ||
|
|
1c6106081f | ||
|
|
8ab6cded2e | ||
|
|
ff63bb959a | ||
|
|
6c79bb1ff0 | ||
|
|
ce73336c0c | ||
|
|
248977c5b5 | ||
|
|
77a6e63210 | ||
|
|
6f6fb35177 | ||
|
|
9db565cb4f | ||
|
|
2a254a7fab | ||
|
|
15e76ab029 | ||
|
|
66b95cdaa0 | ||
|
|
32ea93cb88 | ||
|
|
22a2a64861 | ||
|
|
d841fdf02c | ||
|
|
9421f09ded | ||
|
|
b4af552fb9 | ||
|
|
69dfb17460 | ||
|
|
4703750c27 | ||
|
|
40caf99939 | ||
|
|
df5f648b26 | ||
|
|
55472bbbc0 | ||
|
|
f23c4e2b6a | ||
|
|
0b101d662e | ||
|
|
a7f07afc14 | ||
|
|
2715021898 | ||
|
|
e8fa0036e2 | ||
|
|
f628a5e7ef | ||
|
|
95e6bc120e | ||
|
|
0e35c891c4 | ||
|
|
b7a3e7014c | ||
|
|
0c1c587bc7 | ||
|
|
a0a2c60e63 | ||
|
|
45c912a0d6 | ||
|
|
39945f161d | ||
|
|
ed958302bd | ||
|
|
477f2141d7 | ||
|
|
d321a958f0 | ||
|
|
d14f1df823 | ||
|
|
f7e3b9f6c7 | ||
|
|
cd3ac201f8 | ||
|
|
ceef493b01 | ||
|
|
480a75e30c | ||
|
|
d8cc953fe7 | ||
|
|
0baec88321 | ||
|
|
74bd3f681f | ||
|
|
713b750f4a | ||
|
|
11eee911ca | ||
|
|
b288aaea90 | ||
|
|
7acc018fd2 | ||
|
|
7aba1096f9 | ||
|
|
0be7202144 | ||
|
|
83a5db74e5 | ||
|
|
8dc4371beb | ||
|
|
e5045a0984 | ||
|
|
2c9b37a0cc | ||
|
|
b608482220 | ||
|
|
9290fac899 | ||
|
|
d7901ee575 | ||
|
|
7eb33a5aef | ||
|
|
c5dc103ac0 | ||
|
|
c90172a862 | ||
|
|
8540786c2c | ||
|
|
7aeb8e851d | ||
|
|
2f207dc3d9 | ||
|
|
7e2be73962 | ||
|
|
e2a87eb945 | ||
|
|
f6215d3563 |
@@ -1,8 +1,14 @@
|
||||
FROM python:3.10-slim
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
RUN apt-get update && apt-get install -y gettext && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y gettext tzdata locales && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set the locale default to en_US.UTF-8
|
||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||
dpkg-reconfigure --frontend=noninteractive locales && \
|
||||
update-locale LANG=en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV TZ="America/Los_Angeles"
|
||||
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
31
README.md
31
README.md
@@ -12,7 +12,7 @@ The bot is also capable of using dual radio/nodes, so you can monitor two networ
|
||||
|
||||
Look up data using wiki results, or interact with [Ollama](https://ollama.com) LLM AI see the [OllamaDocs](https://github.com/ollama/ollama/tree/main/docs) If Ollama is enabled you can DM the bot directly. The default model for mesh-bot which is currently `gemma2:2b`
|
||||
|
||||
The bot will report on anyone who is getting close to the configured lat/long, if in a remote location.
|
||||
The bot will report on anyone who is getting close to the configured lat/long, if in a remote location. For example having the bot in your camp site alerts when members arive back at camp.
|
||||
|
||||
Store and forward-like message re-play with `messages`, and there is a repeater module for dual radio bots to cross post messages. Messages are also logged locally to disk.
|
||||
|
||||
@@ -34,6 +34,7 @@ Any messages that are over 160 characters are chunked into 160 message bytes to
|
||||
- `bbsread` read a message example use: `bbsread #1`
|
||||
- `bbspost` post a message to public board or send a DM example use: `bbspost $subject #message, or bbspost @nodeNumber #message or bbspost @nodeShortName #message`
|
||||
- `bbsdelete` delete a message example use: `bbsdelete #4`
|
||||
- `bbsinfo` Stats on BBS delivery and messages (sysop)
|
||||
- Other functions
|
||||
- `whereami` returns the address of location of sender if known
|
||||
- `whoami` returns some details of the node asking
|
||||
@@ -48,11 +49,13 @@ Any messages that are over 160 characters are chunked into 160 message bytes to
|
||||
- `lheard` returns the last 5 heard nodes with SNR, can also use `sitrep`
|
||||
- `history` returns the last commands ran by user(s)
|
||||
- `cmd` returns the list of commands (the help message)
|
||||
- Games
|
||||
- `lemonstand` plays the classic Lemonade Stand Finance game via DM
|
||||
- `dopewars` plays the classic drug trader game via DM
|
||||
- `blackjack` BlackJack
|
||||
- `videopoker` Video Poker
|
||||
- Games - via DM
|
||||
- `lemonstand` plays the classic Lemonade Stand Finance
|
||||
- `dopewars` plays the classic drug trader
|
||||
- `blackjack` BlackJack, Casino 21
|
||||
- `videopoker` Video Poker, basic 5 card hold
|
||||
- `mastermind` Classic code-breaking game
|
||||
- `golfsim` Golf Simulator, 9 Hole
|
||||
|
||||
## pong_bot.sh
|
||||
Stripped-down bot, mostly around for archive purposes. The mesh-bot enhanced modules can be disabled by config to disable features.
|
||||
@@ -192,20 +195,6 @@ googleSearchResults = 3 # number of google search results to include in the cont
|
||||
llm_history_limit = 6 # limit the history to 3 messages (come in pairs) more results = more compute time
|
||||
```
|
||||
|
||||
Logging messages to disk or Syslog to disk uses the python native logging function. Take a look at the [/modules/log.py](/modules/log.py) you can set the file logger for syslog to INFO for example to not log DEBUG messages to file log, or modify the stdOut level.
|
||||
```
|
||||
[general]
|
||||
# logging to file of the non Bot messages
|
||||
LogMessagesToFile = True
|
||||
# Logging of system messages to file
|
||||
SyslogToFile = True
|
||||
```
|
||||
Example to log to disk only INFO and higher (ignore DEBUG)
|
||||
```
|
||||
*log.py
|
||||
file_handler.setLevel(logging.INFO) # DEBUG used by default for system logs to disk example here shows INFO
|
||||
```
|
||||
|
||||
The Scheduler is enabled in the [settings.py](modules/settings.py) by setting `scheduler_enabled = True` the actions and settings are via code only at this time. see [mesh_bot.py](mesh_bot.py) around line [425](https://github.com/SpudGunMan/meshing-around/blob/22983133ee4db3df34f66699f565e506de296197/mesh_bot.py#L425-L435) to edit schedule its most flexible to edit raw code right now. See https://schedule.readthedocs.io/en/stable/ for more.
|
||||
|
||||
```
|
||||
@@ -264,6 +253,8 @@ Games Ported from..
|
||||
- https://github.com/Reconfirefly/drugwars
|
||||
- https://github.com/Himan10/BlackJack
|
||||
- https://github.com/devtronvarma/Video-Poker-Terminal-Game
|
||||
- https://github.com/pwdkramer/pythonMastermind/
|
||||
- https://github.com/danfriedman30/pythongame (Golf)
|
||||
|
||||
GitHub user Nestpebble, for new ideas and enhancments, mrpatrick1991 For Docker configs, PiDiBi looking at test functions and other suggestions like wxc, CPU use, and alerting ideas
|
||||
Discord and Mesh user Cisien, bitflip, and github Hailo1999, for testing and feature ideas! Lots of individuals on the Meshtastic discord who have tossed out ideas and tested code!
|
||||
|
||||
@@ -25,8 +25,10 @@ port = /dev/ttyUSB0
|
||||
[general]
|
||||
# if False will respond on all channels but the default channel
|
||||
respond_by_dm_only = True
|
||||
# defaultChannel is the meshtastic default public channel
|
||||
# defaultChannel is the meshtastic default public channel, e.g. LongFast
|
||||
defaultChannel = 0
|
||||
# ignoreDefaultChannel, the bot will ignore the default channel set above
|
||||
ignoreDefaultChannel = False
|
||||
# motd is reset to this value on boot
|
||||
motd = Thanks for using MeshBOT! Have a good day!
|
||||
welcome_message = MeshBot, here for you like a friend who is not. Try sending: ping @foo or, cmd
|
||||
@@ -64,6 +66,8 @@ dopeWars = True
|
||||
lemonade = True
|
||||
blackjack = True
|
||||
videopoker = True
|
||||
mastermind = True
|
||||
golfsim = True
|
||||
|
||||
[sentry]
|
||||
# detect anyone close to the bot
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# Load the bbs messages from the database file to screen for admin functions
|
||||
import pickle # pip install pickle
|
||||
|
||||
|
||||
# load the bbs messages from the database file
|
||||
try:
|
||||
with open('../bbsdb.pkl', 'rb') as f:
|
||||
bbs_messages = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('bbsdb.pkl', 'rb') as f:
|
||||
bbs_messages = pickle.load(f)
|
||||
except Exception as e:
|
||||
print ("\nSystem: bbsdb.pkl not found")
|
||||
|
||||
try:
|
||||
with open('../bbsdm.pkl', 'rb') as f:
|
||||
bbs_dm = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('bbsdm.pkl', 'rb') as f:
|
||||
bbs_dm = pickle.load(f)
|
||||
except Exception as e:
|
||||
print ("\nSystem: bbsdm.pkl not found")
|
||||
|
||||
# Game HS tables
|
||||
try:
|
||||
with open('../lemonade_hs.pkl', 'rb') as f:
|
||||
lemon_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('lemonade_hs.pkl', 'rb') as f:
|
||||
lemon_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
print ("\nSystem: lemonade_hs.pkl not found")
|
||||
|
||||
try:
|
||||
with open('../dopewar_hs.pkl', 'rb') as f:
|
||||
dopewar_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('dopewar_hs.pkl', 'rb') as f:
|
||||
dopewar_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
print ("\nSystem: dopewar_hs.pkl not found")
|
||||
|
||||
|
||||
print ("\nSystem: bbs_messages")
|
||||
print (bbs_messages)
|
||||
print ("\nSystem: bbs_dm")
|
||||
print (bbs_dm)
|
||||
print ("Game HS tables")
|
||||
print (f"lemon:{lemon_score}")
|
||||
print (f"dopewar:{dopewar_score}")
|
||||
100
etc/db_admin.py
Normal file
100
etc/db_admin.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# Load the bbs messages from the database file to screen for admin functions
|
||||
import pickle # pip install pickle
|
||||
|
||||
|
||||
# load the bbs messages from the database file
|
||||
try:
|
||||
with open('../bbsdb.pkl', 'rb') as f:
|
||||
bbs_messages = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('bbsdb.pkl', 'rb') as f:
|
||||
bbs_messages = pickle.load(f)
|
||||
except Exception as e:
|
||||
bbs_messages = "System: bbsdb.pkl not found"
|
||||
|
||||
try:
|
||||
with open('../bbsdm.pkl', 'rb') as f:
|
||||
bbs_dm = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('bbsdm.pkl', 'rb') as f:
|
||||
bbs_dm = pickle.load(f)
|
||||
except Exception as e:
|
||||
bbs_dm = "System: bbsdm.pkl not found"
|
||||
|
||||
# Game HS tables
|
||||
try:
|
||||
with open('../lemonade_hs.pkl', 'rb') as f:
|
||||
lemon_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('lemonade_hs.pkl', 'rb') as f:
|
||||
lemon_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
lemon_score = "System: lemonade_hs.pkl not found"
|
||||
|
||||
try:
|
||||
with open('../dopewar_hs.pkl', 'rb') as f:
|
||||
dopewar_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('dopewar_hs.pkl', 'rb') as f:
|
||||
dopewar_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
dopewar_score = "System: dopewar_hs.pkl not found"
|
||||
|
||||
try:
|
||||
with open('../blackjack_hs.pkl', 'rb') as f:
|
||||
blackjack_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('blackjack_hs.pkl', 'rb') as f:
|
||||
blackjack_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
blackjack_score = "System: blackjack_hs.pkl not found"
|
||||
|
||||
try:
|
||||
with open('../videopoker_hs.pkl', 'rb') as f:
|
||||
videopoker_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('videopoker_hs.pkl', 'rb') as f:
|
||||
videopoker_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
videopoker_score = "System: videopoker_hs.pkl not found"
|
||||
|
||||
try:
|
||||
with open('../mmind_hs.pkl', 'rb') as f:
|
||||
mmind_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('mmind_hs.pkl', 'rb') as f:
|
||||
mmind_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
mmind_score = "System: mmind_hs.pkl not found"
|
||||
|
||||
try:
|
||||
with open('../golfsim_hs.pkl', 'rb') as f:
|
||||
golfsim_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
try:
|
||||
with open('golfsim_hs.pkl', 'rb') as f:
|
||||
golfsim_score = pickle.load(f)
|
||||
except Exception as e:
|
||||
golfsim_score = "System: golfsim_hs.pkl not found"
|
||||
|
||||
|
||||
print ("\n Meshing-Around Database Admin Tool\n")
|
||||
print ("System: bbs_messages")
|
||||
print (bbs_messages)
|
||||
print ("\nSystem: bbs_dm")
|
||||
print (bbs_dm)
|
||||
print (f"\n\nGame HS tables\n")
|
||||
print (f"lemon:{lemon_score}")
|
||||
print (f"dopewar:{dopewar_score}")
|
||||
print (f"blackjack:{blackjack_score}")
|
||||
print (f"videopoker:{videopoker_score}")
|
||||
print (f"mmind:{mmind_score}")
|
||||
print (f"golfsim:{golfsim_score}")
|
||||
print ("\n")
|
||||
@@ -8,6 +8,7 @@ import random
|
||||
projectName = "example_handler" # name of _handler function to match the function name under test
|
||||
randomNode = False # Set to True to use random node IDs
|
||||
|
||||
# bot.py Simulated functions
|
||||
def get_NodeID():
|
||||
nodeList = [4258675309, 1212121212, 1234567890, 9876543210]
|
||||
if randomNode:
|
||||
@@ -15,6 +16,10 @@ def get_NodeID():
|
||||
else:
|
||||
nodeID = nodeList[0]
|
||||
return nodeID
|
||||
def get_name_from_number(nodeID, length='short', interface=1):
|
||||
# return random name for nodeID
|
||||
names = ["Max","Molly","Jake","Kelly"]
|
||||
return names[nodeID % len(names)]
|
||||
# # end Initialization of the tool
|
||||
|
||||
# # Function to handle, or the project in test
|
||||
@@ -37,7 +42,7 @@ if __name__ == '__main__': # represents the bot's main loop
|
||||
nodeInt = 1 # represents the device/node number
|
||||
logger.info(f"System: Meshing-Around Simulator Starting for {projectName}")
|
||||
nodeID = get_NodeID() # assign a nodeID
|
||||
projectResponse = globals()[projectName](nodeID, " ") # Call the project handler under test
|
||||
projectResponse = globals()[projectName](0, 0, " ") # Call the project handler under test
|
||||
while True: # represents the onReceive() loop in the bot.py
|
||||
projectResponse = ""
|
||||
responseLength = 0
|
||||
@@ -46,7 +51,7 @@ if __name__ == '__main__': # represents the bot's main loop
|
||||
packet = input(f"CLIENT {nodeID} INPUT: " ) # Emulate the client input
|
||||
if packet != "":
|
||||
#try:
|
||||
projectResponse = globals()[projectName](nodeID, packet) # Call the project handler under test
|
||||
projectResponse = globals()[projectName](nodeID, deviceID=nodeInt, message=packet) # Call the project handler under test
|
||||
# except Exception as e:
|
||||
# logger.error(f"System: Handler: {e}")
|
||||
# projectResponse = "Error in handler"
|
||||
|
||||
@@ -15,6 +15,7 @@ printf "\nChecking for dependencies\n"
|
||||
printf "\nAdding user to dialout and tty groups for serial access\n"
|
||||
sudo usermod -a -G dialout $USER
|
||||
sudo usermod -a -G tty $USER
|
||||
sudo usermod -a -G bluetooth $USER
|
||||
|
||||
# check for pip
|
||||
if ! command -v pip &> /dev/null
|
||||
|
||||
15
logs/README.md
Normal file
15
logs/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
Logs will collect here.
|
||||
|
||||
Logging messages to disk or Syslog to disk uses the python native logging function. Take a look at the [/modules/log.py](/modules/log.py) you can set the file logger for syslog to INFO for example to not log DEBUG messages to file log, or modify the stdOut level.
|
||||
```
|
||||
[general]
|
||||
# logging to file of the non Bot messages
|
||||
LogMessagesToFile = True
|
||||
# Logging of system messages to file
|
||||
SyslogToFile = True
|
||||
```
|
||||
Example to log to disk only INFO and higher (ignore DEBUG)
|
||||
```
|
||||
*log.py
|
||||
file_handler.setLevel(logging.INFO) # DEBUG used by default for system logs to disk example here shows INFO
|
||||
```
|
||||
636
mesh_bot.py
636
mesh_bot.py
@@ -4,12 +4,13 @@
|
||||
|
||||
import asyncio
|
||||
import time # for sleep, get some when you can :)
|
||||
import random
|
||||
from pubsub import pub # pip install pubsub
|
||||
from modules.log import *
|
||||
from modules.system import *
|
||||
|
||||
# list of commands to remove from the default list for DM only
|
||||
restrictedCommands = ["blackjack", "videopoker", "dopewars", "lemonstand"]
|
||||
restrictedCommands = ["blackjack", "videopoker", "dopewars", "lemonstand", "golfsim", "mastermind"]
|
||||
restrictedResponse = "🤖only available in a Direct Message📵" # "" for none
|
||||
|
||||
# Global Variables
|
||||
@@ -24,20 +25,23 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
|
||||
# Command List
|
||||
default_commands = {
|
||||
"ping": lambda: handle_ping(message, hop, snr, rssi),
|
||||
"ping": lambda: handle_ping(message, hop, snr, rssi, isDM),
|
||||
"pong": lambda: "🏓PING!!",
|
||||
"motd": lambda: handle_motd(message, message_from_id),
|
||||
"motd": lambda: handle_motd(message, message_from_id, isDM),
|
||||
"bbshelp": bbs_help,
|
||||
"wxalert": lambda: handle_wxalert(message_from_id, deviceID, message),
|
||||
"wxa": lambda: handle_wxalert(message_from_id, deviceID, message),
|
||||
"wxc": lambda: handle_wxc(message_from_id, deviceID, 'wxc'),
|
||||
"wx": lambda: handle_wxc(message_from_id, deviceID, 'wx'),
|
||||
"wiki:": lambda: handle_wiki(message),
|
||||
"wiki:": lambda: handle_wiki(message, isDM),
|
||||
"wiki?": lambda: handle_wiki(message, isDM),
|
||||
"games": lambda: gamesCmdList,
|
||||
"dopewars": lambda: handleDopeWars(message_from_id, message, deviceID),
|
||||
"lemonstand": lambda: handleLemonade(message_from_id, message),
|
||||
"blackjack": lambda: handleBlackJack(message_from_id, message),
|
||||
"videopoker": lambda: handleVideoPoker(message_from_id, message),
|
||||
"mastermind": lambda: handleMmind(message_from_id, deviceID, message),
|
||||
"golfsim": lambda: handleGolf(message_from_id, message),
|
||||
"globalthermonuclearwar": lambda: handle_gTnW(),
|
||||
"ask:": lambda: handle_llm(message_from_id, channel_number, deviceID, message, publicChannel),
|
||||
"askai": lambda: handle_llm(message_from_id, channel_number, deviceID, message, publicChannel),
|
||||
@@ -46,21 +50,22 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
"bbspost": lambda: handle_bbspost(message, message_from_id, deviceID),
|
||||
"bbsread": lambda: handle_bbsread(message),
|
||||
"bbsdelete": lambda: handle_bbsdelete(message, message_from_id),
|
||||
"messages": lambda: handle_messages(deviceID, channel_number, msg_history, publicChannel),
|
||||
"bbsinfo": lambda: get_bbs_stats(),
|
||||
"messages": lambda: handle_messages(message, deviceID, channel_number, msg_history, publicChannel, isDM),
|
||||
"cmd": lambda: help_message,
|
||||
"history": lambda: handle_history(message_from_id, deviceID),
|
||||
"history": lambda: handle_history(message, message_from_id, deviceID, isDM),
|
||||
"sun": lambda: handle_sun(message_from_id, deviceID, channel_number),
|
||||
"hfcond": hf_band_conditions,
|
||||
"solar": lambda: drap_xray_conditions() + "\n" + solar_conditions(),
|
||||
"lheard": lambda: handle_lheard(message_from_id, deviceID),
|
||||
"sitrep": lambda: handle_lheard(message_from_id, deviceID),
|
||||
"lheard": lambda: handle_lheard(message, message_from_id, deviceID, isDM),
|
||||
"sitrep": lambda: handle_lheard(message, message_from_id, deviceID, isDM),
|
||||
"whereami": lambda: handle_whereami(message_from_id, deviceID, channel_number),
|
||||
"tide": lambda: handle_tide(message_from_id, deviceID, channel_number),
|
||||
"moon": lambda: handle_moon(message_from_id, deviceID, channel_number),
|
||||
"ack": lambda: handle_ack(hop, snr, rssi),
|
||||
"testing": lambda: handle_testing(message, hop, snr, rssi),
|
||||
"test": lambda: handle_testing(message, hop, snr, rssi),
|
||||
"whoami": lambda: handle_whoami(message_from_id, deviceID, hop, snr, rssi, pkiStatus)
|
||||
"ack": lambda: handle_ping(message, hop, snr, rssi, isDM),
|
||||
"testing": lambda: handle_ping(message, hop, snr, rssi, isDM),
|
||||
"test": lambda: handle_ping(message, hop, snr, rssi, isDM),
|
||||
"whoami": lambda: handle_whoami(message_from_id, deviceID, hop, snr, rssi, pkiStatus),
|
||||
}
|
||||
|
||||
# set the command handler
|
||||
@@ -97,24 +102,37 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
|
||||
|
||||
return bot_response
|
||||
|
||||
def handle_ping(message, hop, snr, rssi):
|
||||
if "@" in message:
|
||||
if hop == "Direct":
|
||||
return "🏓PONG, " + f"SNR:{snr} RSSI:{rssi}" + " at: " + message.split("@")[1]
|
||||
else:
|
||||
return "🏓PONG, " + hop + " at: " + message.split("@")[1]
|
||||
elif "#" in message:
|
||||
if hop == "Direct":
|
||||
return "🏓PONG, " + f"SNR:{snr} RSSI:{rssi}" + " #" + message.split("#")[1]
|
||||
else:
|
||||
return "🏓PONG, " + hop + " #" + message.split("#")[1]
|
||||
else:
|
||||
if hop == "Direct":
|
||||
return "🏓PONG, " + f"SNR:{snr} RSSI:{rssi}"
|
||||
else:
|
||||
return "🏓PONG, " + hop
|
||||
def handle_ping(message, hop, snr, rssi, isDM):
|
||||
if "?" in message and isDM:
|
||||
return message.split("?")[0].title() + " command returns SNR and RSSI, or hopcount from your message. Try adding e.g. @place or #tag"
|
||||
|
||||
msg = ""
|
||||
|
||||
def handle_motd(message, message_from_id):
|
||||
if "ping" in message.lower():
|
||||
msg = "🏓PONG, "
|
||||
elif "test" in message.lower() or "testing" in message.lower():
|
||||
msg = random.choice(["🎙Testing 1,2,3\n", "🎙Testing, ",\
|
||||
"🎙Testing, testing, ",\
|
||||
"🎙Ah-wun, ah-two... ", "🎙Is this thing on? ",\
|
||||
"🎙Roger that! ",])
|
||||
elif "ack" in message.lower():
|
||||
msg = random.choice(["✋ACK-ACK!\n", "✋Ack to you!\n"])
|
||||
else:
|
||||
msg = ""
|
||||
|
||||
if hop == "Direct":
|
||||
msg = msg + f"SNR:{snr} RSSI:{rssi}"
|
||||
else:
|
||||
msg = msg + hop
|
||||
|
||||
if "@" in message:
|
||||
msg = msg + " @" + message.split("@")[1]
|
||||
elif "#" in message:
|
||||
msg = msg + " #" + message.split("#")[1]
|
||||
|
||||
return msg
|
||||
|
||||
def handle_motd(message, message_from_id, isDM):
|
||||
global MOTD
|
||||
isAdmin = False
|
||||
msg = ""
|
||||
@@ -127,17 +145,19 @@ def handle_motd(message, message_from_id):
|
||||
else:
|
||||
isAdmin = True
|
||||
|
||||
if "$" in message and isAdmin:
|
||||
# admin help via DM
|
||||
if "?" in message and isDM and isAdmin:
|
||||
msg = "Message of the day, set with 'motd $ HelloWorld!'"
|
||||
elif "?" in message and isDM and not isAdmin:
|
||||
# non-admin help via DM
|
||||
msg = "Message of the day"
|
||||
elif "$" in message and isAdmin:
|
||||
motd = message.split("$")[1]
|
||||
MOTD = motd.rstrip()
|
||||
logger.debug(f"System: {message_from_id} changed MOTD: {MOTD}")
|
||||
msg = "MOTD changed to: " + MOTD
|
||||
elif "?" in message:
|
||||
msg = "Message of the day, set with 'motd $ HelloWorld!'"
|
||||
else:
|
||||
logger.debug(f"System: {message_from_id} requested MOTD: {MOTD} isAdmin: {isAdmin}")
|
||||
msg = "MOTD: " + MOTD
|
||||
|
||||
return msg
|
||||
|
||||
def handle_wxalert(message_from_id, deviceID, message):
|
||||
@@ -153,14 +173,16 @@ def handle_wxalert(message_from_id, deviceID, message):
|
||||
|
||||
return weatherAlert
|
||||
|
||||
def handle_wiki(message):
|
||||
def handle_wiki(message, isDM):
|
||||
# location = get_node_location(message_from_id, deviceID)
|
||||
msg = "Wikipedia search function. \nUsage example:📲wiki: travelling gnome"
|
||||
if "wiki:" in message.lower():
|
||||
search = message.split(":")[1]
|
||||
search = search.strip()
|
||||
if search:
|
||||
return get_wikipedia_summary(search)
|
||||
return "Please add a search term example:wiki: travelling gnome"
|
||||
return "Please add a search term example:📲wiki: travelling gnome"
|
||||
return msg
|
||||
|
||||
# Runtime Variables for LLM
|
||||
llmRunCounter = 0
|
||||
@@ -267,11 +289,10 @@ def handleDopeWars(nodeID, message, rxNode):
|
||||
last_cmd = dwPlayerTracker[i].get('cmd')
|
||||
|
||||
# welcome new player
|
||||
if not last_cmd:
|
||||
msg = 'Welcome to 💊Dope Wars!💉 You have ' + str(total_days) + ' days to make as much 💰 as possible! '
|
||||
if not last_cmd and nodeID != 0:
|
||||
msg = 'Welcome to 💊Dope Wars💉 You have ' + str(total_days) + ' days to make as much 💰 as possible! '
|
||||
high_score = getHighScoreDw()
|
||||
msg += 'The High Score is $' + "{:,}".format(high_score.get('cash')) + ' by user ' + get_name_from_number(high_score.get('userID') , 'short', rxNode)
|
||||
msg += 'Game Played via Direct Message.' + f'.\n'
|
||||
msg += 'The High Score is $' + "{:,}".format(high_score.get('cash')) + ' by user ' + get_name_from_number(high_score.get('userID') , 'short', rxNode) +'\n'
|
||||
msg += playDopeWars(nodeID, message)
|
||||
else:
|
||||
logger.debug("System: DopeWars: last_cmd: " + str(last_cmd))
|
||||
@@ -284,8 +305,16 @@ def handle_gTnW():
|
||||
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","?SYNTAX return[ERROR 54]"]
|
||||
return random.choice(response)
|
||||
"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)
|
||||
indices = list(range(length))
|
||||
# Shuffle the indices using a convoluted method
|
||||
for i in range(length):
|
||||
swap_idx = random.randint(0, length - 1)
|
||||
indices[i], indices[swap_idx] = indices[swap_idx], indices[i]
|
||||
# Select a random response from the shuffled list. anyone enjoy the game, killerbunnies(.com)
|
||||
selected_index = random.choice(indices)
|
||||
return response[selected_index]
|
||||
|
||||
def handleLemonade(nodeID, message):
|
||||
global lemonadeTracker, lemonadeCups, lemonadeLemons, lemonadeSugar, lemonadeWeeks, lemonadeScore, lemon_starting_cash, lemon_total_weeks
|
||||
@@ -306,9 +335,19 @@ def handleLemonade(nodeID, message):
|
||||
if lemonadeTracker[i]['nodeID'] == nodeID:
|
||||
last_cmd = lemonadeTracker[i]['cmd']
|
||||
# create new player if not in tracker
|
||||
if last_cmd == "":
|
||||
if last_cmd == "" and nodeID != 0:
|
||||
create_player(nodeID)
|
||||
msg += "Welcome to 🍋Lemonade Stand!🍋 Game Played via Direct Message."
|
||||
msg += "Welcome🍋🥤"
|
||||
|
||||
# high score
|
||||
highScore = {"userID": 0, "cash": 0, "success": 0}
|
||||
highScore = getHighScoreLemon()
|
||||
if highScore != 0:
|
||||
if highScore['userID'] != 0:
|
||||
nodeName = get_name_from_number(highScore['userID'])
|
||||
if nodeName.isnumeric() and interface2_enabled:
|
||||
nodeName = get_name_from_number(highScore['userID'], 'long', 2)
|
||||
msg += f" HighScore🥇{nodeName} 💰{highScore['cash']}k "
|
||||
|
||||
msg += start_lemonade(nodeID=nodeID, message=message, celsius=False)
|
||||
# wait a second to keep from message collision
|
||||
@@ -328,43 +367,28 @@ def handleBlackJack(nodeID, message):
|
||||
# if player sends a L for leave table
|
||||
if message.lower().startswith("l"):
|
||||
logger.debug(f"System: BlackJack: {nodeID} is leaving the table")
|
||||
# add 16 hours to the player time to leave the table, this will be detected by bot logic as player leaving
|
||||
msg = "You have left the table."
|
||||
for i in range(len(jackTracker)):
|
||||
if jackTracker[i]['nodeID'] == nodeID:
|
||||
jackTracker[i]['time'] = time.time() - 57600
|
||||
jackTracker[i]['cmd'] = "new"
|
||||
jackTracker[i]['p_cards'] = []
|
||||
jackTracker[i]['d_cards'] = []
|
||||
jackTracker[i]['p_hand'] = []
|
||||
jackTracker[i]['d_hand'] = []
|
||||
jackTracker.pop(i)
|
||||
return msg
|
||||
|
||||
# # Save the game state to pickle
|
||||
# try:
|
||||
# with open('blackjack_hs.pkl', 'wb') as file:
|
||||
# pickle.dump(jackTracker, file)
|
||||
# except FileNotFoundError:
|
||||
# logger.debug("System: BlackJack: Creating new blackjack_hs.pkl file")
|
||||
# with open('blackjack_hs.pkl', 'wb') as file:
|
||||
# pickle.dump(jackTracker, file)
|
||||
else:
|
||||
# find higest dollar amount in tracker for high score
|
||||
if last_cmd == "new":
|
||||
high_score = 0
|
||||
for i in range(len(jackTracker)):
|
||||
if jackTracker[i]['cash'] > high_score:
|
||||
high_score = int(jackTracker[i]['cash'])
|
||||
user = jackTracker[i]['nodeID']
|
||||
if user != 0:
|
||||
msg += f" Ranking🥇:{get_name_from_number(user)} with {high_score} chips. "
|
||||
|
||||
else:
|
||||
# Play BlackJack
|
||||
msg = playBlackJack(nodeID=nodeID, message=message)
|
||||
|
||||
if last_cmd != "":
|
||||
if last_cmd != "" and nodeID != 0:
|
||||
logger.debug(f"System: BlackJack: {nodeID} last command: {last_cmd}")
|
||||
time.sleep(1)
|
||||
else:
|
||||
highScore = {'nodeID': 0, 'highScore': 0}
|
||||
highScore = loadHSJack()
|
||||
if highScore != 0:
|
||||
if highScore['nodeID'] != 0:
|
||||
nodeName = get_name_from_number(highScore['nodeID'])
|
||||
if nodeName.isnumeric() and interface2_enabled:
|
||||
nodeName = get_name_from_number(highScore['nodeID'], 'long', 2)
|
||||
msg += f" HighScore🥇{nodeName} with {highScore['highScore']} chips. "
|
||||
time.sleep(1.5) # short answers with long replies can cause message collision added wait
|
||||
return msg
|
||||
|
||||
def handleVideoPoker(nodeID, message):
|
||||
@@ -374,12 +398,10 @@ def handleVideoPoker(nodeID, message):
|
||||
# if player sends a L for leave table
|
||||
if message.lower().startswith("l"):
|
||||
logger.debug(f"System: VideoPoker: {nodeID} is leaving the table")
|
||||
# add 16 hours to the player time to leave the table, this will be detected by bot logic as player leaving
|
||||
msg = "You have left the table."
|
||||
for i in range(len(vpTracker)):
|
||||
if vpTracker[i]['nodeID'] == nodeID:
|
||||
vpTracker[i]['time'] = time.time() - 57600
|
||||
vpTracker[i]['cmd'] = "new"
|
||||
vpTracker.pop(i)
|
||||
return msg
|
||||
else:
|
||||
# Play Video Poker
|
||||
@@ -393,27 +415,84 @@ def handleVideoPoker(nodeID, message):
|
||||
|
||||
# find higest dollar amount in tracker for high score
|
||||
if last_cmd == "new":
|
||||
high_score = 0
|
||||
user = 0
|
||||
for i in range(len(vpTracker)):
|
||||
if vpTracker[i]['highScore'] > high_score:
|
||||
high_score = vpTracker[i]['highScore']
|
||||
user = vpTracker[i]['nodeID']
|
||||
if user != 0:
|
||||
msg += f"\nHigh Score: {high_score} by {get_name_from_number(user)}"
|
||||
|
||||
# # Save the game high_score to pickle
|
||||
# try:
|
||||
# with open('videopoker_hs.pkl', 'wb') as file:
|
||||
# pickle.dump(high_score, file)
|
||||
# except FileNotFoundError:
|
||||
# logger.debug("System: BlackJack: Creating new videopoker_hs.pkl file")
|
||||
# with open('videopoker_hs.pkl', 'wb') as file:
|
||||
# pickle.dump(high_score, file)
|
||||
highScore = {'nodeID': 0, 'highScore': 0}
|
||||
highScore = loadHSVp()
|
||||
if highScore != 0:
|
||||
if highScore['nodeID'] != 0:
|
||||
nodeName = get_name_from_number(highScore['nodeID'])
|
||||
if nodeName.isnumeric() and interface2_enabled:
|
||||
nodeName = get_name_from_number(highScore['nodeID'], 'long', 2)
|
||||
msg += f" HighScore🥇{nodeName} with {highScore['highScore']} coins. "
|
||||
|
||||
if last_cmd != "":
|
||||
if last_cmd != "" and nodeID != 0:
|
||||
logger.debug(f"System: VideoPoker: {nodeID} last command: {last_cmd}")
|
||||
time.sleep(1)
|
||||
time.sleep(1.5) # short answers with long replies can cause message collision added wait
|
||||
return msg
|
||||
|
||||
def handleMmind(nodeID, deviceID, message):
|
||||
global mindTracker
|
||||
msg = ''
|
||||
|
||||
if "end" in message.lower() or message.lower().startswith("e"):
|
||||
logger.debug(f"System: MasterMind: {nodeID} is leaving the game")
|
||||
msg = "You have left the Game."
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
mindTracker.pop(i)
|
||||
highscore = getHighScoreMMind(0, 0, 'n')
|
||||
if highscore != 0:
|
||||
nodeName = get_name_from_number(highscore[0]['nodeID'],'long',deviceID)
|
||||
msg += f"🧠HighScore🥇{nodeName} with {highscore[0]['turns']} turns difficulty {highscore[0]['diff'].upper()}"
|
||||
return msg
|
||||
|
||||
# get player's last command from tracker if not new player
|
||||
last_cmd = ""
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
last_cmd = mindTracker[i]['cmd']
|
||||
|
||||
if last_cmd == "" and nodeID != 0:
|
||||
# create new player
|
||||
logger.debug("System: MasterMind: New Player: " + str(nodeID))
|
||||
mindTracker.append({'nodeID': nodeID, 'last_played': time.time(), 'cmd': 'new', 'secret_code': 'RYGB', 'diff': 'n', 'turns': 1})
|
||||
msg = "Welcome to 🟡🔴🔵🟢MasterMind!🧠"
|
||||
msg += "Each Guess hints to correct colors, correct position, wrong position."
|
||||
msg += "You have 10 turns to guess the code. Choose a difficulty: (N)ormal (H)ard e(X)pert"
|
||||
return msg
|
||||
|
||||
msg += start_mMind(nodeID=nodeID, message=message)
|
||||
# wait a second to keep from message collision
|
||||
time.sleep(1.5)
|
||||
return msg
|
||||
|
||||
def handleGolf(nodeID, message):
|
||||
global golfTracker
|
||||
msg = ''
|
||||
|
||||
# get player's last command from tracker if not new player
|
||||
last_cmd = ""
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
last_cmd = golfTracker[i]['cmd']
|
||||
|
||||
if "end" in message.lower() or message.lower().startswith("e"):
|
||||
logger.debug(f"System: GolfSim: {nodeID} is leaving the game")
|
||||
msg = "You have left the Game."
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
golfTracker.pop(i)
|
||||
return msg
|
||||
|
||||
if last_cmd == "" and nodeID != 0:
|
||||
# create new player
|
||||
logger.debug("System: GolfSim: New Player: " + str(nodeID))
|
||||
golfTracker.append({'nodeID': nodeID, 'last_played': time.time(), 'cmd': 'new', 'hole': 1, 'distance_remaining': 0, 'hole_shots': 0, 'hole_strokes': 0, 'hole_to_par': 0, 'total_strokes': 0, 'total_to_par': 0, 'par': 0, 'hazard': ''})
|
||||
msg = f"Welcome to 🏌️GolfSim⛳️\n"
|
||||
msg += f"Clubs: (D)river, (L)ow Iron, (M)id Iron, (H)igh Iron, (G)ap Wedge, Lob (W)edge\n"
|
||||
|
||||
msg += playGolf(nodeID=nodeID, message=message)
|
||||
# wait a second to keep from message collision
|
||||
time.sleep(1.5)
|
||||
return msg
|
||||
|
||||
def handle_wxc(message_from_id, deviceID, cmd):
|
||||
@@ -442,60 +521,75 @@ def handle_bbspost(message, message_from_id, deviceID):
|
||||
logger.info(f"System: BBS Post: {subject} Body: {body}")
|
||||
return bbs_post_message(subject, body, message_from_id)
|
||||
elif not "example:" in message:
|
||||
return "example: bbspost $subject #message"
|
||||
return "example: bbspost $subject #✉️message"
|
||||
elif "@" in message and not "example:" in message:
|
||||
toNode = message.split("@")[1].split("#")[0]
|
||||
toNode = toNode.rstrip()
|
||||
if toNode.isalpha() or not toNode.isnumeric():
|
||||
if toNode.startswith("!") and len(toNode) == 9:
|
||||
# mesh !hex
|
||||
try:
|
||||
toNode = int(toNode.strip("!"),16)
|
||||
except ValueError as e:
|
||||
toNode = 0
|
||||
elif toNode.isalpha() or not toNode.isnumeric():
|
||||
# try short name
|
||||
toNode = get_num_from_short_name(toNode, deviceID)
|
||||
|
||||
if "#" in message:
|
||||
if toNode == 0:
|
||||
return "Node not found " + message.split("@")[1].split("#")[0]
|
||||
if "#" in message:
|
||||
body = message.split("#")[1]
|
||||
return bbs_post_dm(toNode, body, message_from_id)
|
||||
else:
|
||||
return "example: bbspost @nodeNumber/ShortName #message"
|
||||
return "example: bbspost @nodeNumber/ShortName/!hex #✉️message"
|
||||
elif not "example:" in message:
|
||||
return "example: bbspost $subject #message, or bbspost @node #message"
|
||||
return "example: bbspost $subject #✉️message, or bbspost @node #✉️message"
|
||||
|
||||
def handle_bbsread(message):
|
||||
if "#" in message and not "example:" in message:
|
||||
messageID = int(message.split("#")[1])
|
||||
return bbs_read_message(messageID)
|
||||
elif not "example:" in message:
|
||||
return "Please add a message number example: bbsread #14"
|
||||
return "Please add a ✉️message number example: bbsread #14"
|
||||
|
||||
def handle_bbsdelete(message, message_from_id):
|
||||
if "#" in message and not "example:" in message:
|
||||
messageID = int(message.split("#")[1])
|
||||
return bbs_delete_message(messageID, message_from_id)
|
||||
elif not "example:" in message:
|
||||
return "Please add a message number example: bbsdelete #14"
|
||||
return "Please add a ✉️message number example: bbsdelete #14"
|
||||
|
||||
def handle_messages(deviceID, channel_number, msg_history, publicChannel):
|
||||
response = ""
|
||||
for msgH in msg_history:
|
||||
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
|
||||
def handle_messages(message, deviceID, channel_number, msg_history, publicChannel, isDM):
|
||||
if "?" in message and isDM:
|
||||
return message.split("?")[0].title() + " command returns the last " + str(storeFlimit) + " messages sent on a channel."
|
||||
else:
|
||||
return "No messages in history"
|
||||
response = ""
|
||||
for msgH in msg_history:
|
||||
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
|
||||
else:
|
||||
return "No messages in history"
|
||||
|
||||
def handle_sun(message_from_id, deviceID, channel_number):
|
||||
location = get_node_location(message_from_id, deviceID, channel_number)
|
||||
return get_sun(str(location[0]), str(location[1]))
|
||||
|
||||
def handle_lheard(nodeid, deviceID):
|
||||
# display last heard nodes add to response
|
||||
bot_response = str(get_node_list(1))
|
||||
# gather telemetry
|
||||
chutil1 = round(interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("channelUtilization", 0), 1)
|
||||
airUtilTx = round(interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("airUtilTx", 0), 1)
|
||||
uptimeSeconds = interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("uptimeSeconds", 0)
|
||||
batteryLevel = interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("batteryLevel", 0)
|
||||
voltage = interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("voltage", 0)
|
||||
def handle_lheard(message, nodeid, deviceID, isDM):
|
||||
if "?" in message and isDM:
|
||||
return message.split("?")[0].title() + " command returns a list of the nodes that have been heard recently"
|
||||
|
||||
else:
|
||||
# display last heard nodes add to response
|
||||
bot_response = str(get_node_list(1))
|
||||
# gather telemetry
|
||||
chutil1 = round(interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("channelUtilization", 0), 1)
|
||||
airUtilTx = round(interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("airUtilTx", 0), 1)
|
||||
uptimeSeconds = interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("uptimeSeconds", 0)
|
||||
batteryLevel = interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("batteryLevel", 0)
|
||||
voltage = interface1.nodes.get(decimal_to_hex(myNodeNum1), {}).get("deviceMetrics", {}).get("voltage", 0)
|
||||
if interface2_enabled:
|
||||
bot_response += "P2:\n" + str(get_node_list(2))
|
||||
chutil2 = round(interface2.nodes.get(decimal_to_hex(myNodeNum2), {}).get("deviceMetrics", {}).get("channelUtilization", 0), 1)
|
||||
@@ -510,47 +604,38 @@ def handle_lheard(nodeid, deviceID):
|
||||
if interface2_enabled:
|
||||
bot_response += " P2:" + str(chutil2) + "%" + "/" + str(airUtilTx2) + "%"
|
||||
# convert uptime to minutes, hours, or days
|
||||
if uptimeSeconds > 0 or uptimeSeconds2 > 0:
|
||||
uptimeSeconds = round(uptimeSeconds / 60)
|
||||
uptimeSeconds2 = round(uptimeSeconds2 / 60)
|
||||
designator = "m"
|
||||
if uptimeSeconds > 60 or uptimeSeconds2 > 60:
|
||||
uptimeSeconds = round(uptimeSeconds / 60)
|
||||
uptimeSeconds2 = round(uptimeSeconds2 / 60)
|
||||
designator = "h"
|
||||
if uptimeSeconds > 24 or uptimeSeconds2 > 24:
|
||||
uptimeSeconds = round(uptimeSeconds / 24)
|
||||
uptimeSeconds2 = round(uptimeSeconds2 / 24)
|
||||
designator = "d"
|
||||
uptimeSeconds = getPrettyTime(uptimeSeconds)
|
||||
uptimeSeconds2 = getPrettyTime(uptimeSeconds2)
|
||||
# add uptime and battery info to the bot response
|
||||
bot_response += "\nUptime:" + str(uptimeSeconds) + designator
|
||||
bot_response += "\nUptime:" + str(uptimeSeconds)
|
||||
if interface2_enabled:
|
||||
bot_response += f" P2:" + {uptimeSeconds2} + {designator}
|
||||
bot_response += f" P2:" + {uptimeSeconds2}
|
||||
# add battery info to the bot response
|
||||
emji = "🔌" if batteryLevel == 101 else "🪫" if batteryLevel < 10 else "🔋"
|
||||
emji2 = "🔌" if batteryLevel2 == 101 else "🪫" if batteryLevel2 < 10 else "🔋"
|
||||
if not batteryLevel == 101:
|
||||
bot_response += f" Bat: {batteryLevel}% Volt: {voltage}"
|
||||
if interface2_enabled and not batteryLevel2 == 101:
|
||||
bot_response += f" P2: Bat: {batteryLevel2}% Volt: {voltage2}"
|
||||
bot_response += f" {emji}{batteryLevel}% Volt:{voltage}"
|
||||
if interface2_enabled and not batteryLevel2 == 101:
|
||||
bot_response += f" P2:{emji2}{batteryLevel2}% Volt:{voltage2}"
|
||||
# show last users of the bot with the cmdHistory list
|
||||
history = handle_history(nodeid, deviceID, lheard=True)
|
||||
history = handle_history(message, nodeid, deviceID, isDM, lheard=True)
|
||||
if history:
|
||||
bot_response += f'\n{history}'
|
||||
return bot_response
|
||||
|
||||
def handle_history(nodeid, deviceID, lheard=False):
|
||||
def handle_history(message, nodeid, deviceID, isDM, lheard=False):
|
||||
global cmdHistory, lheardCmdIgnoreNode, bbs_admin_list
|
||||
msg = ""
|
||||
buffer = []
|
||||
|
||||
# show the last commands from the user to the bot
|
||||
if not lheard:
|
||||
if "?" in message and isDM:
|
||||
return message.split("?")[0].title() + " command returns a list of commands received."
|
||||
|
||||
# show the last commands from the user to the bot
|
||||
elif not lheard:
|
||||
for i in range(len(cmdHistory)):
|
||||
prettyTime = round((time.time() - cmdHistory[i]['time']) / 600) * 5
|
||||
if prettyTime < 60:
|
||||
prettyTime = str(prettyTime) + "m"
|
||||
elif prettyTime < 1440:
|
||||
prettyTime = str(round(prettyTime/60)) + "h"
|
||||
else:
|
||||
prettyTime = str(round(prettyTime/1440)) + "d"
|
||||
cmdTime = round((time.time() - cmdHistory[i]['time']) / 600) * 5
|
||||
prettyTime = getPrettyTime(cmdTime)
|
||||
|
||||
# history display output
|
||||
if nodeid in bbs_admin_list and cmdHistory[i]['nodeID'] not in lheardCmdIgnoreNode:
|
||||
@@ -565,18 +650,12 @@ def handle_history(nodeid, deviceID, lheard=False):
|
||||
# create the message from the buffer list
|
||||
for i in range(0, len(buffer)):
|
||||
msg += f"{buffer[i][0]}: {buffer[i][1]} :{buffer[i][2]} ago"
|
||||
if i < len(buffer) - 1:
|
||||
msg += "\n"
|
||||
if i < len(buffer) - 1: msg += "\n" # add a new line if not the last line
|
||||
else:
|
||||
# sort the cmdHistory list by time, return the username and time into a new list which used for display
|
||||
for i in range(len(cmdHistory)):
|
||||
prettyTime = round((time.time() - cmdHistory[i]['time']) / 600) * 5
|
||||
if prettyTime < 60:
|
||||
prettyTime = str(prettyTime) + "m"
|
||||
elif prettyTime < 1440:
|
||||
prettyTime = str(round(prettyTime/60)) + "h"
|
||||
else:
|
||||
prettyTime = str(round(prettyTime/1440)) + "d"
|
||||
cmdTime = round((time.time() - cmdHistory[i]['time']) / 600) * 5
|
||||
prettyTime = getPrettyTime(cmdTime)
|
||||
|
||||
if cmdHistory[i]['nodeID'] not in lheardCmdIgnoreNode:
|
||||
# add line to a new list for display
|
||||
@@ -590,10 +669,11 @@ def handle_history(nodeid, deviceID, lheard=False):
|
||||
buffer[j] = (nodeName, prettyTime)
|
||||
|
||||
# create the message from the buffer list
|
||||
buffer.reverse() # reverse the list to show the latest first
|
||||
for i in range(0, len(buffer)):
|
||||
msg += f"{buffer[i][0]} seen {buffer[i][1]} ago"
|
||||
if i < len(buffer) - 1:
|
||||
msg += "\n"
|
||||
msg += f"{buffer[i][0]}, {buffer[i][1]} ago"
|
||||
if i < len(buffer) - 1: msg += "\n" # add a new line if not the last line
|
||||
if i > 3: break # only return the last 4 nodes
|
||||
return msg
|
||||
|
||||
def handle_whereami(message_from_id, deviceID, channel_number):
|
||||
@@ -608,28 +688,6 @@ def handle_moon(message_from_id, deviceID, channel_number):
|
||||
location = get_node_location(message_from_id, deviceID, channel_number)
|
||||
return get_moon(str(location[0]), str(location[1]))
|
||||
|
||||
def handle_ack(hop, snr, rssi):
|
||||
if hop == "Direct":
|
||||
return "✋ACK-ACK! " + f"SNR:{snr} RSSI:{rssi}"
|
||||
else:
|
||||
return "✋ACK-ACK! " + hop
|
||||
|
||||
def handle_testing(message, hop, snr, rssi):
|
||||
if "@" in message:
|
||||
if hop == "Direct":
|
||||
return "🎙Testing, " + f"SNR:{snr} RSSI:{rssi}" + " at: " + message.split("@")[1]
|
||||
else:
|
||||
return "🎙Testing, " + hop + " at: " + message.split("@")[1]
|
||||
elif "#" in message:
|
||||
if hop == "Direct":
|
||||
return "🎙Testing " + f"SNR:{snr} RSSI:{rssi}" + " #" + message.split("#")[1]
|
||||
else:
|
||||
return "🎙Testing " + hop + " #" + message.split("#")[1]
|
||||
else:
|
||||
if hop == "Direct":
|
||||
return "🎙Testing 1,2,3 " + f"SNR:{snr} RSSI:{rssi}"
|
||||
else:
|
||||
return "🎙Testing 1,2,3 " + hop
|
||||
|
||||
def handle_whoami(message_from_id, deviceID, hop, snr, rssi, pkiStatus):
|
||||
loc = []
|
||||
@@ -637,15 +695,118 @@ def handle_whoami(message_from_id, deviceID, hop, snr, rssi, pkiStatus):
|
||||
str(get_name_from_number(message_from_id, 'long', deviceID) + " AKA, " +\
|
||||
str(get_name_from_number(message_from_id, 'short', deviceID)) + " AKA, " +\
|
||||
str(decimal_to_hex(message_from_id)) + f"\n")
|
||||
msg += f"I see the signal strength is {rssi} and the SNR is {snr} with hop count of {hop} \n"
|
||||
if pkiStatus[0]:
|
||||
msg += f"Your PKI bit is {pkiStatus[0]} pubKey: {pkiStatus[1]}"
|
||||
msg += f"I see the signal strength is {rssi} and the SNR is {snr} with hop count of {hop}"
|
||||
if pkiStatus[1] != 'ABC':
|
||||
msg += f"\nYour PKI bit is {pkiStatus[0]} pubKey: {pkiStatus[1]}"
|
||||
|
||||
loc = get_node_location(message_from_id, deviceID)
|
||||
if loc != [latitudeValue,longitudeValue]:
|
||||
msg += f"\nYou are at: lat:{loc[0]} lon:{loc[1]}"
|
||||
return msg
|
||||
|
||||
def checkPlayingGame(message_from_id, message_string, rxNode, channel_number):
|
||||
# Checks if in a game used for, LLM disable for duration of game plays the game.
|
||||
# Also handles stale games and resets the player if the game is older than 8 hours
|
||||
playingGame = False
|
||||
game = "None"
|
||||
|
||||
for i in range(0, len(dwPlayerTracker)):
|
||||
if dwPlayerTracker[i].get('userID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if dwPlayerTracker[i].get('last_played') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "DopeWars"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of Dope Wars")
|
||||
|
||||
#if time exceeds 8 hours reset the player
|
||||
if dwPlayerTracker[i].get('last_played') < (time.time() - GAMEDELAY):
|
||||
logger.debug(f"System: DopeWars: Resetting player {message_from_id}")
|
||||
dwPlayerTracker.pop(i)
|
||||
|
||||
# play the game
|
||||
send_message(handleDopeWars(message_from_id, message_string, rxNode), channel_number, message_from_id, rxNode)
|
||||
|
||||
for i in range(0, len(lemonadeTracker)):
|
||||
if lemonadeTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if lemonadeTracker[i].get('time') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "LemonadeStand"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of Lemonade Stand")
|
||||
|
||||
#if time exceeds 8 hours reset the player
|
||||
if lemonadeTracker[i].get('time') < (time.time() - GAMEDELAY):
|
||||
logger.debug(f"System: LemonadeStand: Resetting player {message_from_id}")
|
||||
lemonadeTracker.pop(i)
|
||||
|
||||
# play the game
|
||||
send_message(handleLemonade(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
|
||||
for i in range(0, len(vpTracker)):
|
||||
if vpTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if vpTracker[i].get('time') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "VideoPoker"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of VideoPoker")
|
||||
|
||||
# play the game
|
||||
send_message(handleVideoPoker(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# pop if the time exceeds 8 hours
|
||||
vpTracker.pop(i)
|
||||
|
||||
for i in range(0, len(jackTracker)):
|
||||
if jackTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if jackTracker[i].get('time') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "BlackJack"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of BlackJack")
|
||||
|
||||
# play the game
|
||||
send_message(handleBlackJack(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# pop if the time exceeds 8 hours
|
||||
jackTracker.pop(i)
|
||||
|
||||
for i in range(0, len(mindTracker)):
|
||||
if mindTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if mindTracker[i].get('last_played') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "MasterMind"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of MasterMind")
|
||||
|
||||
# play the game
|
||||
send_message(handleMmind(message_from_id, rxNode, message_string), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# pop if the time exceeds 8 hours
|
||||
mindTracker.pop(i)
|
||||
|
||||
for i in range(0, len(golfTracker)):
|
||||
if golfTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if golfTracker[i].get('last_played') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "GolfSim"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of GolfSim")
|
||||
|
||||
# play the game
|
||||
send_message(handleGolf(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# pop if the time exceeds 8 hours
|
||||
golfTracker.pop(i)
|
||||
|
||||
#logger.debug(f"System: {message_from_id} is playing {game}")
|
||||
return playingGame
|
||||
|
||||
|
||||
def onDisconnect(interface):
|
||||
global retry_int1, retry_int2
|
||||
@@ -674,6 +835,7 @@ def onDisconnect(interface):
|
||||
retry_int2 = True
|
||||
|
||||
def onReceive(packet, interface):
|
||||
# Priocess the incoming packet, handles the responses to the packet
|
||||
# extract interface defailts from interface object
|
||||
rxType = type(interface).__name__
|
||||
rxNode = 0
|
||||
@@ -796,79 +958,7 @@ def onReceive(packet, interface):
|
||||
else:
|
||||
# DM is useful for games or LLM
|
||||
if games_enabled:
|
||||
playingGame = False
|
||||
# if in a game we cant use LLM disable for duration of game
|
||||
for i in range(0, len(dwPlayerTracker)):
|
||||
if dwPlayerTracker[i].get('userID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if dwPlayerTracker[i].get('last_played') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "DopeWars"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of Dope Wars")
|
||||
|
||||
#if time exceeds 8 hours reset the player
|
||||
if dwPlayerTracker[i].get('last_played') < (time.time() - GAMEDELAY):
|
||||
logger.debug(f"System: DopeWars: Resetting player {message_from_id}")
|
||||
dwPlayerTracker.pop(i)
|
||||
|
||||
# play the game
|
||||
send_message(handleDopeWars(message_from_id, message_string, rxNode), channel_number, message_from_id, rxNode)
|
||||
|
||||
for i in range(0, len(lemonadeTracker)):
|
||||
if lemonadeTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if lemonadeTracker[i].get('time') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "LemonadeStand"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of Lemonade Stand")
|
||||
|
||||
#if time exceeds 8 hours reset the player
|
||||
if lemonadeTracker[i].get('time') < (time.time() - GAMEDELAY):
|
||||
logger.debug(f"System: LemonadeStand: Resetting player {message_from_id}")
|
||||
lemonadeTracker.pop(i)
|
||||
|
||||
# play the game
|
||||
send_message(handleLemonade(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
|
||||
for i in range(0, len(vpTracker)):
|
||||
if vpTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if vpTracker[i].get('time') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "VideoPoker"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of VideoPoker")
|
||||
|
||||
# play the game
|
||||
send_message(handleVideoPoker(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# reset the player if the time exceeds 8 hours
|
||||
vpTracker[i]['cmd'] = "gameOver"
|
||||
vpTracker[i]['player'] = None
|
||||
vpTracker[i]['deck'] = None
|
||||
|
||||
for i in range(0, len(jackTracker)):
|
||||
if jackTracker[i].get('nodeID') == message_from_id:
|
||||
# check if the player has played in the last 8 hours
|
||||
if jackTracker[i].get('time') > (time.time() - GAMEDELAY):
|
||||
playingGame = True
|
||||
game = "BlackJack"
|
||||
if llm_enabled:
|
||||
logger.debug(f"System: LLM Disabled for {message_from_id} for duration of BlackJack")
|
||||
|
||||
# play the game
|
||||
send_message(handleBlackJack(message_from_id, message_string), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# reset the player if the time exceeds 8 hours
|
||||
jackTracker[i]['cmd'] = "new"
|
||||
jackTracker[i]['p_cards'] = []
|
||||
jackTracker[i]['d_cards'] = []
|
||||
jackTracker[i]['p_hand'] = []
|
||||
jackTracker[i]['d_hand'] = []
|
||||
else:
|
||||
playingGame = False
|
||||
playingGame = checkPlayingGame(message_from_id, message_string, rxNode, channel_number)
|
||||
|
||||
if not playingGame:
|
||||
if llm_enabled:
|
||||
@@ -885,23 +975,26 @@ def onReceive(packet, interface):
|
||||
else:
|
||||
# message is on a channel
|
||||
if messageTrap(message_string):
|
||||
# message is for bot to respond to
|
||||
logger.info(f"Device:{rxNode} Channel:{channel_number} " + CustomFormatter.green + "Received: " + CustomFormatter.white + f"{message_string} " + CustomFormatter.purple +\
|
||||
"From: " + CustomFormatter.white + f"{get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
if useDMForResponse:
|
||||
# respond to channel message via direct message
|
||||
send_message(auto_response(message_string, snr, rssi, hop, pkiStatus, message_from_id, channel_number, rxNode, isDM), channel_number, message_from_id, rxNode)
|
||||
if ignoreDefaultChannel and channel_number == publicChannel:
|
||||
logger.debug(f"System: ignoreDefaultChannel CMD:{message_string} From: {get_name_from_number(message_from_id, 'short', rxNode)}")
|
||||
else:
|
||||
# or respond to channel message on the channel itself
|
||||
if channel_number == publicChannel and antiSpam:
|
||||
# warning user spamming default channel
|
||||
logger.error(f"System: AntiSpam protection, sending DM to: {get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
|
||||
# message is for bot to respond to
|
||||
logger.info(f"Device:{rxNode} Channel:{channel_number} " + CustomFormatter.green + "Received: " + CustomFormatter.white + f"{message_string} " + CustomFormatter.purple +\
|
||||
"From: " + CustomFormatter.white + f"{get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
if useDMForResponse:
|
||||
# respond to channel message via direct message
|
||||
send_message(auto_response(message_string, snr, rssi, hop, pkiStatus, message_from_id, channel_number, rxNode, isDM), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# respond to channel message on the channel itself
|
||||
send_message(auto_response(message_string, snr, rssi, hop, pkiStatus, message_from_id, channel_number, rxNode, isDM), channel_number, 0, rxNode)
|
||||
# or respond to channel message on the channel itself
|
||||
if channel_number == publicChannel and antiSpam:
|
||||
# warning user spamming default channel
|
||||
logger.error(f"System: AntiSpam protection, sending DM to: {get_name_from_number(message_from_id, 'long', rxNode)}")
|
||||
|
||||
# respond to channel message via direct message
|
||||
send_message(auto_response(message_string, snr, rssi, hop, pkiStatus, message_from_id, channel_number, rxNode, isDM), channel_number, message_from_id, rxNode)
|
||||
else:
|
||||
# respond to channel message on the channel itself
|
||||
send_message(auto_response(message_string, snr, rssi, hop, pkiStatus, message_from_id, channel_number, rxNode, isDM), channel_number, 0, rxNode)
|
||||
else:
|
||||
# message is not for bot to respond to
|
||||
# ignore the message but add it to the message history list
|
||||
@@ -937,8 +1030,7 @@ def onReceive(packet, interface):
|
||||
send_message(rMsg, channel_number, 0, 1)
|
||||
except KeyError as e:
|
||||
logger.critical(f"System: Error processing packet: {e} Device:{rxNode}")
|
||||
print(packet) # print the packet for debugging
|
||||
print("END of packet \n")
|
||||
logger.critical(f"System: Packet: {packet}")
|
||||
|
||||
async def start_rx():
|
||||
print (CustomFormatter.bold_white + f"\nMeshtastic Autoresponder Bot CTL+C to exit\n" + CustomFormatter.reset)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import pickle # pip install pickle
|
||||
from modules.log import *
|
||||
|
||||
trap_list_bbs = ("bbslist", "bbspost", "bbsread", "bbsdelete", "bbshelp")
|
||||
trap_list_bbs = ("bbslist", "bbspost", "bbsread", "bbsdelete", "bbshelp", "bbsinfo")
|
||||
|
||||
# global message list, later we will use a pickle on disk
|
||||
bbs_messages = []
|
||||
@@ -130,6 +130,11 @@ def bbs_post_dm(toNode, message, fromNode):
|
||||
save_bbsdm()
|
||||
return "BBS DM Posted for node " + str(toNode)
|
||||
|
||||
def get_bbs_stats():
|
||||
global bbs_messages, bbs_dm
|
||||
# Return some stats on the bbs pending messages and total posted messages
|
||||
return f"📡BBSdb has {len(bbs_messages)} messages. Direct ✉️ Messages waiting: {(len(bbs_dm) - 1)}"
|
||||
|
||||
def bbs_check_dm(toNode):
|
||||
global bbs_dm
|
||||
# Check for any messages for toNode
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from random import choices, shuffle
|
||||
from modules.log import *
|
||||
import time
|
||||
import pickle
|
||||
|
||||
jack_starting_cash = 100 # Replace 100 with your desired starting cash value
|
||||
jackTracker= [{'nodeID': 0, 'cmd': 'new', 'time': time.time(), 'cash': jack_starting_cash,\
|
||||
@@ -113,15 +114,6 @@ class jackChips:
|
||||
self.total -= self.bet
|
||||
self.winnings -= 1
|
||||
|
||||
def take_bet(bet_amount, player_money):
|
||||
try:
|
||||
if bet_amount >= player_money or bet_amount <= 0:
|
||||
return f"Enter a bet amount between 1 and {player_money}"
|
||||
return bet_amount
|
||||
|
||||
except TypeError:
|
||||
return "Invalid bet amount"
|
||||
|
||||
def success_rate(card, obj_h):
|
||||
""" Calculate Success rate of 'HIT' new cards """
|
||||
msg = ""
|
||||
@@ -132,13 +124,11 @@ def success_rate(card, obj_h):
|
||||
|
||||
if rate < 100:
|
||||
msg += f"If Hit, chance {int(rate)}% failure, {100-int(rate)}% success."
|
||||
elif rate > 100:
|
||||
else:
|
||||
l_rate = int(rate - (rate - 99)) # Round to 99
|
||||
if card[0][1] == "A":
|
||||
l_rate -= 99
|
||||
msg += f"If Hit, chance {100-l_rate}% failure, and {l_rate}% success"
|
||||
else:
|
||||
msg += "If Hit, a low chance of success."
|
||||
return msg
|
||||
|
||||
def hits(obj_de):
|
||||
@@ -216,9 +206,33 @@ def setLastCmdJack(nodeID, cmd):
|
||||
return True
|
||||
return False
|
||||
|
||||
def saveHSJack(nodeID, highScore):
|
||||
# Save the game state to pickle
|
||||
highScore = {'nodeID': nodeID, 'highScore': highScore}
|
||||
try:
|
||||
with open('blackjack_hs.pkl', 'wb') as file:
|
||||
pickle.dump(highScore, file)
|
||||
except FileNotFoundError:
|
||||
logger.debug("System: BlackJack: Creating new blackjack_hs.pkl file")
|
||||
with open('blackjack_hs.pkl', 'wb') as file:
|
||||
pickle.dump(highScore, file)
|
||||
|
||||
def loadHSJack():
|
||||
try:
|
||||
with open('blackjack_hs.pkl', 'rb') as file:
|
||||
highScore = pickle.load(file)
|
||||
return highScore
|
||||
except FileNotFoundError:
|
||||
logger.debug("System: BlackJack: Creating new blackjack_hs.pkl file")
|
||||
highScore = {'nodeID': 0, 'highScore': 0}
|
||||
with open('blackjack_hs.pkl', 'wb') as file:
|
||||
pickle.dump(highScore, file)
|
||||
return 0
|
||||
|
||||
def playBlackJack(nodeID, message):
|
||||
# Initalize the Game
|
||||
msg, last_cmd = '', None
|
||||
blackJack = False
|
||||
p_win, d_win, draw = 0, 0, 0
|
||||
p_chips = jackChips()
|
||||
p_hand = jackHand()
|
||||
@@ -243,8 +257,8 @@ def playBlackJack(nodeID, message):
|
||||
d_win = jackTracker[i]['gameStats']['d_win']
|
||||
draw = jackTracker[i]['gameStats']['draw']
|
||||
bet_money = jackTracker[i]['bet']
|
||||
p_chips.bet = bet_money
|
||||
if last_cmd == "playing":
|
||||
p_chips.bet = bet_money
|
||||
p_cards = jackTracker[i]['p_cards']
|
||||
d_cards = jackTracker[i]['d_cards']
|
||||
p_hand = jackTracker[i]['p_hand']
|
||||
@@ -256,39 +270,44 @@ def playBlackJack(nodeID, message):
|
||||
logger.debug(f"System: BlackJack: New Player {nodeID}")
|
||||
jackTracker.append({'nodeID': nodeID, 'cmd': 'new', 'time': time.time(), 'cash': jack_starting_cash,\
|
||||
'bet': 0, 'gameStats': {'p_win': p_win, 'd_win': d_win, 'draw': draw}, 'p_cards':p_cards, 'd_cards':d_cards, 'p_hand':p_hand.cards, 'd_hand':d_hand.cards, 'next_card':next_card})
|
||||
return f"Welcome to BlackJack!♠️♥️♣️♦️ you have {p_chips.total} chips. Game Played via Direct Message. Whats your bet?"
|
||||
return f"Welcome to ♠️♥️BlackJack♣️♦️ you have {p_chips.total} chips. Whats your bet?"
|
||||
|
||||
if getLastCmdJack(nodeID) == "new":
|
||||
# Place Bet
|
||||
try:
|
||||
# handle B letter
|
||||
if message == "b":
|
||||
if message.lower() == "b":
|
||||
if bet_money == 0:
|
||||
bet_money = 5
|
||||
else:
|
||||
bet_money = bet_money
|
||||
elif message.lower() == "r":
|
||||
#resend the hand
|
||||
msg += show_some(p_cards, d_cards, p_hand)
|
||||
return msg
|
||||
else:
|
||||
bet_money = int(message)
|
||||
try:
|
||||
bet_money = int(message)
|
||||
except ValueError:
|
||||
return "Invalid Bet, please enter a valid number."
|
||||
|
||||
if bet_money <= p_chips.total or bet_money <= 1:
|
||||
p_chips.bet = take_bet(bet_money, p_chips.total)
|
||||
if bet_money <= p_chips.total and bet_money >= 1:
|
||||
p_chips.bet = bet_money
|
||||
else:
|
||||
return f"Invalid Bet, the maximum bet you can place is {p_chips.total}"
|
||||
return f"Invalid Bet, the maximum bet you can place is {p_chips.total} and the minimum bet is 1."
|
||||
except ValueError:
|
||||
return f"Invalid Bet, the maximum bet, {p_chips.total}"
|
||||
|
||||
# Show the cards
|
||||
msg += show_some(p_cards, d_cards, p_hand)
|
||||
|
||||
# check for blackjack 21 and only two cards
|
||||
if p_hand.value == 21 and len(p_hand.cards) == 2:
|
||||
msg += "Player 🎰 BLAAAACKJACKKKK 💰"
|
||||
p_chips.total += round(p_chips.bet * 1.5)
|
||||
setLastCmdJack(nodeID, "dealerTurn")
|
||||
blackJack = True
|
||||
# Save the game state
|
||||
for i in range(len(jackTracker)):
|
||||
if jackTracker[i]['nodeID'] == nodeID:
|
||||
jackTracker[i]['cash'] = p_chips.total
|
||||
jackTracker[i]['cash'] = int(p_chips.total)
|
||||
break
|
||||
else:
|
||||
# Display the statistics
|
||||
@@ -296,10 +315,9 @@ def playBlackJack(nodeID, message):
|
||||
msg += stats
|
||||
setLastCmdJack(nodeID, "betPlaced")
|
||||
|
||||
|
||||
if getLastCmdJack(nodeID) == "betPlaced":
|
||||
setLastCmdJack(nodeID, "playing")
|
||||
msg += "(H)it,(S)tand,(F)orfit,(D)ouble"
|
||||
msg += "(H)it,(S)tand,(F)orfit,(D)ouble,(R)esend,(L)eave table"
|
||||
|
||||
# save the game state
|
||||
for i in range(len(jackTracker)):
|
||||
@@ -341,27 +359,29 @@ def playBlackJack(nodeID, message):
|
||||
setLastCmdJack(nodeID, "dealerTurn")
|
||||
else:
|
||||
return "You can't Double Down, dont have enough chips"
|
||||
elif choice == "resend" or choice == "r":
|
||||
msg += show_some(p_hand.cards, d_cards, p_hand)
|
||||
else:
|
||||
return "Invalid Choice"
|
||||
return "(H)it,(S)tand,(F)orfit,(D)ouble,(R)esend,(L)eave table"
|
||||
|
||||
# Check if player bust
|
||||
if player_bust(p_hand, p_chips):
|
||||
d_win += 1
|
||||
msg += "Player:BUST💥"
|
||||
msg += "💥PlayerBUST💥"
|
||||
setLastCmdJack(nodeID, "dealerTurn")
|
||||
|
||||
if getLastCmdJack(nodeID) == "playing":
|
||||
msg += stats
|
||||
msg += "[H,S,F,D,L]"
|
||||
msg += "[H,S,F,D]"
|
||||
|
||||
# Save the game state
|
||||
for i in range(len(jackTracker)):
|
||||
if jackTracker[i]['nodeID'] == nodeID:
|
||||
jackTracker[i]['cash'] = p_chips.total
|
||||
jackTracker[i]['bet'] = p_chips.bet
|
||||
jackTracker[i]['gameStats']['p_win'] = p_win
|
||||
jackTracker[i]['gameStats']['d_win'] = d_win
|
||||
jackTracker[i]['gameStats']['draw'] = draw
|
||||
jackTracker[i]['cash'] = int(p_chips.total)
|
||||
jackTracker[i]['bet'] = int(p_chips.bet)
|
||||
jackTracker[i]['gameStats']['p_win'] = int(p_win)
|
||||
jackTracker[i]['gameStats']['d_win'] = int(d_win)
|
||||
jackTracker[i]['gameStats']['draw'] = int(draw)
|
||||
jackTracker[i]['p_cards'] = p_cards
|
||||
jackTracker[i]['d_cards'] = d_cards
|
||||
jackTracker[i]['p_hand'] = p_hand
|
||||
@@ -375,20 +395,22 @@ def playBlackJack(nodeID, message):
|
||||
return msg
|
||||
|
||||
if getLastCmdJack(nodeID) == "dealerTurn":
|
||||
# Dealers Turn
|
||||
if not blackJack:
|
||||
# recall the game state
|
||||
for i in range(len(jackTracker)):
|
||||
if jackTracker[i]['nodeID'] == nodeID:
|
||||
p_chips.total = int(jackTracker[i]['cash'])
|
||||
p_chips.bet = int(jackTracker[i]['bet'])
|
||||
p_win = int(jackTracker[i]['gameStats']['p_win'])
|
||||
d_win = int(jackTracker[i]['gameStats']['d_win'])
|
||||
draw = jackTracker[i]['gameStats']['draw']
|
||||
p_cards = jackTracker[i]['p_cards']
|
||||
d_cards = jackTracker[i]['d_cards']
|
||||
p_hand = jackTracker[i]['p_hand']
|
||||
d_hand = jackTracker[i]['d_hand']
|
||||
next_card = jackTracker[i]['next_card']
|
||||
break
|
||||
for i in range(len(jackTracker)):
|
||||
if jackTracker[i]['nodeID'] == nodeID:
|
||||
p_chips.total = jackTracker[i]['cash']
|
||||
p_chips.bet = jackTracker[i]['bet']
|
||||
p_win = jackTracker[i]['gameStats']['p_win']
|
||||
d_win = jackTracker[i]['gameStats']['d_win']
|
||||
draw = jackTracker[i]['gameStats']['draw']
|
||||
p_cards = jackTracker[i]['p_cards']
|
||||
d_cards = jackTracker[i]['d_cards']
|
||||
p_hand = jackTracker[i]['p_hand']
|
||||
d_hand = jackTracker[i]['d_hand']
|
||||
next_card = jackTracker[i]['next_card']
|
||||
break
|
||||
|
||||
if p_hand.value <= 21:
|
||||
# Dealer's Turn
|
||||
@@ -397,7 +419,7 @@ def playBlackJack(nodeID, message):
|
||||
d_hand.add_cards(d_card)
|
||||
if dealer_bust(d_hand, p_hand, p_chips):
|
||||
p_win += 1
|
||||
msg += "Dealer:BUST💥"
|
||||
msg += "💰DealerBUST💥"
|
||||
break
|
||||
# Show all cards
|
||||
msg += show_all(p_hand.cards, d_hand.cards, p_hand, d_hand)
|
||||
@@ -423,12 +445,18 @@ def playBlackJack(nodeID, message):
|
||||
if p_chips.total > 0:
|
||||
msg += "🪙Keep the change you filthy animal!"
|
||||
else:
|
||||
msg += "💸NO MORE MONEY! Game Over!"
|
||||
msg += "💸NO MORE CHIPS!🏧💳"
|
||||
p_chips.total = jack_starting_cash
|
||||
else:
|
||||
msg += f"💰You have {p_chips.total} chips left"
|
||||
# check high score
|
||||
highScore = loadHSJack()
|
||||
if highScore != 0 and p_chips.total > highScore['highScore']:
|
||||
msg += f"💰HighScore💰{p_chips.total} "
|
||||
saveHSJack(nodeID, p_chips.total)
|
||||
else:
|
||||
msg += f"💰You have {p_chips.total} chips "
|
||||
|
||||
msg += "(B)et or (L)eave table."
|
||||
msg += " Bet or Leave?"
|
||||
|
||||
# Reset the game
|
||||
setLastCmdJack(nodeID, "new")
|
||||
|
||||
@@ -204,7 +204,7 @@ def buy_func(nodeID, price_list, choice=0, value='0'):
|
||||
if drug_choice in range(1, len(my_drugs) + 1):
|
||||
drug_choice = drug_choice - 1
|
||||
msg = my_drugs[drug_choice].name + ": you have🎒 " + str(amount[drug_choice]) + " "
|
||||
msg += " The going price is: $" + str(price_list[drug_choice]) + " "
|
||||
msg += " The going price is: $" + "{:,}".format(price_list[drug_choice]) + " "
|
||||
|
||||
buy_amount = value
|
||||
if buy_amount == 'm':
|
||||
@@ -228,7 +228,7 @@ def buy_func(nodeID, price_list, choice=0, value='0'):
|
||||
cash -= buy_amount * price_list[drug_choice]
|
||||
inventory += buy_amount
|
||||
msg += "You bought " + str(buy_amount) + " " + my_drugs[drug_choice].name + '. Remaining cash: $' + str(cash)
|
||||
msg += f"\nBuy Sell Fly?"
|
||||
msg += f"\nBuy💸, Sell💰, Fly🛫?"
|
||||
else:
|
||||
msg = "You don't have enough cash!😭"
|
||||
return msg
|
||||
@@ -294,7 +294,7 @@ def sell_func(nodeID, price_list, choice=0, value='0'):
|
||||
cash += sell_amount * price_list[drug_choice]
|
||||
inventory -= sell_amount
|
||||
msg += " You sold " + str(sell_amount) + " " + my_drugs[drug_choice].name + ' for $' +\
|
||||
str(sell_amount * price_list[drug_choice]) + '. Total cash: $' + str(cash)
|
||||
str(sell_amount * price_list[drug_choice]) + '. Total cash: $' + "{:,}".format(cash)
|
||||
else:
|
||||
msg = "You don't have that much"
|
||||
return msg
|
||||
@@ -421,11 +421,11 @@ def render_game_screen(userID, day_play, total_day, loc_choice, event_number, pr
|
||||
if dwCashDb[i].get('userID') == userID:
|
||||
cash = dwCashDb[i].get('cash')
|
||||
|
||||
msg += "Location: " + loc[int(loc_choice) - 1] + ", Day:" + str(day_play) + '/' + str(total_day) + " 🎒: " + str(inventory) + "/100" + ", $" + str(cash) + f"\n"
|
||||
msg += "🗺️" + loc[int(loc_choice) - 1] + " 📆" + str(day_play) + '/' + str(total_day) + " 🎒" + str(inventory) + "/100" + " 💵" + "{:,}".format(cash) + f"\n"
|
||||
|
||||
for i, drug in enumerate(my_drugs, 1):
|
||||
qty = amount[i-1]
|
||||
msg += f'#{str(i)}.{drug.name}/${price_list[i-1]}({qty}) '
|
||||
msg += f'#{str(i)}.{drug.name}${"{:,}".format(price_list[i-1])}({qty}) '
|
||||
|
||||
return msg
|
||||
|
||||
@@ -516,7 +516,7 @@ def playDopeWars(nodeID, cmd):
|
||||
dwPlayerTracker[i]['cmd'] = 'location'
|
||||
|
||||
if last_cmd == 'ask_bsf':
|
||||
msg = 'example Buy: b,Drug,Qty or Sell s,1,10. Fly: f. Price list: p or end'
|
||||
msg = f'example buy:\nb,drug#,qty# or Sell: s,1,10 qty can be (m)ax\n f,p or end'
|
||||
menu_choice = cmd.lower()
|
||||
if ',' in menu_choice or '.' in menu_choice:
|
||||
#split the choice into a letter and a number for the buy/sell functions
|
||||
@@ -539,7 +539,7 @@ def playDopeWars(nodeID, cmd):
|
||||
menu_choice[2] = int(menu_choice[2])
|
||||
|
||||
except ValueError:
|
||||
msg = 'a value was bad, example dopeware Buy or Sell b,1,10 or s,1,m'
|
||||
msg = f'a value was bad, example dopeware Buy or Sell\n b,1,10 or s,1,m'
|
||||
return msg
|
||||
|
||||
if menu_choice[0] == 'b':
|
||||
@@ -589,7 +589,7 @@ def playDopeWars(nodeID, cmd):
|
||||
msg = endGameDw(nodeID)
|
||||
return msg
|
||||
else:
|
||||
msg = 'example Buy: b,Drug,Qty or Sell s,1,10. Fly: f. Price list: p or end'
|
||||
msg = f'example buy:\nb,drug#,qty# or Sell: s,1,10 qty can be (m)ax\n f,p or end'
|
||||
return msg
|
||||
|
||||
# Buy
|
||||
@@ -639,7 +639,7 @@ def playDopeWars(nodeID, cmd):
|
||||
# Display Main Game Screen and ask for buy, sell, or fly
|
||||
if last_cmd == 'display_main':
|
||||
msg = dopeWarGameDay(nodeID, game_day, total_days)
|
||||
msg += f"\nBuy, Sell, Fly? Price list?"
|
||||
msg += f"\nBuy💸, Sell💰, (F)ly🛫? (P)riceList?"
|
||||
# set the player's last command
|
||||
for i in range(0, len(dwPlayerTracker)):
|
||||
if dwPlayerTracker[i].get('userID') == nodeID:
|
||||
|
||||
409
modules/golfsim.py
Normal file
409
modules/golfsim.py
Normal file
@@ -0,0 +1,409 @@
|
||||
# https://github.com/danfriedman30/pythongame
|
||||
# Adapted for Meshtastic mesh-bot by K7MHI Kelly Keeton 2024
|
||||
|
||||
import random
|
||||
import time
|
||||
import pickle
|
||||
from modules.log import *
|
||||
|
||||
# Clubs setup
|
||||
driver_distances = list(range(230, 280, 5))
|
||||
low_distances = list(range(185, 215, 5))
|
||||
mid_distances = list(range(130, 185, 5))
|
||||
high_distances = list(range(90, 135, 5))
|
||||
gap_wedge_distances = list(range(50, 85, 5))
|
||||
lob_wedge_distances = list(range(10, 50, 5))
|
||||
putt_outcomes = [1, 2, 3]
|
||||
|
||||
# Hole/Course Setup
|
||||
full_hole_range = list(range(130, 520, 5))
|
||||
par3_range = list(range(130, 255, 5))
|
||||
par4_range = list(range(255, 445, 5))
|
||||
par5_range = list(range(445, 520, 5))
|
||||
par3_4_range = par3_range + par4_range
|
||||
par3_5_range = par3_range + par5_range
|
||||
par4_5_range = par4_range + par5_range
|
||||
|
||||
# Player setup
|
||||
playingHole = False
|
||||
golfTracker = [{'nodeID': 0, 'last_played': time.time(), 'cmd': '', 'hole': 0, 'distance_remaining': 0, 'hole_shots': 0, 'hole_strokes': 0, 'hole_to_par': 0, 'total_strokes': 0, 'total_to_par': 0, 'par': 0, 'hazard': ''}]
|
||||
|
||||
# Club functions
|
||||
def hit_driver():
|
||||
club_distance = random.choice(driver_distances)
|
||||
return club_distance
|
||||
|
||||
def hit_low_iron():
|
||||
club_distance = random.choice(low_distances)
|
||||
return club_distance
|
||||
|
||||
def hit_mid_iron():
|
||||
club_distance = random.choice(mid_distances)
|
||||
return club_distance
|
||||
|
||||
def hit_high_iron():
|
||||
club_distance = random.choice(high_distances)
|
||||
return club_distance
|
||||
|
||||
def hit_gap_wedge():
|
||||
club_distance = random.choice(gap_wedge_distances)
|
||||
return club_distance
|
||||
|
||||
def hit_lob_wedge():
|
||||
club_distance = random.choice(lob_wedge_distances)
|
||||
return club_distance
|
||||
|
||||
def finish_hole():
|
||||
finish = random.choice(putt_outcomes)
|
||||
return finish
|
||||
|
||||
def endGameGolf(nodeID):
|
||||
# pop player from tracker
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
golfTracker.pop(i)
|
||||
logger.debug("System: GolfSim: Player " + str(nodeID) + " has ended their round.")
|
||||
|
||||
def getScorecardGolf(scorecard):
|
||||
# Scorecard messages, convert score to message comment
|
||||
msg = ""
|
||||
if scorecard == 8:
|
||||
# Quadruple bogey
|
||||
msg += " +Quad Bogey☃️ "
|
||||
elif scorecard == 7:
|
||||
# Triple bogey
|
||||
msg += " +Triple Bogey "
|
||||
elif scorecard == 6:
|
||||
# Double bogey
|
||||
msg += " +Double Bogey "
|
||||
elif scorecard == 5:
|
||||
# Bogey
|
||||
msg += " +Bogey "
|
||||
elif scorecard > 0:
|
||||
# Over par
|
||||
msg += f" +Par {str(scorecard)} "
|
||||
elif scorecard == 0:
|
||||
# Even par
|
||||
msg += " Even Par💪 "
|
||||
elif scorecard == -1:
|
||||
# Birdie
|
||||
msg += " -Birdie🐦 "
|
||||
elif scorecard == -2:
|
||||
# Eagle
|
||||
msg += " -Eagle🦅 "
|
||||
elif scorecard == -3:
|
||||
# Albatross
|
||||
msg += " -Albatross🦅🦅 "
|
||||
else:
|
||||
# Under par
|
||||
msg += f" -Par {str(abs(scorecard))} "
|
||||
return msg
|
||||
|
||||
def getHighScoreGolf(nodeID, strokes, par):
|
||||
# check if player is in high score list
|
||||
try:
|
||||
with open('golfsim_hs.pkl', 'rb') as f:
|
||||
golfHighScore = pickle.load(f)
|
||||
except:
|
||||
logger.debug("System: GolfSim: High Score file not found.")
|
||||
golfHighScore = [{'nodeID': nodeID, 'strokes': strokes, 'par': par}]
|
||||
with open('golfsim_hs.pkl', 'wb') as f:
|
||||
pickle.dump(golfHighScore, f)
|
||||
|
||||
if strokes < golfHighScore[0]['strokes']:
|
||||
# player got new low score which is high score
|
||||
golfHighScore[0]['nodeID'] = nodeID
|
||||
golfHighScore[0]['strokes'] = strokes
|
||||
golfHighScore[0]['par'] = par
|
||||
with open('golfsim_hs.pkl', 'wb') as f:
|
||||
pickle.dump(golfHighScore, f)
|
||||
return golfHighScore
|
||||
|
||||
return 0
|
||||
|
||||
# Main game loop
|
||||
def playGolf(nodeID, message, finishedHole=False):
|
||||
msg = ''
|
||||
global golfTracker
|
||||
# Course setup
|
||||
par3_count = 0
|
||||
par4_count = 0
|
||||
par5_count = 0
|
||||
# Scorecard setup
|
||||
total_strokes = 0
|
||||
total_to_par = 0
|
||||
par = 0
|
||||
|
||||
# get player's last command from tracker if not new player
|
||||
last_cmd = ""
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
last_cmd = golfTracker[i]['cmd']
|
||||
hole = golfTracker[i]['hole']
|
||||
distance_remaining = golfTracker[i]['distance_remaining']
|
||||
hole_shots = golfTracker[i]['hole_shots']
|
||||
par = golfTracker[i]['par']
|
||||
total_strokes = golfTracker[i]['total_strokes']
|
||||
total_to_par = golfTracker[i]['total_to_par']
|
||||
|
||||
if last_cmd == "" or last_cmd == "new":
|
||||
# Start a new hole
|
||||
if hole <= 9:
|
||||
# Set up hole count restrictions on par
|
||||
if par3_count < 2 and par4_count < 5 and par5_count < 2:
|
||||
hole_length = random.choice(full_hole_range)
|
||||
if par3_count >= 2 and par4_count < 5 and par5_count < 2:
|
||||
hole_length = random.choice(par4_5_range)
|
||||
if par3_count >= 2 and par4_count < 5 and par5_count >= 2:
|
||||
hole_length = random.choice(par4_range)
|
||||
if par3_count < 2 and par4_count < 5 and par5_count >= 2:
|
||||
hole_length = random.choice(par3_4_range)
|
||||
if par3_count < 2 and par4_count >= 5 and par5_count >= 2:
|
||||
hole_length = random.choice(par3_range)
|
||||
if par3_count >= 2 and par4_count >= 5 and par5_count < 2:
|
||||
hole_length = random.choice(par5_range)
|
||||
if par3_count < 2 and par4_count >= 5 and par5_count < 2:
|
||||
hole_length = random.choice(par3_5_range)
|
||||
|
||||
# Set up par for the hole
|
||||
if hole_length <= 250:
|
||||
par = 3
|
||||
par3_count += 1
|
||||
elif hole_length > 250 and hole_length <= 440:
|
||||
par = 4
|
||||
par4_count += 1
|
||||
elif hole_length > 440:
|
||||
par = 5
|
||||
par5_count += 1
|
||||
|
||||
# roll for chance of hazard
|
||||
hazard_chance = random.randint(1, 100)
|
||||
weather_chance = random.randint(1, 100)
|
||||
# have low chances of hazards and weather
|
||||
hasHazard = False
|
||||
hazard = ""
|
||||
if hazard_chance < 25:
|
||||
# Further reduce chance of hazards with weather
|
||||
if weather_chance < 15:
|
||||
# randomly calculate a hazard for the hole sand, 🌊, 🌲, 🏘️, etc
|
||||
hazard = random.choice(["🏖️", "🌊", "🌲", "🏘️"])
|
||||
hasHazard = True
|
||||
|
||||
|
||||
# Set initial parameters before starting a hole
|
||||
distance_remaining = hole_length
|
||||
hole_shots = 0
|
||||
|
||||
# save player's current game state
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
golfTracker[i]['distance_remaining'] = distance_remaining
|
||||
golfTracker[i]['cmd'] = 'stroking'
|
||||
golfTracker[i]['par'] = par
|
||||
golfTracker[i]['total_strokes'] = total_strokes
|
||||
golfTracker[i]['total_to_par'] = total_to_par
|
||||
golfTracker[i]['hazard'] = hazard
|
||||
golfTracker[i]['hole'] = hole
|
||||
golfTracker[i]['last_played'] = time.time()
|
||||
golfTracker[i]['hole_shots'] = hole_shots
|
||||
|
||||
# Show player the hole information
|
||||
msg += "⛳️#" + str(hole) + " is a " + str(hole_length) + "-yard Par " + str(par) + "."
|
||||
if hasHazard:
|
||||
msg += "⚠️" + hazard + "."
|
||||
else:
|
||||
# add weather conditions with random choice from list, this is fluff
|
||||
msg += random.choice(["☀️", "💨", "☀️", "☀️", "⛅️", "☁️", "☀️"])
|
||||
|
||||
if not finishedHole:
|
||||
msg += f"\nChoose your club."
|
||||
|
||||
return msg
|
||||
|
||||
if last_cmd == 'stroking':
|
||||
|
||||
# Get player's current game state
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
distance_remaining = golfTracker[i]['distance_remaining']
|
||||
hole = golfTracker[i]['hole']
|
||||
hole_shots = golfTracker[i]['hole_shots']
|
||||
par = golfTracker[i]['par']
|
||||
total_strokes = golfTracker[i]['total_strokes']
|
||||
total_to_par = golfTracker[i]['total_to_par']
|
||||
hazard = golfTracker[i]['hazard']
|
||||
|
||||
# Start loop to be able to choose clubs while at least 20 yards away
|
||||
if distance_remaining >= 20:
|
||||
msg = ""
|
||||
club = message.lower()
|
||||
shot_distance = 0
|
||||
|
||||
pin_distance = distance_remaining
|
||||
|
||||
if club == "driver" or club.startswith("d"):
|
||||
shot_distance = hit_driver()
|
||||
msg += "🏌️Hit D " + str(shot_distance) + "yd. "
|
||||
distance_remaining = abs(distance_remaining - shot_distance)
|
||||
hole_shots += 1
|
||||
elif "low" in club or club.startswith("l"):
|
||||
shot_distance = hit_low_iron()
|
||||
msg += "🏌️Hit L Iron " + str(shot_distance) + "yd. "
|
||||
distance_remaining = abs(distance_remaining - shot_distance)
|
||||
hole_shots += 1
|
||||
elif "mid" in club or club.startswith("m"):
|
||||
shot_distance = hit_mid_iron()
|
||||
msg += "🏌️Hit M Iron " + str(shot_distance) + "yd. "
|
||||
distance_remaining = abs(distance_remaining - shot_distance)
|
||||
hole_shots += 1
|
||||
elif "high" in club or club.startswith("h"):
|
||||
shot_distance = hit_high_iron()
|
||||
msg += "🏌️Hit H Iron " + str(shot_distance) + "yd. "
|
||||
distance_remaining = abs(distance_remaining - shot_distance)
|
||||
hole_shots += 1
|
||||
elif "gap" in club or club.startswith("g"):
|
||||
shot_distance = hit_gap_wedge()
|
||||
msg += "🏌️Hit G Wedge " + str(shot_distance) + "yd ."
|
||||
distance_remaining = abs(distance_remaining - shot_distance)
|
||||
hole_shots += 1
|
||||
elif "wedge" in club or club.startswith("w"):
|
||||
shot_distance = hit_lob_wedge()
|
||||
msg += "🏌️Hit L Wedge " + str(shot_distance) + "yd. "
|
||||
distance_remaining = abs(distance_remaining - shot_distance)
|
||||
hole_shots += 1
|
||||
elif club == "caddy" or club.startswith("c"):
|
||||
# Show player the club distances
|
||||
msg += f"Caddy Guess:\nD:{hit_driver()} L:{hit_low_iron()} M:{hit_mid_iron()} H:{hit_high_iron()} G:{hit_gap_wedge()} W:{hit_lob_wedge()}"
|
||||
else:
|
||||
msg += "Didnt get your club 🥪♣️🪩 choice"
|
||||
return msg
|
||||
|
||||
if distance_remaining - pin_distance > pin_distance or shot_distance > pin_distance:
|
||||
# Check for over-shooting the hole
|
||||
if distance_remaining > 20:
|
||||
# did it go off the "green"?
|
||||
msg += "Overshot the green!🚀"
|
||||
if distance_remaining == 0:
|
||||
msg += "🎯Perfect shot! "
|
||||
last_cmd = 'putt'
|
||||
elif distance_remaining < 20:
|
||||
# Roll Dice
|
||||
hole_in_one_chance = random.randint(1, 100)
|
||||
wind_factor = random.randint(1, 10)
|
||||
skill_factor = random.randint(1, 10)
|
||||
critter_factor = random.randint(1, 50)
|
||||
|
||||
# Check for hole in one
|
||||
if hole_in_one_chance <= 5 and wind_factor > 7 and skill_factor > 8:
|
||||
distance_remaining = 0
|
||||
# Check for critters
|
||||
if skill_factor > 8 and critter_factor < 40 and wind_factor > 2 and hole_in_one_chance > 5:
|
||||
msg += random.choice(["A 🐿️ steals your ball!😡 ","You Hit a 🦅 soring past ", "🐊 need we say more? ", "hit a 🪟 of a 🏡 "])
|
||||
distance_remaining = -1
|
||||
# Handle hazard
|
||||
if hazard == "🌊" and skill_factor < 7:
|
||||
msg += "In the water!🌊"
|
||||
distance_remaining = -1
|
||||
if hazard == "🏖️" and skill_factor < 5:
|
||||
msg += "In the sand!🏖️"
|
||||
distance_remaining = random.randint(5, 10)
|
||||
if hazard == "🌲" and skill_factor < 3:
|
||||
msg += "In the trees!🌲"
|
||||
distance_remaining += random.randint(5, 20)
|
||||
if hazard == "🏘️" and skill_factor < 2:
|
||||
msg += "In the parking lot!🚗"
|
||||
distance_remaining += random.randint(10, 30)
|
||||
|
||||
# Check we didnt go off the green or into a hazard
|
||||
if distance_remaining < 20:
|
||||
last_cmd = 'putt'
|
||||
else:
|
||||
last_cmd = 'stroking'
|
||||
else:
|
||||
msg += "\nYou have " + str(distance_remaining) + "yd. ⛳️"
|
||||
msg += "\nClub?[D, L, M, H, G, W]🏌️"
|
||||
|
||||
# save player's current game state, keep stroking
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
golfTracker[i]['distance_remaining'] = distance_remaining
|
||||
golfTracker[i]['hole_shots'] = hole_shots
|
||||
golfTracker[i]['total_strokes'] = total_strokes
|
||||
golfTracker[i]['cmd'] = 'stroking'
|
||||
|
||||
return msg
|
||||
|
||||
if last_cmd == 'putt':
|
||||
# Finish the hole by putting
|
||||
critter = False
|
||||
if distance_remaining < 20:
|
||||
if distance_remaining == 0:
|
||||
putts = 0
|
||||
elif distance_remaining == -1:
|
||||
putts = 0
|
||||
critter = True
|
||||
else:
|
||||
putts = finish_hole()
|
||||
|
||||
# Calculate hole and round scores
|
||||
hole_strokes = hole_shots + putts
|
||||
hole_to_par = hole_strokes - par
|
||||
total_strokes += hole_strokes
|
||||
total_to_par += hole_to_par
|
||||
|
||||
|
||||
if not critter:
|
||||
# Show player hole/round scoring info
|
||||
if putts == 0 and hole_strokes == 1:
|
||||
msg += "🎯Hole in one!⛳️"
|
||||
elif putts == 0:
|
||||
msg += "You're in the hole at " + str(hole_strokes) + " strokes!"
|
||||
else:
|
||||
msg += "You're on the green! After " + str(putts) + " putt(s), you're in for " + str(hole_strokes) + " strokes."
|
||||
msg += getScorecardGolf(hole_to_par)
|
||||
|
||||
if hole not in [1, 10]:
|
||||
# Show player total scoring info for the round, except hole 1 and 10
|
||||
msg += "\nYou've hit a total of " + str(total_strokes) + " strokes today, for"
|
||||
msg += getScorecardGolf(total_to_par)
|
||||
|
||||
# Move to next hole
|
||||
hole += 1
|
||||
else:
|
||||
msg += f"Got a new ball at Pro-Shop, marshal put you @" # flow into same hole haha
|
||||
|
||||
# Scorecard reset
|
||||
hole_to_par = 0
|
||||
total_to_par = 0
|
||||
hole_strokes = 0
|
||||
hole_shots = 0
|
||||
|
||||
# Save player's current game state
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
golfTracker[i]['hole_strokes'] = hole_strokes
|
||||
golfTracker[i]['hole_to_par'] = hole_to_par
|
||||
golfTracker[i]['total_strokes'] = total_strokes
|
||||
golfTracker[i]['total_to_par'] = total_to_par
|
||||
golfTracker[i]['hole'] = hole
|
||||
golfTracker[i]['cmd'] = 'new'
|
||||
golfTracker[i]['last_played'] = time.time()
|
||||
|
||||
if hole >= 9:
|
||||
# Final score messages & exit prompt
|
||||
msg += f"🎉Finished 9-hole round⛳️"
|
||||
#HighScore Display
|
||||
highscore = getHighScoreGolf(nodeID, total_strokes, total_to_par)
|
||||
if highscore != 0:
|
||||
msg += "\n🏆New Club Record🏆"
|
||||
# pop player from tracker
|
||||
for i in range(len(golfTracker)):
|
||||
if golfTracker[i]['nodeID'] == nodeID:
|
||||
golfTracker.pop(i)
|
||||
logger.debug("System: GolfSim: Player " + str(nodeID) + " has finished their round.")
|
||||
else:
|
||||
# Show player the next hole
|
||||
msg += playGolf(nodeID, 'new', True)
|
||||
msg += "\n🏌️[D, L, M, H, G, W, End]🏌️"
|
||||
|
||||
return msg
|
||||
@@ -38,15 +38,13 @@ def get_sales_amount(potential, unit, price):
|
||||
return math.floor(potential * (unit / (price ** 1.5)))
|
||||
|
||||
def getHighScoreLemon():
|
||||
global high_score
|
||||
high_score = {"userID": 0, "cash": 0, "success": 0}
|
||||
# Load high score table
|
||||
try:
|
||||
with open('lemonade_hs.pkl', 'rb') as file:
|
||||
high_score = pickle.load(file)
|
||||
except FileNotFoundError:
|
||||
logger.debug("System: Lemonade: No high score table found")
|
||||
# high score pickle file is a touple of the nodeID and the high score
|
||||
high_score = ({"userID": 4258675309, "cash": 2, "success": 0})
|
||||
# write a new high score file if one is not found
|
||||
with open('lemonade_hs.pkl', 'wb') as file:
|
||||
pickle.dump(high_score, file)
|
||||
|
||||
@@ -59,14 +59,14 @@ stdout_handler.setFormatter(CustomFormatter(logFormat))
|
||||
logger.addHandler(stdout_handler)
|
||||
if syslog_to_file:
|
||||
# Create file handler for logging to a file
|
||||
file_handler = logging.FileHandler('system{}.log'.format(today.strftime('%Y_%m_%d')))
|
||||
file_handler = logging.FileHandler('logs/system{}.log'.format(today.strftime('%Y_%m_%d')))
|
||||
file_handler.setLevel(logging.DEBUG) # DEBUG used by default for system logs to disk
|
||||
file_handler.setFormatter(logging.Formatter(logFormat))
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
if log_messages_to_file:
|
||||
# Create file handler for logging to a file
|
||||
file_handler = logging.FileHandler('messages{}.log'.format(today.strftime('%Y_%m_%d')))
|
||||
file_handler = logging.FileHandler('logs/messages{}.log'.format(today.strftime('%Y_%m_%d')))
|
||||
file_handler.setLevel(logging.INFO) # INFO used for messages to disk
|
||||
file_handler.setFormatter(logging.Formatter(msgLogFormat))
|
||||
msgLogger.addHandler(file_handler)
|
||||
|
||||
341
modules/mmind.py
Normal file
341
modules/mmind.py
Normal file
@@ -0,0 +1,341 @@
|
||||
# https://github.com/pwdkramer/pythonMastermind/blob/main/main.py
|
||||
# Adapted for Meshtastic mesh-bot by K7MHI Kelly Keeton 2024
|
||||
|
||||
import random
|
||||
import time
|
||||
import pickle
|
||||
from modules.log import *
|
||||
|
||||
mindTracker = [{'nodeID': 0, 'last_played': time.time(), 'cmd': '', 'secret_code': '', 'diff': 'n', 'turns': 1}]
|
||||
|
||||
def chooseDifficultyMMind(message):
|
||||
usrInput = message.lower()
|
||||
msg = ''
|
||||
valid_colorsMMind = "RYGB"
|
||||
|
||||
if not usrInput.startswith("n") and not usrInput.startswith("h") and not usrInput.startswith("x"):
|
||||
# default to normal difficulty
|
||||
usrInput = "n"
|
||||
|
||||
if usrInput == "n":
|
||||
msg += f"The colors to choose from are:\nR🔴, Y🟡, G🟢, B🔵"
|
||||
elif usrInput == "h":
|
||||
valid_colorsMMind += "OP"
|
||||
msg += f"The colors to choose from are\nR🔴, Y🟡, G🟢, B🔵, O🟠, P🟣"
|
||||
elif usrInput == "x":
|
||||
valid_colorsMMind += "OPWK"
|
||||
msg += f"The colors to choose from are\nR🔴, Y🟡, G🟢, B🔵, O🟠, P🟣, W⚪, K⚫"
|
||||
return msg
|
||||
|
||||
|
||||
#possible colors on nomral: Red, Yellow, Green, Blue
|
||||
#added colors on hard: Orange, Purple
|
||||
def makeCodeMMind(diff):
|
||||
secret_code = ""
|
||||
for i in range(4):
|
||||
if diff == "n":
|
||||
roll = random.randrange(1, 5)
|
||||
elif diff == "h":
|
||||
roll = random.randrange(1,7)
|
||||
elif diff == "x":
|
||||
roll = random.randrange(1,9)
|
||||
else:
|
||||
print("Difficulty error in makeCode()")
|
||||
if roll == 1:
|
||||
secret_code += "R"
|
||||
elif roll == 2:
|
||||
secret_code += "Y"
|
||||
elif roll == 3:
|
||||
secret_code += "G"
|
||||
elif roll == 4:
|
||||
secret_code += "B"
|
||||
elif roll == 5:
|
||||
secret_code += "O"
|
||||
elif roll == 6:
|
||||
secret_code += "P"
|
||||
elif roll == 7:
|
||||
secret_code += "W"
|
||||
elif roll == 8:
|
||||
secret_code += "K"
|
||||
else:
|
||||
print("Error with range of roll in makeCode()")
|
||||
return secret_code
|
||||
|
||||
#get guess from user
|
||||
def getGuessMMind(diff, guess):
|
||||
msg = ''
|
||||
if diff == "n":
|
||||
valid_colorsMMind = "RYGB"
|
||||
elif diff == "h":
|
||||
valid_colorsMMind = "RYGBOP"
|
||||
elif diff == "x":
|
||||
valid_colorsMMind = "RYGBOPWK"
|
||||
|
||||
user_guess = guess.upper()
|
||||
valid_guess = True
|
||||
if len(user_guess) != 4:
|
||||
valid_guess = False
|
||||
for i in range(len(user_guess)):
|
||||
if user_guess[i] not in valid_colorsMMind:
|
||||
valid_guess = False
|
||||
if valid_guess == False:
|
||||
user_guess = "XXXX"
|
||||
return user_guess
|
||||
|
||||
def getHighScoreMMind(nodeID, turns, diff):
|
||||
# check if player is in high score list and pick the lowest score
|
||||
try:
|
||||
with open('mmind_hs.pkl', 'rb') as f:
|
||||
mindHighScore = pickle.load(f)
|
||||
except:
|
||||
logger.debug("System: MasterMind: High Score file not found.")
|
||||
mindHighScore = [{'nodeID': nodeID, 'turns': turns, 'diff': diff}]
|
||||
with open('mmind_hs.pkl', 'wb') as f:
|
||||
pickle.dump(mindHighScore, f)
|
||||
|
||||
if nodeID == 0:
|
||||
# just return the high score
|
||||
return mindHighScore
|
||||
|
||||
# calculate lowest score
|
||||
lowest_score = mindHighScore[0]['turns']
|
||||
|
||||
if mindHighScore[0]['diff'] == "n" and diff == "n":
|
||||
if lowest_score > turns:
|
||||
# update the high score for normal if new score is lower
|
||||
mindHighScore[0]['nodeID'] = nodeID
|
||||
mindHighScore[0]['turns'] = turns
|
||||
mindHighScore[0]['diff'] = diff
|
||||
|
||||
# write new high score to file
|
||||
with open('mmind_hs.pkl', 'wb') as f:
|
||||
pickle.dump(mindHighScore, f)
|
||||
return mindHighScore
|
||||
elif mindHighScore[0]['diff'] == "n" and diff == "h":
|
||||
# update the high score for hard if normal is the only high score
|
||||
mindHighScore[0]['nodeID'] = nodeID
|
||||
mindHighScore[0]['turns'] = turns
|
||||
mindHighScore[0]['diff'] = diff
|
||||
|
||||
# write new high score to file
|
||||
with open('mmind_hs.pkl', 'wb') as f:
|
||||
pickle.dump(mindHighScore, f)
|
||||
return mindHighScore
|
||||
elif mindHighScore[0]['diff'] == "h" and diff == "h":
|
||||
if lowest_score > turns:
|
||||
# update the high score for hard if new score is lower
|
||||
mindHighScore[0]['nodeID'] = nodeID
|
||||
mindHighScore[0]['turns'] = turns
|
||||
mindHighScore[0]['diff'] = diff
|
||||
|
||||
# write new high score to file
|
||||
with open('mmind_hs.pkl', 'wb') as f:
|
||||
pickle.dump(mindHighScore, f)
|
||||
return mindHighScore
|
||||
elif mindHighScore[0]['diff'] == "n" or mindHighScore[0]['diff'] == "h" and diff == "x":
|
||||
# update the high score for expert if normal or high is the only high score
|
||||
mindHighScore[0]['nodeID'] = nodeID
|
||||
mindHighScore[0]['turns'] = turns
|
||||
mindHighScore[0]['diff'] = diff
|
||||
|
||||
# write new high score to file
|
||||
with open('mmind_hs.pkl', 'wb') as f:
|
||||
pickle.dump(mindHighScore, f)
|
||||
return mindHighScore
|
||||
elif mindHighScore[0]['diff'] == "x" and diff == "x":
|
||||
if lowest_score > turns:
|
||||
# update the high score for expert if new score is lower
|
||||
mindHighScore[0]['nodeID'] = nodeID
|
||||
mindHighScore[0]['turns'] = turns
|
||||
mindHighScore[0]['diff'] = diff
|
||||
|
||||
# write new high score to file
|
||||
with open('mmind_hs.pkl', 'wb') as f:
|
||||
pickle.dump(mindHighScore, f)
|
||||
return mindHighScore
|
||||
return 0
|
||||
|
||||
|
||||
def getEmojiMMind(secret_code):
|
||||
# for each letter in the secret code, convert to emoji for display
|
||||
secret_code = secret_code.upper()
|
||||
secret_code_emoji = ""
|
||||
for i in range(len(secret_code)):
|
||||
if secret_code[i] == "R":
|
||||
secret_code_emoji += "🔴"
|
||||
elif secret_code[i] == "Y":
|
||||
secret_code_emoji += "🟡"
|
||||
elif secret_code[i] == "G":
|
||||
secret_code_emoji += "🟢"
|
||||
elif secret_code[i] == "B":
|
||||
secret_code_emoji += "🔵"
|
||||
elif secret_code[i] == "O":
|
||||
secret_code_emoji += "🟠"
|
||||
elif secret_code[i] == "P":
|
||||
secret_code_emoji += "🟣"
|
||||
elif secret_code[i] == "W":
|
||||
secret_code_emoji += "⚪"
|
||||
elif secret_code[i] == "K":
|
||||
secret_code_emoji += "⚫"
|
||||
elif secret_code[i] == "X":
|
||||
secret_code_emoji += "❌"
|
||||
return secret_code_emoji
|
||||
|
||||
#compare userGuess with secret code and provide feedback
|
||||
def compareCodeMMind(secret_code, user_guess):
|
||||
game_won = False
|
||||
perfect_pins = 0
|
||||
wrong_position = 0
|
||||
msg = ''
|
||||
#logger.debug("System: MasterMind: secret_code: " + str(secret_code) + " user_guess: " + str(user_guess))
|
||||
if secret_code == user_guess: #correct guess, user wins
|
||||
perfect_pins = 4
|
||||
game_won = True
|
||||
else:
|
||||
# check for perfect pins and right color wrong position
|
||||
temp_code = []
|
||||
temp_guess = []
|
||||
for i in range(len(user_guess)): #check for perfect pins
|
||||
if user_guess[i] == secret_code[i]:
|
||||
perfect_pins += 1
|
||||
else:
|
||||
temp_code.append(secret_code[i])
|
||||
temp_guess.append(user_guess[i])
|
||||
for i in range(len(temp_guess)): #check for right color wrong position
|
||||
for j in range(len(temp_code)):
|
||||
if temp_guess[i] == temp_code[j]:
|
||||
wrong_position += 1
|
||||
temp_code[j] = "0"
|
||||
break
|
||||
# display feedback
|
||||
if game_won:
|
||||
msg += f"Correct{getEmojiMMind(user_guess)}\n"
|
||||
else:
|
||||
msg += f"Guess{getEmojiMMind(user_guess)}\n"
|
||||
|
||||
if perfect_pins > 0 and game_won == False:
|
||||
msg += "✅ color ✅ position: {}".format(perfect_pins)
|
||||
|
||||
if wrong_position > 0:
|
||||
if "✅" in msg: msg += f"\n"
|
||||
msg += "✅ color 🚫 position: {}".format(wrong_position)
|
||||
|
||||
if "✅" not in msg and game_won == False:
|
||||
msg += "🚫No pins in your guess😿 are in the code!"
|
||||
|
||||
return msg
|
||||
|
||||
#game loop with turn counter
|
||||
def playGameMMind(diff, secret_code, turn_count, nodeID, message):
|
||||
msg = ''
|
||||
won = False
|
||||
if turn_count <= 10:
|
||||
user_guess = getGuessMMind(diff, message)
|
||||
if user_guess == "XXXX":
|
||||
msg += "Invalid guess. Please enter 4 valid colors."
|
||||
return msg
|
||||
check_guess = compareCodeMMind(secret_code, user_guess)
|
||||
|
||||
# display turn count and feedback
|
||||
msg += "Turn {}:".format(turn_count)
|
||||
if check_guess.startswith("Correct"):
|
||||
won = True
|
||||
msg += check_guess
|
||||
|
||||
if won == True:
|
||||
msg += f"\n🎉🧠 you win 🥷🤯"
|
||||
# get high score
|
||||
high_score = getHighScoreMMind(nodeID, turn_count, diff)
|
||||
if high_score != 0:
|
||||
msg += f"\n🏆 High Score:{high_score[0]['turns']} turns, Difficulty:{high_score[0]['diff'].upper()}"
|
||||
|
||||
msg += "\nWould you like to play again?\n(N)ormal, (H)ard, e(X)pert?"
|
||||
# reset turn count in tracker
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
mindTracker[i]['turns'] = 1
|
||||
mindTracker[i]['secret_code'] = ''
|
||||
mindTracker[i]['cmd'] = 'new'
|
||||
else:
|
||||
# increment turn count and keep playing
|
||||
turn_count += 1
|
||||
# store turn count in tracker
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
mindTracker[i]['turns'] = turn_count
|
||||
elif won == False:
|
||||
msg += f"🙉Game Over🙈\nThe code was: {getEmojiMMind(secret_code)}"
|
||||
msg += "\nYou have run out of turns.😿"
|
||||
msg += "\nWould you like to play again? (N)ormal, (H)ard, or e(X)pert?"
|
||||
# reset turn count in tracker
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
mindTracker[i]['turns'] = 1
|
||||
mindTracker[i]['secret_code'] = ''
|
||||
mindTracker[i]['cmd'] = 'new'
|
||||
|
||||
return msg
|
||||
|
||||
def endGameMMind(nodeID):
|
||||
global mindTracker
|
||||
# remove player from tracker
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
del mindTracker[i]
|
||||
logger.debug("System: MasterMind: Player removed: " + str(nodeID))
|
||||
break
|
||||
|
||||
#main game
|
||||
def start_mMind(nodeID, message):
|
||||
global mindTracker
|
||||
last_cmd = ""
|
||||
msg = ''
|
||||
|
||||
# get player's last command from tracker if not new player
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
last_cmd = mindTracker[i]['cmd']
|
||||
|
||||
logger.debug("System: MasterMind: last_cmd: " + str(last_cmd))
|
||||
|
||||
if last_cmd == "new":
|
||||
if message.lower().startswith("n") or message.lower().startswith("h") or message.lower().startswith("x"):
|
||||
diff = message.lower()[0]
|
||||
else:
|
||||
diff = "n"
|
||||
|
||||
# set player's last command to makeCode
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
mindTracker[i]['cmd'] = 'makeCode'
|
||||
mindTracker[i]['diff'] = diff
|
||||
# Return color message to player
|
||||
msg += chooseDifficultyMMind(message.lower()[0])
|
||||
return msg
|
||||
|
||||
if last_cmd == "makeCode":
|
||||
# get difficulty from tracker
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
diff = mindTracker[i]['diff']
|
||||
|
||||
secret_code = makeCodeMMind(diff)
|
||||
last_cmd = "playGame"
|
||||
# set player's last command to playGame
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
mindTracker[i]['cmd'] = 'playGame'
|
||||
mindTracker[i]['secret_code'] = secret_code
|
||||
mindTracker[i]['last_played'] = time.time()
|
||||
|
||||
if last_cmd == "playGame":
|
||||
# get difficulty, secret code, and turn count from tracker
|
||||
for i in range(len(mindTracker)):
|
||||
if mindTracker[i]['nodeID'] == nodeID:
|
||||
diff = mindTracker[i]['diff']
|
||||
secret_code = mindTracker[i]['secret_code']
|
||||
turn_count = mindTracker[i]['turns']
|
||||
|
||||
msg += playGameMMind(diff, secret_code, turn_count, nodeID=nodeID, message=message)
|
||||
|
||||
return msg
|
||||
@@ -94,6 +94,7 @@ try:
|
||||
# general
|
||||
useDMForResponse = config['general'].getboolean('respond_by_dm_only', True)
|
||||
publicChannel = config['general'].getint('defaultChannel', 0) # the meshtastic public channel
|
||||
ignoreDefaultChannel = config['general'].getboolean('ignoreDefaultChannel', False)
|
||||
zuluTime = config['general'].getboolean('zuluTime', False) # aka 24 hour time
|
||||
log_messages_to_file = config['general'].getboolean('LogMessagesToFile', True) # default True
|
||||
syslog_to_file = config['general'].getboolean('SyslogToFile', False)
|
||||
@@ -154,6 +155,8 @@ try:
|
||||
lemonade_enabled = config['games'].getboolean('lemonade', True)
|
||||
blackjack_enabled = config['games'].getboolean('blackjack', True)
|
||||
videoPoker_enabled = config['games'].getboolean('videoPoker', True)
|
||||
mastermind_enabled = config['games'].getboolean('mastermind', True)
|
||||
golfSim_enabled = config['games'].getboolean('golfSim', True)
|
||||
|
||||
# messaging settings
|
||||
responseDelay = config['messagingSettings'].getfloat('responseDelay', 0.7) # default 0.7
|
||||
|
||||
@@ -85,7 +85,7 @@ if dad_jokes_enabled:
|
||||
# Wikipedia Search Configuration
|
||||
if wikipedia_enabled:
|
||||
import wikipedia # pip install wikipedia
|
||||
trap_list = trap_list + ("wiki:",)
|
||||
trap_list = trap_list + ("wiki:", "wiki?",)
|
||||
help_message = help_message + ", wiki:"
|
||||
|
||||
# LLM Configuration
|
||||
@@ -117,6 +117,16 @@ if videoPoker_enabled:
|
||||
from modules.videopoker import * # from the spudgunman/meshing-around repo
|
||||
trap_list = trap_list + ("videopoker",)
|
||||
games_enabled = True
|
||||
|
||||
if mastermind_enabled:
|
||||
from modules.mmind import * # from the spudgunman/meshing-around repo
|
||||
trap_list = trap_list + ("mastermind",)
|
||||
games_enabled = True
|
||||
|
||||
if golfSim_enabled:
|
||||
from modules.golfsim import * # from the spudgunman/meshing-around repo
|
||||
trap_list = trap_list + ("golfsim",)
|
||||
games_enabled = True
|
||||
|
||||
# Games Configuration
|
||||
if games_enabled is True:
|
||||
@@ -125,16 +135,19 @@ if games_enabled is True:
|
||||
gTnW_enabled = True
|
||||
gamesCmdList = "Play via DM🕹️ CMD: "
|
||||
if dopewars_enabled:
|
||||
gamesCmdList += "DopeWars, "
|
||||
gamesCmdList += "dopeWars, "
|
||||
if lemonade_enabled:
|
||||
gamesCmdList += "LemonStand, "
|
||||
gamesCmdList += "lemonStand, "
|
||||
if gTnW_enabled:
|
||||
import random
|
||||
trap_list = trap_list + ("globalthermonuclearwar",)
|
||||
if blackjack_enabled:
|
||||
gamesCmdList += "BlackJack, "
|
||||
gamesCmdList += "blackJack, "
|
||||
if videoPoker_enabled:
|
||||
gamesCmdList += "VideoPoker, "
|
||||
gamesCmdList += "videoPoker, "
|
||||
if mastermind_enabled:
|
||||
gamesCmdList += "masterMind, "
|
||||
if golfSim_enabled:
|
||||
gamesCmdList += "golfSim, "
|
||||
gamesCmdList = gamesCmdList[:-2] # remove the last comma
|
||||
else:
|
||||
gamesCmdList = ""
|
||||
@@ -586,6 +599,23 @@ def get_wikipedia_summary(search_term):
|
||||
|
||||
return summary
|
||||
|
||||
def getPrettyTime(seconds):
|
||||
# convert unix time to minutes, hours, or days, or years for simple display
|
||||
designator = "s"
|
||||
if seconds > 0:
|
||||
seconds = round(seconds / 60)
|
||||
designator = "m"
|
||||
if seconds > 60:
|
||||
seconds = round(seconds / 60)
|
||||
designator = "h"
|
||||
if seconds > 24:
|
||||
seconds = round(seconds / 24)
|
||||
designator = "d"
|
||||
if seconds > 365:
|
||||
seconds = round(seconds / 365)
|
||||
designator = "y"
|
||||
return str(seconds) + designator
|
||||
|
||||
def messageTrap(msg):
|
||||
# Check if the message contains a trap word, this is the first filter for listning to messages
|
||||
# after this the message is passed to the command_handler in the bot.py which is switch case filter for applying word to function
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Adapted for Meshtastic mesh-bot by K7MHI Kelly Keeton 2024
|
||||
import random
|
||||
import time
|
||||
import pickle
|
||||
from modules.log import *
|
||||
|
||||
vpStartingCash = 20
|
||||
@@ -230,7 +231,6 @@ class PlayerVP:
|
||||
if resetHand:
|
||||
self.bankroll += self.bet_size * payoff[hand_name]
|
||||
elif 2 in points_repeat: # find pair
|
||||
logger.debug(f"System: VideoPoker: 235 self.bankroll: {self.bankroll} bet_size: {self.bet_size}")
|
||||
hand_name = "Pair👯"
|
||||
if resetHand:
|
||||
self.bankroll += self.bet_size * payoff[hand_name]
|
||||
@@ -272,6 +272,30 @@ def setLastCmdVp(nodeID, cmd):
|
||||
if vpTracker[i]['nodeID'] == nodeID:
|
||||
vpTracker[i]['cmd'] = cmd
|
||||
|
||||
def saveHSVp(nodeID, highScore):
|
||||
# Save the game high_score to pickle
|
||||
highScore = {'nodeID': nodeID, 'highScore': highScore}
|
||||
try:
|
||||
with open('videopoker_hs.pkl', 'wb') as file:
|
||||
pickle.dump(highScore, file)
|
||||
except FileNotFoundError:
|
||||
logger.debug("System: BlackJack: Creating new videopoker_hs.pkl file")
|
||||
with open('videopoker_hs.pkl', 'wb') as file:
|
||||
pickle.dump(highScore, file)
|
||||
|
||||
def loadHSVp():
|
||||
# Load the game high_score from pickle
|
||||
try:
|
||||
with open('videopoker_hs.pkl', 'rb') as file:
|
||||
highScore = pickle.load(file)
|
||||
return highScore
|
||||
except FileNotFoundError:
|
||||
logger.debug("System: VideoPoker: Creating new videopoker_hs.pkl file")
|
||||
highScore = {'nodeID': 0, 'highScore': 0}
|
||||
with open('videopoker_hs.pkl', 'wb') as file:
|
||||
pickle.dump(highScore, file)
|
||||
return 0
|
||||
|
||||
def playVideoPoker(nodeID, message):
|
||||
msg = ""
|
||||
|
||||
@@ -280,7 +304,7 @@ def playVideoPoker(nodeID, message):
|
||||
# create new player if not in tracker
|
||||
logger.debug(f"System: VideoPoker: New Player {nodeID}")
|
||||
vpTracker.append({'nodeID': nodeID, 'cmd': 'new', 'time': time.time(), 'cash': vpStartingCash, 'player': None, 'deck': None, 'highScore': 0, 'drawCount': 0})
|
||||
return f"Welcome to 🎰 VideoPoker! you have {vpStartingCash} coins, Game Played via Direct Message. Whats your bet?"
|
||||
return f"Welcome to 🎰VideoPoker♥️ you have {vpStartingCash} coins, Whats your bet?"
|
||||
|
||||
# Gather the player's bet
|
||||
if getLastCmdVp(nodeID) == "new" or getLastCmdVp(nodeID) == "gameOver":
|
||||
@@ -344,7 +368,7 @@ def playVideoPoker(nodeID, message):
|
||||
vpTracker[i]['deck'] = deck
|
||||
vpTracker[i]['drawCount'] = drawCount
|
||||
|
||||
msg += f"\nDeal new card? \nex: 1,3,4 or (N)o,(A)ll"
|
||||
msg += f"\nDeal new card? \nex: 1,3,4 or (N)o,(A)ll (H)and"
|
||||
setLastCmdVp(nodeID, "redraw")
|
||||
return msg
|
||||
|
||||
@@ -406,6 +430,8 @@ def playVideoPoker(nodeID, message):
|
||||
elif player.bankroll > vpTracker[i]['highScore']:
|
||||
vpTracker[i]['highScore'] = player.bankroll
|
||||
msg += " 🎉HighScore!"
|
||||
# save high score
|
||||
saveHSVp(nodeID, vpTracker[i]['highScore'])
|
||||
|
||||
msg += f"\nPlace your Bet, 'L' to leave the game."
|
||||
|
||||
|
||||
@@ -94,10 +94,20 @@ def get_wx_meteo(lat=0, lon=0, unit=0):
|
||||
code_string = ""
|
||||
if daily_weather_code[i] == 0:
|
||||
code_string = "Clear sky"
|
||||
elif daily_weather_code[i] == 1 or 2 or 3:
|
||||
code_string = "Partly cloudy"
|
||||
elif daily_weather_code[i] == 45 or 48:
|
||||
elif daily_weather_code[i] == 1:
|
||||
code_string = "Mostly Cloudy"
|
||||
elif daily_weather_code[i] == 2:
|
||||
code_string = "Partly Cloudy"
|
||||
elif daily_weather_code[i] == 3:
|
||||
code_string = "Overcast"
|
||||
elif daily_weather_code[i] == 5:
|
||||
code_string = "Haze"
|
||||
elif daily_weather_code[i] == 10:
|
||||
code_string = "Mist"
|
||||
elif daily_weather_code[i] == 45:
|
||||
code_string = "Fog"
|
||||
elif daily_weather_code[i] == 48:
|
||||
code_string = "Freezing Fog"
|
||||
elif daily_weather_code[i] == 51:
|
||||
code_string = "Drizzle: Light"
|
||||
elif daily_weather_code[i] == 53:
|
||||
@@ -126,6 +136,10 @@ def get_wx_meteo(lat=0, lon=0, unit=0):
|
||||
code_string = "Snow: Heavy"
|
||||
elif daily_weather_code[i] == 77:
|
||||
code_string = "Snow Grains"
|
||||
elif daily_weather_code[i] == 78:
|
||||
code_string = "Ice Crystals"
|
||||
elif daily_weather_code[i] == 79:
|
||||
code_string = "Ice Pellets"
|
||||
elif daily_weather_code[i] == 80:
|
||||
code_string = "Rain showers: Slight"
|
||||
elif daily_weather_code[i] == 81:
|
||||
@@ -133,15 +147,17 @@ def get_wx_meteo(lat=0, lon=0, unit=0):
|
||||
elif daily_weather_code[i] == 82:
|
||||
code_string = "Rain showers: Heavy"
|
||||
elif daily_weather_code[i] == 85:
|
||||
code_string = "Snow showers: Light"
|
||||
code_string = "Snow showers"
|
||||
elif daily_weather_code[i] == 86:
|
||||
code_string = "Snow showers: Moderate"
|
||||
code_string = "Snow showers: Heavy"
|
||||
elif daily_weather_code[i] == 95:
|
||||
code_string = "Thunderstorm: Slight"
|
||||
code_string = "Thunderstorm"
|
||||
elif daily_weather_code[i] == 96:
|
||||
code_string = "Thunderstorm: Moderate"
|
||||
code_string = "Hailstorm"
|
||||
elif daily_weather_code[i] == 97:
|
||||
code_string = "Thunderstorm Heavy"
|
||||
elif daily_weather_code[i] == 99:
|
||||
code_string = "Thunderstorm: Heavy"
|
||||
code_string = "Hailstorm Heavy"
|
||||
|
||||
weather_report += "Cond: " + code_string + ". "
|
||||
|
||||
|
||||
Reference in New Issue
Block a user