vh-bombsquad-modded-server-.../dist/ba_root/mods/tools/servercheck.py

503 lines
20 KiB
Python
Raw Normal View History

2024-02-26 00:17:10 +05:30
# Released under the MIT License. See LICENSE for details.
from serverData import serverdata
from playersData import pdata
import _ba
import os
import ba.internal
import urllib.request
import json
from files import settings as fs
from tools import joinclaim as jc
import set
from datetime import datetime, timedelta
from members import members
import time
import ba
from ba._general import Call
import threading
from features import discord_bot as dc
import asyncio
import setting
import _thread
import asyncio
2024-02-26 22:36:54 +05:30
import set
2024-02-26 00:17:10 +05:30
from tools import logger, mongo, pinfo
from features import profanity
from playersData import pdata
from . import notification_manager
blacklist = pdata.get_blacklist()
settings = setting.get_settings_data()
new_member_count = os.path.join(
_ba.env()["python_directory_user"], "members" + os.sep)
ipjoin = {}
class checkserver(object):
def start(self):
self.players = []
self.t1 = ba.Timer(1, ba.Call(self.check),
repeat=True, timetype=ba.TimeType.REAL)
def check(self):
newPlayers = []
ipClientMap = {}
deviceClientMap = {}
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 (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.internal.disconnect_client(ros["client_id"])
logger.log(f'Player disconnected, reached max players per device || {ros["account_id"]}',
"playerjoin")
continue
if ip not in ipClientMap:
ipClientMap[ip] = [ros["client_id"]]
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.internal.disconnect_client(ros["client_id"])
logger.log(f'Player disconnected, reached max players per IP address || {ros["account_id"]}',
"playerjoin")
continue
newPlayers.append(ros['account_id'])
if ros['account_id'] not in self.players and ros[
'client_id'] != -1:
# new player joined lobby
d_str = ros['display_string']
acc = ros["account_id"]
d_str2 = profanity.censor(d_str)
try:
logger.log(
f'{d_str} || {ros["account_id"]} || joined server',
"playerjoin")
except:
pass
if d_str2 != d_str:
_ba.screenmessage(
"Profanity in Id , change your ID and join back",
color=(1, 0, 0), transient=True,
clients=[ros['client_id']])
try:
logger.log(f'{d_str} || { ros["account_id"] } || kicked by profanity check',
"sys")
except:
pass
ba.internal.disconnect_client(ros['client_id'], 1)
return
if settings["whitelist"] and ros["account_id"] != None:
if ros["account_id"] not in pdata.CacheData.whitelist:
_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')
ba.internal.disconnect_client(ros['client_id'])
return
if ros['account_id'] != None:
if ros['account_id'] in serverdata.clients:
on_player_join_server(ros['account_id'],
serverdata.clients[
ros['account_id']], ip, device_id)
else:
# from local cache, then call on_player_join_server
LoadProfile(ros['account_id'], ip, device_id).start()
self.players = newPlayers
def check_ban_mongo(clid, pbid):
blackdata = mongo.Banlist.find_one() or {'ban': {'ids': [], 'deviceids': [], 'ips': []}}
if blackdata is None:
return False
ip = _ba.get_client_ip(clid)
device_id = _ba.get_client_public_device_uuid(clid)
if device_id is None:
device_id = _ba.get_client_device_uuid(clid)
if ip in blackdata["ban"]['ips'] or device_id in blackdata['ban']['deviceids'] or pbid in blackdata["ban"]["ids"]:
logger.log(pbid + " kicked > reason: Banned account from discord")
ba.internal.disconnect_client(clid)
return True
return False
#----------------------------------------------------------------------------------
def on_player_join_server(pbid, player_data, ip, device_id):
global ipjoin
now = time.time()
#-------------------------------------------------------
try:
existing_members = members.members
if pbid not in existing_members:
existing_members.append(pbid)
with open(new_member_count + "members.py", "w") as f:
f.write(f"members = {json.dumps(existing_members, indent=4)}")
#else:
#print(f"{pbid} is already in members_count.")
except Exception as e:
print(f"Error updating members file: {e}")
return False
#-----------------------------------------------------------
2024-02-27 10:59:06 +05:30
# stopped pushing cause of lag
# data = mongo.notify_list.find_one() or {'notify': {'ids': [], 'deviceids': [],'ips': []}}
# try:
# for raos in ba.internal.get_game_roster():
# if raos["account_id"] == pbid:
# clid = raos["client_id"]
# devices_string = raos['display_string']
# nows = datetime.now().strftime('%d-%m-%Y %I:%M:%S %p')
# if pbid in data['notify']['ids'] or device_id in data['notify']['deviceids'] or ip in data['notify']['ips']:
# # Replace 'YOUR_DISCORD_CHANNEL_ID' with your actual Discord channel ID
# asyncio.ensure_future(dc.joined_player(pbid=pbid,devices_string=devices_string,time=nows))
# #print(f"{pbid} joined the game")
# #else:
# #print(f"{pbid} is not in notify list ")
# except Exception as e:
# print(f"Error updating notify json file: {e}")
# return False
2024-02-26 00:17:10 +05:30
#----------------------------------------------------------------------------------
player_info = mongo.playerinfo.find_one()
try:
if player_info is None:
return False
for rs in ba.internal.get_game_roster():
if rs["account_id"] == pbid:
name = rs['display_string']
if pbid in player_info["pinfo"]["pbid"]:
2024-02-26 22:36:54 +05:30
return
2024-02-26 00:17:10 +05:30
else:
asyncio.ensure_future(dc.update_playerinfo(pbid=pbid,name=name,deviceid=device_id,ip=ip))
2024-02-26 22:36:54 +05:30
print(f"stats updated..!")
2024-02-26 00:17:10 +05:30
except Exception as e:
2024-02-26 22:36:54 +05:30
print(f"Error updating stats in mongo {e}")
2024-02-26 00:17:10 +05:30
return False
#----------------------------------------------------------------------------------
clid = 113
device_string = ""
for ros in ba.internal.get_game_roster():
if ros["account_id"] == pbid:
clid = ros["client_id"]
device_string = ros['display_string']
if ip in ipjoin:
lastjoin = ipjoin[ip]["lastJoin"]
joincount = ipjoin[ip]["count"]
if now - lastjoin < 15:
joincount += 1
if joincount > 2:
_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')
ba.internal.disconnect_client(clid)
_thread.start_new_thread(reportSpam, (pbid,))
return
else:
joincount = 0
ipjoin[ip]["count"] = joincount
ipjoin[ip]["lastJoin"] = now
else:
ipjoin[ip] = {"lastJoin": now, "count": 0}
if pbid in serverdata.clients:
serverdata.clients[pbid]["lastJoin"] = now
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 check_ban(ip, device_id, pbid):
_ba.chatmessage(
'sad ,you are banned kid, join discord for unban appeal', clients=[clid])
ba.internal.disconnect_client(clid)
return
if check_ban_mongo(clid,pbid):
_ba.chatmessage(
'sad ,you are banned kid, join discord for unban appeal', clients=[clid])
ba.internal.disconnect_client(clid)
return
#to check expired effect :)
if set.coin:
pdata.checkExpiredItems()
pdata.checkExpiredclaim()
# to check expired complainter xD
if fs.complainter:
pdata.checkExpiredcomp()
if get_account_age(player_data["accountAge"]) < \
settings["minAgeToJoinInHours"]:
for ros in ba.internal.get_game_roster():
if ros['account_id'] == pbid:
_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.internal.disconnect_client(ros['client_id'])
return
else:
current_time = datetime.now()
if pbid not in serverdata.clients:
# 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 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)
if (device_id == None):
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(
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])
2024-02-26 22:36:54 +05:30
if set.JoinClaim:
jc.join_claim(device_string, clid, pbid)
2024-02-26 00:17:10 +05:30
notification_manager.player_joined(pbid)
else:
# fetch id for first time.
thread = FetchThread(
target=my_acc_age,
callback=save_age,
pb_id=pbid,
display_string=device_string
)
thread.start()
_ba.screenmessage(settings["firstTimeJoinMsg"], color=(0.6, 0.8, 0.6),
transient=True, clients=[clid])
jc.join_claim(device_string, clid, pbid)
notification_manager.player_joined(pbid)
# pdata.add_profile(pbid,d_string,d_string)
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():
if ros['account_id'] == pb_id:
d_string = ros['display_string']
if d_string not in p_data['display_string']:
thread2 = FetchThread(
target=get_device_accounts,
callback=save_ids,
pb_id=pb_id,
display_string=d_string
)
thread2.start()
else:
serverdata.clients[pb_id]["verified"] = True
# ============== IGNORE BELOW CODE =======================
def _make_request_safe(request, retries=2, raise_err=True):
try:
return request()
except:
if retries > 0:
time.sleep(1)
return _make_request_safe(request, retries=retries - 1,
raise_err=raise_err)
if raise_err:
raise
def get_account_creation_date(pb_id):
# thanks rikko
account_creation_url = "http://bombsquadgame.com/accountquery?id=" + pb_id
account_creation = _make_request_safe(
lambda: urllib.request.urlopen(account_creation_url))
if account_creation is not None:
try:
account_creation = json.loads(account_creation.read())
except ValueError:
pass
else:
creation_time = account_creation["created"]
creation_time = map(str, creation_time)
creation_time = datetime.strptime("/".join(creation_time),
"%Y/%m/%d/%H/%M/%S")
# Convert to IST
creation_time += timedelta(hours=5, minutes=30)
return str(creation_time)
def get_device_accounts(pb_id):
url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + pb_id
data = _make_request_safe(lambda: urllib.request.urlopen(url))
if data is not None:
try:
accounts = json.loads(data.read())["accountDisplayStrings"]
except ValueError:
return ['???']
else:
return accounts
# ======= yes fucking threading code , dont touch ==============
# ============ file I/O =============
class LoadProfile(threading.Thread):
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, self.device_id),
from_other_thread=True)
# ================ http ================
class FetchThread(threading.Thread):
def __init__(self, target, callback=None, pb_id="ji",
display_string="XXX"):
super(FetchThread, self).__init__(target=self.target_with_callback,
args=(pb_id, display_string,))
self.callback = callback
self.method = target
def target_with_callback(self, pb_id, display_string):
data = self.method(pb_id)
if self.callback is not None:
self.callback(data, pb_id, display_string)
def my_acc_age(pb_id):
return get_account_creation_date(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)
time.sleep(2)
thread2 = FetchThread(
target=get_device_accounts,
callback=save_ids,
pb_id=pb_id,
display_string=display_string
)
thread2.start()
if get_account_age(age) < settings["minAgeToJoinInHours"]:
msg = "New Accounts not allowed to play here , come back tmrw."
logger.log(pb_id + "|| kicked > new account")
_ba.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True)
def save_ids(ids, pb_id, display_string):
pdata.update_display_string(pb_id, ids)
if display_string not in ids:
msg = "Spoofed Id detected , Goodbye"
_ba.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True)
serverdata.clients[pb_id]["verified"] = False
logger.log(
pb_id + "|| kicked , for using spoofed id " + display_string)
else:
serverdata.clients[pb_id]["verified"] = True
def kick_by_pb_id(pb_id, msg):
for ros in ba.internal.get_game_roster():
if ros['account_id'] == pb_id:
_ba.screenmessage(msg, transient=True, clients=[ros['client_id']])
ba.internal.disconnect_client(ros['client_id'])
def get_account_age(ct):
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
def reportSpam(id):
now = time.time()
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")
# 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
def on_join_request(ip):
now = time.time()
if ip in serverdata.ips:
lastRequest = serverdata.ips[ip]["lastRequest"]
count = serverdata.ips[ip]["count"]
if now - lastRequest < 5:
count += 1
if count > 40:
_ba.ban_ip(ip)
else:
count = 0
serverdata.ips[ip] = {"lastRequest": time.time(), "count": count}
else:
serverdata.ips[ip] = {"lastRequest": time.time(), "count": 0}