added push notification subscription service

This commit is contained in:
Ayush Saini 2023-06-24 18:02:12 +05:30
parent 59667ede11
commit a070124d1d
8 changed files with 278 additions and 29 deletions

View file

@ -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

View file

@ -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"
}
}
}

View 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"
}
}

View file

@ -0,0 +1,10 @@
{
"jrz6Rg": {
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=8nmgbauneNBqKw7H6IGl",
"expirationTime": null,
"keys": {
"p256dh": "BOG4rFeziA",
"auth": "yNIDrg"
}
}
}

View file

@ -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 = {

View file

@ -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)

View 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()

View file

@ -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)