rework on player restriction , exposing rest apis

This commit is contained in:
Ayush Saini 2023-06-19 21:58:35 +05:30
parent af7fb5a586
commit bf4c01c291
10 changed files with 883 additions and 361 deletions

View file

@ -157,9 +157,12 @@ def get_player_info(arguments, client_id):
if account['client_id'] == int(arguments[0]):
send(pdata.get_detailed_info(account["pbid"]), client_id)
def get_recents(client_id):
for players in serverdata.recents:
send(f"{players['client_id']} {players['deviceId']} {players['pbid']}", client_id)
send(
f"{players['client_id']} {players['deviceId']} {players['pbid']}", client_id)
def changepartysize(arguments):
if len(arguments) == 0:
@ -201,9 +204,9 @@ def kikvote(arguments, clientid):
cl_id = int(arguments[1])
for ros in ba.internal.get_game_roster():
if ros["client_id"] == cl_id:
if ros["account_id"] in serverdata.clients:
serverdata.clients[ros["account_id"]
]["canStartKickVote"] = True
pdata.enable_kick_vote(ros["account_id"])
logger.log(
f'kick vote enabled for {ros["account_id"]} {ros["display_string"]}')
send(
"Upon server restart, Kick-vote will be enabled for this person", clientid)
return
@ -220,9 +223,10 @@ def kikvote(arguments, clientid):
if ros["client_id"] == cl_id:
_ba.disable_kickvote(ros["account_id"])
send("Kick-vote disabled for this person", clientid)
if ros["account_id"] in serverdata.clients:
serverdata.clients[ros["account_id"]
]["canStartKickVote"] = False
logger.log(
f'kick vote disabled for {ros["account_id"]} {ros["display_string"]}')
pdata.disable_kick_vote(
ros["account_id"], 2, "by chat command")
return
except:
return
@ -272,17 +276,19 @@ def end(arguments):
def ban(arguments):
try:
cl_id = int(arguments[0])
ac_id = ""
duration = int(arguments[1]) if len(arguments) >= 2 else 0.5
for ros in ba.internal.get_game_roster():
if ros["client_id"] == cl_id:
ac_id = ros['account_id']
pdata.ban_player(ros['account_id'])
logger.log("banned "+ros["display_string"])
if ac_id in serverdata.clients:
serverdata.clients[ac_id]["isBan"] = True
pdata.ban_player(ros['account_id'], duration,
"by chat command")
logger.log(f'banned {ros["display_string"]} by chat command')
for account in serverdata.recents: # backup case if player left the server
if account['client_id'] == int(arguments[0]):
pdata.ban_player(account["pbid"])
pdata.ban_player(
account["pbid"], duration, "by chat command")
logger.log(
f'banned {ros["display_string"]} by chat command, recents')
kick(arguments)
except:
pass
@ -298,17 +304,18 @@ def mute(arguments):
serverdata.muted = True
try:
cl_id = int(arguments[0])
ac_id = ""
duration = int(arguments[1]) if len(arguments) >= 2 else 0.5
for ros in ba.internal.get_game_roster():
if ros["client_id"] == cl_id:
pdata.mute(ros['account_id'])
ac_id = ros['account_id']
logger.log("muted "+ros["display_string"])
if ac_id in serverdata.clients:
serverdata.clients[ac_id]["isMuted"] = True
logger.log(f'muted {ros["display_string"]}')
pdata.mute(ac_id, duration, "muted by chat command")
return
for account in serverdata.recents: # backup case if player left the server
if account['client_id'] == int(arguments[0]):
pdata.mute(account["pbid"])
pdata.mute(account["pbid"], duration,
"muted by chat command, from recents")
except:
pass
return
@ -319,14 +326,16 @@ def un_mute(arguments):
serverdata.muted = False
try:
cl_id = int(arguments[0])
ac_id = ""
for ros in ba.internal.get_game_roster():
if ros["client_id"] == cl_id:
pdata.unmute(ros['account_id'])
ac_id = ros['account_id']
if ac_id in serverdata.clients:
serverdata.clients[ac_id]["isMuted"] = False
logger.log(f'unmuted {ros["display_string"]} by chat command')
return
for account in serverdata.recents: # backup case if player left the server
if account['client_id'] == int(arguments[0]):
pdata.unmute(account["pbid"])
logger.log(
f'unmuted {ros["display_string"]} by chat command, recents')
except:
pass
@ -485,6 +494,7 @@ def set_custom_tag(arguments):
except:
return
def remove_custom_tag(arguments):
try:
session = ba.internal.get_foreground_host_session()
@ -494,6 +504,7 @@ def remove_custom_tag(arguments):
except:
return
def remove_custom_effect(arguments):
try:
session = ba.internal.get_foreground_host_session()
@ -503,6 +514,7 @@ def remove_custom_effect(arguments):
except:
return
def set_custom_effect(arguments):
try:
session = ba.internal.get_foreground_host_session()

View file

@ -6,14 +6,17 @@ from chatHandle.ChatCommands import Main
from tools import logger, servercheck
from chatHandle.chatFilter import ChatFilter
from features import votingmachine
import ba, _ba
from playersData import pdata
import ba
import _ba
import ba.internal
import setting
from datetime import datetime
settings = setting.get_settings_data()
def filter_chat_message(msg, client_id):
now = datetime.now()
if client_id == -1:
if msg.startswith("/"):
Main.Command(msg, client_id)
@ -45,18 +48,20 @@ def filter_chat_message(msg, client_id):
if msg in ["end", "dv", "nv", "sm"] and settings["allowVotes"]:
votingmachine.vote(acid, client_id, msg)
if acid in serverdata.clients and serverdata.clients[acid]["verified"]:
if serverdata.muted:
_ba.screenmessage("Server on mute", transient=True, clients=[client_id])
_ba.screenmessage("Server on mute",
transient=True, clients=[client_id])
return
elif serverdata.clients[acid]["isMuted"]:
_ba.screenmessage("You are on mute", transient=True, clients=[client_id])
elif acid in pdata.get_blacklist()["muted-ids"] and now < datetime.strptime(pdata.get_blacklist()["muted-ids"][acid]["till"], "%Y-%m-%d %H:%M:%S"):
_ba.screenmessage(
"You are on mute, maybe try after some time", transient=True, clients=[client_id])
return None
elif servercheck.get_account_age(serverdata.clients[acid]["accountAge"]) < settings['minAgeToChatInHours']:
_ba.screenmessage("New accounts not allowed to chat here", transient=True, clients=[client_id])
_ba.screenmessage("New accounts not allowed to chat here",
transient=True, clients=[client_id])
return None
else:
if msg.startswith(",") and settings["allowTeamChat"]:
@ -66,5 +71,6 @@ def filter_chat_message(msg, client_id):
return msg
else:
_ba.screenmessage("Fetching your account info , Wait a minute", transient=True, clients=[client_id])
_ba.screenmessage("Fetching your account info , Wait a minute",
transient=True, clients=[client_id])
return None

View file

@ -4,6 +4,8 @@
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
import shutil
import copy
from typing import TYPE_CHECKING
import time
@ -17,13 +19,13 @@ import _ba
import ba.internal
import json
import datetime
from tools.ServerUpdate import contributeData , checkSpammer
from tools.ServerUpdate import checkSpammer
import setting
from datetime import datetime, timedelta
if TYPE_CHECKING:
pass
setti=setting.get_settings_data()
settings = setting.get_settings_data()
PLAYERS_DATA_PATH = os.path.join(
_ba.env()["python_directory_user"], "playersData" + os.sep
@ -71,10 +73,8 @@ def get_profiles() -> dict:
if CacheData.profiles == {}:
try:
if os.stat(PLAYERS_DATA_PATH+"profiles.json").st_size > 1000000:
newpath = PLAYERS_DATA_PATH + "profiles.json"+str(datetime.datetime.now())
newpath = f'{PLAYERS_DATA_PATH}profiles-{str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))}.json'
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json", newpath)
if setti["contributeData"]:
contributeData(newpath)
profiles = {"pb-sdf": {}}
print("resetting profiles")
else:
@ -95,6 +95,20 @@ def get_profiles() -> dict:
else:
return CacheData.profiles
def get_profiles_archive_index():
return [x for x in os.listdir(PLAYERS_DATA_PATH) if x.startswith("profiles")]
def get_old_profiles(filename):
try:
f = open(PLAYERS_DATA_PATH + filename, "r")
profiles = json.load(f)
return profiles
except:
return {}
def get_blacklist() -> dict:
if CacheData.blacklist == {}:
try:
@ -104,19 +118,22 @@ def get_blacklist() -> dict:
print('error opening blacklist json')
return {
"ban": {
"ids":[],
"ips":[],
"deviceids":[]
"ids": {},
"ips": {},
"deviceids": {}
},
"muted-ids":[]
"muted-ids": {},
"kick-vote-disabled": {}
}
return CacheData.blacklist
def update_blacklist():
with open(PLAYERS_DATA_PATH + "blacklist.json", "w") as f:
json.dump(CacheData.blacklist, f, indent=4)
def commit_profiles(data={}) -> None:
"""Commits the given profiles in the database.
@ -127,6 +144,7 @@ def commit_profiles(data={}) -> None:
# with OpenJson(PLAYERS_DATA_PATH + "profiles.json") as profiles_file:
# profiles_file.dump(CacheData.profiles, indent=4)
def get_detailed_info(pbid):
main_account = get_info(pbid)
if main_account == None:
@ -167,11 +185,8 @@ def add_profile(
"display_string": display_string,
"profiles": [],
"name": current_name,
"isBan": False,
"isMuted": False,
"accountAge": account_age,
"registerOn": time.time(),
"canStartKickVote": True,
"spamCount": 0,
"lastSpam": time.time(),
"totaltimeplayer": 0,
@ -190,14 +205,15 @@ def add_profile(
cid = ros['client_id']
ip = _ba.get_client_ip(cid)
serverdata.clients[account_id]["lastIP"] = ip
serverdata.recents.append({"client_id":cid,"deviceId":display_string,"pbid": account_id})
serverdata.recents.append(
{"client_id": cid, "deviceId": display_string, "pbid": account_id})
serverdata.recents = serverdata.recents[-20:]
device_id = _ba.get_client_public_device_uuid(cid)
if (device_id == None):
device_id = _ba.get_client_device_uuid(cid)
checkSpammer({'id':account_id,'display':display_string,'ip':ip,'device':device_id})
if device_id in get_blacklist()["ban"]["deviceids"]:
serverdata.clients[account_id]["isBan"]=True
checkSpammer({'id': account_id, 'display': display_string,
'ip': ip, 'device': device_id})
if device_id in get_blacklist()["ban"]["deviceids"] or account_id in get_blacklist()["ban"]["ids"]:
ba.internal.disconnect_client(cid)
serverdata.clients[account_id]["deviceUUID"] = device_id
@ -259,7 +275,7 @@ def update_profile(
commit_profiles()
def ban_player(account_id: str) -> None:
def ban_player(account_id: str, duration_in_days: float, reason: str) -> None:
"""Bans the player.
Parameters
@ -267,27 +283,51 @@ def ban_player(account_id: str) -> None:
account_id : str
account id of the player to be banned
"""
profiles = get_profiles()
if account_id in profiles:
profiles[account_id]["isBan"] = True
CacheData.profiles=profiles
# _thread.start_new_thread(commit_profiles, (profiles,))
cid = -1
for ros in ba.internal.get_game_roster():
if ros['account_id'] == account_id:
cid = ros['client_id']
ip = _ba.get_client_ip(cid)
current_profiles = get_profiles()
ip = ""
device_id = ""
if account_id in current_profiles:
ip = current_profiles[account_id]["lastIP"]
device_id = current_profiles[account_id]["deviceUUID"]
device_id = _ba.get_client_public_device_uuid(cid)
if(device_id==None):
device_id = _ba.get_client_device_uuid(cid)
CacheData.blacklist["ban"]["ips"].append(ip)
ban_time = datetime.now() + timedelta(days=duration_in_days)
CacheData.blacklist["ban"]["ids"].append(account_id)
CacheData.blacklist["ban"]["deviceids"].append(device_id)
CacheData.blacklist["ban"]["ips"][ip] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": f'linked with account {account_id}'}
CacheData.blacklist["ban"]["ids"][account_id] = {
"till": ban_time.strftime("%Y-%m-%d %H:%M:%S"), "reason": reason}
CacheData.blacklist["ban"]["deviceids"][device_id] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": f'linked with account {account_id}'}
_thread.start_new_thread(update_blacklist, ())
def mute(account_id: str) -> None:
def unban_player(account_id):
current_profiles = get_profiles()
ip = ""
device_id = ""
if account_id in current_profiles:
ip = current_profiles[account_id]["lastIP"]
device_id = current_profiles[account_id]["deviceUUID"]
CacheData.blacklist["ban"]["ips"].pop(ip, None)
CacheData.blacklist["ban"]["deviceids"].pop(device_id, None)
CacheData.blacklist["ban"]["ids"].pop(account_id, None)
_thread.start_new_thread(update_blacklist, ())
def disable_kick_vote(account_id, duration, reason):
ban_time = datetime.now() + timedelta(days=duration)
CacheData.blacklist["kick-vote-disabled"][account_id] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": reason}
_thread.start_new_thread(update_blacklist, ())
def enable_kick_vote(account_id):
CacheData.blacklist["kick-vote-disabled"].pop(account_id, None)
_thread.start_new_thread(update_blacklist, ())
def mute(account_id: str, duration_in_days: float, reason: str) -> None:
"""Mutes the player.
Parameters
@ -295,11 +335,11 @@ def mute(account_id: str) -> None:
account_id : str
acccount id of the player to be muted
"""
profiles = get_profiles()
if account_id in profiles:
profiles[account_id]["isMuted"] = True
CacheData.profiles=profiles
ban_time = datetime.now() + timedelta(days=duration_in_days)
CacheData.blacklist["muted-ids"][account_id] = {"till": ban_time.strftime(
"%Y-%m-%d %H:%M:%S"), "reason": reason}
_thread.start_new_thread(update_blacklist, ())
def unmute(account_id: str) -> None:
@ -310,11 +350,8 @@ def unmute(account_id: str) -> None:
account_id : str
acccount id of the player to be unmuted
"""
profiles = get_profiles()
if account_id in profiles:
profiles[account_id]["isMuted"] = False
CacheData.profiles=profiles
_thread.start_new_thread(commit_profiles, (profiles,))
CacheData.blacklist["muted-ids"].pop(account_id, None)
_thread.start_new_thread(update_blacklist, ())
def update_spam(account_id: str, spam_count: int, last_spam: float) -> None:
@ -558,6 +595,10 @@ def get_custom() -> dict:
custom = json.load(f)
f.close()
CacheData.custom = custom
for account_id in custom["customeffects"]:
custom["customeffects"][account_id] = [custom["customeffects"][account_id]] if type(
custom["customeffects"][account_id]) is str else custom["customeffects"][account_id]
return CacheData.custom
@ -573,7 +614,8 @@ def set_effect(effect: str, account_id: str) -> None:
"""
custom = get_custom()
if account_id in custom["customeffects"]:
effects = [custom["customeffects"][account_id]] if type(custom["customeffects"][account_id]) is str else custom["customeffects"][account_id]
effects = [custom["customeffects"][account_id]] if type(
custom["customeffects"][account_id]) is str else custom["customeffects"][account_id]
effects.append(effect)
custom["customeffects"][account_id] = effects
else:
@ -598,6 +640,22 @@ def set_tag(tag: str, account_id: str) -> None:
commit_c()
def get_roles():
return CacheData.roles
def update_roles(roles):
CacheData.roles = roles
def get_custom_perks():
return CacheData.custom
def update_custom_perks(custom):
CacheData.custom = custom
def remove_effect(account_id: str) -> None:
"""Removes the effect from player.
@ -609,7 +667,6 @@ def remove_effect(account_id: str) -> None:
custom = get_custom()
custom["customeffects"].pop(account_id)
CacheData.custom = custom
commit_c()
def remove_tag(account_id: str) -> None:
@ -623,7 +680,6 @@ def remove_tag(account_id: str) -> None:
custom = get_custom()
custom["customtag"].pop(account_id)
CacheData.custom = custom
commit_c()
def commit_c():
@ -654,27 +710,30 @@ def load_white_list() -> None:
for account_id in data:
CacheData.whitelist.append(account_id)
def load_cache():
""" to be called on server boot"""
get_profiles()
get_custom()
get_roles()
import shutil
import copy
def dump_cache():
if CacheData.profiles != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json",PLAYERS_DATA_PATH + "profiles.json.backup")
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json",
PLAYERS_DATA_PATH + "profiles.json.backup")
profiles = copy.deepcopy(CacheData.profiles)
with open(PLAYERS_DATA_PATH + "profiles.json", "w") as f:
json.dump(profiles, f, indent=4)
if CacheData.roles != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "roles.json",PLAYERS_DATA_PATH + "roles.json.backup")
shutil.copyfile(PLAYERS_DATA_PATH + "roles.json",
PLAYERS_DATA_PATH + "roles.json.backup")
roles = copy.deepcopy(CacheData.roles)
with open(PLAYERS_DATA_PATH + "roles.json", "w") as f:
json.dump(roles, f, indent=4)
if CacheData.custom != {}:
shutil.copyfile(PLAYERS_DATA_PATH + "custom.json",PLAYERS_DATA_PATH + "custom.json.backup")
shutil.copyfile(PLAYERS_DATA_PATH + "custom.json",
PLAYERS_DATA_PATH + "custom.json.backup")
custom = copy.deepcopy(CacheData.custom)
with open(PLAYERS_DATA_PATH + "custom.json", "w") as f:
json.dump(custom, f, indent=4)

View file

@ -2,119 +2,43 @@
# coding: utf-8
# ba_meta require api 7
from typing import Optional, Any, Dict, List, Type, Sequence
from ba._gameactivity import GameActivity
import ba,_ba
import ba.internal
import json
# from gunicorn.app.base import BaseApplication
# from gunicorn.workers import ggevent as gevent_worker
from flask import Flask, request, jsonify
from functools import wraps
import os
import _thread
# from stats import mystats
# import uvicorn
from . import bombsquad_service
os.environ['FLASK_APP'] = 'bombsquadflaskapi.py'
os.environ['FLASK_ENV'] = 'development'
from stats import mystats
stats={}
leaderboard={}
top200={}
class BsDataThread(object):
def __init__(self):
self.Timer = ba.Timer( 8,ba.Call(self.refreshStats),timetype = ba.TimeType.REAL,repeat = True)
self.Timerr = ba.Timer( 10,ba.Call(self.startThread),timetype = ba.TimeType.REAL,repeat = True)
def startThread(self):
_thread.start_new_thread(self.refreshLeaderboard,())
def refreshLeaderboard(self):
global leaderboard
global top200
_t200={}
lboard=mystats.get_all_stats()
leaderboard=lboard
try:
entries = [(a['scores'], a['kills'], a['deaths'], a['games'], a['name'], a['aid'],a['last_seen']) for a in lboard.values()]
except:
print("stats reset is required , please clear out stats.json records , or download fresh stats.json from github")
import _ba
_ba.quit()
entries.sort(key=lambda x: x[1] or 0, reverse=True)
rank=0
for entry in entries:
rank+=1
if rank >201:
break
_t200[entry[5]]={"rank":rank,"scores":int(entry[0]),"games":int(entry[3]),"kills":int(entry[1]),"deaths":int(entry[2]),"name_html":entry[4],"last_seen":entry[6]}
top200=_t200
def refreshStats(self):
liveplayers={}
nextMap=''
currentMap=''
global stats
for i in ba.internal.get_game_roster():
try:
liveplayers[i['account_id']]={'name':i['players'][0]['name_full'],'client_id':i['client_id'],'device_id':i['display_string']}
except:
liveplayers[i['account_id']]={'name':"<in-lobby>",'clientid':i['client_id'],'device_id':i['display_string']}
try:
nextMap=ba.internal.get_foreground_host_session().get_next_game_description().evaluate()
current_game_spec=ba.internal.get_foreground_host_session()._current_game_spec
gametype: Type[GameActivity] =current_game_spec['resolved_type']
currentMap=gametype.get_settings_display_string(current_game_spec).evaluate()
except:
pass
minigame={'current':currentMap,'next':nextMap}
# system={'cpu':"p.cpu_percent()",'ram':p.virtual_memory().percent}
system={'cpu':"null",'ram':'null'}
stats['system']=system
stats['roster']=liveplayers
stats['chats']=ba.internal.get_chat_messages()
stats['playlist']=minigame
stats['teamInfo']=self.getTeamInfo()
#print(self.getTeamInfo());
def getTeamInfo(self):
data={}
session=ba.internal.get_foreground_host_session()
data['sessionType']=type(session).__name__
teams=session.sessionteams
for team in teams:
data[team.id]={'name':team.name.evaluate(),
'color':list(team.color),
'score':team.customdata['score'],
'players':[]
}
for player in team.players:
teamplayer={'name':player.getname(),
'device_id':player.inputdevice.get_v1_account_name(True),
'inGame':player.in_game,
'character':player.character,
'account_id':player.get_v1_account_id()
}
data[team.id]['players'].append(teamplayer)
return data
BsDataThread()
import flask
from flask import request, jsonify
app = flask.Flask(__name__)
app = Flask(__name__)
app.config["DEBUG"] = False
SECRET_KEY = "my_secret_key"
@app.after_request
def add_cors_headers(response):
# Allow requests from any origin
response.headers['Access-Control-Allow-Origin'] = '*'
# Allow specific headers
response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization,Secret-Key'
# Allow specific HTTP methods
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
return response
def check_admin(func):
@wraps(func)
def wrapper(*args, **kwargs):
if "Secret-Key" not in request.headers or request.headers["Secret-Key"] != SECRET_KEY:
return jsonify({"message": "Invalid secret key provided."}), 401
return func(*args, **kwargs)
return wrapper
@app.route('/', methods=['GET'])
@ -122,26 +46,219 @@ def home():
return '''Nothing here :)'''
# A route to return all of the available entries in our catalog.
@app.route('/getStats', methods=['GET'])
def api_all():
return json.dumps(stats)
@app.route('/api/live-stats', methods=['GET'])
def get_live_stats():
return jsonify(bombsquad_service.get_stats()), 200
@app.route('/getLeaderboard',methods=['GET'])
def get_l():
return json.dumps(leaderboard)
@app.route('/getTop200',methods=['GET'])
@app.route('/api/top-200', methods=['GET'])
def get_top200():
return json.dumps(top200)
return jsonify(bombsquad_service.get_top_200()), 200
class InitalRun:
def __init__(self):
print("start flask")
flask_run = _thread.start_new_thread(app.run, ("0.0.0.0",5000,False ))
# ============ Admin only =========
@app.route('/api/login', methods=['POST'])
@check_admin
def login():
return jsonify({"message": "Successful"}), 200
@app.route('/api/current-leaderboard', methods=['GET'])
@check_admin
def get_complete_leaderboard():
return jsonify(bombsquad_service.get_complete_leaderboard()), 200
@app.route('/api/server-settings', methods=['GET'])
@check_admin
def get_server_settings():
return jsonify(bombsquad_service.get_server_settings()), 200
@app.route('/api/roles', methods=['GET'])
@check_admin
def get_roles():
return jsonify(bombsquad_service.get_roles()), 200
@app.route('/api/roles', methods=['POST'])
@check_admin
def update_roles():
try:
data = request.get_json()
bombsquad_service.update_roles(data)
response = {
'message': 'Roles updated successfully'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
@app.route('/api/perks', methods=['GET'])
@check_admin
def get_perks():
return jsonify(bombsquad_service.get_perks()), 200
@app.route('/api/perks', methods=['POST'])
@check_admin
def update_perks():
try:
data = request.get_json()
bombsquad_service.update_perks(data)
response = {
'message': 'Custom perks updated successfully'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
@app.route('/api/server-settings', methods=['POST'])
@check_admin
def update_server_settings():
try:
data = request.get_json()
bombsquad_service.update_server_settings(data)
response = {
'message': 'Settings updated successfully, server may need restart'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
@app.route('/api/db-list', methods=['GET'])
@check_admin
def fetch_dB_list():
key = request.args.get('type')
if key is None:
return "type required", 400
if key == "logs":
return jsonify(bombsquad_service.get_logs_db_list()), 200
elif key == "players":
return jsonify(bombsquad_service.get_profiles_db_list()), 200
else:
return jsonify({"message": "Invalid db type"}), 400
@app.route('/api/search-logs', methods=['GET'])
@check_admin
def search_logs():
key = request.args.get('key')
db = request.args.get('db')
if key is None or db is None:
return jsonify({"message": "key and db required"}), 400
return jsonify(bombsquad_service.get_matching_logs(key, db)), 200
@app.route('/api/search-player', methods=['GET'])
@check_admin
def search_players():
key = request.args.get('key')
db = request.args.get('db')
if key is None or db is None:
return jsonify({"message": "key and db required"}), 400
return jsonify(bombsquad_service.search_player_profile(key, db)), 200
@app.route('/api/get-player-info', methods=['GET'])
@check_admin
def get_player():
account_id = request.args.get('account-id')
if account_id is None:
return jsonify({"message": "account-id required"}), 400
return jsonify(bombsquad_service.get_player_details(account_id)), 200
@app.route('/api/update-player', methods=['POST'])
@check_admin
def update_player():
account_id = request.args.get('account-id')
action = request.args.get('action')
duration = int(request.args.get('duration'))
if account_id is None or action is None:
return "account-id and action required", 400
if action == "ban":
bombsquad_service.ban_player(account_id, duration)
elif action == "unban":
bombsquad_service.unban_player(account_id)
elif action == "mute":
bombsquad_service.mute_player(account_id, duration)
elif action == "unmute":
bombsquad_service.unmute_player(account_id)
elif action == "disable-kick-vote":
bombsquad_service.disable_kick_vote(account_id, duration)
elif action == "enable-kick-vote":
bombsquad_service.enable_kick_vote(account_id)
else:
return jsonify({"message": "Invalid Action"}), 400
return jsonify({"message": f"{action} done"}), 201
@app.route('/api/config', methods=['GET'])
@check_admin
def get_config():
return jsonify(bombsquad_service.get_server_config()), 200
@app.route('/api/action', methods=['POST'])
@check_admin
def do_action():
action = request.args.get('action')
value = request.args.get('value')
bombsquad_service.do_action(action, value)
return jsonify({"message": f'{action} done'}), 200
@app.route('/api/config', methods=['POST'])
@check_admin
def update_server_config():
try:
data = request.get_json()
bombsquad_service.update_server_config(data)
response = {
'message': 'config updated successfully, server will restart'}
return jsonify(response), 201
except Exception as e:
return jsonify({'message': 'Error processing request', 'error': str(e)}), 400
# from flask_asgi import FlaskASGI
# asgi_app = FlaskASGI(app)
# class FlaskApplication(BaseApplication):
# def __init__(self, app, options=None):
# self.options = options or {}
# self.application = app
# super(FlaskApplication, self).__init__()
# def load_config(self):
# config = {key: value for key, value in self.options.items(
# ) if key in self.cfg.settings and value is not None}
# for key, value in config.items():
# self.cfg.set(key.lower(), value)
# def load(self):
# return self.application
# def start_uvicorn():
# uvicorn.run("main:app", host='0.0.0.0', port=5000,
# reload=False, log_level="debug", workers=3, use_colors=True, no_signal=True)
# flask_run = _thread.start_new_thread(app.run, ("0.0.0.0", 5000, False))
def enable():
InitalRun()
# SAMPLE OUTPUT
# {'system': {'cpu': 80, 'ram': 34}, 'roster': {}, 'chats': [], 'playlist': {'current': 'Meteor Shower @ Rampage', 'next': 'Assault @ Step Right Up'}, 'teamInfo': {'sessionType': 'DualTeamSession', 0: {'name': 'Blue', 'color': (0.1, 0.25, 1.0), 'score': 1, 'players': [{'name': 'Jolly', 'device_id': '\ue030PC295588', 'inGame': True, 'character': 'xmas', 'account_id': 'pb-IF4TVWwZUQ=='}]}, 1: {'name': 'Red', 'color': (1.0, 0.25, 0.2), 'score': 0, 'players': []}}}
flask_run = _thread.start_new_thread(app.run, ("0.0.0.0", 5000, False))
# uvicorn_thread = threading.Thread(target=start_uvicorn)
# uvicorn_thread.start()
# options = {
# 'bind': '0.0.0.0:8000',
# 'workers': 4,
# 'worker_class': 'gevent'
# }
# flask_app = FlaskApplication(app, options)
# gevent_worker.GeventWorker(app.wsgi_app).init_process()
# flask_app.run()

View file

@ -0,0 +1,257 @@
import ba
import _ba
import ba.internal
from stats import mystats
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
import setting
from datetime import datetime
import _thread
import os
import yaml
stats = {}
leaderboard = {}
top200 = {}
serverinfo = {}
class BsDataThread(object):
def __init__(self):
global stats
stats["name"] = _ba.app.server._config.party_name
stats["discord"] = "https://discord.gg/ucyaesh"
stats["vapidKey"] = "sjfbsdjfdsf"
self.refresh_stats_cache_timer = ba.Timer(8, ba.Call(self.refreshStats),
timetype=ba.TimeType.REAL, repeat=True)
self.refresh_leaderboard_cache_timer = ba.Timer(10, ba.Call(
self.refreshLeaderboard), timetype=ba.TimeType.REAL, repeat=True)
def startThread(self):
_thread.start_new_thread(self.refreshLeaderboard, ())
def refreshLeaderboard(self):
global leaderboard
global top200
lboard = mystats.get_cached_stats()
leaderboard = lboard
sorted_data = sorted(lboard.values(), key=lambda x: x["rank"])
top_200_players = sorted_data[:200]
top200 = {player["aid"]: player for player in top_200_players}
def refreshStats(self):
global stats
global serverinfo
liveplayers = {}
nextMap = ''
currentMap = ''
for i in ba.internal.get_game_roster():
try:
liveplayers[i['account_id']] = {
'name': i['players'][0]['name_full'], 'client_id': i['client_id'], 'device_id': i['display_string']}
except:
liveplayers[i['account_id']] = {
'name': "<in-lobby>", 'client_id': i['client_id'], 'device_id': i['display_string']}
try:
nextMap = ba.internal.get_foreground_host_session(
).get_next_game_description().evaluate()
current_game_spec = ba.internal.get_foreground_host_session()._current_game_spec
gametype: Type[GameActivity] = current_game_spec['resolved_type']
currentMap = gametype.get_settings_display_string(
current_game_spec).evaluate()
except:
pass
current_games = {'current': currentMap, 'next': nextMap}
# system={'cpu':"p.cpu_percent()",'ram':p.virtual_memory().percent}
system = {'cpu': "null", 'ram': 'null'}
stats['system'] = system
stats['roster'] = liveplayers
stats['chats'] = ba.internal.get_chat_messages()
stats['playlist'] = current_games
stats['teamInfo'] = self.getTeamInfo()
stats["sessionType"] = type(
ba.internal.get_foreground_host_session()).__name__
# print(self.getTeamInfo());
def getTeamInfo(self):
data = {}
session = ba.internal.get_foreground_host_session()
if session:
teams = session.sessionteams
for team in teams:
data[str(team.id)] = {'name': team.name if isinstance(team.name, str) else team.name.evaluate(),
'color': list(team.color),
'score': team.customdata['score'],
'players': []
}
for player in team.players:
teamplayer = {'name': player.getname(),
'device_id': player.inputdevice.get_v1_account_name(True),
'inGame': player.in_game,
'character': player.character,
'account_id': player.get_v1_account_id()
}
data[str(team.id)]['players'].append(teamplayer)
return data
BsDataThread()
def get_stats():
return stats
def get_complete_leaderboard():
return leaderboard
def get_top_200():
return top200
def get_server_settings():
return setting.get_settings_data()
def update_server_settings(settings):
logger.log(f'updating server settings, request from web')
setting.commit(settings)
def get_roles():
return pdata.get_roles()
def get_perks():
# TODO wire with spaz_effects to fetch list of effects.
return {"perks": pdata.get_custom_perks(), "availableEffects": ["spark", "glow", "fairydust"]}
def update_perks(custom):
logger.log(f'updating custom perks, request from web')
pdata.update_custom_perks(custom)
def update_roles(roles):
logger.log("updated roles from web")
return pdata.update_roles(roles)
def get_profiles_db_list():
return pdata.get_profiles_archive_index()
def get_logs_db_list():
return serverdata.get_stats_index()
def get_matching_logs(key: str, filename: str):
logs = serverdata.read_logs(filename)
matching_lines = [line.strip() for line in logs.split('\n') if key in line]
return matching_lines
def search_player_profile(search_key: str, db: str):
selectedDB = {}
if db == "profiles.json":
selectedDB = pdata.get_profiles()
elif db in pdata.get_profiles_archive_index():
selectedDB = pdata.get_old_profiles(db)
matching_objects = {}
for key in selectedDB.keys():
if (search_key == key or
any(search_key.lower() in s.lower() for s in selectedDB[key].get("display_string", [])) or
search_key.lower() in selectedDB[key].get("name", "").lower()):
matching_objects[key] = selectedDB[key]
return matching_objects
def get_player_details(account_id: str):
current_time = datetime.now()
current_profiles = pdata.get_profiles()
ip = ""
device_id = ""
if account_id in current_profiles:
ip = current_profiles[account_id]["lastIP"]
device_id = current_profiles[account_id]["deviceUUID"]
extra_info = pdata.get_detailed_info(account_id)
isBanned = False
isMuted = False
isKickVoteDisabled = False
haveBanReason = servercheck.check_ban(ip, device_id, account_id, False)
if haveBanReason:
isBanned = True
extra_info += " , Banned for > " + haveBanReason
if account_id in pdata.get_blacklist()["muted-ids"] and current_time < datetime.strptime(pdata.get_blacklist()["muted-ids"][account_id]["till"], "%Y-%m-%d %H:%M:%S"):
isMuted = True
extra_info += f', Muted for > { pdata.get_blacklist()["muted-ids"][account_id]["reason"] } , till > {pdata.get_blacklist()["muted-ids"][account_id]["till"]} ,'
if account_id in pdata.get_blacklist()["kick-vote-disabled"] and current_time < datetime.strptime(pdata.get_blacklist()["kick-vote-disabled"][account_id]["till"], "%Y-%m-%d %H:%M:%S"):
isKickVoteDisabled = True
extra_info += f', Kick vote disabled for > { pdata.get_blacklist()["kick-vote-disabled"][account_id]["reason"] } , till > {pdata.get_blacklist()["kick-vote-disabled"][account_id]["till"]} '
return {"extra": extra_info, "isBan": isBanned, "isMuted": isMuted, "isKickVoteDisabled": isKickVoteDisabled}
def unban_player(account_id):
logger.log(f'unbanning {account_id} , request from web')
pdata.unban_player(account_id)
def unmute_player(account_id):
logger.log(f'unmuting {account_id} , request from web')
pdata.unmute(account_id)
def enable_kick_vote(account_id):
logger.log(f'enabling kick vote for {account_id} , request from web')
pdata.enable_kick_vote(account_id)
# TODO take duration input
def ban_player(account_id, duration):
logger.log(f'banning {account_id} , request from web')
pdata.ban_player(account_id, duration, "manually from website")
def mute_player(account_id, duration):
logger.log(f'muting {account_id} , request from web')
pdata.mute(account_id, duration, "manually from website")
def disable_kick_vote(account_id, duration):
logger.log(f'disable {account_id} , request from web')
pdata.disable_kick_vote(account_id, duration, "manually from website")
def get_server_config():
return _ba.app.server._config.__dict__
def update_server_config(config):
current_dir = os.getcwd()
file_path = os.path.join(current_dir, '..', 'config.yaml')
with open(file_path, "w") as f:
f.write(yaml.dump(config))
def do_action(action, value):
if action == "message":
_ba.pushcall(ba.Call(_ba.chatmessage, value), from_other_thread=True)
elif action == "quit":
_ba.pushcall(ba.Call(_ba.quit), from_other_thread=True)

View file

@ -1,8 +1,37 @@
# Released under the MIT License. See LICENSE for details.
import fcntl
import _ba
import os
clients = {}
cachedclients = []
muted = False
coopmode = False
ips = {}
recents = []
SERVER_DATA_PATH = os.path.join(
_ba.env()["python_directory_user"], "serverData" + os.sep
)
def get_stats_index():
return [x for x in os.listdir(SERVER_DATA_PATH) if "log" in x]
def read_logs(filename):
file_path = SERVER_DATA_PATH+filename
if not os.path.exists(file_path):
return ""
file = open(file_path, "r")
fcntl.flock(file.fileno(), fcntl.LOCK_SH)
contents = ""
try:
contents = file.read()
finally:
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
file.close()
return contents

View file

@ -30,6 +30,10 @@ def get_settings_data() -> dict:
with open(SETTINGS_PATH, mode="r", encoding="utf-8") as data:
return json.load(data)
def refresh_cache() -> None:
get_settings_data.cache_clear()
# lets cache it again
get_settings_data()
def commit(data: dict) -> None:
"""Commits the data in setting file.
@ -41,3 +45,5 @@ def commit(data: dict) -> None:
"""
with open(SETTINGS_PATH, mode="w", encoding="utf-8") as setting_file:
json.dump(data, setting_file, indent=4)
# settings updated ok now update the cache
refresh_cache()

View file

@ -6,29 +6,17 @@ import os
import shutil
import threading
import datetime
import custom_hooks
import urllib.request
from ba._activitytypes import *
from ba import _activitytypes as ba_actypes
from ba._lobby import JoinInfo
from typing import Any, Dict, Optional
from ba._team import EmptyTeam # pylint: disable=W0611
from ba._player import EmptyPlayer # pylint: disable=W0611
from ba._music import setmusic, MusicType
from ba._activity import Activity
damage_data = {}
# Don't touch the above line
"""
mystats module for BombSquad version 1.5.29
Provides functionality for dumping player stats to disk between rounds.
"""
ranks = []
top3Name = []
# False-positive from pylint due to our class-generics-filter.
# variables
our_settings = setting.get_settings_data()
# where our stats file and pretty html output will go
base_path = os.path.join(_ba.env()['python_directory_user'], "stats" + os.sep)
statsfile = base_path + 'stats.json'
cached_stats = {}
@ -47,16 +35,14 @@ statsDefault = {
"last_seen": "2022-04-26 17:01:13.715014"
}
}
# <th><b>Total Damage</b></th> #removed this line as it isn't crt data
# useful functions
seasonStartDate = None
def get_all_stats():
global seasonStartDate
if os.path.exists(statsfile):
renameFile = False
with open(statsfile, 'r', encoding='utf8') as f:
try:
jsonData = json.loads(f.read())
@ -83,7 +69,7 @@ def get_all_stats():
def backupStatsFile():
shutil.copy(statsfile, statsfile.replace(
".json", "") + str(seasonStartDate) + ".json")
".json", "") + str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + ".json")
def dump_stats(s: dict):
@ -107,23 +93,27 @@ def get_stats_by_id(account_id: str):
else:
return None
def get_cached_stats():
return cached_stats
def get_sorted_stats(stats):
entries = [(a['scores'], a['kills'], a['deaths'], a['games'],
a['name'], a['aid']) for a in stats.values()]
# this gives us a list of kills/names sorted high-to-low
entries.sort(key=lambda x: x[1] or 0, reverse=True)
return entries
def refreshStats():
global cached_stats
# lastly, write a pretty html version.
# our stats url could point at something like this...
pStats = get_all_stats()
cached_stats = pStats
entries = [(a['scores'], a['kills'], a['deaths'], a['games'],
a['name'], a['aid']) for a in pStats.values()]
# this gives us a list of kills/names sorted high-to-low
entries.sort(key=lambda x: x[1] or 0, reverse=True)
entries = get_sorted_stats(pStats)
rank = 0
toppers = {}
toppersIDs = []
_ranks = []
for entry in entries:

View file

@ -17,7 +17,7 @@ import shutil
import threading
import setting
import _ba
import fcntl
if TYPE_CHECKING:
pass
@ -33,6 +33,7 @@ if SETTINGS["discordbot"]["enable"]:
WEBHOOK_URL = SETTINGS["discordWebHook"]["webhookURL"]
@dataclass
class RecentLogs:
"""Saves the recent logs."""
@ -54,7 +55,7 @@ def log(msg: str, mtype: str = "sys") -> None:
message = msg.replace("||", "|")
discord_bot.push_log("***" + mtype + ":***" + message)
current_time = datetime.datetime.now()
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = f"{current_time} + : {msg} \n"
if SETTINGS["discordWebHook"]["enable"]:
@ -102,14 +103,38 @@ class dumplogs(threading.Thread):
elif self.type == "cmndlog":
log_path = SERVER_DATA_PATH + "cmndusage.log"
else:
log_path = SERVER_DATA_PATH + "logs.log"
log_path = SERVER_DATA_PATH + "systemlogs.log"
if os.path.exists(log_path):
if os.stat(log_path).st_size > 1000000:
shutil.copy(log_path, log_path+str(datetime.datetime.now()))
os.remove(log_path)
with open(log_path, mode="a+", encoding="utf-8") as file:
for msg in self.msg:
self.copy_file(
log_path, log_path+str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
self.write_file(log_path, self.msg)
def write_file(self, file_path, data):
file = open(file_path, "a+", encoding="utf-8")
fcntl.flock(file.fileno(), fcntl.LOCK_EX)
try:
for msg in data:
file.write(msg)
finally:
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
file.close()
def copy_file(self, file_path, dest_path):
lock_file = open(file_path, "r")
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
try:
shutil.copy(file_path, dest_path)
except Exception as e:
print("Error occurred while copying file:", str(e))
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
lock_file.close()
os.remove(lock_file)
def send_webhook_message():
global webhook_queue

View file

@ -7,7 +7,7 @@ import _ba
import ba.internal
import urllib.request
import json
import datetime
from datetime import datetime
import time
import ba
from ba._general import Call
@ -23,11 +23,13 @@ blacklist = pdata.get_blacklist()
settings = setting.get_settings_data()
ipjoin = {}
class checkserver(object):
def start(self):
self.players = []
self.t1 = ba.Timer(1, ba.Call(self.check), repeat=True, timetype=ba.TimeType.REAL)
self.t1 = ba.Timer(1, ba.Call(self.check),
repeat=True, timetype=ba.TimeType.REAL)
def check(self):
newPlayers = []
@ -36,16 +38,15 @@ class checkserver(object):
for ros in ba.internal.get_game_roster():
ip = _ba.get_client_ip(ros["client_id"])
device_id = _ba.get_client_public_device_uuid(ros["client_id"])
# if not ros["account_id"]:
# logger.log(f'Player disconnected, None account Id || {ros["display_string"] } IP {ip} {device_id}' ,
# "playerjoin")
# ba.internal.disconnect_client(ros["client_id"], 0)
if (device_id == None):
device_id = _ba.get_client_device_uuid(ros["client_id"])
if device_id not in deviceClientMap:
deviceClientMap[device_id] = [ros["client_id"]]
else:
deviceClientMap[device_id].append(ros["client_id"])
if len(deviceClientMap[device_id]) >= settings['maxAccountPerIP']:
_ba.chatmessage(f"Only {settings['maxAccountPerIP']} player per IP allowed, disconnecting this device.", clients=[ros["client_id"]])
_ba.chatmessage(f"Only {settings['maxAccountPerIP']} player per IP allowed, disconnecting this device.", clients=[
ros["client_id"]])
ba.internal.disconnect_client(ros["client_id"])
logger.log(f'Player disconnected, reached max players per device || {ros["account_id"]}',
"playerjoin")
@ -55,7 +56,8 @@ class checkserver(object):
else:
ipClientMap[ip].append(ros["client_id"])
if len(ipClientMap[ip]) >= settings['maxAccountPerIP']:
_ba.chatmessage(f"Only {settings['maxAccountPerIP']} player per IP allowed, disconnecting this device.", clients=[ros["client_id"]])
_ba.chatmessage(f"Only {settings['maxAccountPerIP']} player per IP allowed, disconnecting this device.", clients=[
ros["client_id"]])
ba.internal.disconnect_client(ros["client_id"])
logger.log(f'Player disconnected, reached max players per IP address || {ros["account_id"]}',
"playerjoin")
@ -92,7 +94,8 @@ class checkserver(object):
_ba.screenmessage("Not in whitelist,contact admin",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
logger.log(f'{d_str} || { ros["account_id"]} | kicked > not in whitelist')
logger.log(
f'{d_str} || { ros["account_id"]} | kicked > not in whitelist')
ba.internal.disconnect_client(ros['client_id'])
return
@ -101,14 +104,15 @@ class checkserver(object):
if ros['account_id'] in serverdata.clients:
on_player_join_server(ros['account_id'],
serverdata.clients[
ros['account_id']], ip)
ros['account_id']], ip, device_id)
else:
LoadProfile(ros['account_id'], ip).start() # from local cache, then call on_player_join_server
# from local cache, then call on_player_join_server
LoadProfile(ros['account_id'], ip, device_id).start()
self.players = newPlayers
def on_player_join_server(pbid, player_data, ip):
def on_player_join_server(pbid, player_data, ip, device_id):
global ipjoin
now = time.time()
# player_data=pdata.get_info(pbid)
@ -124,7 +128,7 @@ def on_player_join_server(pbid, player_data, ip):
if now - lastjoin < 15:
joincount += 1
if joincount > 2:
_ba.screenmessage("Joining too fast , slow down dude",
_ba.screenmessage("Joining too fast , slow down dude", # its not possible now tho, network layer will catch it before reaching here
color=(1, 0, 1), transient=True,
clients=[clid])
logger.log(f'{pbid} || kicked for joining too fast')
@ -141,40 +145,40 @@ def on_player_join_server(pbid, player_data, ip):
if pbid in serverdata.clients:
serverdata.clients[pbid]["lastJoin"] = now
if player_data != None: # player data not in serevrdata or in local.json cache
serverdata.recents.append({"client_id":clid,"deviceId":device_string,"pbid":pbid})
if player_data != None: # player data is in serevrdata or in local.json cache
serverdata.recents.append(
{"client_id": clid, "deviceId": device_string, "pbid": pbid})
serverdata.recents = serverdata.recents[-20:]
if player_data["isBan"] or get_account_age(player_data["accountAge"]) < \
if check_ban(ip, device_id, pbid):
_ba.chatmessage(
'sad ,your account is flagged contact server owner for unban', clients=[clid])
ba.internal.disconnect_client(clid)
return
if get_account_age(player_data["accountAge"]) < \
settings["minAgeToJoinInHours"]:
for ros in ba.internal.get_game_roster():
if ros['account_id'] == pbid:
if not player_data["isBan"]:
_ba.screenmessage(
"New Accounts not allowed here , come back later",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
logger.log(pbid + " | kicked > reason:Banned account")
_ba.screenmessage(
"Contact server owner, your account not allowed here",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
ba.internal.disconnect_client(ros['client_id'])
return
else:
current_time = datetime.now()
if pbid not in serverdata.clients:
if check_ban(clid,pbid):
return
# ahh , lets reset if plyer joining after some long time
serverdata.clients[pbid] = player_data
serverdata.clients[pbid]["warnCount"] = 0
serverdata.clients[pbid]["lastWarned"] = time.time()
serverdata.clients[pbid]["verified"] = False
serverdata.clients[pbid]["rejoincount"] = 1
serverdata.clients[pbid]["lastJoin"] = time.time()
if not player_data["canStartKickVote"]:
if pbid in blacklist["kick-vote-disabled"] and current_time < datetime.strptime(blacklist["kick-vote-disabled"][pbid]["till"], "%Y-%m-%d %H:%M:%S"):
_ba.disable_kickvote(pbid)
serverdata.clients[pbid]["lastIP"] = ip
device_id = _ba.get_client_public_device_uuid(clid)
@ -182,7 +186,8 @@ def on_player_join_server(pbid, player_data, ip):
device_id = _ba.get_client_device_uuid(clid)
serverdata.clients[pbid]["deviceUUID"] = device_id
verify_account(pbid, player_data) # checked for spoofed ids
logger.log(pbid+" ip: "+serverdata.clients[pbid]["lastIP"]+", Device id: "+device_id)
logger.log(
f'{pbid} ip: {serverdata.clients[pbid]["lastIP"]} , Device id: {device_id}')
_ba.screenmessage(settings["regularWelcomeMsg"] + " " + device_string,
color=(0.60, 0.8, 0.6), transient=True,
clients=[clid])
@ -201,19 +206,33 @@ def on_player_join_server(pbid, player_data, ip):
# pdata.add_profile(pbid,d_string,d_string)
def check_ban(clid, pbid):
ip = _ba.get_client_ip(clid)
device_id = _ba.get_client_public_device_uuid(clid)
if(device_id==None):
device_id = _ba.get_client_device_uuid(clid)
if (ip in blacklist["ban"]['ips'] or device_id in blacklist['ban']['deviceids'] or pbid in blacklist["ban"]["ids"]):
_ba.chatmessage('sad ,your account is flagged contact server owner for unban',clients=[clid])
logger.log(pbid + " | kicked > reason: Banned account")
ba.internal.disconnect_client(clid)
def check_ban(ip, device_id, pbid, log=True):
current_time = datetime.now()
if ip in blacklist["ban"]['ips'] and current_time < datetime.strptime(blacklist["ban"]["ips"][ip]["till"], "%Y-%m-%d %H:%M:%S"):
msg = f' reason: matched IP | {blacklist["ban"]["ips"][ip]["reason"]} , Till : {blacklist["ban"]["ips"][ip]["till"]}'
if log:
logger.log(f'{pbid} | kicked > {msg}')
return True
return msg
elif device_id in blacklist["ban"]["deviceids"] and current_time < datetime.strptime(blacklist["ban"]["deviceids"][device_id]["till"], "%Y-%m-%d %H:%M:%S"):
msg = f'reason: matched deviceId | {blacklist["ban"]["deviceids"][device_id]["reason"]}, Till : {blacklist["ban"]["deviceids"][device_id]["till"]}'
if log:
logger.log(
f'{pbid} | kicked > {msg}')
return True
return msg
elif pbid in blacklist["ban"]["ids"] and current_time < datetime.strptime(blacklist["ban"]["ids"][pbid]["till"], "%Y-%m-%d %H:%M:%S"):
msg = f'reason: matched ID | {blacklist["ban"]["ids"][pbid]["reason"]} , Till : {blacklist["ban"]["ids"][pbid]["till"]}'
if log:
logger.log(
f'{pbid} | kicked > {msg}')
return True
return msg
return False
def verify_account(pb_id, p_data):
d_string = ""
for ros in ba.internal.get_game_roster():
@ -260,7 +279,7 @@ def get_account_creation_date(pb_id):
else:
creation_time = account_creation["created"]
creation_time = map(str, creation_time)
creation_time = datetime.datetime.strptime("/".join(creation_time),
creation_time = datetime.strptime("/".join(creation_time),
"%Y/%m/%d/%H/%M/%S")
# Convert to IST
creation_time += datetime.timedelta(hours=5, minutes=30)
@ -285,14 +304,15 @@ def get_device_accounts(pb_id):
# ============ file I/O =============
class LoadProfile(threading.Thread):
def __init__(self, pb_id, ip):
def __init__(self, pb_id, ip, device_id):
threading.Thread.__init__(self)
self.pbid = pb_id
self.ip = ip
self.device_id = device_id
def run(self):
player_data = pdata.get_info(self.pbid)
_ba.pushcall(Call(on_player_join_server, self.pbid, player_data, self.ip),
_ba.pushcall(Call(on_player_join_server, self.pbid, player_data, self.ip, self.device_id),
from_other_thread=True)
@ -316,7 +336,8 @@ def my_acc_age(pb_id):
def save_age(age, pb_id, display_string):
_ba.pushcall(Call(pdata.add_profile,pb_id, display_string,display_string, age), from_other_thread=True)
_ba.pushcall(Call(pdata.add_profile, pb_id, display_string,
display_string, age), from_other_thread=True)
time.sleep(2)
thread2 = FetchThread(
target=get_device_accounts,
@ -352,8 +373,8 @@ def kick_by_pb_id(pb_id, msg):
def get_account_age(ct):
creation_time = datetime.datetime.strptime(ct, "%Y-%m-%d %H:%M:%S")
now = datetime.datetime.now()
creation_time = datetime.strptime(ct, "%Y-%m-%d %H:%M:%S")
now = datetime.now()
delta = now - creation_time
delta_hours = delta.total_seconds() / (60 * 60)
return delta_hours
@ -364,18 +385,18 @@ def reportSpam(id):
profiles = pdata.get_profiles()
if id in profiles:
count = profiles[id]["spamCount"]
if now - profiles[id]["lastSpam"] < 2 * 24 * 60 * 60:
count += 1
if count > 3:
logger.log(id+" auto banned for spamming")
profiles[id]["isBan"] = True
# by default ban for 1 day , change here if you want
pdata.ban_player(id, 1, "auto ban exceed warn count")
else:
count = 0
profiles[id]["spamCount"] = count
profiles[id]["lastSpam"] = now
pdata.commit_profiles(profiles)
def on_join_request(ip):
now = time.time()