mirror of
https://github.com/Steffo99/steamleaderboards.git
synced 2024-11-21 15:24:18 +00:00
Create package
This commit is contained in:
parent
8e754a43a7
commit
873e820e78
5 changed files with 210 additions and 0 deletions
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Stefano Pigozzi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
32
README.md
Normal file
32
README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# `steamleaderboards`
|
||||||
|
|
||||||
|
A package designed to help developers access the leaderboards of various Steam games.
|
||||||
|
|
||||||
|
It was created with the Isaac Daily Run scoreboards in mind, but it can be used for other games as well.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use `steamleaderboards`, first create a `LeaderboardGroup` for the desired game.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import steamleaderboards as sl
|
||||||
|
lbgroup = sl.LeaderboardGroup(STEAM_APP_ID)
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have created the `LeaderboardGroup`, you can retrieve the desired leaderboards by using the `LeaderboardGroup.get` method.
|
||||||
|
You can specify the name, the display name or the id of the leaderboard to retrieve.
|
||||||
|
|
||||||
|
```python
|
||||||
|
leaderboard_a = lbgroup.get(name=LEADERBOARD_NAME)
|
||||||
|
leaderboard_b = lbgroup.get(lbid=LEADERBOARD_ID)
|
||||||
|
leaderboard_c = lbgroup.get(display_name=LEADERBOARD_DISPLAY_NAME)
|
||||||
|
```
|
||||||
|
|
||||||
|
When you have the `Leaderboard` object, you can find all the entries in the `Leaderboard.entries` field, or you can search for a specific one through the `Leaderboard.find_entry` method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
all_scores = leaderboard_a.entries
|
||||||
|
my_score = leaderboard_a.find_entry(MY_STEAMID_1)
|
||||||
|
first_place_score = leaderboard_a.find_entry(rank=1)
|
||||||
|
last_place_score = leaderboard_a.find_entry(rank=-1)
|
||||||
|
```
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
lxml
|
||||||
|
requests
|
||||||
|
bs4
|
24
setup.py
Normal file
24
setup.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
with open("README.md", "r") as f:
|
||||||
|
long_description = f.read()
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="steamleaderboards-steffo",
|
||||||
|
version="0.0.1",
|
||||||
|
author="Stefano Pigozzi",
|
||||||
|
author_email="ste.pigozzi@gmail.com",
|
||||||
|
description="A wrapper for the Steam Leaderboards",
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
url="https://github.com/Steffo99/steamleaderboards",
|
||||||
|
packages=setuptools.find_packages(),
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Topic :: Internet"
|
||||||
|
]
|
||||||
|
)
|
130
steamleaderboards/__init__.py
Normal file
130
steamleaderboards/__init__.py
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardGroup:
|
||||||
|
def __init__(self, app_id):
|
||||||
|
xml = requests.get(f"https://steamcommunity.com/stats/{app_id}/leaderboards/?xml=1")
|
||||||
|
_bs = BeautifulSoup(xml.content, features="lxml")
|
||||||
|
self.leaderboards = []
|
||||||
|
for leaderboard in _bs.find_all("leaderboard"):
|
||||||
|
self.leaderboards.append(ProtoLeaderboard(leaderboard, app_id))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<LeaderboardGroup for {self.app_id} with {len(self.leaderboards)} leaderboards>"
|
||||||
|
|
||||||
|
def get(self, name=None, *, display_name=None) -> typing.Optional["Leaderboard"]:
|
||||||
|
"""Get the full leaderboard with the specified parameter."""
|
||||||
|
if bool(lbid) + bool(name) + bool(display_name) > 1:
|
||||||
|
raise ValueError("You can only find a leaderboard by 1 parameter.")
|
||||||
|
if lbid is not None:
|
||||||
|
if not isinstance(lbid, int):
|
||||||
|
raise ValueError("lbid must be an int")
|
||||||
|
for leaderboard in self.leaderboards:
|
||||||
|
if leaderboard.lbid == lbid:
|
||||||
|
return leaderboard.full()
|
||||||
|
elif name is not None:
|
||||||
|
if not isinstance(name, str):
|
||||||
|
raise ValueError("name must be a str")
|
||||||
|
for leaderboard in self.leaderboards:
|
||||||
|
if leaderboard.name == name:
|
||||||
|
return leaderboard.full()
|
||||||
|
elif display_name is not None:
|
||||||
|
if not isinstance(display_name, str):
|
||||||
|
raise ValueError("display_name must be a str")
|
||||||
|
for leaderboard in self.leaderboards:
|
||||||
|
if leaderboard.display_name == display_name:
|
||||||
|
return leaderboard.full()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ProtoLeaderboard:
|
||||||
|
"""Information about a leaderboard retrieved through a leaderboard"""
|
||||||
|
def __init__(self, soup, app_id):
|
||||||
|
self.url = soup.url.text
|
||||||
|
self.lbid = int(soup.lbid.text)
|
||||||
|
self.name = soup.find("name").text
|
||||||
|
self.display_name = soup.display_name.text
|
||||||
|
self.entries = int(soup.entries.text)
|
||||||
|
self.sort_method = int(soup.sortmethod.text)
|
||||||
|
self.display_type = int(soup.displaytype.text)
|
||||||
|
self.app_id = app_id
|
||||||
|
|
||||||
|
def full(self) -> "Leaderboard":
|
||||||
|
return Leaderboard(protoleaderboard=self)
|
||||||
|
|
||||||
|
|
||||||
|
class Leaderboard:
|
||||||
|
# noinspection PyMissingConstructor
|
||||||
|
def __init__(self, app_id=None, lbid=None, *, protoleaderboard=None):
|
||||||
|
if protoleaderboard:
|
||||||
|
self.url = protoleaderboard.url
|
||||||
|
self.lbid = protoleaderboard.lbid
|
||||||
|
self.name = protoleaderboard.name
|
||||||
|
self.display_name = protoleaderboard.display_name
|
||||||
|
self.entries = protoleaderboard.entries
|
||||||
|
self.sort_method = protoleaderboard.sort_method
|
||||||
|
self.display_type = protoleaderboard.display_type
|
||||||
|
self.app_id = protoleaderboard.app_id
|
||||||
|
elif app_id and lbid:
|
||||||
|
self.lbid = lbid
|
||||||
|
self.app_id = app_id
|
||||||
|
self.url = None
|
||||||
|
self.name = None
|
||||||
|
self.display_name = None
|
||||||
|
self.entries = None
|
||||||
|
self.sort_method = None
|
||||||
|
self.display_type = None
|
||||||
|
else:
|
||||||
|
raise ValueError("No app_id, lbid or protoleaderboard specified")
|
||||||
|
next_request_url = f"https://steamcommunity.com/stats/{self.app_id}/leaderboards/{self.lbid}/?xml=1"
|
||||||
|
self.entries = []
|
||||||
|
while next_request_url:
|
||||||
|
xml = requests.get(next_request_url)
|
||||||
|
_bs = BeautifulSoup(xml.content, features="lxml")
|
||||||
|
for entry in _bs.find_all("entry"):
|
||||||
|
self.entries.append(Entry(entry))
|
||||||
|
if _bs.response.entryend:
|
||||||
|
entry_end = int(_bs.response.entryend.text)
|
||||||
|
if entry_end < int(_bs.response.totalleaderboardentries.text):
|
||||||
|
next_request_url = f"https://steamcommunity.com/stats/{app_id}/leaderboards/{lbid}/?xml=1&start={entry_end + 1}"
|
||||||
|
else:
|
||||||
|
next_request_url = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if self.name:
|
||||||
|
return f'<Leaderboard "{self.name}" for {self.app_id} with {len(self.entries)}>'
|
||||||
|
else:
|
||||||
|
return f'<Leaderboard [{self.lbid}] for {self.app_id} with {len(self.entries)}>'
|
||||||
|
|
||||||
|
def find_entry(self, steam_id=None, *, rank=None):
|
||||||
|
if bool(steam_id) + bool(rank) > 1:
|
||||||
|
raise ValueError("You can only find an entry by 1 parameter.")
|
||||||
|
if steam_id is not None:
|
||||||
|
if not isinstance(steam_id, str):
|
||||||
|
raise ValueError("steam_id must be a str")
|
||||||
|
for entry in self.entries:
|
||||||
|
if entry.steam_id == steam_id:
|
||||||
|
return entry
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
elif rank is not None:
|
||||||
|
if not isinstance(rank, int):
|
||||||
|
raise ValueError("steam_id must be an int")
|
||||||
|
try:
|
||||||
|
return self.entries[rank - 1]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Entry:
|
||||||
|
def __init__(self, soup):
|
||||||
|
self.steam_id = soup.steamid.text
|
||||||
|
self.score = int(soup.score.text)
|
||||||
|
self.rank = int(soup.rank.text)
|
||||||
|
self.ugcid = soup.ugcid.text
|
||||||
|
self.details = soup.details.text
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Entry #{self.rank} {self.steam_id}: {self.score} pts>"
|
Loading…
Reference in a new issue