Ejemplo n.º 1
0
 def __init__(self, api_key: str) -> None:
     self._api_key = api_key
     self._session = requests.Session()
     self.steam_guard = None
     self.was_login_executed = False
     self.username = None
     self.market = SteamMarket(self._session)
     self.chat = SteamChat(self._session)
Ejemplo n.º 2
0
 def __init__(
     self,
     username: str = None,
     password: str = None,
     api_key: str = None,
     shared_secret: str = None,
     identity_secret: str = None,
     steam_id: str = None,
     reuse_session: bool = False,
     session_file: Path = None,
     sessions_dir: Path = None,
 ) -> None:
     self._api_key = api_key
     self._identity_secret = identity_secret
     self._password = password
     self._shared_secret = shared_secret
     self._session = aiohttp.ClientSession()
     self._api_session = aiohttp.ClientSession()
     self.username = username
     self.steam_id = steam_id
     self.was_login_executed = False
     self.market = SteamMarket(self._session)
     self.chat = SteamChat(self._session)
     self.reuse_session = reuse_session
     self.session_file = session_file
     self.sessions_dir = sessions_dir
Ejemplo n.º 3
0
 def __init__(self, api_key: str, username: str = None, password: str = None, steam_guard:str = None) -> None:
     self._api_key = api_key
     self._session = requests.Session()
     self.steam_guard = steam_guard
     self.was_login_executed = False
     self.username = username
     self._password = password
     self.market = SteamMarket(self._session)
     self.chat = SteamChat(self._session)
Ejemplo n.º 4
0
 def __init__(self,
              api_key: str,
              username: str = None,
              password: str = None,
              steam_guard: str = None,
              rucaptcha_key: str = None,
              proxy: dict = None,
              rucaptcha_kwargs=None) -> None:
     self._api_key = api_key
     self._session = requests.Session()
     self._session.headers.update({
         "Accept":
         "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
         "Accept-Encoding":
         "gzip, deflate, br",
         "Cache-Control":
         "max-age=0",
         "Connection":
         "keep-alive",
         "DNT":
         "1",
         "User-Agent":
         "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36"
     })
     if proxy:
         self._session.proxies.update(proxy)
     self.steam_guard = steam_guard
     self.was_login_executed = False
     self.username = username
     self._password = password
     self._rucaptcha_key = rucaptcha_key
     if rucaptcha_kwargs is None:
         rucaptcha_kwargs = {}
     self.rucaptcha_kwargs = rucaptcha_kwargs
     self.market = SteamMarket(self._session)
     self.chat = SteamChat(self._session)
Ejemplo n.º 5
0
class SteamClient:
    def __init__(self, api_key: str) -> None:
        self._api_key = api_key
        self._session = requests.Session()
        self.steam_guard = None
        self.was_login_executed = False
        self.username = None
        self.market = SteamMarket(self._session)
        self.chat = SteamChat(self._session)

    def login(self, username: str, password: str, steam_guard: str) -> None:
        self.steam_guard = guard.load_steam_guard(steam_guard)
        self.username = username
        LoginExecutor(username, password, self.steam_guard['shared_secret'],
                      self._session).login()
        self.was_login_executed = True
        self.market._set_login_executed(self.steam_guard,
                                        self._get_session_id())

    @login_required
    def logout(self) -> None:
        url = LoginExecutor.STORE_URL + '/logout/'
        params = {'sessionid': self._get_session_id()}
        self._session.post(url, params)
        if self.is_session_alive():
            raise Exception("Logout unsuccessful")
        self.was_login_executed = False
        self.chat._logout()

    @login_required
    def is_session_alive(self):
        steam_login = self.username
        main_page_response = self._session.get(SteamUrl.COMMUNITY_URL)
        return steam_login.lower() in main_page_response.text.lower()

    def api_call(self,
                 request_method: str,
                 interface: str,
                 api_method: str,
                 version: str,
                 params: dict = None) -> requests.Response:
        url = '/'.join([SteamUrl.API_URL, interface, api_method, version])
        if request_method == 'GET':
            response = requests.get(url, params=params)
        else:
            response = requests.post(url, data=params)
        if self.is_invalid_api_key(response):
            raise InvalidCredentials('Invalid API key')
        return response

    @staticmethod
    def is_invalid_api_key(response: requests.Response) -> bool:
        msg = 'Access is denied. Retrying will not help. Please verify your <pre>key=</pre> parameter'
        return msg in response.text

    @login_required
    def get_my_inventory(self, game: GameOptions, merge: bool = True) -> dict:
        url = SteamUrl.COMMUNITY_URL + '/my/inventory/json/' + \
              game.app_id + '/' + \
              game.context_id
        response_dict = self._session.get(url).json()
        if merge:
            return merge_items_with_descriptions_from_inventory(
                response_dict, game)
        return response_dict

    @login_required
    def get_partner_inventory(self,
                              partner_steam_id: str,
                              game: GameOptions,
                              merge: bool = True) -> dict:
        params = {
            'sessionid': self._get_session_id(),
            'partner': partner_steam_id,
            'appid': int(game.app_id),
            'contextid': game.context_id
        }
        partner_account_id = steam_id_to_account_id(partner_steam_id)
        headers = {
            'X-Requested-With': 'XMLHttpRequest',
            'Referer': SteamUrl.COMMUNITY_URL + '/tradeoffer/new/?partner=' +
            partner_account_id,
            'X-Prototype-Version': '1.7'
        }
        response_dict = self._session.get(SteamUrl.COMMUNITY_URL +
                                          '/tradeoffer/new/partnerinventory/',
                                          params=params,
                                          headers=headers).json()
        if merge:
            return merge_items_with_descriptions_from_inventory(
                response_dict, game)
        return response_dict

    def _get_session_id(self) -> str:
        return self._session.cookies.get_dict()['sessionid']

    def get_trade_offers_summary(self) -> dict:
        params = {'key': self._api_key}
        return self.api_call('GET', 'IEconService', 'GetTradeOffersSummary',
                             'v1', params).json()

    def get_trade_offers(self, merge: bool = True) -> dict:
        params = {
            'key': self._api_key,
            'get_sent_offers': 1,
            'get_received_offers': 1,
            'get_descriptions': 1,
            'language': 'english',
            'active_only': 1,
            'historical_only': 0,
            'time_historical_cutoff': ''
        }
        response = self.api_call('GET', 'IEconService', 'GetTradeOffers', 'v1',
                                 params).json()
        response = self._filter_non_active_offers(response)
        if merge:
            response = merge_items_with_descriptions_from_offers(response)
        return response

    @staticmethod
    def _filter_non_active_offers(offers_response):
        offers_received = offers_response['response'].get(
            'trade_offers_received', [])
        offers_sent = offers_response['response'].get('trade_offers_sent', [])
        offers_response['response']['trade_offers_received'] = list(
            filter(
                lambda offer: offer['trade_offer_state'] == TradeOfferState.
                Active, offers_received))
        offers_response['response']['trade_offers_sent'] = list(
            filter(
                lambda offer: offer['trade_offer_state'] == TradeOfferState.
                Active, offers_sent))
        return offers_response

    def get_trade_offer(self, trade_offer_id: str, merge: bool = True) -> dict:
        params = {
            'key': self._api_key,
            'tradeofferid': trade_offer_id,
            'language': 'english'
        }
        response = self.api_call('GET', 'IEconService', 'GetTradeOffer', 'v1',
                                 params).json()
        if merge and "descriptions" in response['response']:
            descriptions = {
                get_description_key(offer): offer
                for offer in response['response']['descriptions']
            }
            offer = response['response']['offer']
            response['response'][
                'offer'] = merge_items_with_descriptions_from_offer(
                    offer, descriptions)
        return response

    def get_trade_history(self,
                          max_trades=100,
                          start_after_time=None,
                          start_after_tradeid=None,
                          get_descriptions=True,
                          navigating_back=True,
                          include_failed=True,
                          include_total=True) -> dict:
        params = {
            'key': self._api_key,
            'max_trades': max_trades,
            'start_after_time': start_after_time,
            'start_after_tradeid': start_after_tradeid,
            'get_descriptions': get_descriptions,
            'navigating_back': navigating_back,
            'include_failed': include_failed,
            'include_total': include_total
        }
        response = self.api_call('GET', 'IEconService', 'GetTradeHistory',
                                 'v1', params).json()
        return response

    @login_required
    def get_trade_receipt(self, trade_id: str) -> list:
        html = self._session.get(
            "https://steamcommunity.com/trade/{}/receipt".format(
                trade_id)).content.decode()
        items = []
        for item in texts_between(html, "oItem = ", ";\r\n\toItem"):
            items.append(json.loads(item))
        return items

    @login_required
    def accept_trade_offer(self, trade_offer_id: str) -> dict:
        trade = self.get_trade_offer(trade_offer_id)
        trade_offer_state = TradeOfferState(
            trade['response']['offer']['trade_offer_state'])
        if trade_offer_state is not TradeOfferState.Active:
            raise ApiException("Invalid trade offer state: {} ({})".format(
                trade_offer_state.name, trade_offer_state.value))
        partner = self._fetch_trade_partner_id(trade_offer_id)
        session_id = self._get_session_id()
        accept_url = SteamUrl.COMMUNITY_URL + '/tradeoffer/' + trade_offer_id + '/accept'
        params = {
            'sessionid': session_id,
            'tradeofferid': trade_offer_id,
            'serverid': '1',
            'partner': partner,
            'captcha': ''
        }
        headers = {'Referer': self._get_trade_offer_url(trade_offer_id)}
        response = self._session.post(accept_url, data=params,
                                      headers=headers).json()
        if response.get('needs_mobile_confirmation', False):
            return self._confirm_transaction(trade_offer_id)
        return response

    def _fetch_trade_partner_id(self, trade_offer_id: str) -> str:
        url = self._get_trade_offer_url(trade_offer_id)
        offer_response_text = self._session.get(url).text
        if 'You have logged in from a new device. In order to protect the items' in offer_response_text:
            raise SevenDaysHoldException(
                "Account has logged in a new device and can't trade for 7 days"
            )
        return text_between(offer_response_text,
                            "var g_ulTradePartnerSteamID = '", "';")

    def _confirm_transaction(self, trade_offer_id: str) -> dict:
        confirmation_executor = ConfirmationExecutor(
            self.steam_guard['identity_secret'], self.steam_guard['steamid'],
            self._session)
        return confirmation_executor.send_trade_allow_request(trade_offer_id)

    def decline_trade_offer(self, trade_offer_id: str) -> dict:
        params = {'key': self._api_key, 'tradeofferid': trade_offer_id}
        return self.api_call('POST', 'IEconService', 'DeclineTradeOffer', 'v1',
                             params).json()

    def cancel_trade_offer(self, trade_offer_id: str) -> dict:
        params = {'key': self._api_key, 'tradeofferid': trade_offer_id}
        return self.api_call('POST', 'IEconService', 'CancelTradeOffer', 'v1',
                             params).json()

    @login_required
    def make_offer(self,
                   items_from_me: List[Asset],
                   items_from_them: List[Asset],
                   partner_steam_id: str,
                   message: str = '') -> dict:
        offer = self._create_offer_dict(items_from_me, items_from_them)
        session_id = self._get_session_id()
        url = SteamUrl.COMMUNITY_URL + '/tradeoffer/new/send'
        server_id = 1
        params = {
            'sessionid': session_id,
            'serverid': server_id,
            'partner': partner_steam_id,
            'tradeoffermessage': message,
            'json_tradeoffer': json.dumps(offer),
            'captcha': '',
            'trade_offer_create_params': '{}'
        }
        partner_account_id = steam_id_to_account_id(partner_steam_id)
        headers = {
            'Referer': SteamUrl.COMMUNITY_URL + '/tradeoffer/new/?partner=' +
            partner_account_id,
            'Origin': SteamUrl.COMMUNITY_URL
        }
        response = self._session.post(url, data=params, headers=headers).json()
        if response.get('needs_mobile_confirmation'):
            response.update(self._confirm_transaction(
                response['tradeofferid']))
        return response

    def get_profile(self, steam_id: str) -> dict:
        params = {'steamids': steam_id, 'key': self._api_key}
        response = self.api_call('GET', 'ISteamUser', 'GetPlayerSummaries',
                                 'v0002', params)
        data = response.json()
        return data['response']['players'][0]

    def get_friend_list(self,
                        steam_id: str,
                        relationship_filter: str = "all") -> dict:
        params = {
            'key': self._api_key,
            'steamid': steam_id,
            'relationship': relationship_filter
        }
        resp = self.api_call("GET", "ISteamUser", "GetFriendList", "v1",
                             params)
        data = resp.json()
        return data['friendslist']['friends']

    @staticmethod
    def _create_offer_dict(items_from_me: List[Asset],
                           items_from_them: List[Asset]) -> dict:
        return {
            'newversion': True,
            'version': 4,
            'me': {
                'assets': [asset.to_dict() for asset in items_from_me],
                'currency': [],
                'ready': False
            },
            'them': {
                'assets': [asset.to_dict() for asset in items_from_them],
                'currency': [],
                'ready': False
            }
        }

    @login_required
    def get_escrow_duration(self, trade_offer_url: str) -> int:
        headers = {
            'Referer':
            SteamUrl.COMMUNITY_URL + urlparse.urlparse(trade_offer_url).path,
            'Origin':
            SteamUrl.COMMUNITY_URL
        }
        response = self._session.get(trade_offer_url, headers=headers).text
        my_escrow_duration = int(
            text_between(response, "var g_daysMyEscrow = ", ";"))
        their_escrow_duration = int(
            text_between(response, "var g_daysTheirEscrow = ", ";"))
        return max(my_escrow_duration, their_escrow_duration)

    @login_required
    def make_offer_with_url(self,
                            items_from_me: List[Asset],
                            items_from_them: List[Asset],
                            trade_offer_url: str,
                            message: str = '') -> dict:
        token = get_key_value_from_url(trade_offer_url, 'token')
        partner_account_id = get_key_value_from_url(trade_offer_url, 'partner')
        partner_steam_id = account_id_to_steam_id(partner_account_id)
        offer = self._create_offer_dict(items_from_me, items_from_them)
        session_id = self._get_session_id()
        url = SteamUrl.COMMUNITY_URL + '/tradeoffer/new/send'
        server_id = 1
        trade_offer_create_params = {'trade_offer_access_token': token}
        params = {
            'sessionid': session_id,
            'serverid': server_id,
            'partner': partner_steam_id,
            'tradeoffermessage': message,
            'json_tradeoffer': json.dumps(offer),
            'captcha': '',
            'trade_offer_create_params': json.dumps(trade_offer_create_params)
        }
        headers = {
            'Referer':
            SteamUrl.COMMUNITY_URL + urlparse.urlparse(trade_offer_url).path,
            'Origin':
            SteamUrl.COMMUNITY_URL
        }
        response = self._session.post(url, data=params, headers=headers).json()
        if response.get('needs_mobile_confirmation'):
            response.update(self._confirm_transaction(
                response['tradeofferid']))
        return response

    @staticmethod
    def _get_trade_offer_url(trade_offer_id: str) -> str:
        return SteamUrl.COMMUNITY_URL + '/tradeoffer/' + trade_offer_id