Code Monkey home page Code Monkey logo

callofduty.py's People

Contributors

ethanc avatar tustin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

callofduty.py's Issues

Trying to retrieve match details gives "200 - The specified key does not exist."

Summary

When trying to retrieve match details, either via client.GetPlayerMatches and then trying to get results[0].details(), or directly via client.GetMatchDetails with a specified match ID, the function fails with an "HTTP 200 - The specified key does not exist" error.

I wanted to check to make sure I'm using this right, or if there's some nuance I'm missing as well.

Reproduction Steps

After setting up the login as presented on the main page, I did:

    matches = await client.GetPlayerMatches(platform=Platform.BattleNet, username="stermy#2796", title=Title.ModernWarfare, mode=Mode.Warzone)
    pprint.pprint(await matches[0].details())

which successfully gets a list of matches but fails to get the details for any of the matches,

or

    match = await client.GetMatchDetails(title=Title.ModernWarfare, platform=Platform.BattleNet, matchId=14719957774703801961)
    pprint.pprint(match)

which fails to get the details for any of the matches.

Expected Results

I expected to be able to find data relating to the match from calling either .details() or from using GetMatchDetails.

Actual Results

Traceback (most recent call last):
  File ".\cod_api_test.py", line 39, in <module>
    asyncio.get_event_loop().run_until_complete(main())
  File "c:\users\<username removed>\scoop\apps\python\3.8.3\lib\asyncio\base_events.py", line 616, in run_until_complete
    return future.result()
  File ".\cod_api_test.py", line 34, in main
    pprint.pprint(await matches[0].details())
  File "C:\Users\<username removed>\.virtualenvs\warzone20200618_stats-pkWWEdsn\lib\site-packages\callofduty\match.py", line 58, in details
    return await self._client.GetMatchDetails(self.title, self.platform, self.id)
  File "C:\Users\<username removed>\.virtualenvs\warzone20200618_stats-pkWWEdsn\lib\site-packages\callofduty\client.py", line 799, in GetMatchDetails
    return (await self.http.GetMatch(title.value, platform.value, matchId))["data"]
  File "C:\Users\<username removed>\.virtualenvs\warzone20200618_stats-pkWWEdsn\lib\site-packages\callofduty\http.py", line 263, in GetMatch
    return await self.Send(
  File "C:\Users\<username removed>\.virtualenvs\warzone20200618_stats-pkWWEdsn\lib\site-packages\callofduty\http.py", line 113, in Send
    raise HTTPException(res.status_code, data)
callofduty.errors.HTTPException: HTTP 200 - The specified key does not exist. (Service: Amazon S3; Status Code: 404; Error Code: NoSuchKey; Request ID: 6C8C445C3C151A37; S3 Extended Request ID: dsJ8YDIwQ10wDJD9srbyYHKEnk78MzK5txEJCf/ZwXQrEB0GcdcbUHgd0hXyMefq5E1fbSmNd60=)

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

No module named callofduty.__main__; 'callofduty' is a package and cannot be directly executed

pip show callofduty.py gives:

Name: callofduty.py
Version: 1.2.1
Summary: CallofDuty.py is an asynchronous, object-oriented Python wrapper for the Call of Duty API.
Home-page: https://github.com/EthanC/CallofDuty.py
Author: EthanC
Author-email: [email protected]
License: MIT
Location: (path removed due to presence of some usernames and stuff)
Requires: httpx
Required-by:

Python version 3.8.3, running on Windows 10.

Zero index "me = results[1]" in main func

This is awesome! Just downloaded and playing around with it.

Your example in README.md selects the second result in a zero indexed list. Should be results[0] unless there are more of us out there!

async def main():
    client = await callofduty.Login("[email protected]", "YourPassword")

    results = await client.SearchPlayers(Platform.Activision, "Captain Price", limit=3)
    for player in results:
        print(f"{player.username} ({player.platform.name})")

   
    #me = results[1] <-- change to [0]
    me = results[0]
    profile = await me.profile(Title.ModernWarfare, Mode.Multiplayer)

    level = profile["level"]
    kd = profile["lifetime"]["all"]["properties"]["kdRatio"]
    wl = profile["lifetime"]["all"]["properties"]["wlRatio"]

    print(f"\n{me.username} ({me.platform.name})")
    print(f"Level: {level}, K/D Ratio: {kd}, W/L Ratio: {wl}")

    await client.Logout()

Private match data

Hi, is it in the pipe to fetch my private matches data from the CoD API?
I tried to find the attribute in this package with no result. Perhaps it's a limitation from the official API, or potential feature for your module?

Add Friend Request, Removal, and Relationship Functionality

LastWin or getWin - Help needed

The Problem

I'm currently trying to find a way to build a loop to find when player get a win on warzone
It's just a funny idea to receive a notification when one of my friends win.

The Ideal Solution

I thought about running the code every 5 minutes so I won't miss a game.
I'm trying to compare the team placement in match details but I'm lost with the whole dictionnary.

The Current Solution

Get the last match of a player than trying to match the key value

Summary

Feel free to send me a private message on discord at thebabeu#1709

RemoveFriend error - Not permitted: not authenticated

Summary

When i try to use the function RemoveFriend on a friend from my friend list, it returns:

callofduty.errors.HTTPException: HTTP 200 - Not permitted: not authenticated

I dont think this is a library problem... maybe this endpoint doesn't work anymore?

Reproduction Steps

Add this piece of code on the test.py, to try to remove the first friend of user list

    friends = await client.GetMyFriends()
    for friend in friends:
        print(f"{friend.platform.name}: {friend.username} ({friend.accountId}), Online? {friend.online}")
        await client.RemoveFriend(friend.accountId)
        break

Expected Results

A friend removed without an error

Actual Results

Traceback (most recent call last):
  File "test.py", line 266, in <module>
    asyncio.get_event_loop().run_until_complete(main())
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "test.py", line 33, in main
    await client.RemoveFriend(friend.accountId)
  File "/home/fabriciols/joysticket/CallofDuty.py/callofduty/client.py", line 1250, in RemoveFriend
    return (await self.http.RemoveFriend(accountId))["data"]
  File "/home/fabriciols/joysticket/CallofDuty.py/callofduty/http.py", line 348, in RemoveFriend
    return await self.Send(
  File "/home/fabriciols/joysticket/CallofDuty.py/callofduty/http.py", line 108, in Send
    if isinstance(data, dict):
callofduty.errors.HTTPException: HTTP 200 - Not permitted: not authenticated

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

Python 3.8.2 (default, Jul 16 2020, 14:00:26)
callofduty.py version = "1.2.1"

Warzone Match Stats Functionality

The Problem

I want to be able to get a players matches from Warzone and look at their match data the same way I would look at multiplayer matches in order to do some analysis on them.

The Ideal Solution

The ideal solution would be to add functionality for the match object if the Mode is Warzone.

The Current Solution

Summary

Not working GetPlayerMatches for Warzone mode

Summary

Function GetPlayerMatches not working if the mode is Mode.Warzone

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

Example of the code i tried

myCodID = 'gianluca2414#5528619'

client = await callofduty.Login("emailActivison", "PASSWORD")

match = (await client.GetPlayerMatches(Platform.Activision, myCodID, Title.ModernWarfare, Mode.Warzone, limit=1))[0]
teams = await match.teams()
print(teams)

Traceback

Traceback (most recent call last):
File "Test.py", line 30, in
asyncio.get_event_loop().run_until_complete(main())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "Test.py", line 20, in main
match = (await client.GetPlayerMatches(Platform.Activision, "Yeah#8649242", Title.ModernWarfare, Mode.MultiPlayer, limit=3))[0]
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/enum.py", line 341, in getattr
raise AttributeError(name) from None
AttributeError: MultiPlayer
GIALLU:python3 Test.py
[[, , , , ]]
GIALLU:clear

GIALLU:python3 Test.py
Traceback (most recent call last):
File "Test.py", line 30, in
asyncio.get_event_loop().run_until_complete(main())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "Test.py", line 21, in main
teams = await match.teams()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/callofduty/match.py", line 46, in teams
return await self._client.GetMatchTeams(self.title, self.platform, self.id)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/callofduty/client.py", line 827, in GetMatchTeams
data: dict = (await self.http.GetMatch(title.value, platform.value, matchId))[
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/callofduty/http.py", line 263, in GetMatch
return await self.Send(
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/callofduty/http.py", line 127, in Send
raise HTTPException(res.status_code, data)
callofduty.errors.HTTPException: HTTP 500 - 

<link href="/cod/error/css/error-404.css" rel="stylesheet" type="text/css"/>
<title>Call of Duty&reg; - Unexpected Error</title>
<script src="//assets.adobedtm.com/386a89de09ad0d27406fc28432e1961e4a099d4c/satelliteLib-695194b1db0dbf3c812ec72d3f11a95f199cedc1.js"></script>
<div style="height: 40px">
</div>
<script> var digitalData = {"page": { "pageInfo": { "charSet": "UTF-8", "language": "EN", "country": "US", "site": "callofduty", "referPageURL": window.location.href }, "pageCategory": { "pageType": "errorPage" } }}; </script> <script type="text/javascript">_satellite.pageBottom();</script>

Add Friend Feed Reaction and Favorite Functionality

Python 3.9 and Windows 7

Is python 3.9 absolutely required ? Because this version of python can't be installed on windows 7 .. Python 3.8 would be great.

Loadout URL Bad

The loadout URL that is provided was removed from COD servers weeks ago, Can we get that updated?

Not authenticated when getting profile

Summary

When making a request to get a players profile, the request is rejected with Not permitted: not authenticated.

If I make a request to /api/papi-client/stats/cod/v1/title/mw/platform/uno/gamer/<username>/profile/type/wz in the browser, the request returns successfully.

The profiles I'm attempting to fetch are my own, and people on my friends list.

Potentially related: #43. I've raised this because it's a different endpoint. If it's the same issue, this can be closed.

Reproduction Steps

import asyncio
import json

import callofduty


async def main():
    client = await callofduty.Login("username", "password")

    player = await client.GetPlayer(callofduty.Platform.Activision, "username")

    profile = await player.profile(callofduty.Title.ModernWarfare, callofduty.Mode.Warzone)

    print(f"{profile['type']}")


asyncio.get_event_loop().run_until_complete(main())

Actual Results

Traceback (most recent call last):
  File "/mnt/c/Users/Alex/repos/statman/test.py", line 20, in <module>
    asyncio.get_event_loop().run_until_complete(main())
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/mnt/c/Users/Alex/repos/statman/test.py", line 15, in main
    profile = await player.profile(callofduty.Title.ModernWarfare, callofduty.Mode.Warzone)
  File "/home/alex/.cache/pypoetry/virtualenvs/statman-OzLc5na7-py3.9/lib/python3.9/site-packages/callofduty/player.py", line 62, in profile
    return await self._client.GetPlayerProfile(
  File "/home/alex/.cache/pypoetry/virtualenvs/statman-OzLc5na7-py3.9/lib/python3.9/site-packages/callofduty/client.py", line 595, in GetPlayerProfile
    await self.http.GetPlayerProfile(
  File "/home/alex/.cache/pypoetry/virtualenvs/statman-OzLc5na7-py3.9/lib/python3.9/site-packages/callofduty/http.py", line 219, in GetPlayerProfile
    return await self.Send(
  File "/home/alex/.cache/pypoetry/virtualenvs/statman-OzLc5na7-py3.9/lib/python3.9/site-packages/callofduty/http.py", line 113, in Send
    raise HTTPException(res.status_code, data)
callofduty.errors.HTTPException: HTTP 200 - Not permitted: not authenticated

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

Version: 1.2.2
Python: 3.9

Add Create-a-Class Functionality

I get an error when trying to get a matches info (teams/details)

Summary

Upon getting the teams or details of a match, I always get [WinError 10054] An existing connection was forcibly closed by the remote host. This occurs for any match.

Reproduction Steps

from callofduty import Login, Platform, Title, Mode

client = await Login("email", "password"))
match = await client.GetPlayerMatches(Platform.BattleNet, 
                                      "Slvercyclops#1984", 
                                      Title.ModernWarfare, 
                                      Mode.Warzone,
                                      limit=5)[0]
print(await match.details())

Expected Results

A dict with the data should have been returned,

Actual Results

The error [WinError 10054] An existing connection was forcibly closed by the remote host returned instead.
I have tried turning off my firewall/antivirus but this does not stop the error.

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

Python 3.9.1
Callofduty.py 1.2.2
Windows 10 Version 10.0.19042 Build 19042

Match Details request fails

Summary

A request for Match Details fails on the API side

Reproduction Steps

Log in, find my own profile, look at my matches, and get details

myself = await client.GetPlayer(Platform.Activision, "xxx#123")
matches = await myself.matches(Title.ModernWarfare, Mode.Warzone)
for match in matches:
match_details = await match.details()

Expected Results

I expect to see details of the match

Actual Results

The API itself fails

File "/home/me/warzone.py", line 30, in main
match_details = await match.details()
File "/home/me/venv/lib/python3.9/site-packages/callofduty/match.py", line 58, in details
return await self._client.GetMatchDetails(self.title, self.platform, self.id)
File "/home/me/venv/lib/python3.9/site-packages/callofduty/client.py", line 840, in GetMatchDetails
return (await self.http.GetMatch(title.value, platform.value, matchId))["data"]
File "/home/me/venv/lib/python3.9/site-packages/callofduty/http.py", line 263, in GetMatch
return await self.Send(
File "/home/me/venv/lib/python3.9/site-packages/callofduty/http.py", line 113, in Send
raise HTTPException(res.status_code, data)
callofduty.errors.HTTPException: HTTP 200 - The specified key does not exist. (Service: Amazon S3; Status Code: 404; Error Code: NoSuchKey; Request ID: 12345; S3 Extended Request ID: abcdefg)

Looks like they are using some S3 bucket behind the API, but whatever the endpoint is doing is failing to look that up... not sure if this is because something changed in the API or the library needs to use a different endpoint or something. I don't see any documentation from Activision to try to decipher what request it should be making.

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

python -m callofduty -v
/home/me/venv/bin/python: No module named callofduty.main; 'callofduty' is a package and cannot be directly executed

Python 3.9.0
callofduty.py==1.2.2

Fedora 33

Not authenticated despite i have inserted my password and email

Summary

Reproduction Steps

It's the code from the readme

import asyncio

import callofduty
from callofduty import Mode, Platform, Title


async def main():
    client = await callofduty.Login("[email protected]", "YourPassword")

    results = await client.SearchPlayers(Platform.Activision, "Captain Price", limit=3)
    for player in results:
        print(f"{player.username} ({player.platform.name})")

    me = results[1]
    profile = await me.profile(Title.ModernWarfare, Mode.Multiplayer)

    level = profile["level"]
    kd = profile["lifetime"]["all"]["properties"]["kdRatio"]
    wl = profile["lifetime"]["all"]["properties"]["wlRatio"]

    print(f"\n{me.username} ({me.platform.name})")
    print(f"Level: {level}, K/D Ratio: {kd}, W/L Ratio: {wl}")

asyncio.get_event_loop().run_until_complete(main())

Actual Results

Traceback (most recent call last):
  File "<input>", line 24, in <module>
  File "C:\Program Files\Python39\lib\asyncio\base_events.py", line 642, in run_until_complete
    return future.result()
  File "<input>", line 15, in main
  File "C:\Users\Stefano\Desktop\cod_39\lib\site-packages\callofduty\player.py", line 62, in profile
    return await self._client.GetPlayerProfile(
  File "C:\Users\Stefano\Desktop\cod_39\lib\site-packages\callofduty\client.py", line 595, in GetPlayerProfile
    await self.http.GetPlayerProfile(
  File "C:\Users\Stefano\Desktop\cod_39\lib\site-packages\callofduty\http.py", line 219, in GetPlayerProfile
    return await self.Send(
  File "C:\Users\Stefano\Desktop\cod_39\lib\site-packages\callofduty\http.py", line 113, in Send
    raise HTTPException(res.status_code, data)
callofduty.errors.HTTPException: HTTP 200 - Not permitted: not allowed

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

callofduty.py version 1.2.2

Add Tests

CallofDuty.py will feature both Unit and Integration Tests, the former of which will ideally be ran after each commit using GitHub Actions or Travis CI.

While Unit Tests will be ran quickly as no network requests will be made, Integration Tests will exercise the entire CallofDuty.py library against the live Call of Duty API.

Readme Code doesn't work?

Hey,

I used the readme code - added my login username and password. Then I replaced the "Captain Price" with my own Activision ID (ZB3#3180831) but I get this error.

Traceback (most recent call last):
File "/Users/zafarb/PycharmProjects/TrackerAPI/API.py", line 27, in
asyncio.get_event_loop().run_until_complete(main())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "/Users/zafarb/PycharmProjects/TrackerAPI/API.py", line 15, in main
me = results[1]
IndexError: list index out of range
ZB3#3180831 (Activision)

Process finished with exit code 1

API changes for friends list

Summary

The API endpoint used for;

async def GetMyFriends(self) -> Union[dict, list, str]:
        return await self.Send(
            Request("GET", "api/papi-client/codfriends/v1/compendium")
        )

is returning a 301 Moved Permanently response.

Reproduction Steps

await client.GetMyFriends()

Expected Results

Friends list returned

Actual Results

python     | DEBUG:httpx._client:HTTP Request: POST https://profile.callofduty.com/cod/mapp/login "HTTP/1.1 200 OK"
python     | DEBUG:httpx._client:HTTP Request: GET https://callofduty.com/api/papi-client/codfriends/v1/compendium "HTTP/1.1 301 Moved Permanently"
python     | WARNING:root:ERROR RUNNING UPDATE
python     | WARNING:root:

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

callofduty.py-1.2.1-py3-none-any.whl

Crash on clients.py when searching for a player

When running the example from README.md the library crashes. It seems that the function SearchPlayers from Client class is expecting a return of type dictionary from the function SearchPlayers from HTTP class but a string is being returned instead and thus getting a value with the key data isn't possible.

Traceback (most recent call last):
  File "/path/to/project/bot/playground.py", line 10, in main
    results = await client.SearchPlayers(Platform.Activision, "Captain Price", limit=3)
  File "/path/to/project/venv3.8/lib/python3.8/site-packages/callofduty/client.py", line 551, in SearchPlayers
    data: dict = (await self.http.SearchPlayer(platform.value, username))["data"]
TypeError: string indices must be integers

The value being returned by self.http.SearchPlayer(platform.value, username) is

'{"status":"success","data":[{"platform":"uno","username":"captain price","accountId":"10976285","avatar":null},{"platform":"uno","username":"Captain Price#1183498","accountId":"1324429271582812843","avatar":null},{"platform":"uno","username":"Captain Price#1894839","accountId":"1324684011875463063","avatar":null},{"platform":"uno","username":"Captain Price#2049033","accountId":"8890642","avatar":null},{"platform":"uno","username":"Captain Price#2051998","accountId":"4862470","avatar":null},{"platform":"uno","username":"Captain Price#2737932","accountId":"17897478","avatar":null},{"platform":"uno","username":"Captain Price#3877870","accountId":"17258425793960667868","avatar":null},{"platform":"uno","username":"Captain Price#4186334","accountId":"12959940","avatar":null},{"platform":"uno","username":"Captain Price#4201762","accountId":"1324370735821307157","avatar":null},{"platform":"uno","username":"Captain Price#4862224","accountId":"14700140","avatar":null},{"platform":"uno","username":"Captain Price#5121243","accountId":"20537284","avatar":null},{"platform":"uno","username":"Captain Price#5290091","accountId":"16571244","avatar":null},{"platform":"uno","username":"Captain Price#5922773","accountId":"1324883696992576368","avatar":null},{"platform":"uno","username":"Captain Price#6234889","accountId":"19114688","avatar":null},{"platform":"uno","username":"Captain Price#6670917","accountId":"12107540","avatar":null},{"platform":"uno","username":"Captain Price#7489986","accountId":"19548698","avatar":null},{"platform":"uno","username":"Captain Price#8556707","accountId":"7466647","avatar":null},{"platform":"uno","username":"Captain Price#8720947","accountId":"18787540","avatar":null},{"platform":"uno","username":"Captain Price#8954774","accountId":"15911924","avatar":null},{"platform":"uno","username":"Captain Price#9140400","accountId":"17566006","avatar":null},{"platform":"uno","username":"Captain Price#9270875","accountId":"4318036","avatar":null}]}'

System info:

  • MacOS 10.15.4
  • Python 3.8 (installed via Homebrew) with clean virtual environment

Add Get Killfeed From Match request

The Problem

I want to identify if I was killed by a cheater or if I killed a cheater.
I want to identify if I was killed by a Pro player or killed by a Pro player.

The Ideal Solution

There must be a helper class which already has the api request defined just like the others.

The Current Solution

???

Summary

I want to be able to retrieve the kill feed for a match so I can see if a cheater killed me.

SSL error: application data after close_notify

Summary

When using ayncio.gather on GetFullMatch, I get an SSL error. I think this is a python core issue (https://bugs.python.org/issue39951) but I'm hoping someone here might be able to recommend a work around.

Reproduction Steps

This snippet should be able to reproduce the error:

import callofduty as cod
import asyncio

async def main():
    id = 'BattleNetID of interest'
    client = await cod.Login("UserName", "Password")
    player = await client.GetPlayer(cod.Platform.BattleNet, id)
    matches = await player.matches(cod.Title.ModernWarfare, cod.Mode.Warzone)
    match_details = await asyncio.gather(*(client.GetFullMatch(cod.Platform.BattleNet, cod.Title.ModernWarfare, cod.Mode.Warzone, match.id) for match in matches))
asyncio.run(main())

I tried it using Activision instead of BattleNet as the platform and still got the error.

Expected Results

I expected the asyncio.gather call to return a list of match-details dictionaries.

Actual Results

Traceback (most recent call last):
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_exceptions.py", line 326, in map_exceptions
    yield
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_client.py", line 1502, in _send_single_request
    (status_code, headers, stream, ext,) = await transport.arequest(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_async/connection_pool.py", line 218, in arequest
    response = await connection.arequest(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_async/connection.py", line 106, in arequest
    return await self.connection.arequest(method, url, headers, stream, ext)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_async/http11.py", line 72, in arequest
    ) = await self._receive_response(timeout)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_async/http11.py", line 133, in _receive_response
    event = await self._receive_event(timeout)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_async/http11.py", line 172, in _receive_event
    data = await self.socket.read(self.READ_NUM_BYTES, timeout)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_backends/asyncio.py", line 153, in read
    raise
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/contextlib.py", line 135, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpcore/_exceptions.py", line 12, in map_exceptions
    raise to_exc(exc) from None
httpcore.ReadError: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2744)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/mnt/c/Users/mlapp/CodStats/bug.py", line 51, in <module>
    asyncio.run(main())
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/mnt/c/Users/mlapp/CodStats/bug.py", line 14, in main
    match_details = await asyncio.gather(*(client.GetFullMatch(cod.Platform.BattleNet, cod.Title.ModernWarfare, cod.Mode.Warzone, match.id) for match in matches))
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/callofduty/client.py", line 662, in GetFullMatch
    await self.http.GetFullMatch(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/callofduty/http.py", line 273, in GetFullMatch
    return await self.Send(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/callofduty/http.py", line 103, in Send
    res: Response = await client.request(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_client.py", line 1371, in request
    response = await self.send(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_client.py", line 1406, in send
    response = await self._send_handling_auth(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_client.py", line 1444, in _send_handling_auth
    response = await self._send_handling_redirects(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_client.py", line 1476, in _send_handling_redirects
    response = await self._send_single_request(request, timeout)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_client.py", line 1502, in _send_single_request
    (status_code, headers, stream, ext,) = await transport.arequest(
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/contextlib.py", line 135, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/matt/miniconda3/envs/cod/lib/python3.9/site-packages/httpx/_exceptions.py", line 343, in map_exceptions
    raise mapped_exc(message, **kwargs) from exc  # type: ignore
httpx.ReadError: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2744)

Process finished with exit code 1

Checklist

  • [x ] I have searched the open Issues for duplicates
  • [x ] I have shown the entire traceback, if possible
  • [x ] I have removed my token from display, if visible

System Information

This command python -m callofduty -v didn't work for me but I am using python 3.9, callofduty.py v1.2.2 on WSL Ubuntu with up to date packages.

Match History Limit

Summary

Match history only seems to return last 20 results, yet cod.tracker.gg is able to pull everything?

I've tried looking at the url manually and the result is the same. So how does tracker network manage this?

https://www.callofduty.com/api/papi-client/crm/cod/v2/title/mw/platform/uno/gamer/hazzeh/matches/mp/start/0/end/0/details

Setting the startTimestamp and endTimestamp in milliseconds seems to do be the only way to get older matches. Is there a more efficient way of getting them all?

Reproduction Steps

async def main():
    load_dotenv()
    client = await callofduty.Login("[email protected]","password")

    player = await client.GetPlayer(Platform.Activision, "Hazzeh")
    matches = (await player.matches(Title.ModernWarfare, Mode.Multiplayer, limit=100))
    for match in matches:
        print(match.id)


asyncio.get_event_loop().run_until_complete(main())

Expected Results

A list of 100 matches, or as many matches as has been played by the player.

Actual Results

A list of just 20 matches.

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

Add friends failing

When I try to add a friend it returns: callofduty.errors.HTTPException: HTTP 200 - Not permitted: not authenticated, even when I'm getting my friend list correctly.

Issue with client.GetPlayerLoadouts

Summary

I'm trying to play with this repo, I have look at the documentation but also loadout.py and client.py. I'm still a beginner with python, feel free to guide me.

Reproduction Steps

async def main():
client = await callofduty.Login("#email", "#password")

loadouts = await client.GetPlayerLoadouts(Platform.Activision,"BeauBrun#4481907",Title.ModernWarfare)

Expected Results

I'm expecting the list of loadouts

Actual Results

File "/usr/local/lib/python3.8/site-packages/callofduty/client.py", line 1128, in GetPlayerLoadouts
for _loadout in data["loadouts"]:

Checklist

I left the Activision ID , this way you would be able to recreate the same test

System Information

Python3.8 and lastest version of Callofduty

Not permitted: not authenticated while trying to login

Summary

Recently, with credentials that always worked, I've started to receive error response from Call of Duty API:

{
  'status': 'error',
  'data': {
    'type': 'com.activision.mt.common.stdtools.exceptions.NoStackTraceException',
    'message': 'Not permitted: not authenticated'
  }
}

With given credentials I'm able to login and browse stats on https://my.callofduty.com/ website or in mobile app. I've also tried many different credentials, always the same error.

This is similar to the issue described in this article from 1 March 2019 https://charlieintel.com/call-of-duty-api-changes-kill-third-party-statistic-tracking-websites-and-services/52576/

Reproduction Steps

async def get_data() -> Report:
    client = await callofduty.Login("", "")
    client.GetMySquad() # or any other call
...

Expected Results

There's no error returned from API and data is received.
or
There's confirmation that the issue only appears in my project.

Actual Results

Snippet described in Reproduction Steps returns error described in Summary

Traceback:

Traceback (most recent call last):
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 2450, in wsgi_app
    response = self.handle_exception(e)
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 1867, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "<project_path>venv/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "<project_path>venv/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "<project_path>venv/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "<project_path>cod_stats/start.py", line 125, in hello_world
    self._py_db_command_thread_event = py_db._py_db_command_thread_event
  File "<project_path>venv/lib/python3.8/site-packages/nest_asyncio.py", line 95, in run_until_complete
    return f.result()
  File "<pyenv_path>versions/3.8.5/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "<pyenv_path>versions/3.8.5/lib/python3.8/asyncio/tasks.py", line 280, in __step
    result = coro.send(None)
  File "<project_path>cod_stats/start.py", line 32, in get_data
    from _pydev_imps._pydev_saved_modules import thread
  File "<project_path>cod_stats/start.py", line 33, in <listcomp>
    from _pydevd_bundle import pydevd_io, pydevd_vm_type
  File "<project_path>venv/lib/python3.8/site-packages/callofduty/client.py", line 595, in GetPlayerProfile
    await self.http.GetPlayerProfile(
  File "<project_path>venv/lib/python3.8/site-packages/callofduty/http.py", line 219, in GetPlayerProfile
    return await self.Send(
  File "<project_path>venv/lib/python3.8/site-packages/callofduty/http.py", line 113, in Send
    raise HTTPException(res.status_code, data)

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

Python version: 3.8.5
callofduty.py: 1.2.1
operating system: tested on macOS and Linux

Warzone matches

Summary

When you try to use the function getMatch on a warzone match the API doesn't work

Reproduction Steps

import asyncio
from callofduty import Mode, Platform, Title


async def main():
    client = await callofduty.Login("[email protected]", "psw")
  
    # match = await client.GetMatch(Title.ModernWarfare, Platform.Activision, 10001)
    # il WILLICO #7036849
    match = await client.GetPlayerMatches(Platform.Activision, "il WILLICO#7036849", Title.ModernWarfare, Mode.Warzone)
    prova1 = await match[1].teams()
    print(type(prova1))

#    await client.Logout()

asyncio.get_event_loop().run_until_complete(main())

Expected Results

I'm expecting the details/teams about the match

Actual Results

it gives an error because it doesn't find the match
Traceback (most recent call last): File "C:\Users\Stefano\Desktop\cod_app\cod_stats.py", line 49, in <module> asyncio.get_event_loop().run_until_complete(main()) File "C:\Program Files\Python39\lib\asyncio\base_events.py", line 642, in run_until_complete return future.result() File "C:\Users\Stefano\Desktop\cod_app\cod_stats.py", line 40, in main prova1 = await match[1].teams() File "C:\Users\Stefano\Desktop\cod_app\lib\site-packages\callofduty\match.py", line 46, in teams return await self._client.GetMatchTeams(self.title, self.platform, self.id) File "C:\Users\Stefano\Desktop\cod_app\lib\site-packages\callofduty\client.py", line 827, in GetMatchTeams data: dict = (await self.http.GetMatch(title.value, platform.value, matchId))[ File "C:\Users\Stefano\Desktop\cod_app\lib\site-packages\callofduty\http.py", line 263, in GetMatch return await self.Send( File "C:\Users\Stefano\Desktop\cod_app\lib\site-packages\callofduty\http.py", line 113, in Send raise HTTPException(res.status_code, data) callofduty.errors.HTTPException: HTTP 200 - The specified key does not exist. (Service: Amazon S3; Status Code: 404; Error Code: NoSuchKey; Request ID: 238D84755E903789; S3 Extended Request ID: u7Sz2H48EHaElwLLiODYniNfkmwfz96ARewkcn59i99t3Eowvi234bmTqpyPN1IIi/XKWxEUJIc=)

The problem is inside http.py at line 266, indeed the get request doesn't work for warzone matches and it works only on multiplayer matches according to this link:
https://documenter.getpostman.com/view/5519582/SzzgAefq#f03a4eeb-58c3-4486-aea2-f59ed692a655

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

python 3.9

Warzone data

I am trying to extract the KD for a list of 80k players. I am not sure how to do that, could you please provide me an example?

For a single player, this is not working:

import asyncio

import callofduty
from callofduty import Mode, Platform, Title

async def main():
client = await callofduty.Login("Mail", "Password")

results = await client.SearchPlayers(Platform.Xbox, "Int183347", limit=3)
for player in results:
    print(f"{player.username} ({player.platform.name})")

me = results[0]
profile = await me.profile(Title.ModernWarfare, Mode.Warzone)

level = profile["level"]
kd = profile["lifetime"]["all"]["properties"]["kdRatio"]

print(f"\n{me.username} ({me.platform.name})")
print(f"Level: {level}, K/D Ratio: {kd}")

await client.Logout()

asyncio.get_event_loop().run_until_complete(main())

Furthermore, I have a list of 600 match ids from which I would like to extract the list of players. Now, this is pretty simple working with json. However, the problem is that I am not able to extract the platform these players are using. Is there a way to extract it from your docker?

Warzone match details gets 404

Summary

If I try to get the details of a Warzone match I get this a 404 and an error

Reproduction Steps

This is my code:

   client = await callofduty.Login(
        os.environ["ATVI_EMAIL"], os.environ["ATVI_PASSWORD"]
    )
    platform = Platform.PlayStation

    title = Title.ModernWarfare

    mode = Mode.Warzone

    matches = await client.GetPlayerMatches(platform=platform, title=title, mode=mode, username="edobounce")

    print(matches)

    match_details = await matches[0].details()

    print(matches)

Expected Results

Details from the Warzone match.

Actual Results

Traceback (most recent call last):
  File "/Users/edoardopedrotti/PycharmProjects/CallofDuty.py/test.py", line 25, in main
    match_details = await matches[0].details()
  File "/Users/edoardopedrotti/PycharmProjects/CallofDuty.py/callofduty/match.py", line 58, in details
    return await self._client.GetMatchDetails(self.title, self.platform, self.id)
  File "/Users/edoardopedrotti/PycharmProjects/CallofDuty.py/callofduty/client.py", line 799, in GetMatchDetails
    return (await self.http.GetMatch(title.value, platform.value, matchId))["data"]
  File "/Users/edoardopedrotti/PycharmProjects/CallofDuty.py/callofduty/http.py", line 263, in GetMatch
    return await self.Send(
  File "/Users/edoardopedrotti/PycharmProjects/CallofDuty.py/callofduty/http.py", line 113, in Send
    raise HTTPException(res.status_code, data)
callofduty.errors.HTTPException: HTTP 200 - The specified key does not exist. (Service: Amazon S3; Status Code: 404; Error Code: NoSuchKey; Request ID: DBD552424A508E1D; S3 Extended Request ID: st37DHPW1CXLiS8egWUCLGDaX56g+cY0HKsG5/pu0py/m+ce4D7x4VlaQrUhYorsvKI8nfsfG0c=)

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

startTimestamp and endTimestamp arguments do not work for matches search

Summary

First up. Great work getting this up and running.
Happy to help out improving where I can.

I'm having an issue with the startTimestamp and endTimestamp parameters on the player.matchesSummary() and player.matches() functions returning empty responses regardless of how far back the parameters go.

API call returns 200 so I suspect it's on their side.
I'm not sure how to check whether the param is available on their API so I haven't done this. Maybe you could point me in the right direction.

Reproduction Steps

import asyncio, json, datetime, callofduty, time
from callofduty import Mode, Platform, Title
from decouple import config

client = await callofduty.Login(USER, PASSWORD)
friends = await client.GetMyFriends()

date_start = datetime.datetime(2000,1,1,0,0)
date_start = int(time.mktime(date_start.timetuple()))

date_end = datetime.datetime(2020,6,23,23, 0)
date_end = int(time.mktime(date_end.timetuple()))

print("date_start: %s | date_end: %s\n" % (date_start, date_end))

me = await client.GetMyAccounts()
matches_with = await me[0].matches(Title.ModernWarfare, Mode.Warzone, startTimestamp=date_start, endTimestamp=date_end)
matches_without = await me[0].matches(Title.ModernWarfare, Mode.Warzone)

print("matches with filters: %s" % (len(matches_with) if matches_with else 0))
print("matches without filters: %s" %len(matches_without))

Expected Results

matches with filters: ???? not zero ???
matches without filters: 10

Actual Results

matches with filters: 0
matches without filters: 10

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

don't think it's needed.
Python 3.8.2
callofduty.py 1.2.1

player.matches get empty list

Summary

Hello, i tried to use player.matches after getPlayer method, but it returns an empty list. I tried to do the same request on postman to /matches endpoint and it returns empty list too. I guess should be used only /matches/details endpoint

Reproduction Steps

look for a player and to player.matches

Expected Results

matches list

Actual Results

Empty list

  • [x ] I have searched the open Issues for duplicates
  • [x ] I have shown the entire traceback, if possible
  • [x ] I have removed my token from display, if visible

Unable to pull down warzone match info

Summary

Attempting to pull down warzone matches results in the following error:

HTTP 200 - The specified key does not exist. (Service: Amazon S3; Status Code: 404; Error Code: NoSuchKey; Request ID: B4ACCB323C5277CF; S3 Extended Request ID: /nxElXneStDr8MdgbBtxLJ+x12n54Hnpt463Qf/TYofGbSTmu8L56coRyhF2yRYUwZPLvCWEZGw=)

Reproduction Steps

async def main():
client = await callofduty.Login("[email protected]", "MyPassword")

results = await client.SearchPlayers(Platform.BattleNet, "MyUser", limit=3)
for player in results:
    print(f"{player.username} ({player.platform.name})")

me = results[0]
matches_list = me.matches(Title.ModernWarfare, Mode.Warzone)
for match in matches_list:
    details = match.details()

Expected Results

match.details to return an object containing match detials

Actual Results

HTTPException Traceback (most recent call last)
in
----> 1 res = await matches_list[0].details()

/mnt/c/Users/Jake/Documents/Python/CallofDuty.py/callofduty/match.py in details(self)
56 """
57
---> 58 return await self._client.GetMatchDetails(self.title, self.platform, self.id)

/mnt/c/Users/Jake/Documents/Python/CallofDuty.py/callofduty/client.py in GetMatchDetails(self, title, platform, matchId)
797 VerifyTitle(title)
798
--> 799 return (await self.http.GetMatch(title.value, platform.value, matchId))["data"]
800
801 async def GetMatchTeams(

/mnt/c/Users/Jake/Documents/Python/CallofDuty.py/callofduty/http.py in GetMatch(self, title, platform, matchId)
264 ) -> Union[dict, list, str]:
265 print( f"api/papi-client/ce/v1/title/{title}/platform/{platform}/match/{matchId}/matchMapEvents")
--> 266 return await self.Send(
267 Request(
268 "GET",

/mnt/c/Users/Jake/Documents/Python/CallofDuty.py/callofduty/http.py in Send(self, req)
111 # The API tends to return HTTP 200 even when an error occurs
112 if status == "error":
--> 113 raise HTTPException(res.status_code, data)
114
115 # HTTP 2XX: Success

HTTPException: HTTP 200 - The specified key does not exist. (Service: Amazon S3; Status Code: 404; Error Code: NoSuchKey; Request ID: 241B52B18B0B084E; S3 Extended Request ID: pu5ky+2yntwdBJamck7onLQdpzdhbVUt44PTmdZvpuTNYyZ5Yy/ExLEPT6STGAdmuZ/WKZqkvJQ=)

Checklist

  • [x ] I have searched the open Issues for duplicates
  • [x ] I have shown the entire traceback, if possible
  • [x ] I have removed my token from display, if visible

System Information

python 3.8, callofduty version 1.2.1

Error: Recaptcha failed, when logging in

Summary

I tried to log in using the API. I made a new Activision account and it didn't work. Then I tried it with my battle.net account (where I have purchased a Call of Duty) and it still didn't work.

Reproduction Steps

I used the same exact code from the readme.md, just with my email and password.
This is the code I used:

import asyncio

import callofduty
from callofduty import Mode, Platform, Title


async def main():
    client = await callofduty.Login("[email protected]", "password")

    results = await client.SearchPlayers(Platform.Activision, "Captain Price", limit=3)
    for player in results:
        print(f"{player.username} ({player.platform.name})")

    me = results[1]
    profile = await me.profile(Title.ModernWarfare, Mode.Multiplayer)

    level = profile["level"]
    kd = profile["lifetime"]["all"]["properties"]["kdRatio"]
    wl = profile["lifetime"]["all"]["properties"]["wlRatio"]

    print(f"\n{me.username} ({me.platform.name})")
    print(f"Level: {level}, K/D Ratio: {kd}, W/L Ratio: {wl}")

asyncio.get_event_loop().run_until_complete(main())

Expected Results

I expected it to print the second player from the search result (as it should).

Actual Results

This happened (Traceback):

Traceback (most recent call last):
  File "/Users/path/to/file.py", line 24, in <module>
    asyncio.get_event_loop().run_until_complete(main())
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/Users/path/to/file.py", line 8, in main
    client = await callofduty.Login("[email protected]", "password")
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/callofduty/auth.py", line 160, in Login
    await auth.SubmitLogin()
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/callofduty/auth.py", line 124, in SubmitLogin
    raise LoginFailure(
callofduty.errors.LoginFailure: Failed to login (HTTP 200), Recaptcha failed.

Checklist

  • I have searched the open Issues for duplicates
  • I have shown the entire traceback, if possible
  • I have removed my token from display, if visible

System Information

python -m callofduty -v didn't work (weirdly). I use python version 3.9 and I use version 1.3.0 of callofduty.py. I installed it via pip.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.