Add option to announce joining and leaving players

This commit is contained in:
oldnapalm
2025-02-28 12:42:11 -03:00
parent bc3ea25ed4
commit 250360615b
4 changed files with 62 additions and 38 deletions

View File

@@ -370,6 +370,7 @@ To enable support for multiple users perform the steps below:
channel =
welcome_message =
help_message =
announce_players =
```
</details>

View File

@@ -11,11 +11,12 @@ import zwift_offline
class DiscordBot(discord.Client):
def __init__(self, intents, channel, welcome_msg, help_msg):
def __init__(self, intents, channel, welcome_msg, help_msg, announce):
discord.Client.__init__(self, intents=intents)
self.channel_id = channel
self.welcome_msg = welcome_msg
self.help_msg = help_msg
self.announce = announce
async def on_ready(self):
self.channel = self.get_channel(self.channel_id)
@@ -49,8 +50,9 @@ class DiscordThread(threading.Thread):
self.token = self.CONFIG.get(SECTION, 'token')
self.webhook = self.CONFIG.get(SECTION, 'webhook')
self.channel = self.CONFIG.getint(SECTION, 'channel')
self.welcome_msg = self.CONFIG.get(SECTION, 'welcome_message')
self.help_msg = self.CONFIG.get(SECTION, 'help_message')
self.welcome_msg = self.CONFIG.get(SECTION, 'welcome_message', fallback='')
self.help_msg = self.CONFIG.get(SECTION, 'help_message', fallback='')
self.announce = self.CONFIG.getboolean(SECTION, 'announce_players', fallback=False)
self.intents = discord.Intents.default()
self.intents.members = True
@@ -59,7 +61,7 @@ class DiscordThread(threading.Thread):
self.start()
async def starter(self):
self.discord_bot = DiscordBot(self.intents, self.channel, self.welcome_msg, self.help_msg)
self.discord_bot = DiscordBot(self.intents, self.channel, self.welcome_msg, self.help_msg, self.announce)
await self.discord_bot.start(self.token)
def run(self):

View File

@@ -72,6 +72,7 @@ else:
pass
def change_presence(self, n):
pass
announce = False
discord = DummyDiscord()
bot_update_freq = 3
@@ -399,6 +400,16 @@ class GhostsVariables:
start_road = 0
start_rt = 0
def get_routes():
with open('%s/data/start_lines.txt' % SCRIPT_DIR) as fd:
return json.load(fd, object_hook=lambda d: {int(k) if k.lstrip('-').isdigit() else k: v for k, v in d.items()})
def get_route_name(state):
routes = get_routes()
if state.route in routes:
return routes[state.route]['name']
return zo.courses_lookup[zo.get_course(state)]
def load_ghosts_folder(folder, ghosts):
if os.path.isdir(folder):
for f in os.listdir(folder):
@@ -419,11 +430,10 @@ def load_ghosts(player_id, state, ghosts):
load_ghosts_folder('%s/%s' % (folder, state.route), ghosts)
ghosts.start_road = zo.road_id(state)
ghosts.start_rt = state.roadTime
with open('%s/data/start_lines.txt' % SCRIPT_DIR) as fd:
sl = json.load(fd, object_hook=lambda d: {int(k) if k.lstrip('-').isdigit() else k: v for k, v in d.items()})
if state.route in sl:
ghosts.start_road = sl[state.route]['road']
ghosts.start_rt = sl[state.route]['time']
sl = get_routes()
if state.route in sl:
ghosts.start_road = sl[state.route]['road']
ghosts.start_rt = sl[state.route]['time']
def regroup_ghosts(player_id):
p = online[player_id]
@@ -555,9 +565,14 @@ def play_bots():
def remove_inactive():
while True:
for p_id in list(online.keys()):
if zo.world_time() > online[p_id].worldTime + 10000:
if zo.world_time() > online[p_id].worldTime + 30000:
zo.save_bookmark(online[p_id], 'Last ' + ('run' if online[p_id].sport == profile_pb2.Sport.RUNNING else 'ride'))
online.pop(p_id)
discord.change_presence(len(online))
if discord.announce:
discord.send_message("Leaving", p_id)
zo.logout_player(p_id)
time.sleep(1)
time.sleep(5)
def is_state_new_for(peer_player_state, player_id):
if not player_id in global_news.keys():
@@ -639,9 +654,12 @@ class UDPHandler(socketserver.BaseRequestHandler):
if player_id in online.keys():
if online[player_id].worldTime > state.worldTime:
return #udp is unordered -> drop old state
else:
discord.change_presence(len(online) + 1)
online[player_id] = state
online[player_id] = state
elif zo.world_time() < state.worldTime + 10000:
online[player_id] = state
discord.change_presence(len(online))
if discord.announce:
discord.send_message("%s in %s" % (('Running' if state.sport == profile_pb2.Sport.RUNNING else 'Riding'), get_route_name(state)), player_id)
#Add handling of ghosts for player if it's missing
if not player_id in global_ghosts.keys():
@@ -690,7 +708,7 @@ class UDPHandler(socketserver.BaseRequestHandler):
nearby = {}
for p_id in online.keys():
player = online[p_id]
if player.id != player_id:
if player.id != player_id and zo.world_time() < player.worldTime + 10000:
is_nearby, distance = nearby_distance(watching_state, player)
if is_nearby and is_state_new_for(player, player_id):
nearby[p_id] = distance

View File

@@ -391,6 +391,7 @@ class PartialProfile:
male = True
weight_in_grams = 0
imageSrc = ''
time = 0
def to_json(self):
return {"countryCode": self.country_code,
"enrolledZwiftAcademy": False, #don't need
@@ -481,6 +482,10 @@ def get_partial_profile(player_id):
partial_profile.player_id = player_id
if player_id in global_pace_partners.keys():
profile = global_pace_partners[player_id].profile
for f in profile.public_attributes:
if f.id == 1766985504: #crc32 of "PACE PARTNER - ROUTE"
partial_profile.route = toSigned(f.number_value, 4) if f.number_value >= 0 else -toSigned(-f.number_value, 4)
break
elif player_id in global_bots.keys():
profile = global_bots[player_id].profile
elif player_id > 10000000:
@@ -509,15 +514,8 @@ def get_partial_profile(player_id):
partial_profile.player_type = profile_pb2.PlayerType.Name(jsf(profile, 'player_type', 1))
partial_profile.male = profile.is_male
partial_profile.weight_in_grams = profile.weight_in_grams
for f in profile.public_attributes:
#0x69520F20=1766985504 - crc32 of "PACE PARTNER - ROUTE"
if f.id == 1766985504:
if f.number_value >= 0:
partial_profile.route = toSigned(f.number_value, 4)
else:
partial_profile.route = -toSigned(-f.number_value, 4)
break
player_partial_profiles[player_id] = partial_profile
player_partial_profiles[player_id].time = time.monotonic()
return player_partial_profiles[player_id]
@@ -1420,31 +1418,20 @@ def relay_session_refresh():
return refresh.SerializeToString(), 200
def save_bookmark(state, name):
bookmarks_dir = os.path.join(STORAGE_DIR, str(state.id), 'bookmarks', str(get_course(state)), str(state.sport))
if not make_dir(bookmarks_dir):
return
with open(os.path.join(bookmarks_dir, name + '.bin'), 'wb') as f:
f.write(state.SerializeToString())
def logout_player(player_id):
#Remove player from online when leaving game/world
if player_id in online:
activity = 'run' if online[player_id].sport == profile_pb2.Sport.RUNNING else 'ride'
save_bookmark(online[player_id], 'Last ' + activity)
online.pop(player_id)
discord.change_presence(len(online))
if player_id in global_ghosts:
del global_ghosts[player_id].rec.states[:]
global_ghosts[player_id].play.clear()
global_ghosts.pop(player_id)
if player_id in player_partial_profiles:
player_partial_profiles.pop(player_id)
if player_id in global_bookmarks:
global_bookmarks[player_id].clear()
global_bookmarks.pop(player_id)
@app.route('/api/users/logout', methods=['POST'])
@jwt_to_session_cookie
@login_required
def api_users_logout():
logout_player(current_user.player_id)
return '', 204
@@ -3286,6 +3273,13 @@ def relay_worlds_hash_seeds():
return seeds.SerializeToString(), 200
def save_bookmark(state, name):
bookmarks_dir = os.path.join(STORAGE_DIR, str(state.id), 'bookmarks', str(get_course(state)), str(state.sport))
if not make_dir(bookmarks_dir):
return
with open(os.path.join(bookmarks_dir, name + '.bin'), 'wb') as f:
f.write(state.SerializeToString())
@app.route('/relay/worlds/attributes', methods=['POST'])
@jwt_to_session_cookie
@login_required
@@ -3893,6 +3887,13 @@ def send_server_back_online_message():
send_message(message)
discord.send_message(message)
def remove_inactive():
while True:
for p_id in list(player_partial_profiles.keys()):
if time.monotonic() > player_partial_profiles[p_id].time + 3600:
player_partial_profiles.pop(p_id)
time.sleep(600)
with app.app_context():
db.create_all()
@@ -4088,6 +4089,8 @@ def run_standalone(passed_online, passed_global_relay, passed_global_pace_partne
send_message_thread = threading.Thread(target=send_server_back_online_message)
send_message_thread.start()
remove_inactive_thread = threading.Thread(target=remove_inactive)
remove_inactive_thread.start()
logger.info("Server version %s is running." % ZWIFT_VER_CUR)
server = WSGIServer(('0.0.0.0', 443), app, certfile='%s/cert-zwift-com.pem' % SSL_DIR, keyfile='%s/key-zwift-com.pem' % SSL_DIR, log=logger)
server.serve_forever()