Skip to content

Commit

Permalink
Change minimal Python version to 3.12 (#421)
Browse files Browse the repository at this point in the history
* Add minor fixes and bump minimal python version to 3.12

* Update setup.py to version 1.2.0

---------

Authored-by: Wok <[email protected]>
  • Loading branch information
bukson authored Dec 23, 2024
1 parent d856175 commit 4d623f6
Show file tree
Hide file tree
Showing 14 changed files with 69 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pythonpr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8]
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Table of Content
Installation
============

Requires python 3.8 at least
Requires python 3.12 at least
```
pip install steampy
```
Expand Down
2 changes: 1 addition & 1 deletion examples/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@

# Dump all the info to inventory_(app_id)_(context_id).json file
print('Saving information...')
with Path(f'inventory_{app_id}_{context_id}.json').open('w') as file:
with Path(f'inventory_{app_id}_{context_id}.json').open('w', encoding='utf-8') as file:
json.dump(item_amounts, file)
print(f'Done! Saved to file: inventory_{app_id}_{context_id}.json')
10 changes: 7 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import sys

from setuptools import setup

version = '1.1.3'
if sys.version_info < (3, 12):
sys.exit("Python < 3.12 is not supported")

version = '1.2.0'

setup(
name='steampy',
packages=['steampy', 'test', 'examples' ],
packages=['steampy', 'test', 'examples'],
version=version,
description='A Steam lib for trade automation',
author='Michał Bukowski',
author_email='[email protected]',
license='MIT',
url='https://github.com/bukson/steampy',
download_url='https://github.com/bukson/steampy/tarball/' + version,
keywords=['steam', 'trade' ],
keywords=['steam', 'trade'],
classifiers=[],
install_requires=[
"requests",
Expand Down
7 changes: 3 additions & 4 deletions steampy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,11 @@ def get_steam_id(self) -> int:
response = self._session.get(url)
if steam_id := re.search(r'g_steamID = "(\d+)";', response.text):
return int(steam_id.group(1))
else:
raise ValueError(f'Invalid steam_id: {steam_id}')
raise ValueError(f'Invalid steam_id: {steam_id}')

def login(self, username: str | None = None, password: str | None = None, steam_guard: str | None = None) -> None:
invalid_client_credentials_is_present = None in (self.username, self._password, self.steam_guard_string)
invalid_login_credentials_is_present = None in (username, password, steam_guard)
invalid_client_credentials_is_present = None in {self.username, self._password, self.steam_guard_string}
invalid_login_credentials_is_present = None in {username, password, steam_guard}

if invalid_client_credentials_is_present and invalid_login_credentials_is_present:
raise InvalidCredentials(
Expand Down
3 changes: 1 addition & 2 deletions steampy/confirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ def _get_confirmations(self) -> list[Confirmation]:
nonce = conf['nonce']
confirmations.append(Confirmation(data_confid, nonce))
return confirmations
else:
raise ConfirmationExpected
raise ConfirmationExpected

def _fetch_confirmations_page(self) -> requests.Response:
tag = Tag.CONF.value
Expand Down
4 changes: 3 additions & 1 deletion steampy/guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ def load_steam_guard(steam_guard: str) -> dict[str, str]:
Arguments:
steam_guard (str): If this string is a path to a file, then its contents will be parsed as a json data.
Otherwise, the string will be parsed as a json data.
Returns:
Dict[str, str]: Parsed json data as a dictionary of strings (both key and value).
"""
if Path(steam_guard).is_file():
with Path(steam_guard).open() as f:
with Path(steam_guard).open(encoding='utf-8') as f:
return json.loads(f.read(), parse_int=str)
else:
return json.loads(steam_guard, parse_int=str)
Expand Down
7 changes: 3 additions & 4 deletions steampy/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ def _api_call(self, method: str, service: str, endpoint: str, version: str = 'v1
headers = {'Referer': f'{SteamUrl.COMMUNITY_URL}/', 'Origin': SteamUrl.COMMUNITY_URL}
if method.upper() == 'GET':
return self.session.get(url, params=params, headers=headers)
elif method.upper() == 'POST':
if method.upper() == 'POST':
return self.session.post(url, data=params, headers=headers)
else:
raise ValueError('Method must be either GET or POST')
raise ValueError('Method must be either GET or POST')

def login(self) -> Session:
login_response = self._send_login_request()
Expand All @@ -53,7 +52,7 @@ def _send_login_request(self) -> Response:
request_data = self._prepare_login_request_data(encrypted_password, rsa_timestamp)
return self._api_call('POST', 'IAuthenticationService', 'BeginAuthSessionViaCredentials', params=request_data)

def set_sessionid_cookies(self):
def set_sessionid_cookies(self) -> None:
community_domain = SteamUrl.COMMUNITY_URL[8:]
store_domain = SteamUrl.STORE_URL[8:]
community_cookie_dic = self.session.cookies.get_dict(domain=community_domain)
Expand Down
1 change: 1 addition & 0 deletions steampy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class PredefinedOptions(NamedTuple):
app_id: str
context_id: str


class GameOptions:
STEAM = PredefinedOptions('753', '6')
DOTA2 = PredefinedOptions('570', '2')
Expand Down
9 changes: 6 additions & 3 deletions steampy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ def login_required(func):
def func_wrapper(self, *args, **kwargs):
if not self.was_login_executed:
raise LoginRequired('Use login method first')
else:
return func(self, *args, **kwargs)
return func(self, *args, **kwargs)

return func_wrapper

Expand Down Expand Up @@ -67,8 +66,10 @@ def calculate_gross_price(price_net: Decimal, publisher_fee: Decimal, steam_fee:
fraud incidents and cover the cost of development of this and future Steam economy features. The fee is
currently `5%` (with a minimum fee of `$0.01`). This fee may be increased or decreased by Steam in the
future.
Returns:
Decimal: Gross price (including fees) - the amount that the buyer pays during a market transaction
"""
price_net *= 100
steam_fee_amount = int(math.floor(max(price_net * steam_fee, 1)))
Expand All @@ -88,8 +89,10 @@ def calculate_net_price(price_gross: Decimal, publisher_fee: Decimal, steam_fee:
fraud incidents and cover the cost of development of this and future Steam economy features. The fee is
currently `5%` (with a minimum fee of `$0.01`). This fee may be increased or decreased by Steam in the
future.
Returns:
Decimal: Net price (without fees) - the amount that the seller receives after a market transaction.
"""
price_gross *= 100
estimated_net_price = Decimal(int(price_gross / (steam_fee + publisher_fee + 1)))
Expand Down Expand Up @@ -244,7 +247,7 @@ def get_key_value_from_url(url: str, key: str, case_sensitive: bool = True) -> s

def load_credentials():
dirname = Path(__file__).resolve().parent
with Path(f'{dirname}/../secrets/credentials.pwd').open() as f:
with Path(f'{dirname}/../secrets/credentials.pwd').open(encoding='utf-8') as f:
return [Credentials(line.split()[0], line.split()[1], line.split()[2]) for line in f]


Expand Down
42 changes: 21 additions & 21 deletions test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,38 @@
@unittest.skip('Requires secrets/Steamguard.txt')
class TestSteamClient(TestCase):
@classmethod
def setUpClass(cls):
def setUpClass(cls) -> None:
cls.credentials = load_credentials()[0]
dirname = Path(__file__).resolve().parent
cls.steam_guard_file = f'{dirname}/../secrets/Steamguard.txt'

def test_get_steam_id(self):
def test_get_steam_id(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
assert client.get_steam_id() == int(self.steam_guard_file['Session']['SteamID'])

def test_login(self):
def test_login(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)

def test_is_session_alive(self):
def test_is_session_alive(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
assert client.is_session_alive()

def test_logout(self):
def test_logout(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
assert client.is_session_alive()
client.logout()

def test_client_with_statement(self):
def test_client_with_statement(self) -> None:
with SteamClient(
self.credentials.api_key, self.credentials.login, self.credentials.password, self.steam_guard_file,
) as client:
assert client.is_session_alive()

def test_send_offer_without_sessionid_cookie(self):
def test_send_offer_without_sessionid_cookie(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
client._session.cookies.set('sessionid', None, domain='steamcommunity.com')
Expand All @@ -55,70 +55,70 @@ def test_send_offer_without_sessionid_cookie(self):
trade_offer_url = ''
self.assertRaises(AttributeError, lambda: client.make_offer_with_url([my_asset], [], trade_offer_url, 'TEST'))

def test_sessionid_cookie(self):
def test_sessionid_cookie(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
community_cookies = client._session.cookies.get_dict('steamcommunity.com')
store_cookies = client._session.cookies.get_dict('store.steampowered.com')
assert 'sessionid' in community_cookies
assert 'sessionid' in store_cookies

def test_get_my_inventory(self):
def test_get_my_inventory(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
inventory = client.get_my_inventory(GameOptions.CS)
assert inventory is not None

def test_get_partner_inventory(self):
def test_get_partner_inventory(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
partner_id = ''
game = GameOptions.TF2
inventory = client.get_partner_inventory(partner_id, game)
assert inventory is not None

def test_get_trade_offers_summary(self):
def test_get_trade_offers_summary(self) -> None:
client = SteamClient(self.credentials.api_key)
summary = client.get_trade_offers_summary()
assert summary is not None

def test_get_trade_offers(self):
def test_get_trade_offers(self) -> None:
client = SteamClient(self.credentials.api_key)
offers = client.get_trade_offers()
assert offers is not None

def test_get_trade_offer(self):
def test_get_trade_offer(self) -> None:
client = SteamClient(self.credentials.api_key)
trade_offer_id = '1442685162'
offer = client.get_trade_offer(trade_offer_id)
assert offer is not None

def test_accept_trade_offer_without_login(self):
def test_accept_trade_offer_without_login(self) -> None:
client = SteamClient(self.credentials.api_key)
self.assertRaises(LoginRequired, client.accept_trade_offer, 'id')

def test_accept_trade_offer(self):
def test_accept_trade_offer(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
trade_offer_id = '1451378159'
response_dict = client.accept_trade_offer(trade_offer_id)
assert response_dict is not None

def test_decline_trade_offer(self):
def test_decline_trade_offer(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
trade_offer_id = '1449530707'
response_dict = client.decline_trade_offer(trade_offer_id)
assert response_dict['response'] == {}

def test_cancel_trade_offer(self):
def test_cancel_trade_offer(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
trade_offer_id = '1450637835'
response_dict = client.cancel_trade_offer(trade_offer_id)
assert response_dict['response'] == {}

def test_make_offer(self):
def test_make_offer(self) -> None:
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
partner_id = ''
Expand All @@ -133,7 +133,7 @@ def test_make_offer(self):
assert response is not None
assert 'tradeofferid' in response

def test_make_offer_url(self):
def test_make_offer_url(self) -> None:
partner_account_id = '32384925'
partner_token = '7vqRtBpC'
sample_trade_url = (
Expand All @@ -154,15 +154,15 @@ def test_make_offer_url(self):
assert response is not None
assert 'tradeofferid' in response

def test_get_escrow_duration(self):
def test_get_escrow_duration(self) -> None:
# A sample trade URL with escrow time of 15 days cause mobile auth not added
sample_trade_url = 'https://steamcommunity.com/tradeoffer/new/?partner=314218906&token=sgA4FdNm'
client = SteamClient(self.credentials.api_key)
client.login(self.credentials.login, self.credentials.password, self.steam_guard_file)
response = client.get_escrow_duration(sample_trade_url)
assert response == 15

def test_get_wallet_balance(self):
def test_get_wallet_balance(self) -> None:
with SteamClient(
self.credentials.api_key, self.credentials.login, self.credentials.password, self.steam_guard_file,
) as client:
Expand Down
10 changes: 5 additions & 5 deletions test/test_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@

class TestGuard(TestCase):
@classmethod
def setUpClass(cls):
def setUpClass(cls) -> None:
cls.shared_secret = b64encode(b'1234567890abcdefghij')
cls.identity_secret = b64encode(b'abcdefghijklmnoprstu')

def test_one_time_code(self):
def test_one_time_code(self) -> None:
timestamp = 1469184207
code = guard.generate_one_time_code(self.shared_secret, timestamp)
assert code == 'P2QJN'

def test_confirmation_key(self):
def test_confirmation_key(self) -> None:
timestamp = 1470838334
confirmation_key = guard.generate_confirmation_key(self.identity_secret, Tag.CONF.value, timestamp)
assert confirmation_key == b'pWqjnkcwqni+t/n+5xXaEa0SGeA='

def test_generate_device_id(self):
def test_generate_device_id(self) -> None:
steam_id = '12341234123412345'
device_id = guard.generate_device_id(steam_id)
assert device_id == 'android:677cf5aa-3300-7807-d1e2-c408142742e2'

def test_load_steam_guard(self):
def test_load_steam_guard(self) -> None:
expected_keys = ('steamid', 'shared_secret', 'identity_secret')

guard_json_str = '{"steamid": 12345678, "shared_secret": "SHARED_SECRET", "identity_secret": "IDENTITY_SECRET"}'
Expand Down
Loading

0 comments on commit 4d623f6

Please sign in to comment.