def __init__(self, steamid, key=None, language: str='en', identity_secret: str='', poll_delay: int=30, login_delay_time: int=0): """ :param steamid: stemid64 :param key: steam api key :param language: :param identity_secret: :param poll_delay: how often trades should be polled (too often can cause errors, too infrequent can make your bot too slow to respond :param login_delay_time: how long to wait after our session died to retry """ EventEmitter.__init__(self) self.session = aiohttp.ClientSession() ConfManager.__init__(self, identity_secret, steamid, self.session) self.steamid = SteamID(steamid) if not self.steamid.isValid() or not self.steamid.type == SteamID.Type['INDIVIDUAL']: raise ValueError(f"Steam ID {self.steamid} is not valid, or is not a user ID") self.key = key self.language = language self.poll_delay = poll_delay self.last_poll = 0 self.logged_in = False self._trade_cache = {} self._conf_cache = {} self.first_run = True self.login_delay_time = login_delay_time
def formatSteamID3(arg): my_id = SteamID(arg) steamid3 = my_id.steam3() steamid3 = steamid3[5:] steamid3 = steamid3[:-1] return steamid3
def _load(self, data, desc, manager, parse_items): # Define basic variables, set attributes self.raw = data self.raw_descriptions = desc self.manager = manager self.tradeofferid = data.get('tradeofferid', '') self.steamid_other = SteamID( str(data.get('accountid_other', 309304171) + 76561197960265728)) self.message = data.get('message') self.expiration_time = data.get('expiration_time') self.is_our_offer = data.get('is_our_offer') self.from_real_time_trade = data.get('from_real_time_trade') self.escrow_end_date = data.get('escrow_end_date') self.items_to_give = [] self.items_to_receive = [] self._counter = 0 self._sent = True if self.tradeofferid else False # Check each enum for the one equal to the state. Should it raise an error, if the status isn't valid? for enum in ETradeOfferState: if enum.value == data.get('trade_offer_state', 1): self.trade_offer_state = enum # If it shouldn't parse the items, just set them. # REMEMBER: if you choose to have items pre-parsed, it will assume you supplied both attributes if not parse_items: self.items_to_give = data['items_to_give'] self.items_to_receive = data['items_to_receive'] return # For each item in the items we're giving, check it for item in data.get('items_to_give', []): if not item.get('missing', False): # If missing, it's not in the trade for description in desc: # For each description in descriptions, check to see it matches item desc_id = description['classid'] + '_' + description[ 'instanceid'] if desc_id == item['classid'] + '_' + item['instanceid']: self.items_to_give.append( Item(merge_item( item, description))) # If so, parse and append break else: self.items_to_give.append(Item( item, True)) # If it's missing, just add the item ids as an Item for item in data.get('items_to_receive', []): if not item.get('missing', False): for description in desc: desc_id = description['classid'] + '_' + description[ 'instanceid'] if desc_id == item['classid'] + '_' + item['instanceid']: self.items_to_receive.append( Item(merge_item(item, description))) break else: self.items_to_receive.append(Item(item, True))
def __init__(self, client, steam_id): if not isinstance(steam_id, SteamID): steam_id = SteamID(steam_id) self.client = client self.steam_id = steam_id self.steam_64 = long(steam_id) # these get updated when steam provides us the info self.name = '[unknown]' self.relationship = 0
def __init__(self, steamid, key=None, language='en', identity_secret='', poll_delay=30): EventEmitter.__init__(self) self.session = aiohttp.ClientSession() ConfManager.__init__(self, identity_secret, steamid, self.session) self.steamid = SteamID(steamid) if not self.steamid.isValid( ) or not self.steamid.type == SteamID.Type['INDIVIDUAL']: raise ValueError( f"Steam ID {self.steamid} is not valid, or is not a user ID") self.key = key self.language = language self.poll_delay = poll_delay self.logged_in = False self._trade_cache = {} self._conf_cache = {}
def login(steamid, login_key): steamid = SteamID(steamid) session_key, crypted_key, _ = encrypt.make_session_key() ticket = encrypt.encrypt(login_key, session_key) key = call('ISteamUserAuth', 'AuthenticateUser', data={ 'steamid': steamid.id, 'sessionkey': crypted_key, 'encrypted_loginkey': ticket, }) return key.json().get('authenticateuser', {}).get('token')
class TradeManager(EventEmitter, ConfManager): """ This is the TradeManager object, it inherits from the ConfManager and EventEmitter objects. """ def __init__(self, steamid, key=None, language='en', identity_secret='', poll_delay=30): EventEmitter.__init__(self) self.session = aiohttp.ClientSession() ConfManager.__init__(self, identity_secret, steamid, self.session) self.steamid = SteamID(steamid) if not self.steamid.isValid( ) or not self.steamid.type == SteamID.Type['INDIVIDUAL']: raise ValueError( f"Steam ID {self.steamid} is not valid, or is not a user ID") self.key = key self.language = language self.poll_delay = poll_delay self.logged_in = False self._trade_cache = {} self._conf_cache = {} async def login(self, async_client): """ Take a AsyncClient object, using the do_login method is optional. You must have passed in all credentials and requirements to the client already. Please make sure to run this first, just like in the example.py :param async_client: Does not need to be logged in, import from pytrade.login """ if async_client.logged_in: self.session = async_client.session self.logged_in = True else: session = await async_client.do_login() if await async_client.test_login(): self.session = session self.logged_in = True else: raise ValueError("Login Failed") self.emit('logged_on') async def _run_forever(self): while True: self.emit('start_poll') #Check for current actions, then parse them await self._trade_poll() await self._confirmation_poll() self.emit('end_poll') await asyncio.sleep(self.poll_delay) def run_forever(self): """ Run the bot forever, unless the time sense calling the function is greater than timeout. :param timeout: :class int/float: :return: (Will never return, but if it does, None) """ loop = asyncio.get_event_loop() asyncio.ensure_future(self._run_forever()) loop.run_forever() @require_key async def get_trade_offers(self, active_only=True, sent=False, received=True): """ get your trade offers, key is required. :param active_only: :param sent: :param received: :return trade_list: """ offers = await self.api_call('GET', 'IEconService', 'GetTradeOffers', 'v1', langauge=self.language, get_descriptions=1, active_only=1, get_sent_offers=1, get_received_offers=1, key=self.key) sent_offers = [] got_offers = [] trade_offers = {} if sent: for offer in offers['response'].get('trade_offers_sent', []): trade_offer = TradeOffer( offer, offers['response'].get('descriptions', []), self) if trade_offer.trade_offer_state != ETradeOfferState.Active and active_only: continue sent_offers.append(trade_offer) trade_offers['sent'] = sent_offers if received: for offer in offers['response'].get('trade_offers_received', []): trade_offer = TradeOffer( offer, offers['response'].get('descriptions', []), self) if trade_offer.trade_offer_state != ETradeOfferState.Active and active_only: continue got_offers.append(trade_offer) trade_offers['received'] = got_offers return trade_offers async def _trade_poll(self): #First, check for new trades trades = await self.get_trade_offers(active_only=False, sent=True, received=True) got_trades = trades.get('received', []) sent_trades = trades.get('sent', []) for trade in got_trades: if trade.tradeofferid not in self._trade_cache.keys(): self._trade_cache[trade.tradeofferid] = trade if trade.trade_offer_state == ETradeOfferState.Active: self.emit('new_trade', trade) else: self._test_states(trade) for trade in sent_trades: if trade.tradeofferid not in self._trade_cache.keys(): self._trade_cache[trade.tradeofferid] = trade self.emit('trade_sent', trade) else: self._test_states(trade) async def _confirmation_poll(self): confs = await self.get_confirmations() for conf in confs: if conf.id not in self._conf_cache.keys(): self._conf_cache[conf.id] = conf self.emit('new_conf', conf) def _test_states(self, trade): if trade.trade_offer_state != self._trade_cache[ trade.tradeofferid].trade_offer_state: self._trade_cache[trade.tradeofferid] = trade if trade.trade_offer_state == ETradeOfferState.Accepted: self.emit('trade_accepted', trade) elif trade.trade_offer_state == ETradeOfferState.Canceled: self.emit('trade_canceled', trade) elif trade.trade_offer_state == ETradeOfferState.Declined: self.emit('trade_declined', trade) elif trade.trade_offer_state == ETradeOfferState.Expired: self.emit('trade_expired', trade) elif trade.trade_offer_state == ETradeOfferState.Countered: self.emit('trade_countered', trade) else: self.emit('trade_state_changed', trade) async def api_call(self, method, api, call, version, **data): """ Request data from steam through this function when using the API :param method: :param api: :param call: :param version: :kwargs data: :return json: """ new_url = SteamUrls.Api.value + '/'.join(['', api, call, version]) if method.lower() == 'get': async with self.session.get(new_url, params=data) as resp: #print(await resp.json()) return await resp.json() #Should be json elif method.lower() == 'post': async with self.session.post(new_url, data=data) as resp: return await resp.json() elif method.lower() == 'delete': async with self.session.delete(new_url, data=data) as resp: return await resp.json() elif method.lower() == 'put': async with self.session.put(new_url, data=data) as resp: return await resp.json() else: raise ValueError(f"Invalid method: {method}") def get_session(self): return self.session.cookie_jar._cookies['steamcommunity.com'][ 'sessionid'].value def parse_token_from_url(self, trade_offer_url: str): reg = re.compile( "https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=(?P<token>[a-zA-Z0-9-_]+)" ) match = reg.match(trade_offer_url) if not match: raise Exception("Unable to match token from trade_offer_url") return match['token'] async def get_inventory(self, steamid: SteamID, appid, contextid=2, tradable_only=1): """ Get a user's inventory :param steamid: :param appid: :param contextid: :param tradable_only: :return: """ async with self.session.get( f"https://steamcommunity.com/profiles/{steamid.toString()}/inventory/json/{appid}/{contextid}", params={'trading': tradable_only}, headers= { "Referer": f"https://steamcommunity.com/profiles/{steamid.toString()}/inventory" }) as resp: inv = await resp.json() if not inv['success']: return [] items = [] for _, item_id in inv['rgInventory'].items(): id = item_id['classid'] + '_' + item_id['instanceid'] item_desc = inv['rgDescriptions'].get(id) if item_desc is None: items.append(Item(item_id, True)) items.append(Item(merge_item(item_id, item_desc))) return items
class TradeOffer: def __init__(self, data, descriptions=None, manager=None, parse_items=True): if descriptions is None: descriptions = [] self._load(data, descriptions, manager, parse_items) def _load(self, data, desc, manager, parse_items): # Define basic variables, set attributes self.raw = data self.raw_descriptions = desc self.manager = manager self.tradeofferid = data.get('tradeofferid', '') self.steamid_other = SteamID( str(data.get('accountid_other', 309304171) + 76561197960265728)) self.message = data.get('message') self.expiration_time = data.get('expiration_time') self.is_our_offer = data.get('is_our_offer') self.from_real_time_trade = data.get('from_real_time_trade') self.escrow_end_date = data.get('escrow_end_date') self.items_to_give = [] self.items_to_receive = [] self._counter = 0 self._sent = True if self.tradeofferid else False # Check each enum for the one equal to the state. Should it raise an error, if the status isn't valid? for enum in ETradeOfferState: if enum.value == data.get('trade_offer_state', 1): self.trade_offer_state = enum # If it shouldn't parse the items, just set them. # REMEMBER: if you choose to have items pre-parsed, it will assume you supplied both attributes if not parse_items: self.items_to_give = data['items_to_give'] self.items_to_receive = data['items_to_receive'] return # For each item in the items we're giving, check it for item in data.get('items_to_give', []): if not item.get('missing', False): # If missing, it's not in the trade for description in desc: # For each description in descriptions, check to see it matches item desc_id = description['classid'] + '_' + description[ 'instanceid'] if desc_id == item['classid'] + '_' + item['instanceid']: self.items_to_give.append( Item(merge_item( item, description))) # If so, parse and append break else: self.items_to_give.append(Item( item, True)) # If it's missing, just add the item ids as an Item for item in data.get('items_to_receive', []): if not item.get('missing', False): for description in desc: desc_id = description['classid'] + '_' + description[ 'instanceid'] if desc_id == item['classid'] + '_' + item['instanceid']: self.items_to_receive.append( Item(merge_item(item, description))) break else: self.items_to_receive.append(Item(item, True)) @require_manager_key async def update(self): this_offer = await self.manager.api_call( 'GET', 'IEconService', 'GetTradeOffer', 'v1', key=self.manager.key, tradeofferid=self.tradeofferid, language=self.manager.language) self._load(this_offer['response']['offer'], this_offer['response'].get('descriptions', []), self.manager, True) @require_manager_key async def cancel(self): if self.trade_offer_state != ETradeOfferState.Active and \ self.trade_offer_state != ETradeOfferState.CreatedNeedsConfirmation: raise ValueError('This trade is not active') elif not self.is_our_offer: raise ValueError("This is not our trade, try declining.") return await self.manager.api_call('POST', 'IEconService', 'CancelTradeOffer', 'v1', key=self.manager.key, tradeofferid=self.tradeofferid) @require_manager_key async def decline(self): if self.trade_offer_state != ETradeOfferState.Active and \ self.trade_offer_state != ETradeOfferState.CreatedNeedsConfirmation: raise ValueError('This trade is not active') elif self.is_our_offer: raise ValueError("This is our trade, try canceling.") return await self.manager.api_call('POST', 'IEconService', 'DeclineTradeOffer', 'v1', key=self.manager.key, tradeofferid=self.tradeofferid) @require_manager_key async def accept(self, token=''): if self.trade_offer_state != ETradeOfferState.Active: raise ValueError( "This trade is not active. If you think it is, try updating it" ) session_cookie = self.manager.get_session() url = SteamUrls.Community.value + '/tradeoffer/' + self.tradeofferid + '/accept' info = { 'sessionid': session_cookie, 'tradeofferid': self.tradeofferid, 'serverid': 1, 'partner': self.steamid_other.toString(), 'captcha': '' } headers = { 'Referer': f"https://steamcommunity.com/tradeoffer/{self.tradeofferid}/" } async with self.manager.session.post(url, data=info, headers=headers) as resp: resp_json = await resp.json() if resp_json.get('needs_mobile_confirmation', False): if self.manager.id_secret: await asyncio.sleep(2) tries = 1 while tries <= 3: try: conf = await self.manager.get_trade_confirmation( self.tradeofferid) except IndexError: tries += 1 await asyncio.sleep(2) else: await conf.confirm() return True return False return True @require_manager_key async def ship(self, counter='', token=''): if not self._sent: raise ValueError("Trade already shipped") if not self.items_to_receive and not self.items_to_give: raise ValueError("Can't have trade with 0 items") our_offer = { "newversion": True, "version": 4, "me": { "assets": list(map(get_ids, self.items_to_give)), "currency": [], "ready": False }, "them": { "assets": list(map(get_ids, self.items_to_receive)), "currency": [], "ready": False } } trade_prams = '{}' if token: trade_prams = {'trade_offer_access_token': token} trade_prams = json.dumps(trade_prams) data = { 'sessionid': self.manager.get_session(), 'serverid': 1, 'partner': self.steamid_other.toString(), 'tradeoffermessage': self.message[:128], 'json_tradeoffer': json.dumps(our_offer), 'captcha': '', 'trade_offer_create_params': trade_prams } headers = { 'Referer': SteamUrls.Community.value + '/tradeoffer/new/?partner=' + self.steamid_other.accountid, 'Origin': SteamUrls.Community.value } async with self.manager.session.post(SteamUrls.Community.value + '/tradeoffer/new/send', data=data, headers=headers): pass
steamUser = {"Timestamp": 0} with open(steamLoginUsersPath, "r", encoding="UTF-8", errors="ignore") as steamLoginUsersFile: steamLoginUsers = vdf.load(steamLoginUsersFile, mapper=CaseInsensitiveDict) steamLoginUsers = steamLoginUsers["users"] for userSteamID64 in steamLoginUsers: curSteamUser = steamLoginUsers[userSteamID64] if str(steamLoginUsers[userSteamID64]["mostrecent"]) == "1": steamUser = {"steamID64": userSteamID64, "PersonaName": curSteamUser["PersonaName"], "Timestamp": int(curSteamUser["Timestamp"])} break elif int(steamLoginUsers[userSteamID64]["Timestamp"]) > steamUser["Timestamp"]: steamUser = {"steamID64": userSteamID64, "PersonaName": curSteamUser["PersonaName"], "Timestamp": int(curSteamUser["Timestamp"])} if steamUser["Timestamp"] > 0: steamUser["steamID3"] = SteamID(steamUser["steamID64"]).steam3() print("\nGot Most Recent Steam User: "******"PersonaName"] + " (" + steamUser["steamID64"] + " / " + steamUser["steamID3"] + ")") else: sys.exit(colored("Error: Could not find Most Recent Steam User! Have you ever launched Steam?" + contactInfo, "red")) # Find GMod foundGMod = False gmodPath = "" possibleGModPaths = [ ["steamapps", "common", "GarrysMod"], ["common", "GarrysMod"], ["GarrysMod"] ] for path in steamLibraries: for curGModPath in possibleGModPaths: curGModPath = os.path.join(path, *curGModPath)
class TradeManager(EventEmitter, ConfManager): """ This is the TradeManager object, it inherits from the ConfManager and EventEmitter objects. """ def __init__(self, steamid, key=None, language: str='en', identity_secret: str='', poll_delay: int=30, login_delay_time: int=0): """ :param steamid: stemid64 :param key: steam api key :param language: :param identity_secret: :param poll_delay: how often trades should be polled (too often can cause errors, too infrequent can make your bot too slow to respond :param login_delay_time: how long to wait after our session died to retry """ EventEmitter.__init__(self) self.session = aiohttp.ClientSession() ConfManager.__init__(self, identity_secret, steamid, self.session) self.steamid = SteamID(steamid) if not self.steamid.isValid() or not self.steamid.type == SteamID.Type['INDIVIDUAL']: raise ValueError(f"Steam ID {self.steamid} is not valid, or is not a user ID") self.key = key self.language = language self.poll_delay = poll_delay self.last_poll = 0 self.logged_in = False self._trade_cache = {} self._conf_cache = {} self.first_run = True self.login_delay_time = login_delay_time async def login(self, async_client): """ Take a AsyncClient object, using the do_login method is optional. You must have passed in all credentials and requirements to the client already. Please make sure to run this first, just like in the example.py :param async_client: Does not need to be logged in, import from pytrade.login """ if self.first_run: self.async_client = copy(async_client) self.first_run = False if async_client.logged_in: self.session = async_client.session self.logged_in = True else: session = await async_client.do_login() if await async_client.test_login(): self.session = session self.logged_in = True else: raise ValueError("Login Failed") self.emit("logged_on") async def poll(self): if time() - self.last_poll > self.poll_delay: self.emit("trade_start_poll") await self._trade_poll() await self._confirmation_poll() self.emit("trade_end_poll") self.last_poll = time() @require_key async def get_trade_offers(self, active_only=True, sent=False, received=True): """ Get your trade offers, key is required. :param active_only: :param sent: :param received: :return trade_list: """ try: offers = await self.api_call('GET', 'IEconService', 'GetTradeOffers', 'v1', langauge=self.language, get_descriptions=1, active_only=1, get_sent_offers=1, get_received_offers=1, key=self.key) except ValueError: await self.login(self.async_client) offers = await self.api_call('GET', 'IEconService', 'GetTradeOffers', 'v1', langauge=self.language, get_descriptions=1, active_only=1, get_sent_offers=1, get_received_offers=1, key=self.key) except (aiohttp.client_exceptions.ClientOSError, aiohttp.client_exceptions.ServerDisconnectedError): # aiohttp.client_exceptions.ClientOSError: # [WinError 10054] An existing connection was forcibly closed by the remote host offers = await self.api_call('GET', 'IEconService', 'GetTradeOffers', 'v1', langauge=self.language, get_descriptions=1, active_only=1, get_sent_offers=1, get_received_offers=1, key=self.key) if offers[0]: offers = offers[1] else: return False, offers[1] sent_offers = [] got_offers = [] trade_offers = {} if sent: for offer in offers['response'].get('trade_offers_sent', []): trade_offer = TradeOffer(offer, offers['response'].get('descriptions', []), self) if trade_offer.trade_offer_state != ETradeOfferState.Active and active_only: continue sent_offers.append(trade_offer) trade_offers['sent'] = sent_offers if received: for offer in offers['response'].get('trade_offers_received', []): trade_offer = TradeOffer(offer, offers['response'].get('descriptions', []), self) if trade_offer.trade_offer_state != ETradeOfferState.Active and active_only: continue got_offers.append(trade_offer) trade_offers['received'] = got_offers return True, trade_offers async def _trade_poll(self): trades = await self.get_trade_offers(True, True, True) if not trades[0]: self.emit('trade_poll_error', trades[1]) return trades = trades[1] got_trades = trades.get('received', []) sent_trades = trades.get('sent', []) for trade in got_trades: if trade.tradeofferid not in self._trade_cache.keys(): self._trade_cache[trade.tradeofferid] = trade if trade.trade_offer_state == ETradeOfferState.Active: self.emit('new_trade', trade) else: self._test_states(trade) for trade in sent_trades: if trade.tradeofferid not in self._trade_cache.keys(): self._trade_cache[trade.tradeofferid] = trade self.emit('trade_sent', trade) else: self._test_states(trade) async def _confirmation_poll(self): confs = await self.get_confirmations() if not confs[0]: self.emit('trade_poll_error', confs[1]) return for conf in confs[1]: if conf.id not in self._conf_cache.keys(): self._conf_cache[conf.id] = conf self.emit('new_conf', conf) def _test_states(self, trade): if trade.trade_offer_state != self._trade_cache[trade.tradeofferid].trade_offer_state: self._trade_cache[trade.tradeofferid] = trade if trade.trade_offer_state == ETradeOfferState.Accepted: self.emit('trade_accepted', trade) elif trade.trade_offer_state == ETradeOfferState.Canceled: self.emit('trade_canceled', trade) elif trade.trade_offer_state == ETradeOfferState.Declined: self.emit('trade_declined', trade) elif trade.trade_offer_state == ETradeOfferState.Expired: self.emit('trade_expired', trade) elif trade.trade_offer_state == ETradeOfferState.Countered: self.emit('trade_countered', trade) else: self.emit('trade_state_changed', trade) async def api_call(self, method, api, call, version, **data): """ Request data from steam through this function when using the API :param method: :param api: :param call: :param version: :kwargs data: :return json: """ new_url = SteamUrls.Api.value + '/'.join(['', api, call, version]) try: if method.lower() == 'get': async with self.session.get(new_url, params=data) as resp: j = await resp.json() return True, j elif method.lower() == 'post': async with self.session.post(new_url, data=data) as resp: j = await resp.json() return True, j elif method.lower() == 'delete': async with self.session.delete(new_url, data=data) as resp: j = await resp.json() return True, j elif method.lower() == 'put': async with self.session.put(new_url, data=data) as resp: j = await resp.json() return True, j else: raise ValueError(f"Invalid method: {method}") except aiohttp.ContentTypeError: html = await resp.text() return False, html def get_session(self): return self.session.cookie_jar._cookies['steamcommunity.com']['sessionid'].value def parse_token_from_url(self, trade_offer_url: str): reg = re.compile("https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=(?P<token>[a-zA-Z0-9-_]+)") match = reg.match(trade_offer_url) if not match: return False, match return True, match['token'] async def get_inventory(self, steamid: SteamID, appid, contextid=2, tradable_only=1): """ Get a user's inventory :param steamid: :param appid: :param contextid: :param tradable_only: :return: """ url = f"https://steamcommunity.com/profiles/{steamid.toString()}/inventory/json/{appid}/{contextid}" params = {'trading': tradable_only} headers = {"Referer": f"https://steamcommunity.com/profiles/{steamid.toString()}/inventory"} async with self.session.get(url, params=params, headers=headers) as resp: inv = await resp.json() if not inv['success']: return False, inv items = [] for _, item_id in inv['rgInventory'].items(): id = item_id['classid'] + '_' + item_id['instanceid'] item_desc = inv['rgDescriptions'].get(id) if item_desc is None: items.append(Item(item_id, True)) items.append(Item(merge_item(item_id, item_desc))) return True, items
def __init__(self, userid: int, name: str, steamid: str, connected: str): self.userid = userid self.name = name self.steamid = SteamID(steamid) self.connected_str = connected self.connected = parse_time(connected)