confirm_poll.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #!/usr/bin/env python3
  2. """
  3. confirm_poll.py
  4. Usage:
  5. # Use config and perform initial purchase to get confirmation id, then poll
  6. python confirm_poll.py
  7. # Provide an existing confirmation id to poll directly
  8. python confirm_poll.py --confirmation 1374710298789955445
  9. This script mimics the browser behavior: when a buylisting returns need_confirmation,
  10. it extracts the confirmation id and repeatedly POSTs the buylisting URL with that
  11. confirmation id until the purchase succeeds or times out.
  12. """
  13. import time
  14. import json
  15. import argparse
  16. from urllib.parse import urlencode
  17. import requests
  18. try:
  19. import config
  20. except Exception:
  21. print("❌ Could not import config.py. Make sure you're running this from the steam-buy directory.")
  22. raise
  23. DEFAULT_HEADERS = {
  24. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  25. 'Accept': '*/*',
  26. 'Accept-Encoding': 'gzip, deflate, br',
  27. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
  28. 'Cache-Control': 'no-cache',
  29. 'Connection': 'keep-alive',
  30. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  31. 'Origin': 'https://steamcommunity.com',
  32. 'Referer': f'https://steamcommunity.com/market/listings/730/Test-Item'
  33. }
  34. def _post_buylisting(session, listing_id, params, headers):
  35. url = f"https://steamcommunity.com/market/buylisting/{listing_id}"
  36. data = urlencode(params)
  37. resp = session.post(url, data=data, headers=headers, timeout=30)
  38. text = resp.text or ""
  39. try:
  40. parsed = json.loads(text)
  41. except Exception:
  42. parsed = None
  43. return resp.status_code, text, parsed
  44. def poll_with_confirmation(session, listing_id, purchase_params, confirmation_id, max_attempts=12, interval=3):
  45. """Poll buylisting using the provided confirmation_id until success or timeout."""
  46. headers = DEFAULT_HEADERS.copy()
  47. # attach cookies via Cookie header (we assume config.BROWSER_COOKIE_STRING contains full cookie string)
  48. if getattr(config, 'BROWSER_COOKIE_STRING', None):
  49. headers['Cookie'] = config.BROWSER_COOKIE_STRING
  50. print(f"🔁 Start polling buylisting {listing_id} with confirmation id: {confirmation_id}")
  51. for attempt in range(1, max_attempts + 1):
  52. params = purchase_params.copy()
  53. params['confirmation'] = str(confirmation_id)
  54. status, text, parsed = _post_buylisting(session, listing_id, params, headers)
  55. print(f"[{attempt}/{max_attempts}] HTTP {status}")
  56. if parsed:
  57. print(json.dumps(parsed, indent=2, ensure_ascii=False))
  58. else:
  59. print(text[:400])
  60. if parsed and parsed.get('success') == 1:
  61. print("🎉 Purchase succeeded.")
  62. return True
  63. # If server still requests confirmation, wait and retry
  64. if parsed and parsed.get('need_confirmation') or status == 406:
  65. print(f"⏳ Still needs confirmation, sleeping {interval}s before retry...")
  66. time.sleep(interval)
  67. continue
  68. # Other non-success result -> log and break (or retry depending on code)
  69. print("⚠️ Received non-success response and not explicitly need_confirmation. Stopping.")
  70. return False
  71. print("❌ Max polling attempts reached without success.")
  72. return False
  73. def run(initial_only=False, provided_confirmation=None):
  74. listing_id = getattr(config, "LISTING_ID", None)
  75. purchase_params = getattr(config, "PURCHASE_PARAMS", {}).copy()
  76. if not listing_id or not purchase_params:
  77. print("❌ LISTING_ID or PURCHASE_PARAMS missing in config.py")
  78. return
  79. headers = DEFAULT_HEADERS.copy()
  80. if getattr(config, 'BROWSER_COOKIE_STRING', None):
  81. headers['Cookie'] = config.BROWSER_COOKIE_STRING
  82. session = requests.Session()
  83. # If user provided confirmation id, poll directly
  84. if provided_confirmation:
  85. return poll_with_confirmation(session, listing_id, purchase_params, provided_confirmation)
  86. # Otherwise perform initial purchase attempt to obtain confirmation id
  87. print("🔄 Sending initial purchase request with confirmation=0 ...")
  88. purchase_params['confirmation'] = '0'
  89. status, text, parsed = _post_buylisting(session, listing_id, purchase_params, headers)
  90. print(f"Initial HTTP {status}")
  91. if parsed:
  92. print(json.dumps(parsed, indent=2, ensure_ascii=False))
  93. else:
  94. print(text[:400])
  95. if parsed and parsed.get('success') == 1:
  96. print("🎉 Purchase succeeded without confirmation.")
  97. return True
  98. # Extract confirmation id
  99. confirmation_id = None
  100. if parsed:
  101. confirmation = parsed.get('confirmation') or {}
  102. confirmation_id = confirmation.get('confirmation_id') or parsed.get('confirmation_id')
  103. if not confirmation_id:
  104. print("⚠️ No confirmation_id found in initial response. If the response indicated need_confirmation,")
  105. print("you may need to open the account in a browser or confirm via mobileconf. Exiting.")
  106. return False
  107. print(f"🔢 Found confirmation_id: {confirmation_id}")
  108. # At this point we should ensure the mobile confirmation is accepted (by another process/tool).
  109. # This script will poll buylisting with the confirmation_id, assuming the confirmation
  110. # will be accepted externally (by SteamDesktopAuthenticator) or was auto-accepted.
  111. return poll_with_confirmation(session, listing_id, getattr(config, "PURCHASE_PARAMS", {}), confirmation_id)
  112. if __name__ == "__main__":
  113. parser = argparse.ArgumentParser(description="Poll Steam buylisting using confirmation id.")
  114. parser.add_argument("--confirmation", "-c", help="Confirmation id to poll (optional)", default=None)
  115. args = parser.parse_args()
  116. conf = args.confirmation
  117. result = run(provided_confirmation=conf)
  118. if result:
  119. print("Done: purchase complete.")
  120. else:
  121. print("Done: purchase NOT completed.")