From cddb52824f2126e201517be283d2dca0ad0199ab Mon Sep 17 00:00:00 2001 From: StrangePlay02 <136957264+StrangePlay02@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:46:02 +0000 Subject: [PATCH 1/7] align has_strange_in_name() methods per prev. fix --- conftest.py | 6 ++++++ src/tf2_utils/__init__.py | 2 +- src/tf2_utils/item.py | 2 +- tests/test_sku.py | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/conftest.py b/conftest.py index a981554..f98fddc 100644 --- a/conftest.py +++ b/conftest.py @@ -26,6 +26,7 @@ def get_item_data(file_name: str) -> dict: SPELLED_ITEM = get_item_data("spelled_item") PAINTED_HAT = get_item_data("painted_hat") ELLIS_CAP = get_item_data("ellis_cap") +STRANGE_PART = get_item_data("strange_part") @pytest.fixture @@ -84,6 +85,11 @@ def ellis_cap() -> dict: return ELLIS_CAP +@pytest.fixture +def strange_part() -> dict: + return STRANGE_PART + + @pytest.fixture async def aiohttp_session(): async with ClientSession() as session: diff --git a/src/tf2_utils/__init__.py b/src/tf2_utils/__init__.py index 2f222f4..7489104 100644 --- a/src/tf2_utils/__init__.py +++ b/src/tf2_utils/__init__.py @@ -1,7 +1,7 @@ # flake8: noqa: F401, F403 __title__ = "tf2-utils" __author__ = "offish" -__version__ = "2.4.1" +__version__ = "2.4.2" __license__ = "MIT" from .currency import CurrencyExchange diff --git a/src/tf2_utils/item.py b/src/tf2_utils/item.py index b517b0b..8b2e35f 100644 --- a/src/tf2_utils/item.py +++ b/src/tf2_utils/item.py @@ -64,7 +64,7 @@ def has_quality(self, quality: str) -> bool: return self.get_quality() == quality def has_strange_in_name(self) -> bool: - return "Strange" in self.name + return "Strange " in self.name and "Strange Part: " not in self.name def has_vintage_in_name(self) -> bool: return "Vintage" in self.name diff --git a/tests/test_sku.py b/tests/test_sku.py index b238ff2..3ce218c 100644 --- a/tests/test_sku.py +++ b/tests/test_sku.py @@ -27,11 +27,25 @@ def test_ellis_cap_sku_properties(ellis_cap: dict) -> None: } +def test_strange_part_sku_properties(strange_part: dict) -> None: + assert get_sku_properties(strange_part) == { + "defindex": 6012, + "quality": 6, + "australium": False, + "craftable": True, + "wear": -1, + "killstreak_tier": -1, + "festivized": False, + "strange": False, + } + + def test_get_sku_items( ellis_cap: dict, crusaders_crossbow: dict, hong_kong_cone: dict, uncraftable_hat: dict, + strange_part: dict, ) -> None: # https://marketplace.tf/items/tf2/263;6 assert get_sku(ellis_cap) == "263;6" @@ -41,6 +55,8 @@ def test_get_sku_items( assert get_sku(hong_kong_cone) == "30177;5;u107;strange" # https://marketplace.tf/items/tf2/734;6;uncraftable assert get_sku(uncraftable_hat) == "734;6;uncraftable" + # https://marketplace.tf/items/tf2/6012;6 + assert get_sku(strange_part) == "6012;6" def test_properties() -> None: From 954d99f0dc25f9db3abfb548fe65a2ec95a9ae80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:08:39 +0000 Subject: [PATCH 2/7] Initial plan From a449cb4299f346ae22f5bfb4e896ec6679b0a740 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:11:47 +0000 Subject: [PATCH 3/7] Add configurable browser headers to SteamCommunity inventory requests Co-authored-by: StrangePlay02 <136957264+StrangePlay02@users.noreply.github.com> --- docs/usage/inventory.py | 7 ++++ src/tf2_utils/providers/steamcommunity.py | 21 +++++++++++ tests/test_inventory.py | 33 +++++++++++++++++ tests/test_providers.py | 45 ++++++++++++++++++++++- 4 files changed, 105 insertions(+), 1 deletion(-) diff --git a/docs/usage/inventory.py b/docs/usage/inventory.py index f2cb186..9dd1089 100644 --- a/docs/usage/inventory.py +++ b/docs/usage/inventory.py @@ -1,4 +1,5 @@ from tf2_utils import Inventory, map_inventory +from tf2_utils.providers.steamcommunity import SteamCommunity # Create an inventory object provider = Inventory("steamsupply", "API KEY") @@ -18,6 +19,12 @@ provider = Inventory() # (the same as above) # using steamcommunity will not require an API key and fetches 5000 items at once +# SteamCommunity uses a browser-style User-Agent by default. +# Override headers at construction or update provider.provider.headers. +provider.provider = SteamCommunity(headers={"User-Agent": "your-browser-user-agent"}) +# Referer is set to the target user's profile inventory page for every fetch. +# These headers do not guarantee that Steam's rate limits will be avoided. + # Get the inventory of a user user_inventory = provider.fetch("76561198828172881") diff --git a/src/tf2_utils/providers/steamcommunity.py b/src/tf2_utils/providers/steamcommunity.py index d459a7c..ab2e9b7 100644 --- a/src/tf2_utils/providers/steamcommunity.py +++ b/src/tf2_utils/providers/steamcommunity.py @@ -1,10 +1,31 @@ +from requests.structures import CaseInsensitiveDict + from .provider import Provider class SteamCommunity(Provider): + def __init__( + self, api_key: str = "", headers: dict[str, str] | None = None + ) -> None: + super().__init__(api_key) + self.headers = CaseInsensitiveDict( + { + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/130.0.0.0 Safari/537.36" + ) + } + ) + if headers is not None: + self.headers.update(headers) + def get_url_and_params( self, steam_id: str, app_id: int, context_id: int ) -> tuple[str, dict]: + self.headers["Referer"] = ( + f"https://steamcommunity.com/profiles/{steam_id}/inventory" + ) return ( f"https://steamcommunity.com/inventory/{steam_id}/{app_id}/{context_id}", {"l": "english", "count": 2500}, diff --git a/tests/test_inventory.py b/tests/test_inventory.py index ea1ea5a..4e7aebe 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -1,7 +1,10 @@ +from unittest.mock import Mock, patch + import pytest from src.tf2_utils import InvalidInventory, map_inventory from src.tf2_utils.inventory import Inventory +from src.tf2_utils.providers.steamcommunity import SteamCommunity from src.tf2_utils.utils import read_json_file INVENTORY = read_json_file("./tests/json/bot_inventory.json") @@ -56,6 +59,36 @@ def test_steamapis_inventory_provider() -> None: assert inventory.provider.api_key == "api_key" +@pytest.mark.parametrize("custom_headers", [None, {"user-agent": "custom-agent"}]) +@pytest.mark.parametrize("app_id,context_id", [(440, 2), (730, 3)]) +def test_steamcommunity_inventory_request(custom_headers, app_id, context_id) -> None: + inventory = Inventory() + if custom_headers is not None: + inventory.provider = SteamCommunity(headers=custom_headers) + response = Mock() + response.json.return_value = INVENTORY + + with patch("src.tf2_utils.inventory.requests.get", return_value=response) as get: + for steam_id in ["76561198253325712", "76561198828172881"]: + assert inventory.fetch(steam_id, app_id, context_id) is INVENTORY + get.assert_called_with( + f"https://steamcommunity.com/inventory/" + f"{steam_id}/{app_id}/{context_id}", + params={"l": "english", "count": 2500}, + headers=inventory.provider.headers, + ) + headers = get.call_args.kwargs["headers"] + assert headers["Referer"] == ( + f"https://steamcommunity.com/profiles/{steam_id}/inventory" + ) + if custom_headers is None: + assert headers["User-Agent"].startswith("Mozilla/5.0 ") + assert "Chrome/" in headers["User-Agent"] + else: + assert headers["User-Agent"] == "custom-agent" + assert "Origin" not in headers + + def test_steamcommunity_inventory_fetch(steam_id: str) -> None: provider = Inventory("steamcommunity") inventory = provider.fetch(steam_id) diff --git a/tests/test_providers.py b/tests/test_providers.py index 8d21475..ce72dd1 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -1,7 +1,10 @@ +import pytest + from src.tf2_utils.providers.express_load import ExpressLoad from src.tf2_utils.providers.steamcommunity import SteamCommunity +@pytest.mark.parametrize("steam_id", ["76561198253325712"]) def test_steam_community_inventory(steam_id: str) -> None: provider = SteamCommunity() url, params = provider.get_url_and_params(steam_id, 440, 2) @@ -12,7 +15,47 @@ def test_steam_community_inventory(steam_id: str) -> None: "l": "english", "count": 2500, } - assert provider.headers == {} + assert provider.headers["User-Agent"].startswith("Mozilla/5.0 ") + assert "Chrome/" in provider.headers["User-Agent"] + assert provider.headers["Referer"] == ( + f"https://steamcommunity.com/profiles/{steam_id}/inventory" + ) + + +@pytest.mark.parametrize("header_name", ["User-Agent", "user-agent"]) +def test_steam_community_custom_headers(header_name: str) -> None: + headers = {header_name: "custom-agent", "Accept-Language": "en-US"} + provider = SteamCommunity("api_key", headers=headers) + + assert provider.api_key == "api_key" + assert provider.headers["User-Agent"] == "custom-agent" + assert provider.headers["Accept-Language"] == "en-US" + assert len(provider.headers) == 2 + + provider.get_url_and_params("76561198253325712", 440, 2) + assert headers == {header_name: "custom-agent", "Accept-Language": "en-US"} + + +def test_steam_community_headers_are_independent() -> None: + first = SteamCommunity() + second = SteamCommunity(headers={}) + first.headers["User-Agent"] = "custom-agent" + first.get_url_and_params("76561198253325712", 440, 2) + + assert second.headers["User-Agent"].startswith("Mozilla/5.0 ") + assert "Referer" not in second.headers + + +def test_steam_community_referer_tracks_inventory() -> None: + provider = SteamCommunity(headers={"referer": "https://steamcommunity.com/"}) + for steam_id in ["76561198253325712", "76561198828172881"]: + url, params = provider.get_url_and_params(steam_id, 730, 2) + assert url == f"https://steamcommunity.com/inventory/{steam_id}/730/2" + assert params == {"l": "english", "count": 2500} + assert provider.headers["Referer"] == ( + f"https://steamcommunity.com/profiles/{steam_id}/inventory" + ) + assert len(provider.headers) == 2 def test_express_load_inventory(express_load_api_key: str, steam_id: str) -> None: From 27c16ff345a69fb8d62d7311c67f5b0918fe34b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:33:33 +0000 Subject: [PATCH 4/7] Remove SteamCommunity header customization per review Co-authored-by: StrangePlay02 <136957264+StrangePlay02@users.noreply.github.com> --- docs/usage/inventory.py | 5 +---- src/tf2_utils/providers/steamcommunity.py | 6 +----- tests/test_inventory.py | 16 ++++++---------- tests/test_providers.py | 22 +++++++++------------- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/docs/usage/inventory.py b/docs/usage/inventory.py index 9dd1089..0f5fbe3 100644 --- a/docs/usage/inventory.py +++ b/docs/usage/inventory.py @@ -1,5 +1,4 @@ from tf2_utils import Inventory, map_inventory -from tf2_utils.providers.steamcommunity import SteamCommunity # Create an inventory object provider = Inventory("steamsupply", "API KEY") @@ -19,9 +18,7 @@ provider = Inventory() # (the same as above) # using steamcommunity will not require an API key and fetches 5000 items at once -# SteamCommunity uses a browser-style User-Agent by default. -# Override headers at construction or update provider.provider.headers. -provider.provider = SteamCommunity(headers={"User-Agent": "your-browser-user-agent"}) +# SteamCommunity uses a hardcoded browser-style User-Agent. # Referer is set to the target user's profile inventory page for every fetch. # These headers do not guarantee that Steam's rate limits will be avoided. diff --git a/src/tf2_utils/providers/steamcommunity.py b/src/tf2_utils/providers/steamcommunity.py index ab2e9b7..6f995ff 100644 --- a/src/tf2_utils/providers/steamcommunity.py +++ b/src/tf2_utils/providers/steamcommunity.py @@ -4,9 +4,7 @@ class SteamCommunity(Provider): - def __init__( - self, api_key: str = "", headers: dict[str, str] | None = None - ) -> None: + def __init__(self, api_key: str = "") -> None: super().__init__(api_key) self.headers = CaseInsensitiveDict( { @@ -17,8 +15,6 @@ def __init__( ) } ) - if headers is not None: - self.headers.update(headers) def get_url_and_params( self, steam_id: str, app_id: int, context_id: int diff --git a/tests/test_inventory.py b/tests/test_inventory.py index 4e7aebe..a7221b6 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -4,7 +4,6 @@ from src.tf2_utils import InvalidInventory, map_inventory from src.tf2_utils.inventory import Inventory -from src.tf2_utils.providers.steamcommunity import SteamCommunity from src.tf2_utils.utils import read_json_file INVENTORY = read_json_file("./tests/json/bot_inventory.json") @@ -59,12 +58,9 @@ def test_steamapis_inventory_provider() -> None: assert inventory.provider.api_key == "api_key" -@pytest.mark.parametrize("custom_headers", [None, {"user-agent": "custom-agent"}]) @pytest.mark.parametrize("app_id,context_id", [(440, 2), (730, 3)]) -def test_steamcommunity_inventory_request(custom_headers, app_id, context_id) -> None: +def test_steamcommunity_inventory_request(app_id, context_id) -> None: inventory = Inventory() - if custom_headers is not None: - inventory.provider = SteamCommunity(headers=custom_headers) response = Mock() response.json.return_value = INVENTORY @@ -81,11 +77,11 @@ def test_steamcommunity_inventory_request(custom_headers, app_id, context_id) -> assert headers["Referer"] == ( f"https://steamcommunity.com/profiles/{steam_id}/inventory" ) - if custom_headers is None: - assert headers["User-Agent"].startswith("Mozilla/5.0 ") - assert "Chrome/" in headers["User-Agent"] - else: - assert headers["User-Agent"] == "custom-agent" + assert headers["User-Agent"] == ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/130.0.0.0 Safari/537.36" + ) assert "Origin" not in headers diff --git a/tests/test_providers.py b/tests/test_providers.py index ce72dd1..54d7b11 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -22,24 +22,20 @@ def test_steam_community_inventory(steam_id: str) -> None: ) -@pytest.mark.parametrize("header_name", ["User-Agent", "user-agent"]) -def test_steam_community_custom_headers(header_name: str) -> None: - headers = {header_name: "custom-agent", "Accept-Language": "en-US"} - provider = SteamCommunity("api_key", headers=headers) +def test_steam_community_api_key() -> None: + provider = SteamCommunity("api_key") assert provider.api_key == "api_key" - assert provider.headers["User-Agent"] == "custom-agent" - assert provider.headers["Accept-Language"] == "en-US" - assert len(provider.headers) == 2 - - provider.get_url_and_params("76561198253325712", 440, 2) - assert headers == {header_name: "custom-agent", "Accept-Language": "en-US"} + assert provider.headers["User-Agent"] == ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/130.0.0.0 Safari/537.36" + ) def test_steam_community_headers_are_independent() -> None: first = SteamCommunity() - second = SteamCommunity(headers={}) - first.headers["User-Agent"] = "custom-agent" + second = SteamCommunity() first.get_url_and_params("76561198253325712", 440, 2) assert second.headers["User-Agent"].startswith("Mozilla/5.0 ") @@ -47,7 +43,7 @@ def test_steam_community_headers_are_independent() -> None: def test_steam_community_referer_tracks_inventory() -> None: - provider = SteamCommunity(headers={"referer": "https://steamcommunity.com/"}) + provider = SteamCommunity() for steam_id in ["76561198253325712", "76561198828172881"]: url, params = provider.get_url_and_params(steam_id, 730, 2) assert url == f"https://steamcommunity.com/inventory/{steam_id}/730/2" From df4b8f4107325e120e0abcf1896120beec86fe81 Mon Sep 17 00:00:00 2001 From: StrangePlay02 Date: Sun, 11 Oct 2026 20:39:55 +0800 Subject: [PATCH 5/7] start_assetid pagination --- src/tf2_utils/inventory.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tf2_utils/inventory.py b/src/tf2_utils/inventory.py index 830ab68..66dd85c 100644 --- a/src/tf2_utils/inventory.py +++ b/src/tf2_utils/inventory.py @@ -62,11 +62,19 @@ def __init__( self.provider = i(api_key) break - def fetch(self, steam_id: str, app_id: int = 440, context_id: int = 2) -> dict: + def fetch( + self, + steam_id: str, + app_id: int = 440, + context_id: int = 2, + start_assetid: str | None = None, + ) -> dict: url, params = self.provider.get_url_and_params(steam_id, app_id, context_id) + if start_assetid is not None: + url = f"{url}?start_assetid={start_assetid}" response = requests.get(url, params=params, headers=self.provider.headers) try: return response.json() - except Exception as e: + except ValueError as e: return {"success": False, "error": str(e)} From 190c7136b2075c2da097bce52216316494b9ac7f Mon Sep 17 00:00:00 2001 From: StrangePlay02 Date: Sun, 11 Oct 2026 20:40:07 +0800 Subject: [PATCH 6/7] extra headers --- src/tf2_utils/providers/steamcommunity.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/tf2_utils/providers/steamcommunity.py b/src/tf2_utils/providers/steamcommunity.py index 6f995ff..ed638a3 100644 --- a/src/tf2_utils/providers/steamcommunity.py +++ b/src/tf2_utils/providers/steamcommunity.py @@ -11,8 +11,25 @@ def __init__(self, api_key: str = "") -> None: "User-Agent": ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/130.0.0.0 Safari/537.36" - ) + "Chrome/131.0.0.0 Safari/537.36" + ), + "Accept": ( + "text/html,application/xhtml+xml,application/xml;q=0.9," + "image/avif,image/webp,image/apng,*/*;q=0.8," + "application/signed-exchange;v=b3;q=0.7" + ), + "Accept-Language": "en-US,en;q=0.9,ja;q=0.8,ms;q=0.7", + "Accept-Encoding": "gzip, deflate, br, zstd", + "Cache-Control": "max-age=0", + "Connection": "keep-alive", + "Cookie": "", + "Host": "steamcommunity.com", + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + "Referer": "https://steamcommunity.com/", } ) From 016bc20f8e3f80420ca74e74692883ec7efd3f54 Mon Sep 17 00:00:00 2001 From: StrangePlay02 <136957264+StrangePlay02@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:04:37 +0800 Subject: [PATCH 7/7] Bump version from 2.4.2 to 2.4.3 --- src/tf2_utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tf2_utils/__init__.py b/src/tf2_utils/__init__.py index 7489104..60bacbd 100644 --- a/src/tf2_utils/__init__.py +++ b/src/tf2_utils/__init__.py @@ -1,7 +1,7 @@ # flake8: noqa: F401, F403 __title__ = "tf2-utils" __author__ = "offish" -__version__ = "2.4.2" +__version__ = "2.4.3" __license__ = "MIT" from .currency import CurrencyExchange