1
Fork 0
mirror of https://github.com/Steffo99/steamleaderboards.git synced 2024-11-21 15:24:18 +00:00

Create package

This commit is contained in:
Steffo 2019-02-05 21:25:36 +01:00
parent 8e754a43a7
commit 873e820e78
5 changed files with 210 additions and 0 deletions

21
LICENSE.txt Normal file
View 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
View 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
View file

@ -0,0 +1,3 @@
lxml
requests
bs4

24
setup.py Normal file
View 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"
]
)

View 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>"