mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-10-20 00:00:39 +00:00
added push notification subscription service
This commit is contained in:
parent
59667ede11
commit
a070124d1d
8 changed files with 278 additions and 29 deletions
40
dist/ba_root/mods/custom_hooks.py
vendored
40
dist/ba_root/mods/custom_hooks.py
vendored
|
|
@ -35,6 +35,7 @@ from features import votingmachine
|
|||
from features import text_on_map, announcement
|
||||
from features import map_fun
|
||||
from spazmod import modifyspaz
|
||||
from tools import notification_manager
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional, Any
|
||||
|
||||
|
|
@ -46,6 +47,8 @@ def filter_chat_message(msg: str, client_id: int) -> str | None:
|
|||
return handlechat.filter_chat_message(msg, client_id)
|
||||
|
||||
# ba_meta export plugin
|
||||
|
||||
|
||||
class modSetup(ba.Plugin):
|
||||
def on_app_running(self):
|
||||
"""Runs when app is launched."""
|
||||
|
|
@ -69,8 +72,11 @@ class modSetup(ba.Plugin):
|
|||
ba.internal.sign_in_v1('Local')
|
||||
ba.timer(60, playlist.flush_playlists)
|
||||
|
||||
def on_app_shutdown(self):
|
||||
pass
|
||||
def on_app_shutdown(self): # TODO not working, fix this, also dump server logs
|
||||
print("Server shutting down , lets save cache")
|
||||
pdata.dump_cache()
|
||||
notification_manager.dump_cache()
|
||||
print("Done dumping memory")
|
||||
|
||||
|
||||
def score_screen_on_begin(_stats: ba.Stats) -> None:
|
||||
|
|
@ -99,6 +105,7 @@ def bootstraping():
|
|||
_thread.start_new_thread(mystats.refreshStats, ())
|
||||
pdata.load_cache()
|
||||
_thread.start_new_thread(pdata.dump_cache, ())
|
||||
_thread.start_new_thread(notification_manager.dump_cache, ())
|
||||
|
||||
# import plugins
|
||||
if settings["elPatronPowerups"]["enable"]:
|
||||
|
|
@ -124,8 +131,7 @@ def bootstraping():
|
|||
from plugins import colorfulmaps2
|
||||
try:
|
||||
pass
|
||||
# from tools import healthcheck
|
||||
# healthcheck.main() spamming logs , will increase log interval later
|
||||
# from tools import healthcheck
|
||||
except Exception as e:
|
||||
print(e)
|
||||
try:
|
||||
|
|
@ -283,24 +289,25 @@ def on_map_init():
|
|||
text_on_map.textonmap()
|
||||
modifyspaz.setTeamCharacter()
|
||||
|
||||
|
||||
def shutdown(func) -> None:
|
||||
"""Set the app to quit either now or at the next clean opportunity."""
|
||||
def wrapper(*args, **kwargs):
|
||||
# add screen text and tell players we are going to restart soon.
|
||||
ba.internal.chatmessage(
|
||||
"Server will restart on next opportunity. (series end)")
|
||||
"Server will restart on next opportunity. (series end)")
|
||||
_ba.restart_scheduled = True
|
||||
_ba.get_foreground_host_activity().restart_msg = _ba.newnode('text',
|
||||
attrs={
|
||||
'text': "Server going to restart after this series.",
|
||||
'flatness': 1.0,
|
||||
'h_align': 'right',
|
||||
'v_attach': 'bottom',
|
||||
'h_attach': 'right',
|
||||
'scale': 0.5,
|
||||
'position': (-25, 54),
|
||||
'color': (1, 0.5, 0.7)
|
||||
})
|
||||
attrs={
|
||||
'text': "Server going to restart after this series.",
|
||||
'flatness': 1.0,
|
||||
'h_align': 'right',
|
||||
'v_attach': 'bottom',
|
||||
'h_attach': 'right',
|
||||
'scale': 0.5,
|
||||
'position': (-25, 54),
|
||||
'color': (1, 0.5, 0.7)
|
||||
})
|
||||
func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
|
@ -318,7 +325,8 @@ def on_player_request(func) -> bool:
|
|||
if current_player.get_v1_account_id() == player.get_v1_account_id():
|
||||
count += 1
|
||||
if count >= settings["maxPlayersPerDevice"]:
|
||||
_ba.screenmessage("Reached max players limit per device", clients=[player.inputdevice.client_id], transient=True,)
|
||||
_ba.screenmessage("Reached max players limit per device", clients=[
|
||||
player.inputdevice.client_id], transient=True,)
|
||||
return False
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
|
|
|||
37
dist/ba_root/mods/playersData/blacklist.json
vendored
37
dist/ba_root/mods/playersData/blacklist.json
vendored
|
|
@ -1,11 +1,34 @@
|
|||
{
|
||||
"ban": {
|
||||
"ids": [
|
||||
],
|
||||
"ips": [
|
||||
],
|
||||
"deviceids": [
|
||||
]
|
||||
"ids": {
|
||||
"pb-234": {
|
||||
"till": "2023-06-07 21:59:20",
|
||||
"reason": "auto ban for spam"
|
||||
}
|
||||
},
|
||||
"ips": {
|
||||
"19.168.0.0.1": {
|
||||
"till": "2023-06-07 21:59:20",
|
||||
"reason": "auto ban for spam"
|
||||
}
|
||||
},
|
||||
"deviceids": {
|
||||
"sdfdsfwr3": {
|
||||
"till": "2023-06-07 21:59:20",
|
||||
"reason": "auto ban for spam"
|
||||
}
|
||||
}
|
||||
},
|
||||
"muted-ids": []
|
||||
"muted-ids": {
|
||||
"pb-IF4iU0QaEw==": {
|
||||
"till": "2023-06-19 19:44:47",
|
||||
"reason": "manually from website"
|
||||
}
|
||||
},
|
||||
"kick-vote-disabled": {
|
||||
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE": {
|
||||
"till": "2023-06-12 19:37:48",
|
||||
"reason": "manually from website"
|
||||
}
|
||||
}
|
||||
}
|
||||
31
dist/ba_root/mods/playersData/subscribed_players.json
vendored
Normal file
31
dist/ba_root/mods/playersData/subscribed_players.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"pb-IF41U2scIg==": {
|
||||
"subscribers": [
|
||||
"jrz6Rg"
|
||||
]
|
||||
},
|
||||
"pb-IF4IVFkKNA==": {
|
||||
"subscribers": [
|
||||
"jrz6Rg"
|
||||
],
|
||||
"name": "\ue063ATTITUDEB2"
|
||||
},
|
||||
"pb-IF4CKhYn": {
|
||||
"subscribers": [
|
||||
"jrz6Rg"
|
||||
],
|
||||
"name": "\ue032Rico Un\u2122\u00a9\u00ae"
|
||||
},
|
||||
"pb-IF4jF1NY": {
|
||||
"subscribers": [
|
||||
"jrz6Rg"
|
||||
],
|
||||
"name": "\ue01eHoemaster"
|
||||
},
|
||||
"pb-IF4wVVk8Jg==": {
|
||||
"subscribers": [
|
||||
"jrz6Rg"
|
||||
],
|
||||
"name": "\ue063Homulilly"
|
||||
}
|
||||
}
|
||||
10
dist/ba_root/mods/playersData/subscriptions.json
vendored
Normal file
10
dist/ba_root/mods/playersData/subscriptions.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"jrz6Rg": {
|
||||
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=8nmgbauneNBqKw7H6IGl",
|
||||
"expirationTime": null,
|
||||
"keys": {
|
||||
"p256dh": "BOG4rFeziA",
|
||||
"auth": "yNIDrg"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
dist/ba_root/mods/plugins/bcs_plugin.py
vendored
18
dist/ba_root/mods/plugins/bcs_plugin.py
vendored
|
|
@ -18,7 +18,7 @@ os.environ['FLASK_ENV'] = 'development'
|
|||
|
||||
app = Flask(__name__)
|
||||
app.config["DEBUG"] = False
|
||||
SECRET_KEY = "my_secret_key"
|
||||
SECRET_KEY = "my_secret_key2"
|
||||
|
||||
|
||||
@app.after_request
|
||||
|
|
@ -56,6 +56,19 @@ def get_top200():
|
|||
return jsonify(bombsquad_service.get_top_200()), 200
|
||||
|
||||
|
||||
@app.route('/api/subscribe', methods=['POST'])
|
||||
def subscribe_player():
|
||||
try:
|
||||
data = request.get_json()
|
||||
bombsquad_service.subscribe_player(
|
||||
data["subscription"], data["player_id"], data["name"])
|
||||
response = {
|
||||
'message': f'Subscribed {data["name"]} successfully , will send confirmation notification to test'}
|
||||
return jsonify(response), 201
|
||||
except Exception as e:
|
||||
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
|
||||
|
||||
|
||||
# ============ Admin only =========
|
||||
|
||||
|
||||
|
|
@ -250,7 +263,8 @@ def update_server_config():
|
|||
|
||||
|
||||
def enable():
|
||||
flask_run = _thread.start_new_thread(app.run, ("0.0.0.0", _ba.get_game_port(), False))
|
||||
flask_run = _thread.start_new_thread(
|
||||
app.run, ("0.0.0.0", _ba.get_game_port(), False))
|
||||
# uvicorn_thread = threading.Thread(target=start_uvicorn)
|
||||
# uvicorn_thread.start()
|
||||
# options = {
|
||||
|
|
|
|||
13
dist/ba_root/mods/plugins/bombsquad_service.py
vendored
13
dist/ba_root/mods/plugins/bombsquad_service.py
vendored
|
|
@ -1,3 +1,6 @@
|
|||
import ecdsa
|
||||
import base64
|
||||
import json
|
||||
import ba
|
||||
import _ba
|
||||
import ba.internal
|
||||
|
|
@ -6,7 +9,7 @@ from typing import Optional, Any, Dict, List, Type, Sequence
|
|||
from ba._gameactivity import GameActivity
|
||||
from playersData import pdata
|
||||
from serverData import serverdata
|
||||
from tools import servercheck, logger
|
||||
from tools import servercheck, logger, notification_manager
|
||||
import setting
|
||||
from datetime import datetime
|
||||
import _thread
|
||||
|
|
@ -15,7 +18,7 @@ import yaml
|
|||
stats = {}
|
||||
leaderboard = {}
|
||||
top200 = {}
|
||||
|
||||
vapidkeys = {}
|
||||
serverinfo = {}
|
||||
|
||||
|
||||
|
|
@ -24,7 +27,7 @@ class BsDataThread(object):
|
|||
global stats
|
||||
stats["name"] = _ba.app.server._config.party_name
|
||||
stats["discord"] = "https://discord.gg/ucyaesh"
|
||||
stats["vapidKey"] = "sjfbsdjfdsf"
|
||||
stats["vapidKey"] = notification_manager.get_vapid_keys()["public_key"]
|
||||
|
||||
self.refresh_stats_cache_timer = ba.Timer(8, ba.Call(self.refreshStats),
|
||||
timetype=ba.TimeType.REAL, repeat=True)
|
||||
|
|
@ -255,3 +258,7 @@ def do_action(action, value):
|
|||
_ba.pushcall(ba.Call(_ba.chatmessage, value), from_other_thread=True)
|
||||
elif action == "quit":
|
||||
_ba.pushcall(ba.Call(_ba.quit), from_other_thread=True)
|
||||
|
||||
|
||||
def subscribe_player(sub, account_id, name):
|
||||
notification_manager.subscribe(sub, account_id, name)
|
||||
|
|
|
|||
154
dist/ba_root/mods/tools/notification_manager.py
vendored
Normal file
154
dist/ba_root/mods/tools/notification_manager.py
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
import time
|
||||
import shutil
|
||||
import random
|
||||
import string
|
||||
from pywebpush import webpush
|
||||
import json
|
||||
import base64
|
||||
import ecdsa
|
||||
import os
|
||||
import _ba
|
||||
from datetime import datetime
|
||||
vapidkeys = {}
|
||||
subscriptions = {}
|
||||
subscribed_players = {}
|
||||
PLAYERS_DATA_PATH = os.path.join(
|
||||
_ba.env()["python_directory_user"], "playersData" + os.sep
|
||||
)
|
||||
|
||||
|
||||
def get_vapid_keys():
|
||||
global vapidkeys
|
||||
if vapidkeys != {}:
|
||||
return vapidkeys
|
||||
try:
|
||||
f = open(".keys", "r")
|
||||
vapidkeys = json.load(f)
|
||||
f.close()
|
||||
return vapidkeys
|
||||
except:
|
||||
pk = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p)
|
||||
vk = pk.get_verifying_key()
|
||||
vapidkeys = {
|
||||
'private_key': base64.urlsafe_b64encode(pk.to_string()).rstrip(b'=').decode('utf-8'),
|
||||
'public_key': base64.urlsafe_b64encode(b'\x04' + vk.to_string()).rstrip(b'=').decode('utf-8')
|
||||
}
|
||||
f = open(".keys", "w")
|
||||
json.dump(vapidkeys, f)
|
||||
f.close()
|
||||
return vapidkeys
|
||||
|
||||
|
||||
def send_push_notification(subscription, payload):
|
||||
# try:
|
||||
# Send the push notification using the subscription and payload
|
||||
print(subscription)
|
||||
print(payload)
|
||||
print(get_vapid_keys()["private_key"])
|
||||
|
||||
webpush(subscription_info=subscription, data=json.dumps(payload),
|
||||
vapid_private_key=get_vapid_keys()["private_key"], vapid_claims={
|
||||
'sub': 'mailto:{}'.format("test@ballistica.net"),
|
||||
})
|
||||
print("Push notification sent successfully")
|
||||
# except Exception as e:
|
||||
# print("Error sending push notification:", str(e))
|
||||
|
||||
|
||||
# if we already have that browser subscription saved get id or generate new one
|
||||
def get_subscriber_id(sub):
|
||||
subscriber_id = None
|
||||
|
||||
for key, value in subscriptions.items():
|
||||
if value["endpoint"] == sub["endpoint"]:
|
||||
subscriber_id = key
|
||||
break
|
||||
|
||||
if not subscriber_id:
|
||||
subscriber_id = generate_random_string(6)
|
||||
subscriptions[subscriber_id] = sub
|
||||
return subscriber_id
|
||||
|
||||
|
||||
def generate_random_string(length):
|
||||
letters = string.ascii_letters + string.digits
|
||||
return ''.join(random.choice(letters) for _ in range(length))
|
||||
|
||||
|
||||
def subscribe(sub, account_id, name):
|
||||
|
||||
id = get_subscriber_id(sub)
|
||||
if account_id in subscribed_players:
|
||||
if id not in subscribed_players[account_id]["subscribers"]:
|
||||
subscribed_players[account_id]["subscribers"].append(id)
|
||||
subscribed_players[account_id]["name"] = name
|
||||
else:
|
||||
subscribed_players[account_id] = {"subscribers": [id], "name": name}
|
||||
send_push_notification(sub, {"notification": {
|
||||
"title": "Notification working !", "body": f'subscribed {name}'}})
|
||||
|
||||
|
||||
def player_joined(pb_id):
|
||||
now = datetime.now()
|
||||
if pb_id in subscribed_players:
|
||||
if "last_notification" in subscribed_players[pb_id] and (now - subscribed_players[pb_id]["last_notification"]).seconds < 15 * 60:
|
||||
pass
|
||||
else:
|
||||
subscribed_players[pb_id]["last_notification"] = now
|
||||
subscribes = subscribed_players[pb_id]["subscribers"]
|
||||
for subscriber_id in subscribes:
|
||||
sub = subscriptions[subscriber_id]
|
||||
send_push_notification(
|
||||
sub, {
|
||||
"notification": {
|
||||
"title": f'{subscribed_players[pb_id]["name"] } is playing now',
|
||||
"body": f'Join {_ba.app.server._config.party_name} server {subscribed_players[pb_id]["name"]} is waiting for you ',
|
||||
"icon": "assets/icons/icon-96x96.png",
|
||||
"vibrate": [100, 50, 100],
|
||||
"requireInteraction": True,
|
||||
"data": {"dateOfArrival": datetime.now().strftime("%Y-%m-%d %H:%M:%S")},
|
||||
"actions": [{"action": "nothing", "title": "Launch Bombsquad"}],
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def loadCache():
|
||||
global subscriptions
|
||||
global subscribed_players
|
||||
try:
|
||||
f = open(PLAYERS_DATA_PATH+"subscriptions.json", "r")
|
||||
subscriptions = json.load(f)
|
||||
f.close()
|
||||
except:
|
||||
f = open(PLAYERS_DATA_PATH+"subscriptions.json.backup", "r")
|
||||
subscriptions = json.load(f)
|
||||
f.close()
|
||||
try:
|
||||
f = open(PLAYERS_DATA_PATH+"subscribed_players.json", "r")
|
||||
subscribed_players = json.load(f)
|
||||
f.close()
|
||||
except:
|
||||
f = open(PLAYERS_DATA_PATH+"subscribed_players.json.backup", "r")
|
||||
subscribed_players = json.load(f)
|
||||
f.close()
|
||||
|
||||
|
||||
def dump_cache():
|
||||
if subscriptions != {}:
|
||||
shutil.copyfile(PLAYERS_DATA_PATH + "subscriptions.json",
|
||||
PLAYERS_DATA_PATH + "subscriptions.json.backup")
|
||||
|
||||
with open(PLAYERS_DATA_PATH + "subscriptions.json", "w") as f:
|
||||
json.dump(subscriptions, f, indent=4)
|
||||
if subscribed_players != {}:
|
||||
shutil.copyfile(PLAYERS_DATA_PATH + "subscribed_players.json",
|
||||
PLAYERS_DATA_PATH + "subscribed_players.json.backup")
|
||||
|
||||
with open(PLAYERS_DATA_PATH + "subscribed_players.json", "w") as f:
|
||||
json.dump(subscribed_players, f, indent=4)
|
||||
|
||||
time.sleep(60)
|
||||
dump_cache()
|
||||
|
||||
|
||||
loadCache()
|
||||
4
dist/ba_root/mods/tools/servercheck.py
vendored
4
dist/ba_root/mods/tools/servercheck.py
vendored
|
|
@ -17,7 +17,7 @@ import _thread
|
|||
from tools import logger
|
||||
from features import profanity
|
||||
from playersData import pdata
|
||||
|
||||
from . import notification_manager
|
||||
blacklist = pdata.get_blacklist()
|
||||
|
||||
settings = setting.get_settings_data()
|
||||
|
|
@ -191,6 +191,7 @@ def on_player_join_server(pbid, player_data, ip, device_id):
|
|||
_ba.screenmessage(settings["regularWelcomeMsg"] + " " + device_string,
|
||||
color=(0.60, 0.8, 0.6), transient=True,
|
||||
clients=[clid])
|
||||
notification_manager.player_joined(pbid)
|
||||
else:
|
||||
# fetch id for first time.
|
||||
thread = FetchThread(
|
||||
|
|
@ -203,6 +204,7 @@ def on_player_join_server(pbid, player_data, ip, device_id):
|
|||
thread.start()
|
||||
_ba.screenmessage(settings["firstTimeJoinMsg"], color=(0.6, 0.8, 0.6),
|
||||
transient=True, clients=[clid])
|
||||
notification_manager.player_joined(pbid)
|
||||
|
||||
# pdata.add_profile(pbid,d_string,d_string)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue