Merge branch develop into main

This commit is contained in:
Rim 2023-12-25 09:01:46 -05:00
commit e1d5abea1b
8 changed files with 1213 additions and 400 deletions

View File

@ -67,7 +67,7 @@ If done correctly, this should return the extra API information.
## Command Line Arguments
```
usage: get_cod_stats.py [-h] -p PLAYER_NAME [-a] [-sl] [-i] [-m] [-c] [-sm] [-csd] [-cmd]
usage: get_cod_stats.py [-h] [-p PLAYER_NAME] [-a] [-sl] [-id] [-m] [-i] [-f] [-e] [-cp] [-ca] [-s] [-c] [-sm] [-csd] [-cmd] [-cff] [-cef]
Detailed Modern Warfare (2019) Statistics Tool
@ -79,8 +79,15 @@ Data Fetching Options:
Player's username (with #1234567)
-a, --all_stats Fetch all the different types of stats data
-sl, --season_loot Fetch only the season loot data
-i, --identities Fetch only the logged-in identities data
-id, --identities Fetch only the logged-in identities data
-m, --maps Fetch only the map list data
-i, --info Fetch only general information
-f, --friendFeed Fetch only your friend feed
-e, --eventFeed Fetch only your event feed
-cp, --cod_points Fetch only your COD Point balance
-ca, --connected_accounts
Fetch only the map list data
-s, --settings Fetch only your account settings
Data Cleaning Options:
-c, --clean Beautify all data
@ -89,6 +96,10 @@ Data Cleaning Options:
Beautify the data and convert to human-readable strings in stats.json
-cmd, --clean_match_data
Beautify the match data and convert to human-readable strings in match_info.json
-cff, --clean_friend_feed
Clean the friend feed data
-cef, --clean_event_feed
Clean the event feed data
```
## Command Examples

Binary file not shown.

View File

@ -1 +0,0 @@
<span class="|</span>|">|mp-stat-items|kills-value|headshots-value|username|game-mode|kdr-value

View File

@ -547,30 +547,28 @@ class API:
class __USER(_Common):
def info(self):
if self.loggedIn:
# Assuming 'user_info.json' is the file you've downloaded with the information.
# Assuming 'user_info.json' is the file you've downloaded with the information
file_path = 'userInfo.json'
# Load the JSON content from the local file.
# Load the JSON content from the local file
with open(file_path, 'r') as file:
rawData = json.load(file)
try:
userInfo = rawData['userInfo'] # Accessing the nested 'userInfo' dictionary.
identities = rawData.get('identities', []) # Accessing the 'identities' if it exists or default to empty list.
userInfo = rawData['userInfo'] # Accessing the nested 'userInfo' dictionary
identities = rawData.get('identities', []) # Accessing the 'identities' if it exists or default to empty list
data = {'userName': userInfo['userName'], 'identities': []} # Getting 'userName' from the nested dictionary.
for i in identities: # Loop through each identity in the 'identities' list.
data = {'userName': userInfo['userName'], 'identities': []} # Getting 'userName' from the nested dictionary
for i in identities: # Loop through each identity in the 'identities' list
data['identities'].append({
'platform': i['provider'],
'gamertag': i['username'],
'accountID': i['accountID'] # Assuming 'accountID' exists; otherwise, you might need a default value.
'accountID': i['accountID'] # Assuming 'accountID' exists; otherwise, you might need a default value
})
return data
except KeyError as e:
# Handle the case where the expected key is not found in the dictionary.
# Handle the case where the expected key is not found in the dictionary
print(f"Error: A required field is missing in the data. Details: {str(e)}")
# Re-raise the exception or handle it as required for your application's logic.
# Re-raise the exception or handle it as required
raise
else:

725
cod_api/__init__dev.py Normal file
View File

@ -0,0 +1,725 @@
__version__ = "2.0.1"
# Imports
import asyncio
import enum
import json
import uuid
import os
from abc import abstractmethod
from datetime import datetime
from urllib.parse import quote
import aiohttp
import requests
from aiohttp import ClientResponseError
# Enums
class platforms(enum.Enum):
All = 'all'
Activision = 'acti'
Battlenet = 'battle'
PSN = 'psn'
Steam = 'steam'
Uno = 'uno'
XBOX = 'xbl'
class games(enum.Enum):
ColdWar = 'cw'
ModernWarfare = 'mw'
ModernWarfare2 = 'mw2'
Vanguard = 'vg'
Warzone = 'wz'
Warzone2 = 'wz2'
class friendActions(enum.Enum):
Invite = "invite"
Uninvite = "uninvite"
Remove = "remove"
Block = "block"
Unblock = "unblock"
class API:
"""
Call Of Duty API Wrapper
Developed by Todo Lodo & Engineer152
Contributors
- Werseter
Source Code: https://github.com/TodoLodo/cod-python-api
"""
def __init__(self):
# sub classes
self.Warzone = self.__WZ()
self.ModernWarfare = self.__MW()
self.Warzone2 = self.__WZ2()
self.ModernWarfare2 = self.__MW2()
self.ColdWar = self.__CW()
self.Vanguard = self.__VG()
self.Shop = self.__SHOP()
self.Me = self.__USER()
self.Misc = self.__ALT()
async def loginAsync(self, sso_token: str) -> None:
await API._Common.loginAsync(sso_token)
# Login
def login(self, ssoToken: str):
API._Common.login(ssoToken)
class _Common:
requestHeaders = {
"content-type": "application/json",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/74.0.3729.169 "
"Safari/537.36",
"Accept": "application/json",
"Connection": "Keep-Alive"
}
cookies = {"new_SiteId": "cod", "ACT_SSO_LOCALE": "en_US", "country": "US",
"ACT_SSO_COOKIE_EXPIRY": "1645556143194"}
cachedMappings = None
fakeXSRF = str(uuid.uuid4())
baseUrl: str = "https://my.callofduty.com/api/papi-client"
loggedIn: bool = False
# endPoints
# game platform lookupType gamertag type
fullDataUrl = "/stats/cod/v1/title/%s/platform/%s/%s/%s/profile/type/%s"
# game platform lookupType gamertag type start end [?limit=n or '']
combatHistoryUrl = "/crm/cod/v2/title/%s/platform/%s/%s/%s/matches/%s/start/%d/end/%d/details"
# game platform lookupType gamertag type start end
breakdownUrl = "/crm/cod/v2/title/%s/platform/%s/%s/%s/matches/%s/start/%d/end/%d"
# game platform lookupType gamertag
seasonLootUrl = "/loot/title/%s/platform/%s/%s/%s/status/en"
# game platform
mapListUrl = "/ce/v1/title/%s/platform/%s/gameType/mp/communityMapData/availability"
# game platform type matchId
matchInfoUrl = "/crm/cod/v2/title/%s/platform/%s/fullMatch/%s/%d/en"
@staticmethod
async def loginAsync(sso_token: str) -> None:
API._Common.cookies["ACT_SSO_COOKIE"] = sso_token
API._Common.baseSsoToken = sso_token
r = await API._Common.__Request(f"{API._Common.baseUrl}/crm/cod/v2/identities/{sso_token}")
if r['status'] == 'success':
API._Common.loggedIn = True
else:
raise InvalidToken(sso_token)
@staticmethod
def login(sso_token: str) -> None:
API._Common.cookies["ACT_SSO_COOKIE"] = sso_token
API._Common.baseSsoToken = sso_token
r = requests.get(f"{API._Common.baseUrl}/crm/cod/v2/identities/{sso_token}",
headers=API._Common.requestHeaders, cookies=API._Common.cookies)
if r.json()['status'] == 'success':
API._Common.loggedIn = True
API._Common.cookies.update(r.cookies)
else:
raise InvalidToken(sso_token)
@staticmethod
def sso_token() -> str:
return API._Common.cookies["ACT_SSO_COOKIE"]
# Requests
@staticmethod
async def __Request(url):
async with aiohttp.client.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=True),
timeout=aiohttp.ClientTimeout(total=30)) as session:
try:
async with session.get(url, cookies=API._Common.cookies,
headers=API._Common.requestHeaders) as resp:
try:
resp.raise_for_status()
except ClientResponseError as err:
return {'status': 'error', 'data': {'type': type(err), 'message': err.message}}
else:
API._Common.cookies.update({c.key: c.value for c in session.cookie_jar})
return await resp.json()
except asyncio.TimeoutError as err:
return {'status': 'error', 'data': {'type': type(err), 'message': str(err)}}
async def __sendRequest(self, url: str):
if self.loggedIn:
response = await API._Common.__Request(f"{self.baseUrl}{url}")
if response['status'] == 'success':
response['data'] = await self.__perform_mapping(response['data'])
return response
else:
raise NotLoggedIn
# client name url formatter
def __cleanClientName(self, gamertag):
return quote(gamertag.encode("utf-8"))
# helper
def __helper(self, platform, gamertag):
lookUpType = "gamer"
if platform == platforms.Uno:
lookUpType = "id"
if platform == platforms.Activision:
platform = platforms.Uno
if platform not in [platforms.Activision, platforms.Battlenet, platforms.Uno, platforms.All, platforms.PSN,
platforms.XBOX]:
raise InvalidPlatform(platform)
else:
gamertag = self.__cleanClientName(gamertag)
return lookUpType, gamertag, platform
async def __get_mappings(self):
if API._Common.cachedMappings is None:
API._Common.cachedMappings = (
await API._Common.__Request('https://engineer152.github.io/wz-data/weapon-ids.json'),
await API._Common.__Request('https://engineer152.github.io/wz-data/game-modes.json'),
await API._Common.__Request('https://engineer152.github.io/wz-data/perks.json'))
return API._Common.cachedMappings
# mapping
async def __perform_mapping(self, data):
guns, modes, perks = await self.__get_mappings()
if not isinstance(data, list) or 'matches' not in data:
return data
try:
for match in data['matches']:
# time stamps
try:
match['utcStartDateTime'] = datetime.fromtimestamp(
match['utcStartSeconds']).strftime("%A, %B %d, %Y, %I:%M:%S")
match['utcEndDateTime'] = datetime.fromtimestamp(
match['utcEndSeconds']).strftime("%A, %B %d, %Y, %I:%M:%S")
except KeyError:
pass
# loadouts list
for loadout in match['player']['loadouts']:
# weapons
if loadout['primaryWeapon']['label'] is None:
try:
loadout['primaryWeapon']['label'] = guns[loadout['primaryWeapon']['name']]
except KeyError:
pass
if loadout['secondaryWeapon']['label'] is None:
try:
loadout['secondaryWeapon']['label'] = guns[loadout['secondaryWeapon']['name']]
except KeyError:
pass
# perks list
for perk in loadout['perks']:
if perk['label'] is None:
try:
perk['label'] = perks[perk['name']]
except KeyError:
pass
# extra perks list
for perk in loadout['extraPerks']:
if perk['label'] is None:
try:
perk['label'] = perks[perk['name']]
except KeyError:
pass
# loadout list
for loadout in match['player']['loadout']:
if loadout['primaryWeapon']['label'] is None:
try:
loadout['primaryWeapon']['label'] = guns[loadout['primaryWeapon']['name']]
except KeyError:
pass
if loadout['secondaryWeapon']['label'] is None:
try:
loadout['secondaryWeapon']['label'] = guns[loadout['secondaryWeapon']['name']]
except KeyError:
pass
# perks list
for perk in loadout['perks']:
if perk['label'] is None:
try:
perk['label'] = perks[perk['name']]
except KeyError:
pass
# extra perks list
for perk in loadout['extraPerks']:
if perk['label'] is None:
try:
perk['label'] = perks[perk['name']]
except KeyError:
pass
except KeyError:
pass
# return mapped or unmapped data
return data
# API Requests
async def _fullDataReq(self, game, platform, gamertag, type):
lookUpType, gamertag, platform = self.__helper(platform, gamertag)
return await self.__sendRequest(self.fullDataUrl % (game, platform.value, lookUpType, gamertag, type))
async def _combatHistoryReq(self, game, platform, gamertag, type, start, end):
lookUpType, gamertag, platform = self.__helper(platform, gamertag)
return await self.__sendRequest(
self.combatHistoryUrl % (game, platform.value, lookUpType, gamertag, type, start, end))
async def _breakdownReq(self, game, platform, gamertag, type, start, end):
lookUpType, gamertag, platform = self.__helper(platform, gamertag)
return await self.__sendRequest(
self.breakdownUrl % (game, platform.value, lookUpType, gamertag, type, start, end))
async def _seasonLootReq(self, game, platform, gamertag):
lookUpType, gamertag, platform = self.__helper(platform, gamertag)
return await self.__sendRequest(self.seasonLootUrl % (game, platform.value, lookUpType, gamertag))
async def _mapListReq(self, game, platform):
return await self.__sendRequest(self.mapListUrl % (game, platform.value))
async def _matchInfoReq(self, game, platform, type, matchId):
return await self.__sendRequest(self.matchInfoUrl % (game, platform.value, type, matchId))
class __GameDataCommons(_Common):
"""
Methods
=======
Sync
----
fullData(platform:platforms, gamertag:str)
returns player's game data of type dict
combatHistory(platform:platforms, gamertag:str)
returns player's combat history of type dict
combatHistoryWithDate(platform:platforms, gamertag:str, start:int, end:int)
returns player's combat history within the specified timeline of type dict
breakdown(platform:platforms, gamertag:str)
returns player's combat history breakdown of type dict
breakdownWithDate(platform:platforms, gamertag:str, start:int, end:int)
returns player's combat history breakdown within the specified timeline of type dict
seasonLoot(platform:platforms, gamertag:str)
returns player's season loot
mapList(platform:platforms)
returns available maps and available modes for each
matchInfo(platform:platforms, matchId:int)
returns details match details of type dict
Async
----
fullDataAsync(platform:platforms, gamertag:str)
returns player's game data of type dict
combatHistoryAsync(platform:platforms, gamertag:str)
returns player's combat history of type dict
combatHistoryWithDateAsync(platform:platforms, gamertag:str, start:int, end:int)
returns player's combat history within the specified timeline of type dict
breakdownAsync(platform:platforms, gamertag:str)
returns player's combat history breakdown of type dict
breakdownWithDateAsync(platform:platforms, gamertag:str, start:int, end:int)
returns player's combat history breakdown within the specified timeline of type dict
seasonLootAsync(platform:platforms, gamertag:str)
returns player's season loot
mapListAsync(platform:platforms)
returns available maps and available modes for each
matchInfoAsync(platform:platforms, matchId:int)
returns details match details of type dict
"""
def __init_subclass__(cls, **kwargs):
cls.__doc__ = cls.__doc__ + super(cls, cls).__doc__
@property
@abstractmethod
def _game(self) -> str:
raise NotImplementedError
@property
@abstractmethod
def _type(self) -> str:
raise NotImplementedError
async def fullDataAsync(self, platform: platforms, gamertag: str):
data = await self._fullDataReq(self._game, platform, gamertag, self._type)
return data
def fullData(self, platform: platforms, gamertag: str):
return asyncio.run(self.fullDataAsync(platform, gamertag))
async def combatHistoryAsync(self, platform: platforms, gamertag: str):
data = await self._combatHistoryReq(self._game, platform, gamertag, self._type, 0, 0)
return data
def combatHistory(self, platform: platforms, gamertag: str):
return asyncio.run(self.combatHistoryAsync(platform, gamertag))
async def combatHistoryWithDateAsync(self, platform, gamertag: str, start: int, end: int):
data = await self._combatHistoryReq(self._game, platform, gamertag, self._type, start, end)
return data
def combatHistoryWithDate(self, platform, gamertag: str, start: int, end: int):
return asyncio.run(self.combatHistoryWithDateAsync(platform, gamertag, start, end))
async def breakdownAsync(self, platform, gamertag: str):
data = await self._breakdownReq(self._game, platform, gamertag, self._type, 0, 0)
return data
def breakdown(self, platform, gamertag: str):
return asyncio.run(self.breakdownAsync(platform, gamertag))
async def breakdownWithDateAsync(self, platform, gamertag: str, start: int, end: int):
data = await self._breakdownReq(self._game, platform, gamertag, self._type, start, end)
return data
def breakdownWithDate(self, platform, gamertag: str, start: int, end: int):
return asyncio.run(self.breakdownWithDateAsync(platform, gamertag, start, end))
async def matchInfoAsync(self, platform, matchId: int):
data = await self._matchInfoReq(self._game, platform, self._type, matchId)
return data
def matchInfo(self, platform, matchId: int):
return asyncio.run(self.matchInfoAsync(platform, matchId))
async def seasonLootAsync(self, platform, gamertag):
data = await self._seasonLootReq(self._game, platform, gamertag)
return data
def seasonLoot(self, platform, gamertag):
return asyncio.run(self.seasonLootAsync(platform, gamertag))
async def mapListAsync(self, platform):
data = await self._mapListReq(self._game, platform)
return data
def mapList(self, platform):
return asyncio.run(self.mapListAsync(platform))
# WZ
class __WZ(__GameDataCommons):
"""
Warzone class: A class to get players warzone stats, warzone combat history and specific warzone match details
classCategory: game
gameId/gameTitle: mw or wz
gameType: wz
"""
@property
def _game(self) -> str:
return "mw"
@property
def _type(self) -> str:
return "wz"
async def seasonLootAsync(self, platform, gamertag):
raise InvalidEndpoint
async def mapListAsync(self, platform):
raise InvalidEndpoint
# WZ2
class __WZ2(__GameDataCommons):
"""
Warzone 2 class: A class to get players warzone 2 stats, warzone 2 combat history and specific warzone 2 match details
classCategory: game
gameId/gameTitle: mw or wz
gameType: wz2
"""
@property
def _game(self) -> str:
return "mw2"
@property
def _type(self) -> str:
return "wz2"
async def seasonLootAsync(self, platform, gamertag):
raise InvalidEndpoint
async def mapListAsync(self, platform):
raise InvalidEndpoint
# MW
class __MW(__GameDataCommons):
"""
ModernWarfare class: A class to get players modernwarfare stats, modernwarfare combat history, a player's modernwarfare season loot, modernwarfare map list and specific modernwarfare match details
classCategory: game
gameId/gameTitle: mw
gameType: mp
"""
@property
def _game(self) -> str:
return "mw"
@property
def _type(self) -> str:
return "mp"
# CW
class __CW(__GameDataCommons):
"""
ColdWar class: A class to get players coldwar stats, coldwar combat history, a player's coldwar season loot, coldwar map list and specific coldwar match details
classCategory: game
gameId/gameTitle: cw
gameType: mp
"""
@property
def _game(self) -> str:
return "cw"
@property
def _type(self) -> str:
return "mp"
# VG
class __VG(__GameDataCommons):
"""
Vanguard class: A class to get players vanguard stats, vanguard combat history, a player's vanguard season loot, vanguard map list and specific vanguard match details
classCategory: game
gameId/gameTitle: vg
gameType: pm
"""
@property
def _game(self) -> str:
return "vg"
@property
def _type(self) -> str:
return "mp"
# MW2
class __MW2(__GameDataCommons):
"""
ModernWarfare 2 class: A class to get players modernwarfare 2 stats, modernwarfare 2 combat history, a player's modernwarfare 2 season loot, modernwarfare 2 map list and specific modernwarfare 2 match details
classCategory: game
gameId/gameTitle: mw
gameType: mp
"""
@property
def _game(self) -> str:
return "mw2"
@property
def _type(self) -> str:
return "mp"
# USER
class __USER(_Common):
def info(self):
if self.loggedIn:
# First, try to load the user info from a local file if it exists.
if os.path.exists('userInfo.json'):
with open('userInfo.json', 'r') as file:
rawData = json.load(file)
else:
# If the file doesn't exist, make the HTTP request.
rawData = requests.get(f"https://profile.callofduty.com/cod/userInfo/{self.sso_token()}",
headers=API._Common.requestHeaders)
# Process the raw data (either from the file or HTTP response)
data = {'userName': rawData['userInfo']['userName'], 'identities': []}
for i in rawData['identities']:
data['identities'].append({
'platform': i['provider'],
'gamertag': i['username'],
'accountID': i['accountID']
})
return data
else:
raise NotLoggedIn
def __priv(self):
d = self.info()
return d['identities'][0]['platform'], quote(d['identities'][0]['gamertag'].encode("utf-8"))
async def friendFeedAsync(self):
p, g = self.__priv()
data = await self._Common__sendRequest(
f"/userfeed/v1/friendFeed/platform/{p}/gamer/{g}/friendFeedEvents/en")
return data
def friendFeed(self):
return asyncio.run(self.friendFeedAsync())
async def eventFeedAsync(self):
data = await self._Common__sendRequest(f"/userfeed/v1/friendFeed/rendered/en/{self.sso_token()}")
return data
def eventFeed(self):
return asyncio.run(self.eventFeedAsync())
async def loggedInIdentitiesAsync(self):
data = await self._Common__sendRequest(f"/crm/cod/v2/identities/{self.sso_token()}")
return data
def loggedInIdentities(self):
return asyncio.run(self.loggedInIdentitiesAsync())
async def codPointsAsync(self):
p, g = self.__priv()
data = await self._Common__sendRequest(f"/inventory/v1/title/mw/platform/{p}/gamer/{g}/currency")
return data
def codPoints(self):
return asyncio.run(self.codPointsAsync())
async def connectedAccountsAsync(self):
p, g = self.__priv()
data = await self._Common__sendRequest(f"/crm/cod/v2/accounts/platform/{p}/gamer/{g}")
return data
def connectedAccounts(self):
return asyncio.run(self.connectedAccountsAsync())
async def settingsAsync(self):
p, g = self.__priv()
data = await self._Common__sendRequest(f"/preferences/v1/platform/{p}/gamer/{g}/list")
return data
def settings(self):
return asyncio.run(self.settingsAsync())
# SHOP
class __SHOP(_Common):
"""
Shop class: A class to get bundle details and battle pass loot
classCategory: other
Methods
=======
Sync
----
purchasableItems(game: games)
returns purchasable items for a specific gameId/gameTitle
bundleInformation(game: games, bundleId: int)
returns bundle details for the specific gameId/gameTitle and bundleId
battlePassLoot(game: games, platform: platforms, season: int)
returns battle pass loot for specific game and season on given platform
Async
----
purchasableItemsAsync(game: games)
returns purchasable items for a specific gameId/gameTitle
bundleInformationAsync(game: games, bundleId: int)
returns bundle details for the specific gameId/gameTitle and bundleId
battlePassLootAsync(game: games, platform: platforms, season: int)
returns battle pass loot for specific game and season on given platform
"""
async def purchasableItemsAsync(self, game: games):
data = await self._Common__sendRequest(f"/inventory/v1/title/{game.value}/platform/uno/purchasable/public/en")
return data
def purchasableItems(self, game: games):
return asyncio.run(self.purchasableItemsAsync(game))
async def bundleInformationAsync(self, game: games, bundleId: int):
data = await self._Common__sendRequest(f"/inventory/v1/title/{game.value}/bundle/{bundleId}/en")
return data
def bundleInformation(self, game: games, bundleId: int):
return asyncio.run(self.bundleInformationAsync(game, bundleId))
async def battlePassLootAsync(self, game: games, platform: platforms, season: int):
data = await self._Common__sendRequest(
f"/loot/title/{game.value}/platform/{platform.value}/list/loot_season_{season}/en")
return data
def battlePassLoot(self, game: games, platform: platforms, season: int):
return asyncio.run(self.battlePassLootAsync(game, platform, season))
# ALT
class __ALT(_Common):
async def searchAsync(self, platform, gamertag: str):
lookUpType, gamertag, platform = self._Common__helper(platform, gamertag)
data = await self._Common__sendRequest(f"/crm/cod/v2/platform/{platform.value}/username/{gamertag}/search")
return data
def search(self, platform, gamertag: str):
return asyncio.run(self.searchAsync(platform, gamertag))
# Exceptions
class NotLoggedIn(Exception):
def __str__(self):
return "Not logged in!"
class InvalidToken(Exception):
def __init__(self, token):
self.token = token
def __str__(self):
return f"Token is invalid, token: {self.token}"
class InvalidPlatform(Exception):
def __init__(self, platform: platforms):
self.message: str
if platform == platforms.Steam:
self.message = "Steam cannot be used till further updates."
else:
self.message = "Invalid platform, use platform class!"
super().__init__(self.message)
def __str__(self):
return self.message
class InvalidEndpoint(Exception):
def __str__(self):
return "This endpoint is not available for selected title"
class StatusError(Exception):
def __str__(self):
return "Status Error, Check if your sso token is valid or try again later."

361
cod_api/replacements.py Normal file
View File

@ -0,0 +1,361 @@
replacements = {
# Maps
"career": "Career",
"mp_hackney_yard": "Hackney Yard (Night)",
"mp_aniyah": "Aniyah Palace",
"mp_euphrates": "Euphrates Bridge",
"mp_raid": "Grazna Raid",
"mp_m_pine": "Pine",
"mp_m_stack": "Stack",
"mp_deadzone": "Arklov Peak",
"mp_quarry": "Karst River Quarry",
"mp_m_overunder": "Docks",
"mp_cave_am": "Azhir Cave",
"mp_cave": "Azhir Cave (Night)",
"mp_runner": "Gun Runner",
"mp_runner_pm": "Gun Runner (Night)",
"mp_hackney_am": "Hackney Yard",
"mp_piccadilly": "Piccadilly",
"mp_spear": "Rammaza",
"mp_spear_pm": "Rammaza (Night)",
"mp_petrograd": "St. Petrograd",
"mp_m_hill": "Hill",
"mp_m_king": "King",
"mp_m_speedball": "Speedball",
"mp_m_showers": "Gulag Showers",
"mp_downtown_gw": "Tarvosk District",
"mp_m_speed": "Shoot House",
"mp_farms2_gw": "Krovnik Farmland",
"mp_port2_gw": "Port",
"mp_crash": "Crash",
"mp_vacant": "Vacant",
"mp_shipment": "Shipment",
"mp_m_cargo": "Cargo",
"mp_m_cage": "Atrium",
"mp_m_overwinter": "Docks",
"mp_emporium": "Atlas Superstore",
"mp_rust": "Rust",
"mp_boneyard_gw": "Zhokov Boneyard",
"mp_m_fork": "Bazaar",
"mp_donetsk": "Verdansk",
"mp_hideout": "Khandor Hideout",
"loading_mp_hideout": "Khandor Hideout",
"mp_aniyah_tac": "Aniyah Incursion",
"mp_backlot": "Talsik Backlot",
"mp_village": "Hovec Sawmill",
"mp_hardhat": "Hardhat",
"mp_m_wallco": "Aisle 9",
"mp_donetsk": "Verdansk",
"mp_scrapyard": "Zhokov Scrapyard",
"mp_m_trench": "Trench",
"mp_promenade_gw": "Barakett Promenade",
"mp_don": "Verdansk",
"mp_garden": "Cheshire Park",
"mp_oilrig": "Petrov Oil Rig",
"mp_harbor": "Suldal Harbor",
"mp_layover_gw": "Verdansk International Airport",
"mp_m_cornfield": "Livestock",
"mp_m_stadium": "Verdansk Stadium",
"mp_malyshev": "Mialstor Tank Factory",
"mp_malyshev_10v10": "Mialstor Tank Factory",
"mp_broadcast": "Broadcast",
"mp_riverside_gw": "Verdansk Riverside",
"mp_m_train": "Station",
"mp_kstenod": "Verdansk (Night)",
"mp_escape": "Rebirth",
"mp_herat": "Al-Raab Airbase",
"mp_killhouse": "Killhouse",
"mp_m_drainage": "Drainage",
# Gamemodes
"war": "Team Deathmatch",
"sd": "Search and Destroy",
"dom": "Domination",
"tdef": "Team Defender",
"dm": "Free-for-all",
"koth": "Hardpoint",
"hq": "Headquarters",
"arena": "Gunfight",
"arm": "Ground War",
"conf": "Kill Confirmed",
"cyber": "Cyber Attack",
"hc_war": "Team Deathmatch Hardcore",
"hc_arena": "Gunfight Hardcore",
"hc_arm": "Ground War Hardcore",
"hc_conf": "Kill Confirmed Hardcore",
"hc_cyber": "Cyber Attack Hardcore",
"hc_dm": "Free-for-all Hardcore",
"hc_hq": "Headquarters Hardcore",
"hc_dom": "Domination Hardcore",
"hc_sd": "Search and Destroy Hardcore",
"cyber_hc": "Cyber Attack Hardcore",
"war_hc": "Team Deathmatch Hardcore",
"dom_hc": "Domination Hardcore",
"sd_hc": "Search and Destroy Hardcore",
"conf_hc": "Kill Confirmed Hardcore",
"gun": "Gun Game",
"gun_hc": "Gun Game Hardcore",
"siege": "Reinforce",
"infect": "Infected",
"arena_osp": "Gunfight O.S.P.",
"hq_hc": "Headquarters Hardcore",
"grnd": "Grind",
"grind": "Grind",
"ctf": "Capture the Flag",
"br_all": "All",
"br": "Battle Royale",
"br_dmz": "Plunder",
"br_dmz_38": "Plunder Quads",
"br_87": "BR Solos",
"br_dmz_104": "Blood Money",
"koth_hc": "Hardpoint Hardcore",
"br_25": "BR Trios",
"br_89": "BR Quads",
"br_dmz_76": "Plunder Quads",
"br_77": "BR Scopes & Scatterguns",
"br_dmz_85": "Plunder Duos",
"dd_hc": "Demolition Hardcore",
"dd": "Demolition",
"br_71": "BR Solos",
"br_74": "BR Trios",
"br_88": "BR Duos",
"brtdm_113": "Warzone Rumble",
"brtdm_rmbl": "Warzone Rumble",
"br_brsolo": "BR Solos",
"br_brduos": "BR Duos",
"br_brtrios": "BR Trios",
"br_brquads": "BR Quads",
"br_dmz_plnbld": "Blood Money",
"br_br_real": "Realism Battle Royale",
"br_86": "Realism Battle Royale",
"br_brthquad": "BR 200 Quads",
"br_jugg_brtriojugr": "Juggernaut Royal Trios",
"br_dmz_plunquad": "Plunder Quads",
"br_dmz_bldmnytrio": "Blood Money Trios",
"br_mini_miniroyale": "Mini Royale",
"br_brbbsolo": "BR Buyback Solos",
"br_jugg_brquadjugr": "Juggernaut Royal Quads",
"br_kingslayer_kingsltrios": "King Slayer Trios",
"br_truckwar_trwarsquads": "Armored Royale Quads",
"br_zxp_zmbroy": "Zombie Royale",
"br_brhwntrios": "BR Trick-Or-Trios",
"rugby": "Onslaughter",
"br_brsolohwn": "BR Solo Survivor",
"br_dmz_plndcndy": "Plunder: Candy Collector",
"br_jugg_jugpmpkn": "Juggourdnaut Royale",
"br_rebirth_rbrthtrios": "Resurgence Trio",
"br_rebirth_rbrthduos": "Resurgence Duos",
"br_rebirth_rbrthquad": "Rebirth Resurgance Quads",
"br_dmz_plndtrios": "Plunder Trios",
"br_rebirth_resurgence_trios": "Verdansk Resurgence Trios",
"br_mini_rebirth_mini_royale_quads": "Rebirth Mini Royale Quads",
"br_bodycount_pwergrb": "Power Grab",
"br_rebirth_resurgence_mini": "Verdansk Resurgence Mini",
"br_payload_payload": "Payload",
"br_mini_rebirth_mini_royale_trios": "Rebirth Mini Royale Trios",
"br_x2_br_reveal_x2_event/event_title_x2": "Battle of Verdansk",
"br_rumble_clash": "Clash",
"br_dbd_dbd": "Iron Trials '84",
"br_gxp_gov": "Ghosts of Verdansk",
# Weapons
"scorestreak": "Scorestreak",
"equipment": "Equipment",
"gear": "Gear",
"weapon_bare_hands": "Bare Hands",
"weapon_tactical_rifle": "Tactical Rifle",
"weapon_shotgun": "Shotgun",
"weapon_sniper": "Sniper",
"weapon_lmg": "Light Machine Guns",
"weapon_launcher": "Launcher",
"weapon_pistol": "Pistol",
"weapon_smg": "Submachine Guns",
"weapon_melee": "Melee",
"weapon_assault_rifle": "Assault Rifle",
"attachments": "Attachments",
"weapons": "Weapons",
"specialist": "Specialist",
"weapon": "Weapon",
"weapon_special": "Special",
"iw8_ar_akilo47": "AK-47",
"iw8_ar_kilo433": "Kilo-141",
"iw8_ar_mcharlie": "M13",
"iw8_ar_falima": "FAL",
"iw8_ar_asierra12": "Oden",
"iw8_sm_mpapa7": "MP7",
"iw8_sm_augolf": "AUG",
"iw8_sm_uzulu": "Uzi",
"iw8_sh_romeo870": "Model 680",
"iw8_sh_charlie725": "725",
"iw8_sh_aalpha12": "JAK-12",
"iw8_sh_oscar12": "Origin 12",
"iw8_lm_pkilo": "PKM",
"iw8_lm_mgolf34": "MG34",
"iw8_lm_lima86": "SA87",
"iw8_lm_dblmg": "MP Juggernaut",
"iw8_sn_mike14": "EBR-14",
"iw8_sn_delta": "Dragunov",
"iw8_sn_alpha50": "AX-50",
"iw8_sn_hdromeo": "HDR",
"iw8_sn_sbeta": "Mk2 Carbine",
"iw8_pi_papa320": "M19",
"iw8_pi_cpapa": ".357",
"iw8_la_rpapa7": "RPG-7",
"iw8_la_juliet": "JOKR",
"iw8_la_gromeo": "PILA",
"iw8_la_kgolf": "Strela-P",
"iw8_me_riotshield": "Riot Shield",
"equip_gas_grenade": "Gas Grenade",
"equip_snapshot_grenade": "Snapshot Grenade",
"equip_decoy": "Decoy Grenade",
"equip_smoke": "Smoke Grenade",
"equip_concussion": "Stun Grenade",
"equip_hb_sensor": "Heartbeat Sensor",
"equip_flash": "Flash Grenade",
"equip_adrenaline": "Stim",
"equip_frag": "Frag Grenade",
"equip_thermite": "Thermite",
"equip_semtex": "Semtex",
"equip_claymore": "Claymore",
"equip_c4": "C4",
"equip_at_mine": "Proximity Mine",
"equip_throwing_knife": "Throwing Knife",
"equip_molotov": "Molotov Cocktail",
"iw8_knife": "Combat Knife",
"weapon_other": "Primary Melee",
"iw8_ar_tango21": "RAM-7",
"iw8_ar_falpha": "FR 5.56",
"iw8_ar_mike4": "M4A1",
"iw8_sm_papa90": "P90",
"iw8_sm_mpapa5": "MP5",
"iw8_sm_beta": "PP19 Bizon",
"iw8_sh_dpapa12": "R9-0",
"iw8_lm_mgolf36": "Holger-26",
"iw8_sn_kilo98": "Kar98k",
"iw8_pi_mike1911": "1911",
"iw8_pi_golf21": "X16",
"iw8_pi_decho": ".50 GS",
"weapon_marksman": "Marksman Rifles",
"iw8_lm_kilo121": "M91",
"iw8_ar_scharlie": "FN Scar 17",
"iw8_ar_sierra552": "Grau 5.56",
"iw8_sm_smgolf45": "Striker 45",
"iw8_pi_mike9a3": "Renetti",
"iw8_lm_mkilo3": "Bruen MK9",
"iw8_sh_mike26": "VLK Rogue",
"iw8_sn_crossbow": "Crossbow",
"iw8_sn_sksierra": "SKS",
"iw8_ar_galima": "CR-56 AMAX",
"iw8_me_kalistick": "Kali Sticks",
"iw8_sm_victor": "Fennec Mk9",
"iw8_sn_xmike109": "Rytec AMR",
"iw8_pi_mike9": "Renetti",
"iw8_me_akimboblunt": "Kali Sticks",
"iw8_ar_anovember94": "AN-94",
"iw8_sm_charlie9": "ISO",
"iw8_me_akimboblades": "Dual Kodachis",
"iw8_lm_sierrax": "FiNN",
"iw8_ar_valpha": "AS VAL",
"iw8_sn_romeo700": "SP-R 208",
"cruise_predator": "Cruise Missile",
"manual_turret": "Shield Turret",
"toma_strike": "Cluster Strike",
"sentry_gun": "Sentry Gun",
"hover_jet": "VTOL Jet",
"precision_airstrike": "Precision Airstrike",
"juggernaut": "Juggernaut",
"pac_sentry": "",
"chopper_gunner": "Chopper Gunner",
"gunship": "Gunship",
"white_phosphorus": "White Phosphorus",
"nuke": "Nuke",
"chopper_support": "Support Helo",
"bradley": "Infantry Assault Vehicle",
"uav": "UAV",
"directional_uav": "Advanced UAV",
"airdrop": "Care Package",
"airdrop_multiple": "Emergency Airdrop",
"radar_drone_overwatch": "Personal Radar",
"scrambler_drone_guard": "Counter UAV",
"super_emp_drone": "EMP Drone",
"super_trophy": "Trophy System",
"super_ammo_drop": "Munitions Box",
"super_weapon_drop": "Weapon Drop",
"super_fulton": "Cash Deposit Balloon",
"super_armor_drop": "Armor Box",
"super_select": "Field Upgrade Pro (Any)",
"super_tac_insert": "Tactical Insertion",
"super_recon_drone": "Recon Drone",
"super_deadsilence": "Dead Silence",
"super_supply_drop": "Loadout Drop", ### Unsure if this is Loadout Drop
"super_tac_cover": "Deployable Cover",
"super_support_box": "Stopping Power Rounds",
# Accolades
# "accoladeData": "Accolades",
# "classChanges": "Most classes changed (Evolver)",
# "highestAvgAltitude": "Highest average altitude (High Command)",
# "killsFromBehind": "Most kills from behind (Flanker)",
# "lmgDeaths": "Most LMG deaths (Target Practice)",
# "riotShieldDamageAbsorbed": "Most damage absorbed with Riot Shield (Guardian)",
# "flashbangHits": "Most Flashbang hits (Blinder)",
# "meleeKills": "Most Melee kills (Brawler)",
# "tagsLargestBank": "Largest bank (Bank Account)",
# "shotgunKills": "Most Shotgun kills (Buckshot)",
# "sniperDeaths": "Most Sniper deaths (Zeroed In)",
# "timeProne": "Most time spent Prone (Grassy Knoll)",
# "killstreakWhitePhosphorousKillsAssists": "Most kills and assists with White Phosphorus (Burnout)",
# "shortestLife": "Shortest life (Terminal)",
# "deathsFromBehind": "Most deaths from behind (Blindsided)",
# "higherRankedKills": "Most kills on higher ranked scoreboard players (Upriser)",
# "mostAssists": "Most assists (Wingman)",
# "leastKills": "Fewest kills (The Fearful)",
# "tagsDenied": "Denied the most tags (Denied)",
# "killstreakWheelsonKills": "Most Wheelson kills",
# "sniperHeadshots": "Most Sniper headshots (Dead Aim)",
# "killstreakJuggernautKills": "Most Juggernaut kills (Heavy Metal)",
# "smokesUsed": "Most Smoke Grenades used (Chimney)",
# "avengerKills": "Most avenger kills (Avenger)",
# "decoyHits": "Most Decoy Grenade hits (Made You Look)",
# "killstreakCarePackageUsed": "Most Care Packages called in (Helping Hand)",
# "molotovKills": "Most Molotov kills (Arsonist)",
# "gasHits": "Most Gas Grenade hits (Gaseous)",
# "comebackKills": "Most comebacks (Rally)",
# "lmgHeadshots": "Most LMG headshots (LMG Expert)",
# "smgDeaths": "Most SMG deaths (Run and Gunned)",
# "carrierKills": "Most kills as carrier (Carrier)",
# "deployableCoverUsed": "Most Deployable Covers used (Combat Engineer)",
# "thermiteKills": "Most Thermite kills (Red Iron)",
# "arKills": "Most assault rifle kills (AR Specialist)",
# "c4Kills": "Most C4 kills (Handle With Care)",
# "suicides": "Most suicides (Accident Prone)",
# "clutch": "Most kills as the last alive (Clutched)",
# "survivorKills": "Most kills as survivor (Survivalist)",
# "killstreakGunshipKills": "Most Gunship kills (Death From Above)",
# "timeSpentAsPassenger": "Most time spent as a passenger (Navigator)",
# "returns": "Most flags returned (Flag Returner)",
# "smgHeadshots": "Most SMG headshots (SMG Expert)",
# "launcherDeaths": "Most launcher deaths (Fubar)",
# "oneShotOneKills": "Most one shot kills (One Shot Kill)",
# "ammoBoxUsed": "Most Munitions Boxes used (Provider)",
# #"spawnSelectSquad": "",
# "weaponPickups": "Most picked up weapons (Loaner)",
# "pointBlankKills": "Most point blank kills (Personal Space)",
# "tagsCaptured": "Collected the most tags (Confirmed Kills)",
# "killstreakGroundKills": "Most ground based killstreak kills (Ground Control)",
# "distanceTraveledInVehicle": "Longest distance travelled in a vehicle (Cross Country)",
# "longestLife": "Longest life (Lifer)",
# "stunHits": "Most Stun Grenade hits (Stunner)",
# "spawnSelectFlag": "Most FOB Spawns (Objective Focused)", # Unsure
# "shotgunHeadshots": "Most Shotgun headshots (Boomstick)",
# "bombDefused": "Most defuses (Defuser)",
# "snapshotHits": "Most Snapshot Grenade hits (Photographer)",
# "noKillsWithDeath": "No kills with at least 1 death (Participant)",
# "killstreakAUAVAssists": "Most Advanced UAV assists (Target Rich Environment)",
# "killstreakPersonalUAVKills": "Most kills with a Personal Radar active (Nothing Personal)",
# "tacticalInsertionSpawns": "Most Tactical Insertions used (Revenant)",
# "launcherKills": "Most Launcher kills (Explosive)",
# "spawnSelectVehicle": "Most vehicle spawns (Oscar Mike)",
# "mostKillsLeastDeaths": "Most kills and fewest deaths (MVP)",
# "mostKills": "Most kills (The Feared)",
# "defends": "Most defend kills (Defense)",
# "timeSpentAsDriver": "Most time spent driving (Driver)",
# "": "" # WIP - Still adding more
}

View File

@ -25,3 +25,6 @@ https://my.callofduty.com/api/papi-client/inventory/v1/title/mw/platform/battle/
https://my.callofduty.com/api/papi-client/crm/cod/v2/accounts/platform/battle/gamer/$PROF/
https://my.callofduty.com/api/papi-client/preferences/v1/platform/battle/gamer/$PROF/list
# Get Bundle Info
https://my.callofduty.com/api/papi-client/inventory/v1/title/mw/bundle/400525/en

View File

@ -1,8 +1,10 @@
import re
import sys
import json
import os
import argparse
from cod_api import API, platforms
from cod_api.replacements import replacements
import asyncio
import datetime
@ -10,368 +12,6 @@ import datetime
if os.name == 'nt':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
replacements = {
# Maps
"career": "Career",
"mp_hackney_yard": "Hackney Yard (Night)",
"mp_aniyah": "Aniyah Palace",
"mp_euphrates": "Euphrates Bridge",
"mp_raid": "Grazna Raid",
"mp_m_pine": "Pine",
"mp_m_stack": "Stack",
"mp_deadzone": "Arklov Peak",
"mp_quarry": "Karst River Quarry",
"mp_m_overunder": "Docks",
"mp_cave_am": "Azhir Cave",
"mp_cave": "Azhir Cave (Night)",
"mp_runner": "Gun Runner",
"mp_runner_pm": "Gun Runner (Night)",
"mp_hackney_am": "Hackney Yard",
"mp_piccadilly": "Piccadilly",
"mp_spear": "Rammaza",
"mp_spear_pm": "Rammaza (Night)",
"mp_petrograd": "St. Petrograd",
"mp_m_hill": "Hill",
"mp_m_king": "King",
"mp_m_speedball": "Speedball",
"mp_m_showers": "Gulag Showers",
"mp_downtown_gw": "Tarvosk District",
"mp_m_speed": "Shoot House",
"mp_farms2_gw": "Krovnik Farmland",
"mp_port2_gw": "Port",
"mp_crash": "Crash",
"mp_vacant": "Vacant",
"mp_shipment": "Shipment",
"mp_m_cargo": "Cargo",
"mp_m_cage": "Atrium",
"mp_m_overwinter": "Docks",
"mp_emporium": "Atlas Superstore",
"mp_rust": "Rust",
"mp_boneyard_gw": "Zhokov Boneyard",
"mp_m_fork": "Bazaar",
"mp_donetsk": "Verdansk",
"mp_hideout": "Khandor Hideout",
"loading_mp_hideout": "Khandor Hideout",
"mp_aniyah_tac": "Aniyah Incursion",
"mp_backlot": "Talsik Backlot",
"mp_village": "Hovec Sawmill",
"mp_hardhat": "Hardhat",
"mp_m_wallco": "Aisle 9",
"mp_donetsk": "Verdansk",
"mp_scrapyard": "Zhokov Scrapyard",
"mp_m_trench": "Trench",
"mp_promenade_gw": "Barakett Promenade",
"mp_don": "Verdansk",
"mp_garden": "Cheshire Park",
"mp_oilrig": "Petrov Oil Rig",
"mp_harbor": "Suldal Harbor",
"mp_layover_gw": "Verdansk International Airport",
"mp_m_cornfield": "Livestock",
"mp_m_stadium": "Verdansk Stadium",
"mp_malyshev": "Mialstor Tank Factory",
"mp_malyshev_10v10": "Mialstor Tank Factory",
"mp_broadcast": "Broadcast",
"mp_riverside_gw": "Verdansk Riverside",
"mp_m_train": "Station",
"mp_kstenod": "Verdansk (Night)",
"mp_escape": "Rebirth",
"mp_herat": "Al-Raab Airbase",
"mp_killhouse": "Killhouse",
"mp_m_drainage": "Drainage",
# Gamemodes
"war": "Team Deathmatch",
"sd": "Search and Destroy",
"dom": "Domination",
"tdef": "Team Defender",
"dm": "Free-for-all",
"koth": "Hardpoint",
"hq": "Headquarters",
"arena": "Gunfight",
"arm": "Ground War",
"conf": "Kill Confirmed",
"cyber": "Cyber Attack",
"hc_war": "Team Deathmatch Hardcore",
"hc_arena": "Gunfight Hardcore",
"hc_arm": "Ground War Hardcore",
"hc_conf": "Kill Confirmed Hardcore",
"hc_cyber": "Cyber Attack Hardcore",
"hc_dm": "Free-for-all Hardcore",
"hc_hq": "Headquarters Hardcore",
"hc_dom": "Domination Hardcore",
"hc_sd": "Search and Destroy Hardcore",
"cyber_hc": "Cyber Attack Hardcore",
"war_hc": "Team Deathmatch Hardcore",
"dom_hc": "Domination Hardcore",
"sd_hc": "Search and Destroy Hardcore",
"conf_hc": "Kill Confirmed Hardcore",
"gun": "Gun Game",
"gun_hc": "Gun Game Hardcore",
"siege": "Reinforce",
"infect": "Infected",
"arena_osp": "Gunfight O.S.P.",
"hq_hc": "Headquarters Hardcore",
"grnd": "Grind",
"grind": "Grind",
"ctf": "Capture the Flag",
"br_all": "All",
"br": "Battle Royale",
"br_dmz": "Plunder",
"br_dmz_38": "Plunder Quads",
"br_87": "BR Solos",
"br_dmz_104": "Blood Money",
"koth_hc": "Hardpoint Hardcore",
"br_25": "BR Trios",
"br_89": "BR Quads",
"br_dmz_76": "Plunder Quads",
"br_77": "BR Scopes & Scatterguns",
"br_dmz_85": "Plunder Duos",
"dd_hc": "Demolition Hardcore",
"dd": "Demolition",
"br_71": "BR Solos",
"br_74": "BR Trios",
"br_88": "BR Duos",
"brtdm_113": "Warzone Rumble",
"brtdm_rmbl": "Warzone Rumble",
"br_brsolo": "BR Solos",
"br_brduos": "BR Duos",
"br_brtrios": "BR Trios",
"br_brquads": "BR Quads",
"br_dmz_plnbld": "Blood Money",
"br_br_real": "Realism Battle Royale",
"br_86": "Realism Battle Royale",
"br_brthquad": "BR 200 Quads",
"br_jugg_brtriojugr": "Juggernaut Royal Trios",
"br_dmz_plunquad": "Plunder Quads",
"br_dmz_bldmnytrio": "Blood Money Trios",
"br_mini_miniroyale": "Mini Royale",
"br_brbbsolo": "BR Buyback Solos",
"br_jugg_brquadjugr": "Juggernaut Royal Quads",
"br_kingslayer_kingsltrios": "King Slayer Trios",
"br_truckwar_trwarsquads": "Armored Royale Quads",
"br_zxp_zmbroy": "Zombie Royale",
"br_brhwntrios": "BR Trick-Or-Trios",
"rugby": "Onslaughter",
"br_brsolohwn": "BR Solo Survivor",
"br_dmz_plndcndy": "Plunder: Candy Collector",
"br_jugg_jugpmpkn": "Juggourdnaut Royale",
"br_rebirth_rbrthtrios": "Resurgence Trio",
"br_rebirth_rbrthduos": "Resurgence Duos",
"br_rebirth_rbrthquad": "Rebirth Resurgance Quads",
"br_dmz_plndtrios": "Plunder Trios",
"br_rebirth_resurgence_trios": "Verdansk Resurgence Trios",
"br_mini_rebirth_mini_royale_quads": "Rebirth Mini Royale Quads",
"br_bodycount_pwergrb": "Power Grab",
"br_rebirth_resurgence_mini": "Verdansk Resurgence Mini",
"br_payload_payload": "Payload",
"br_mini_rebirth_mini_royale_trios": "Rebirth Mini Royale Trios",
"br_x2_br_reveal_x2_event/event_title_x2": "Battle of Verdansk",
"br_rumble_clash": "Clash",
"br_dbd_dbd": "Iron Trials '84",
"br_gxp_gov": "Ghosts of Verdansk",
# Weapons
"scorestreak": "Scorestreak",
"equipment": "Equipment",
"gear": "Gear",
"weapon_bare_hands": "Bare Hands",
"weapon_tactical_rifle": "Tactical Rifle",
"weapon_shotgun": "Shotgun",
"weapon_sniper": "Sniper",
"weapon_lmg": "Light Machine Guns",
"weapon_launcher": "Launcher",
"weapon_pistol": "Pistol",
"weapon_smg": "Submachine Guns",
"weapon_melee": "Melee",
"weapon_assault_rifle": "Assault Rifle",
"attachments": "Attachments",
"weapons": "Weapons",
"specialist": "Specialist",
"weapon": "Weapon",
"weapon_special": "Special",
"iw8_ar_akilo47": "AK-47",
"iw8_ar_kilo433": "Kilo-141",
"iw8_ar_mcharlie": "M13",
"iw8_ar_falima": "FAL",
"iw8_ar_asierra12": "Oden",
"iw8_sm_mpapa7": "MP7",
"iw8_sm_augolf": "AUG",
"iw8_sm_uzulu": "Uzi",
"iw8_sh_romeo870": "Model 680",
"iw8_sh_charlie725": "725",
"iw8_sh_aalpha12": "JAK-12",
"iw8_sh_oscar12": "Origin 12",
"iw8_lm_pkilo": "PKM",
"iw8_lm_mgolf34": "MG34",
"iw8_lm_lima86": "SA87",
"iw8_lm_dblmg": "MP Juggernaut",
"iw8_sn_mike14": "EBR-14",
"iw8_sn_delta": "Dragunov",
"iw8_sn_alpha50": "AX-50",
"iw8_sn_hdromeo": "HDR",
"iw8_sn_sbeta": "Mk2 Carbine",
"iw8_pi_papa320": "M19",
"iw8_pi_cpapa": ".357",
"iw8_la_rpapa7": "RPG-7",
"iw8_la_juliet": "JOKR",
"iw8_la_gromeo": "PILA",
"iw8_la_kgolf": "Strela-P",
"iw8_me_riotshield": "Riot Shield",
"equip_gas_grenade": "Gas Grenade",
"equip_snapshot_grenade": "Snapshot Grenade",
"equip_decoy": "Decoy Grenade",
"equip_smoke": "Smoke Grenade",
"equip_concussion": "Stun Grenade",
"equip_hb_sensor": "Heartbeat Sensor",
"equip_flash": "Flash Grenade",
"equip_adrenaline": "Stim",
"equip_frag": "Frag Grenade",
"equip_thermite": "Thermite",
"equip_semtex": "Semtex",
"equip_claymore": "Claymore",
"equip_c4": "C4",
"equip_at_mine": "Proximity Mine",
"equip_throwing_knife": "Throwing Knife",
"equip_molotov": "Molotov Cocktail",
"iw8_knife": "Combat Knife",
"weapon_other": "Primary Melee",
"iw8_ar_tango21": "RAM-7",
"iw8_ar_falpha": "FR 5.56",
"iw8_ar_mike4": "M4A1",
"iw8_sm_papa90": "P90",
"iw8_sm_mpapa5": "MP5",
"iw8_sm_beta": "PP19 Bizon",
"iw8_sh_dpapa12": "R9-0",
"iw8_lm_mgolf36": "Holger-26",
"iw8_sn_kilo98": "Kar98k",
"iw8_pi_mike1911": "1911",
"iw8_pi_golf21": "X16",
"iw8_pi_decho": ".50 GS",
"weapon_marksman": "Marksman Rifles",
"iw8_lm_kilo121": "M91",
"iw8_ar_scharlie": "FN Scar 17",
"iw8_ar_sierra552": "Grau 5.56",
"iw8_sm_smgolf45": "Striker 45",
"iw8_pi_mike9a3": "Renetti",
"iw8_lm_mkilo3": "Bruen MK9",
"iw8_sh_mike26": "VLK Rogue",
"iw8_sn_crossbow": "Crossbow",
"iw8_sn_sksierra": "SKS",
"iw8_ar_galima": "CR-56 AMAX",
"iw8_me_kalistick": "Kali Sticks",
"iw8_sm_victor": "Fennec Mk9",
"iw8_sn_xmike109": "Rytec AMR",
"iw8_pi_mike9": "Renetti",
"iw8_me_akimboblunt": "Kali Sticks",
"iw8_ar_anovember94": "AN-94",
"iw8_sm_charlie9": "ISO",
"iw8_me_akimboblades": "Dual Kodachis",
"iw8_lm_sierrax": "FiNN",
"iw8_ar_valpha": "AS VAL",
"iw8_sn_romeo700": "SP-R 208",
"cruise_predator": "Cruise Missile",
"manual_turret": "Shield Turret",
"toma_strike": "Cluster Strike",
"sentry_gun": "Sentry Gun",
"hover_jet": "VTOL Jet",
"precision_airstrike": "Precision Airstrike",
"juggernaut": "Juggernaut",
"pac_sentry": "",
"chopper_gunner": "Chopper Gunner",
"gunship": "Gunship",
"white_phosphorus": "White Phosphorus",
"nuke": "Nuke",
"chopper_support": "Support Helo",
"bradley": "Infantry Assault Vehicle",
"uav": "UAV",
"directional_uav": "Advanced UAV",
"airdrop": "Care Package",
"airdrop_multiple": "Emergency Airdrop",
"radar_drone_overwatch": "Personal Radar",
"scrambler_drone_guard": "Counter UAV",
"super_emp_drone": "EMP Drone",
"super_trophy": "Trophy System",
"super_ammo_drop": "Munitions Box",
"super_weapon_drop": "Weapon Drop",
"super_fulton": "Cash Deposit Balloon",
"super_armor_drop": "Armor Box",
"super_select": "Field Upgrade Pro (Any)",
"super_tac_insert": "Tactical Insertion",
"super_recon_drone": "Recon Drone",
"super_deadsilence": "Dead Silence",
"super_supply_drop": "Loadout Drop", ### Unsure if this is Loadout Drop
"super_tac_cover": "Deployable Cover",
"super_support_box": "Stopping Power Rounds",
# Accolades
# "accoladeData": "Accolades",
# "classChanges": "Most classes changed (Evolver)",
# "highestAvgAltitude": "Highest average altitude (High Command)",
# "killsFromBehind": "Most kills from behind (Flanker)",
# "lmgDeaths": "Most LMG deaths (Target Practice)",
# "riotShieldDamageAbsorbed": "Most damage absorbed with Riot Shield (Guardian)",
# "flashbangHits": "Most Flashbang hits (Blinder)",
# "meleeKills": "Most Melee kills (Brawler)",
# "tagsLargestBank": "Largest bank (Bank Account)",
# "shotgunKills": "Most Shotgun kills (Buckshot)",
# "sniperDeaths": "Most Sniper deaths (Zeroed In)",
# "timeProne": "Most time spent Prone (Grassy Knoll)",
# "killstreakWhitePhosphorousKillsAssists": "Most kills and assists with White Phosphorus (Burnout)",
# "shortestLife": "Shortest life (Terminal)",
# "deathsFromBehind": "Most deaths from behind (Blindsided)",
# "higherRankedKills": "Most kills on higher ranked scoreboard players (Upriser)",
# "mostAssists": "Most assists (Wingman)",
# "leastKills": "Fewest kills (The Fearful)",
# "tagsDenied": "Denied the most tags (Denied)",
# "killstreakWheelsonKills": "Most Wheelson kills",
# "sniperHeadshots": "Most Sniper headshots (Dead Aim)",
# "killstreakJuggernautKills": "Most Juggernaut kills (Heavy Metal)",
# "smokesUsed": "Most Smoke Grenades used (Chimney)",
# "avengerKills": "Most avenger kills (Avenger)",
# "decoyHits": "Most Decoy Grenade hits (Made You Look)",
# "killstreakCarePackageUsed": "Most Care Packages called in (Helping Hand)",
# "molotovKills": "Most Molotov kills (Arsonist)",
# "gasHits": "Most Gas Grenade hits (Gaseous)",
# "comebackKills": "Most comebacks (Rally)",
# "lmgHeadshots": "Most LMG headshots (LMG Expert)",
# "smgDeaths": "Most SMG deaths (Run and Gunned)",
# "carrierKills": "Most kills as carrier (Carrier)",
# "deployableCoverUsed": "Most Deployable Covers used (Combat Engineer)",
# "thermiteKills": "Most Thermite kills (Red Iron)",
# "arKills": "Most assault rifle kills (AR Specialist)",
# "c4Kills": "Most C4 kills (Handle With Care)",
# "suicides": "Most suicides (Accident Prone)",
# "clutch": "Most kills as the last alive (Clutched)",
# "survivorKills": "Most kills as survivor (Survivalist)",
# "killstreakGunshipKills": "Most Gunship kills (Death From Above)",
# "timeSpentAsPassenger": "Most time spent as a passenger (Navigator)",
# "returns": "Most flags returned (Flag Returner)",
# "smgHeadshots": "Most SMG headshots (SMG Expert)",
# "launcherDeaths": "Most launcher deaths (Fubar)",
# "oneShotOneKills": "Most one shot kills (One Shot Kill)",
# "ammoBoxUsed": "Most Munitions Boxes used (Provider)",
# #"spawnSelectSquad": "",
# "weaponPickups": "Most picked up weapons (Loaner)",
# "pointBlankKills": "Most point blank kills (Personal Space)",
# "tagsCaptured": "Collected the most tags (Confirmed Kills)",
# "killstreakGroundKills": "Most ground based killstreak kills (Ground Control)",
# "distanceTraveledInVehicle": "Longest distance travelled in a vehicle (Cross Country)",
# "longestLife": "Longest life (Lifer)",
# "stunHits": "Most Stun Grenade hits (Stunner)",
# "spawnSelectFlag": "Most FOB Spawns (Objective Focused)", # Unsure
# "shotgunHeadshots": "Most Shotgun headshots (Boomstick)",
# "bombDefused": "Most defuses (Defuser)",
# "snapshotHits": "Most Snapshot Grenade hits (Photographer)",
# "noKillsWithDeath": "No kills with at least 1 death (Participant)",
# "killstreakAUAVAssists": "Most Advanced UAV assists (Target Rich Environment)",
# "killstreakPersonalUAVKills": "Most kills with a Personal Radar active (Nothing Personal)",
# "tacticalInsertionSpawns": "Most Tactical Insertions used (Revenant)",
# "launcherKills": "Most Launcher kills (Explosive)",
# "spawnSelectVehicle": "Most vehicle spawns (Oscar Mike)",
# "mostKillsLeastDeaths": "Most kills and fewest deaths (MVP)",
# "mostKills": "Most kills (The Feared)",
# "defends": "Most defend kills (Defense)",
# "timeSpentAsDriver": "Most time spent driving (Driver)",
# "": "" # WIP - Still adding more
}
# Initiating the API class
api = API()
COOKIE_FILE = 'cookie.txt'
@ -383,7 +23,7 @@ def save_to_file(data, filename, dir_name='stats'):
with open(os.path.join(dir_name, filename), 'w') as json_file:
json.dump(data, json_file, indent=4)
def get_and_save_data(player_name=None, all_stats=False, season_loot=False, identities=False, maps=False):
def get_and_save_data(player_name=None, all_stats=False, season_loot=False, identities=False, maps=False, info=False, friendFeed=False, eventFeed=False, cod_points=False, connected_accounts=False, settings=False):
# Create the stats directory if it doesn't exist
DIR_NAME = 'stats'
if not os.path.exists(DIR_NAME):
@ -398,6 +38,12 @@ def get_and_save_data(player_name=None, all_stats=False, season_loot=False, iden
with open(COOKIE_FILE, 'w') as f:
f.write(api_key)
# # Check if userInfo.json exists, create it if it doesn't
USER_INFO_FILE = os.path.join('userInfo.json')
# if not os.path.exists(USER_INFO_FILE):
# with open(USER_INFO_FILE, 'w') as f:
# pass # Creates an empty file
# If player_name is not provided via command line, get it from user input
if not player_name:
player_name = input("Please enter the player's username (with #1234567): ")
@ -407,20 +53,42 @@ def get_and_save_data(player_name=None, all_stats=False, season_loot=False, iden
# Retrieve data from API
# First, determine if any specific optional arguments were given
if not (all_stats or season_loot or identities or maps):
if not (all_stats or season_loot or identities or maps or info or friendFeed or eventFeed or cod_points or connected_accounts or settings):
# If no specific optional arguments are given, then default behavior:
player_stats = api.ModernWarfare.fullData(platforms.Activision, player_name)
match_info = api.ModernWarfare.combatHistory(platforms.Activision, player_name)
save_to_file(player_stats, 'stats.json')
save_to_file(match_info, 'match_info.json')
elif all_stats:
# If the all_stats argument is given:
elif all_stats: # If the all_stats argument is given:
if os.path.exists(USER_INFO_FILE): # Check if the userInfo.json file exists
player_stats = api.ModernWarfare.fullData(platforms.Activision, player_name)
match_info = api.ModernWarfare.combatHistory(platforms.Activision, player_name)
season_loot_data = api.ModernWarfare.seasonLoot(platforms.Activision, player_name)
identities_data = api.Me.loggedInIdentities()
map_list = api.ModernWarfare.mapList(platforms.Activision)
info = api.Me.info()
friendFeed = api.Me.friendFeed()
eventFeed = api.Me.eventFeed()
cod_points = api.Me.codPoints()
connectedAccounts = api.Me.connectedAccounts()
settings = api.Me.settings()
save_to_file(player_stats, 'stats.json')
save_to_file(match_info, 'match_info.json')
save_to_file(season_loot_data, 'season_loot.json')
save_to_file(map_list, 'map_list.json')
save_to_file(identities_data, 'identities.json')
save_to_file(info, 'info.json')
save_to_file(friendFeed, 'friendFeed.json')
save_to_file(eventFeed, 'eventFeed.json')
save_to_file(cod_points, 'cp.json')
save_to_file(connectedAccounts, 'connectedAccounts.json')
save_to_file(settings, 'settings.json')
else:
player_stats = api.ModernWarfare.fullData(platforms.Activision, player_name)
match_info = api.ModernWarfare.combatHistory(platforms.Activision, player_name)
season_loot_data = api.ModernWarfare.seasonLoot(platforms.Activision, player_name)
map_list = api.ModernWarfare.mapList(platforms.Activision)
identities_data = api.Me.loggedInIdentities()
map_list = api.ModernWarfare.mapList(platforms.Activision)
save_to_file(player_stats, 'stats.json')
save_to_file(match_info, 'match_info.json')
save_to_file(season_loot_data, 'season_loot.json')
@ -438,23 +106,58 @@ def get_and_save_data(player_name=None, all_stats=False, season_loot=False, iden
map_list = api.ModernWarfare.mapList(platforms.Activision)
save_to_file(map_list, 'map_list.json')
if info:
info = api.Me.info()
save_to_file(info, 'info.json')
if friendFeed:
friendFeed = api.Me.friendFeed()
save_to_file(friendFeed, 'friendFeed.json')
if eventFeed:
eventFeed = api.Me.eventFeed()
save_to_file(eventFeed, 'eventFeed.json')
if cod_points:
cod_points = api.Me.codPoints()
save_to_file(cod_points, 'cp.json')
if connected_accounts:
connectedAccounts = api.Me.connectedAccounts()
save_to_file(connectedAccounts, 'connectedAccounts.json')
if settings:
settings = api.Me.settings()
save_to_file(settings, 'settings.json')
# Save results to a JSON file inside the stats directory
def recursive_key_replace(obj, replacements):
def recursive_key_replace(obj):
if isinstance(obj, dict):
new_obj = {}
for key, value in obj.items():
new_key = replacements.get(key, key)
if isinstance(value, str):
new_value = replacements.get(value, value)
new_obj[new_key] = recursive_key_replace(new_value, replacements)
new_obj[new_key] = recursive_key_replace(new_value)
else:
new_obj[new_key] = recursive_key_replace(value, replacements)
new_obj[new_key] = recursive_key_replace(value)
return new_obj
elif isinstance(obj, list):
return [recursive_key_replace(item, replacements) for item in obj]
return [recursive_key_replace(item) for item in obj]
else:
return replacements.get(obj, obj) if isinstance(obj, str) else obj
def clean_json_files(*filenames, dir_name='stats'):
regex_pattern = r'<span class="|</span>|">|mp-stat-items|kills-value|headshots-value|username|game-mode|kdr-value'
replace = ''
for filename in filenames:
file_path = os.path.join(dir_name, filename)
if os.path.exists(file_path):
with open(file_path, 'r') as file:
content = file.read()
modified_content = re.sub(regex_pattern, replace, content)
with open(file_path, 'w') as file:
file.write(modified_content)
print(f"Cleaned {filename}.")
else:
print(f"{filename} does not exist, skipping.")
def sort_data(data):
if isinstance(data, dict):
for key, value in data.items():
@ -558,7 +261,7 @@ def beautify_data():
with open(file_path, 'r') as file:
data = json.load(file)
replace_time_and_duration_recursive(data)
data = recursive_key_replace(data, replacements)
data = recursive_key_replace(data)
data = sort_data(data)
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)
@ -569,7 +272,7 @@ def beautify_match_data():
with open(file_path, 'r') as file:
data = json.load(file)
replace_time_and_duration_recursive(data)
data = recursive_key_replace(data, replacements)
data = recursive_key_replace(data)
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)
print(f"Keys replaced in {file_path}.")
@ -639,14 +342,22 @@ def main():
group_data.add_argument("-p", "--player_name", type=str, help="Player's username (with #1234567)")
group_data.add_argument("-a", "--all_stats", action="store_true", help="Fetch all the different types of stats data")
group_data.add_argument("-sl", "--season_loot", action="store_true", help="Fetch only the season loot data")
group_data.add_argument("-i", "--identities", action="store_true", help="Fetch only the logged-in identities data")
group_data.add_argument("-id", "--identities", action="store_true", help="Fetch only the logged-in identities data")
group_data.add_argument("-m", "--maps", action="store_true", help="Fetch only the map list data")
group_data.add_argument("-i", "--info", action="store_true", help="Fetch only general information")
group_data.add_argument("-f", "--friendFeed", action="store_true", help="Fetch only your friend feed")
group_data.add_argument("-e", "--eventFeed", action="store_true", help="Fetch only your event feed")
group_data.add_argument("-cp", "--cod_points", action="store_true", help="Fetch only your COD Point balance")
group_data.add_argument("-ca", "--connected_accounts", action="store_true", help="Fetch only the map list data")
group_data.add_argument("-s", "--settings", action="store_true", help="Fetch only your account settings")
# Add arguments for Cleaning Options
group_cleaning.add_argument("-c", "--clean", action="store_true", help="Beautify all data")
group_cleaning.add_argument("-sm", "--split_matches", action="store_true", help="Split the matches into separate JSON files within the 'matches' subfolder")
group_cleaning.add_argument("-csd", "--clean_stats_data", action="store_true", help="Beautify the data and convert to human-readable strings in stats.json")
group_cleaning.add_argument("-cmd", "--clean_match_data", action="store_true", help="Beautify the match data and convert to human-readable strings in match_info.json")
group_cleaning.add_argument("-cff", "--clean_friend_feed", action="store_true", help="Clean the friend feed data")
group_cleaning.add_argument("-cef", "--clean_event_feed", action="store_true", help="Clean the event feed data")
args = parser.parse_args()
@ -669,8 +380,13 @@ def main():
elif args.clean:
beautify_data()
beautify_match_data()
clean_json_files('friendFeed.json', 'eventFeed.json')
elif args.clean_friend_feed:
clean_json_files('friendFeed.json')
elif args.clean_event_feed:
clean_json_files('eventFeed.json')
else:
get_and_save_data(args.player_name, args.all_stats, args.season_loot, args.identities, args.maps)
get_and_save_data(args.player_name, args.all_stats, args.season_loot, args.identities, args.maps, args.info, args.friendFeed, args.eventFeed, args.cod_points, args.connected_accounts, args.settings)
if __name__ == "__main__":
main()