import contracts
import events
+import db_utils
import db_store
upgrade_cache = {}
QueryCoop = EIProto.QueryCoopRequest()
QueryCoop.ParseFromString(data)
QueryCoopResp = EIProto.QueryCoopResponse()
- db_query = db_store.is_coop_identifier_used(QueryCoop.coop_identifier)
+ ContractInfo = contracts.get_contract_by_identifier(QueryCoop.contract_identifier)
+ if ContractInfo is None:
+ QueryCoopResp.exists = True
+ return base64.b64encode(QueryCoopResp.SerializeToString())
+ db_query = db_store.is_coop_identifier_used(QueryCoop.coop_identifier, QueryCoop.contract_identifier)
if db_query is not None:
QueryCoopResp.exists = True
else:
if QueryCoop.league != db_query:
QueryCoopResp.different_league = True
else:
- ContractInfo = contracts.get_contract_by_identifier(QueryCoop.contract_identifier)
if db_store.is_coop_full(QueryCoop.coop_identifier, ContractInfo.max_coop_size):
QueryCoopResp.full = True
return base64.b64encode(QueryCoopResp.SerializeToString())
CreateCoop.ParseFromString(data)
CreateResponse = EIProto.CreateCoopResponse()
# Double check if in use
- db_query = db_store.is_coop_identifier_used(CreateCoop.coop_identifier)
+ db_query = db_store.is_coop_identifier_used(CreateCoop.coop_identifier, CreateCoop.contract_identifier)
if db_query is not None:
CreateResponse.success = False
CreateResponse.message = "That co-op already exists."
contract = contracts.get_contract_by_identifier(CreateCoop.contract_identifier)
if contract is None:
CreateResponse.success = False
- CreateResponse.message = "Couldn't find your contract."
+ CreateResponse.message = "You're no fun."
return base64.b64encode(CreateResponse.SerializeToString())
# Calculate timestamp of the contract so we can later tell actual seconds left to new joins.
stamp = int(time.time() - contract.length_seconds + CreateCoop.seconds_remaining)
CreateResponse.message = "Co-op created."
return base64.b64encode(CreateResponse.SerializeToString())
+def construct_contract_status(coop_name, contract_name, requester_id):
+ StatusResp = EIProto.ContractCoopStatusResponse()
+ BaseInfo = db_store.get_contract_info(coop_name, contract_name)
+ ContribInfo = db_store.get_coop_contributors(coop_name, contract_name)
+ ContractInfo = contracts.get_contract_by_identifier(contract_name)
+ if BaseInfo is None or ContribInfo is None or ContractInfo is None:
+ return None
+ i = 0
+ for x in ContribInfo:
+ i += 1
+ contributor = StatusResp.contributors.add()
+ contributor.user_id = x[0] if x[0] == requester_id else "cool-guy-" + str(i)
+ contributor.user_name = x[3]
+ contributor.contribution_amount = x[5]
+ StatusResp.total_amount += x[5]
+ contributor.contribution_rate = x[6]
+ contributor.soul_power = x[7]
+ contributor.active = not (int(time.time()) - x[4]) >= 86400
+ contributor.boost_tokens = x[8]
+ StatusResp.coop_identifier = coop_name
+ StatusResp.contract_identifier = contract_name
+ StatusResp.auto_generated = BaseInfo[7]
+ StatusResp.public = BaseInfo[6]
+ StatusResp.creator_id = "hidden-for-safety" if requester_id != BaseInfo[5] else BaseInfo[5]
+ StatusResp.seconds_remaining = (BaseInfo[4] + int(ContractInfo.length_seconds)) - int(time.time())
+ GiftInfo = db_store.get_coop_gifts(coop_name, contract_name, requester_id)
+ for g in GiftInfo:
+ Gift = StatusResp.gifts.add()
+ Gift.user_id = "hidden-for-safety"
+ Gift.user_name = g[3]
+ Gift.amount = g[4]
+ return StatusResp
+
@app.route('/ei/coop_status', methods=['POST'])
def ei_coop_status():
data = base64.b64decode(request.form["data"].replace(" ", "+"))
StatusReq = EIProto.ContractCoopStatusRequest()
StatusReq.ParseFromString(data)
- StatusResp = EIProto.ContractCoopStatusResponse()
- # Get some base info (TODO: error handling)
- BaseInfo = db_store.get_contract_info(StatusReq.coop_identifier)
- ContribInfo = db_store.get_coop_contributors(StatusReq.coop_identifier)
- ContractInfo = contracts.get_contract_by_identifier(BaseInfo[2])
- TotalEggs = 0
- for x in ContribInfo:
- contributor = StatusResp.contributors.add()
- contributor.user_id = x[0]
- contributor.user_name = x[2]
- contributor.contribution_amount = x[4]
- TotalEggs += x[4]
- contributor.contribution_rate = x[5]
- contributor.soul_power = x[6]
- contributor.active = True
- StatusResp.coop_identifier = StatusReq.coop_identifier
- StatusResp.total_amount = TotalEggs
- StatusResp.auto_generated = False
- StatusResp.public = False
- StatusResp.creator_id = BaseInfo[5]
- StatusResp.seconds_remaining = (BaseInfo[4] + int(ContractInfo.length_seconds)) - int(time.time())
- return base64.b64encode(StatusResp.SerializeToString())
+ return base64.b64encode(construct_contract_status(StatusReq.coop_identifier, StatusReq.contract_identifier, StatusReq.user_id).SerializeToString())
@app.route('/ei/update_coop_status', methods=['POST'])
def ei_update_coop_status():
data = base64.b64decode(request.form["data"].replace(" ", "+"))
UpdateReq = EIProto.ContractCoopStatusUpdateRequest()
UpdateReq.ParseFromString(data)
- db_store.update_coop_contribution(UpdateReq.coop_identifier, UpdateReq.user_id, UpdateReq.amount, UpdateReq.rate, UpdateReq.soul_power, UpdateReq.boost_tokens, UpdateReq.time_cheats_detected)
+ db_store.update_coop_contribution(UpdateReq.coop_identifier, UpdateReq.contract_identifier, UpdateReq.user_id, UpdateReq.amount, UpdateReq.rate, UpdateReq.soul_power, UpdateReq.boost_tokens, UpdateReq.time_cheats_detected)
Resp = EIProto.ContractCoopStatusUpdateResponse()
Resp.finalized = True
return base64.b64encode(Resp.SerializeToString())
JoinCoopRequest.ParseFromString(data)
JoinResponse = EIProto.JoinCoopResponse()
JoinResponse.coop_identifier = JoinCoopRequest.coop_identifier
- db_query = db_store.is_coop_identifier_used(JoinCoopRequest.coop_identifier)
+ ContractInfo = contracts.get_contract_by_identifier(JoinCoopRequest.contract_identifier)
+ if ContractInfo is None:
+ JoinResponse.success = False
+ JoinResponse.banned = True
+ JoinResponse.message = "You must be fun at parties."
+ return base64.b64encode(JoinResponse.SerializeToString())
+ db_query = db_store.is_coop_identifier_used(JoinCoopRequest.coop_identifier, JoinCoopRequest.contract_identifier)
if db_query is None:
JoinResponse.success = False
JoinResponse.message = "That co-op doesn't exist."
JoinResponse.success = False
JoinResponse.message = "You can't join a " + ("Elite" if db_query == 1 else "Standard") + " contract."
return base64.b64encode(JoinResponse.SerializeToString())
- BaseInfo = db_store.get_contract_info(JoinCoopRequest.coop_identifier)
- ContribInfo = db_store.get_coop_contributors(JoinCoopRequest.coop_identifier)
- ContractInfo = contracts.get_contract_by_identifier(BaseInfo[2])
+ BaseInfo = db_store.get_contract_info(JoinCoopRequest.coop_identifier, JoinCoopRequest.contract_identifier)
+ ContribInfo = db_store.get_coop_contributors(JoinCoopRequest.coop_identifier, JoinCoopRequest.contract_identifier)
if len(ContribInfo) - 1 >= ContractInfo.max_coop_size:
JoinResponse.success = False
JoinResponse.message = "Co-op is full!"
return base64.b64encode(JoinResponse.SerializeToString())
- if BaseInfo[2] != JoinCoopRequest.contract_identifier:
- JoinResponse.success = False
- JoinResponse.message = "This co-op is not made for this contract."
- return base64.b64encode(JoinResponse.SerializeToString())
# TODO: bans from coops
db_store.insert_coop_contribution(JoinCoopRequest.coop_identifier, JoinCoopRequest.user_id, JoinCoopRequest.user_name, JoinCoopRequest.soul_power)
JoinResponse.success = True
# TODO: Ban check
if not db_store.is_coop_full(coop_identifier, Contract.max_coop_size):
Resp.success = True
- db_store.insert_coop_contribution(coop_identifier, AutoJoinCoopRequest.user_id, AutoJoinCoopRequest.user_name, AutoJoinCoopRequest.soul_power)
+ db_store.insert_coop_contribution(coop_identifier, AutoJoinCoopRequest.contract_identifier, AutoJoinCoopRequest.user_id, AutoJoinCoopRequest.user_name, AutoJoinCoopRequest.soul_power)
BaseInfo = db_store.get_contract_info(coop_identifier)
Resp.coop_identifier = coop_identifier
Resp.banned = False
# TODO: Auto-create co-op if none found
return base64.b64encode(Resp.SerializeToString())
+@app.route('/ei/gift_player_coop', methods=['POST'])
+def ei_gift_player():
+ # TODO: How do we validate the player even has as many boost tokens as they are about to gift?
+ data = base64.b64decode(request.form["data"].replace(" ", "+"))
+ GiftReq = EIProto.GiftPlayerCoopRequest()
+ GiftReq.ParseFromString(data)
+ if not db_store.is_user_in_coop(GiftReq.requesting_user_id, GiftReq.coop_identifier, GiftReq.contract_identifier):
+ return "", 404
+ BaseInfo = db_store.get_contract_info(GiftReq.coop_identifier, GiftReq.contract_identifier)
+ ContribInfo = db_store.get_coop_contributors(GiftReq.coop_identifier, GiftReq.contract_identifier)
+ if len(ContribInfo) <= 1:
+ return "", 404
+ if GiftReq.player_identifier.startswith("cool-guy-"):
+ tmp = GiftReq.player_identifier[9:]
+ try:
+ id = int(tmp) - 1
+ if id >= len(ContribInfo) or id < 0:
+ return "", 404
+ real_id = ContribInfo[id][0]
+ db_store.add_coop_gift(GiftReq.coop_identifier, GiftReq.contract_identifier, GiftReq.amount, GiftReq.requesting_user_name, real_id)
+ return base64.b64encode(construct_contract_status(GiftReq.coop_identifier, GiftReq.contract_identifier, GiftReq.requesting_user_id).SerializeToString())
+ except:
+ return "", 404
+ else:
+ return "", 404
+
@app.route('/ei/leave_coop', methods=['POST'])
def ei_leave_coop():
data = base64.b64decode(request.form["data"].replace(" ", "+"))
LeaveCoopRequest = EIProto.LeaveCoopRequest()
LeaveCoopRequest.ParseFromString(data)
- db_store.erase_coop_contribution(LeaveCoopRequest.coop_identifier, LeaveCoopRequest.player_identifier)
- # TODO: Deepdive ghidra to see what this expects?
+ db_store.erase_coop_contribution(LeaveCoopRequest.coop_identifier, LeaveCoopRequest.contract_identifier, LeaveCoopRequest.player_identifier)
return ""
@app.route('/ei/update_coop_permissions', methods=['POST'])
PermUpdateReq = EIProto.UpdateCoopPermissionsRequest()
PermUpdateReq.ParseFromString(data)
PermUpdateResp = EIProto.UpdateCoopPermissionsResponse()
- BaseInfo = db_store.get_contract_info(PermUpdateReq.coop_identifier)
+ BaseInfo = db_store.get_contract_info(PermUpdateReq.coop_identifier, PermUpateReq.contract_identifier)
if BaseInfo[5] != PermUpdateReq.requesting_user_id:
PermUpdateResp.success = False
PermUpdateResp.message = "Only the co-op creator can change the permissions."
return base64.b64encode(PermUpdateResp.SerializeToString())
- db_store.change_coop_public_state(PermUpdateReq.coop_identifier, PermUpdateReq.public)
+ db_store.change_coop_public_state(PermUpdateReq.coop_identifier, PermUpdateReq.contract_identifier, PermUpdateReq.public)
PermUpdateResp.success = True
return base64.b64encode(PermUpdateResp.SerializeToString())
# db_store.py - Providing "cloud saves" and such
import sqlite3
+import db_utils
import time
# TODO: Truncate last x backups from a given device ID
con = get_connection("contracts")
cur = con.cursor()
try:
- cur.execute("CREATE TABLE Contracts(ID INTEGER PRIMARY KEY AUTOINCREMENT, CoopName TEXT, ContractName TEXT, League SMALLINT, ContractStamp BIGINT, OwnerDevice TEXT, Public BOOL)")
- cur.execute("CREATE TABLE ContractMember(DeviceID TEXT, CoopName TEXT, DisplayName TEXT, LastVisit BIGINT, Contribution BIGINT, ContribRate BIGINT, SoulPower DOUBLE, BoostTokens INTEGER, TimeCheats INT, Banned BOOL, LeftCoop BOOL)")
- cur.execute("CREATE TABLE ContractGift(DeviceID TEXT, RewardType TEXT, Quantity INTEGER)")
+ cur.execute("CREATE TABLE Contracts(ID INTEGER PRIMARY KEY AUTOINCREMENT, CoopName TEXT, ContractName TEXT, League SMALLINT, ContractStamp BIGINT, OwnerDevice TEXT, Public BOOL, AutoGenerated BOOL)")
+ except:
+ FreshInstall = False
+ try:
+ cur.execute("CREATE TABLE ContractMember(DeviceID TEXT, CoopName TEXT, ContractName TEXT, DisplayName TEXT, LastVisit BIGINT, Contribution BIGINT, ContribRate BIGINT, SoulPower DOUBLE, BoostTokens INTEGER, TimeCheats INT, TurnedIn BOOL)")
+ except:
+ FreshInstall = False
+ try:
+ cur.execute("CREATE TABLE ContractGift(DeviceID TEXT, CoopName TEXT, ContractName TEXT, SenderName TEXT, Quantity INTEGER)")
except:
FreshInstall = False
con.commit()
con.close()
return FreshInstall
-def get_contract_info(coop_identifier):
- if not coop_identifier.isalnum():
+def get_contract_info(coop_identifier, contract_name):
+ if not (db_utils.is_valid_coop_or_contract(coop_identifier) or db_utils.is_valid_coop_or_contract(contract_name)):
return None
con = get_connection("contracts")
cur = con.cursor()
- res = cur.execute('SELECT * FROM Contracts WHERE CoopName="' + coop_identifier + '"')
+ res = cur.execute('SELECT * FROM Contracts WHERE CoopName="' + coop_identifier + '" AND ContractName="' + contract_name + '"')
retval = res.fetchone()
con.close()
return retval
-def is_coop_full(coop_identifier, max_members):
- if not coop_identifier.isalnum():
+def is_coop_full(coop_identifier, contract_name, max_members):
+ if not (db_utils.is_valid_coop_or_contract(coop_identifier) or db_utils.is_valid_coop_or_contract(contract_name)):
return True
con = get_connection("contracts")
cur = con.cursor()
- res = cur.execute('SELECT COUNT(DeviceID) FROM ContractMember WHERE CoopName="' + coop_identifier + '"')
+ res = cur.execute('SELECT COUNT(DeviceID) FROM ContractMember WHERE CoopName="' + coop_identifier + '" AND ContractName="' + contract_name + '"')
retval = res.fetchone()
con.close()
if retval is None:
return retval[0] >= max_members
def get_public_coops(contract_identifier):
+ if not db_utils.is_valid_coop_or_contract(contract_name):
+ return []
con = get_connection("contracts")
cur = con.cursor()
res = cur.execute('SELECT CoopName FROM Contracts WHERE Public=1 AND ContractName="' + contract_identifier + '"')
else:
return ret
-def change_coop_public_state(coop_identifier, public):
- if not is_coop_identifier_used(coop_identifier):
+def change_coop_public_state(coop_identifier, contract_name, public):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
return False
con = get_connection("contracts")
cur = con.cursor()
- cur.execute("UPDATE Contracts SET Public=? WHERE CoopName=?", (public, coop_identifier))
+ cur.execute("UPDATE Contracts SET Public=? WHERE CoopName=? AND ContractName=?", (public, coop_identifier, contract_name))
con.commit()
con.close()
return True
-def is_coop_identifier_used(coop_identifier):
- if not coop_identifier.isalnum():
+def is_coop_identifier_used(coop_identifier, contract_name):
+ if not (db_utils.is_valid_coop_or_contract(coop_identifier) or db_utils.is_valid_coop_or_contract(contract_name)):
return True
con = get_connection("contracts")
cur = con.cursor()
- res = cur.execute('SELECT League FROM Contracts WHERE CoopName="' + coop_identifier + '"')
+ res = cur.execute('SELECT League FROM Contracts WHERE CoopName="' + coop_identifier + '" AND ContractName="' + contract_name + '"')
retval = res.fetchone()
con.close()
return retval[0] if retval is not None else None
-def create_coop_contract(coop_identifier, contract_id, league, stamp, device_id, display_name):
- if is_coop_identifier_used(coop_identifier):
+def is_user_in_coop(device_id, coop_identifier, contract_name):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
+ return None
+ if not db_utils.is_valid_username(device_id):
return False
- if not device_id.isalnum():
+ con = get_connection("contracts")
+ cur = con.cursor()
+ res = cur.execute('SELECT DeviceID FROM ContractMember WHERE DeviceID="' + device_id + '" AND CoopName="' + coop_identifier + '\" AND ContractName="' + contract_name + '"')
+ x = res.fetchall()
+ con.close()
+ if x is None:
+ return False
+ else:
+ return True
+
+def create_coop_contract(coop_identifier, contract_name, league, stamp, device_id, display_name, auto_gen=False):
+ if is_coop_identifier_used(coop_identifier, contract_name):
+ return False
+ if not db_utils.is_valid_username(device_id):
+ return False
+ if not db_utils.is_valid_display_name(display_name):
return False
con = get_connection("contracts")
cur = con.cursor()
stamp = int(time.time())
- cur.execute("INSERT INTO Contracts(CoopName, ContractName, League, ContractStamp, OwnerDevice) VALUES(?, ?, ?, ?, ?)", (coop_identifier, contract_id, league, stamp, device_id))
+ cur.execute("INSERT INTO Contracts(CoopName, ContractName, League, ContractStamp, OwnerDevice, AutoGenerated) VALUES(?, ?, ?, ?, ?, ?)", (coop_identifier, contract_name, league, stamp, device_id, auto_gen))
cur.execute("INSERT INTO ContractMember(DeviceID, CoopName, DisplayName, LastVisit) VALUES(?, ?, ?, ?)", (device_id, coop_identifier, display_name, int(time.time())))
con.commit()
con.close()
return True
-def get_coop_contributors(coop_identifier):
- if not is_coop_identifier_used(coop_identifier):
+def get_coop_contributors(coop_identifier, contract_name):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
return None
con = get_connection("contracts")
cur = con.cursor()
- stamp = int(time.time())
- res = cur.execute('SELECT * FROM ContractMember WHERE CoopName="' + coop_identifier + '\"')
+ res = cur.execute('SELECT * FROM ContractMember WHERE CoopName="' + coop_identifier + '\" AND ContractName="' + contract_name + '"')
x = res.fetchall()
con.close()
if x is None:
else:
return x
-def update_coop_contribution(coop_identifier, device_id, contribution, rate, soul_power, boost_tokens, cheats):
- if not is_coop_identifier_used(coop_identifier):
+def update_coop_contribution(coop_identifier, contract_name, device_id, contribution, rate, soul_power, boost_tokens, cheats):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
return False
- if not device_id.isalnum():
+ if not db_utils.is_valid_username(device_id):
return False
con = get_connection("contracts")
cur = con.cursor()
stamp = int(time.time())
- values = (int(time.time()), contribution, rate, soul_power, boost_tokens, cheats, device_id, coop_identifier)
- cur.execute("UPDATE ContractMember SET LastVisit=?, Contribution=?, ContribRate=?, SoulPower=?, BoostTokens=?, TimeCheats=? WHERE DeviceID=? AND CoopName=?", values)
+ values = (int(time.time()), contribution, rate, soul_power, boost_tokens, cheats, device_id, coop_identifier, contract_name)
+ cur.execute("UPDATE ContractMember SET LastVisit=?, Contribution=?, ContribRate=?, SoulPower=?, BoostTokens=?, TimeCheats=? WHERE DeviceID=? AND CoopName=? AND ContractName=?", values)
con.commit()
con.close()
return True
-def insert_coop_contribution(coop_identifier, device_id, device_name, soul_power):
- if not is_coop_identifier_used(coop_identifier):
+def insert_coop_contribution(coop_identifier, contract_name, device_id, device_name, soul_power):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
return False
- if not device_id.isalnum():
+ if not db_utils.is_valid_username(device_id):
+ return False
+ if not db_utils.is_valid_display_name(device_name):
return False
con = get_connection("contracts")
cur = con.cursor()
stamp = int(time.time())
- values = (device_id, coop_identifier, device_name, int(time.time()), 0, 0, soul_power)
- cur.execute("INSERT INTO ContractMember(DeviceID, CoopName, DisplayName, LastVisit, Contribution, ContribRate, SoulPower) VALUES(?, ?, ?, ?, ?, ?, ?)", values)
+ values = (device_id, contract_name, coop_identifier, device_name, int(time.time()), 0, 0, soul_power)
+ cur.execute("INSERT INTO ContractMember(DeviceID, ContractName, CoopName, DisplayName, LastVisit, Contribution, ContribRate, SoulPower) VALUES(?, ?, ?, ?, ?, ?, ?, ?)", values)
con.commit()
con.close()
return True
-def erase_coop_contribution(coop_identifier, device_id):
- if not is_coop_identifier_used(coop_identifier):
+def erase_coop_contribution(coop_identifier, contract_name, device_id):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
return
- if not device_id.isalnum():
+ if not db_utils.is_valid_username(device_id):
return
con = get_connection("contracts")
cur = con.cursor()
- cur.execute("DELETE FROM ContractMember WHERE DeviceID=? AND CoopName=?", (device_id, coop_identifier))
+ cur.execute("DELETE FROM ContractMember WHERE DeviceID=? AND CoopName=? AND ContractName=?", (device_id, contract_name, coop_identifier))
con.commit()
con.close()
return
def get_coop_memberships(device_id):
- if not device_id.isalnum():
+ if not db_utils.is_valid_username(device_id):
return None
con = get_connection("contracts")
cur = con.cursor()
else:
return x
+def get_coop_gifts(coop_identifier, contract_name, device_id):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
+ return []
+ if not db_utils.is_valid_username(device_id):
+ return []
+ con = get_connection("contracts")
+ cur = con.cursor()
+ res = cur.execute('SELECT * FROM ContractGift WHERE DeviceID="' + device_id + '" AND CoopName="' + coop_identifier + '" AND ContractName="' + contract_name + '"')
+ x = res.fetchall()
+ cur.execute("DELETE FROM ContractGift WHERE DeviceID=? AND CoopName=? AND ContractName=?", (device_id, coop_identifier, contract_name))
+ con.commit()
+ con.close()
+ return x
+
+def add_coop_gift(coop_identifier, contract_name, amount, sender_name, target_id):
+ if not is_coop_identifier_used(coop_identifier, contract_name):
+ return False
+ if not db_utils.is_valid_username(target_id):
+ return False
+ if not db_utils.is_valid_display_name(sender_name):
+ return False
+ con = get_connection("contracts")
+ cur = con.cursor()
+ cur.execute('INSERT INTO ContractGift(DeviceID, CoopName, ContractName, Quantity, SenderName) VALUES(?, ?, ?, ?, ?)', (target_id, coop_identifier, contract_name, amount, sender_name))
+ con.commit()
+ con.close()
+ return True
+
def get_backup(ref_id):
if not isinstance(ref_id, int):
return None