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