raju014 commited on
Commit
fa066c8
·
verified ·
1 Parent(s): bccf129

Upload 9 files

Browse files
Files changed (4) hide show
  1. Dockerfile +6 -5
  2. bot.py +319 -0
  3. game_history.csv +54 -0
  4. run_bot.py +1 -1
Dockerfile CHANGED
@@ -1,13 +1,14 @@
1
  FROM mcr.microsoft.com/playwright/python:v1.42.0-jammy
2
 
3
- RUN useradd -m -u 1000 user
4
- USER user
5
- ENV HOME=/home/user \
6
- PATH=/home/user/.local/bin:$PATH
 
7
 
8
  WORKDIR $HOME/app
9
 
10
- COPY --chown=user . $HOME/app
11
 
12
  RUN pip install --no-cache-dir --upgrade pip && \
13
  pip install --no-cache-dir -r requirements.txt
 
1
  FROM mcr.microsoft.com/playwright/python:v1.42.0-jammy
2
 
3
+ # Playwright image already has a default user "pwuser" with UID 1000,
4
+ # which is exactly what Hugging Face needs. We don't need to create one.
5
+ USER pwuser
6
+ ENV HOME=/home/pwuser \
7
+ PATH=/home/pwuser/.local/bin:$PATH
8
 
9
  WORKDIR $HOME/app
10
 
11
+ COPY --chown=pwuser . $HOME/app
12
 
13
  RUN pip install --no-cache-dir --upgrade pip && \
14
  pip install --no-cache-dir -r requirements.txt
bot.py ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import logging
3
+ from playwright.async_api import async_playwright
4
+ from playwright_stealth import Stealth
5
+ import os
6
+ import json
7
+ from dotenv import load_dotenv
8
+ from datetime import datetime
9
+
10
+ # Setup logging
11
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
12
+ logger = logging.getLogger(__name__)
13
+
14
+ class AviatorBot:
15
+ def __init__(self, config):
16
+ self.config = config
17
+ self.browser = None
18
+ self.context = None
19
+ self.page = None
20
+ self.game_frame = None
21
+ self.is_running = False
22
+ self.dry_run = config.get('DRY_RUN', True) # Default to True for safety
23
+
24
+ async def setup(self):
25
+ """Initialize browser, stealth mode and session state"""
26
+
27
+ # Performance/Server args
28
+ launch_args = ["--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"]
29
+
30
+ self.browser = await self.playwright.chromium.launch(
31
+ headless=True,
32
+ args=["--disable-blink-features=AutomationControlled"]
33
+ )
34
+
35
+ # Load session state if exists
36
+ state_path = "state.json"
37
+ if os.path.exists(state_path):
38
+ logger.info("Loading existing session state from state.json")
39
+ self.context = await self.browser.new_context(
40
+ storage_state=state_path,
41
+ viewport={'width': 1920, 'height': 1080},
42
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
43
+ )
44
+ else:
45
+ self.context = await self.browser.new_context(
46
+ viewport={'width': 1920, 'height': 1080},
47
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
48
+ )
49
+
50
+ self.page = await self.context.new_page()
51
+ try:
52
+ await Stealth().apply_stealth_async(self.page)
53
+ logger.info("Stealth applied.")
54
+ except Exception as e:
55
+ logger.warning(f"Stealth application failed: {e}")
56
+ logger.info("Browser setup complete.")
57
+
58
+ async def login(self):
59
+ """Use state.json if it exists, otherwise do fresh login via visible browser"""
60
+ if os.path.exists("state.json"):
61
+ logger.info("state.json found — checking session validity...")
62
+ # Navigate to site to verify session
63
+ await self.page.goto("https://luckyeie.com/", wait_until="domcontentloaded", timeout=60000)
64
+ await asyncio.sleep(3)
65
+
66
+ # If Login button is still visible, session is invalid
67
+ login_btn = self.page.locator("button:visible:has-text('Log in'), button:visible:has-text('Login')").first
68
+ is_logged_out = await login_btn.count() > 0
69
+
70
+ if is_logged_out:
71
+ logger.warning("Session is invalid (Login button visible). Re-logging in...")
72
+ if os.path.exists("state.json"): os.remove("state.json")
73
+ await self._do_login()
74
+ else:
75
+ logger.info("Session is valid. Proceeding to game.")
76
+ return
77
+
78
+ await self._do_login()
79
+
80
+ async def _do_login(self):
81
+ """Perform actual browser login and save HttpOnly cookies"""
82
+ logger.info("Opening login page in browser...")
83
+ await self.page.goto("https://luckyeie.com/", wait_until="domcontentloaded", timeout=60000)
84
+ await asyncio.sleep(2)
85
+
86
+ try:
87
+ # Click login button on header
88
+ logger.info("Step 1: Clicking header login button...")
89
+ login_btn = self.page.locator("button:visible:has-text('Log in'), button:visible:has-text('Login'), a:visible:has-text('Log in'), a:visible:has-text('Login')").first
90
+ await login_btn.click()
91
+ await asyncio.sleep(3)
92
+ await self.page.screenshot(path="login_step1_modal.png")
93
+
94
+ # Switch to E-mail/Email tab in login modal
95
+ logger.info("Step 2: Switching to Email tab...")
96
+ # Use the specific data-test-id we found: signInByPhone-form-tab-email
97
+ email_tab = self.page.locator("[data-test-id*='tab-email'], button:has-text('Email'), [role='tab']:has-text('Email')").locator("visible=true").first
98
+ try:
99
+ await email_tab.wait_for(state='visible', timeout=7000)
100
+ await email_tab.click(force=True)
101
+ logger.info("Email tab clicked (force=True).")
102
+ await asyncio.sleep(2)
103
+
104
+ # Double check if we switched - if not try clicking again or using dispatch_event
105
+ # Look for an email input to confirm switch
106
+ if await self.page.locator("input[type='email'], input[placeholder*='mail' i]").locator("visible=true").count() == 0:
107
+ logger.warning("Email input not visible after click, trying dispatch_event('click')...")
108
+ await email_tab.evaluate("el => el.click()")
109
+ await asyncio.sleep(2)
110
+ except Exception as e:
111
+ logger.warning(f"Email tab switch failed: {e}")
112
+ await self.page.screenshot(path="login_step2_email_tab.png")
113
+
114
+ # Fill email
115
+ logger.info("Step 3: Filling email...")
116
+ email_input = self.page.locator("input[type='email'], input[name='email'], input[placeholder*='mail' i]").locator("visible=true").first
117
+ await email_input.wait_for(state='visible', timeout=10000)
118
+ await email_input.fill(self.config['EMAIL'])
119
+
120
+ # Fill password
121
+ logger.info("Step 4: Filling password...")
122
+ pass_input = self.page.locator("input[type='password'], input[name='password']").locator("visible=true").first
123
+ await pass_input.fill(self.config['PASSWORD'])
124
+ await self.page.screenshot(path="login_step3_filled.png")
125
+
126
+ # Submit login
127
+ logger.info("Step 5: Submitting login form...")
128
+ # Target the green 'Log in' button inside the modal specifically
129
+ submit_btn = self.page.locator("[class*='modal'] button:has-text('Log in'), [class*='modal'] button[type='submit']").locator("visible=true").first
130
+ await submit_btn.click()
131
+
132
+ logger.info("Credentials submitted. Waiting 15s for login to finalize and redirect...")
133
+ await asyncio.sleep(15)
134
+ await self.page.screenshot(path="login_step4_after_submit.png")
135
+
136
+ # Save full state including HttpOnly cookies from Playwright context
137
+ await self.context.storage_state(path="state.json")
138
+ logger.info("SUCCESS: Full session saved to state.json (includes HttpOnly cookies)")
139
+
140
+ except Exception as e:
141
+ logger.error(f"Login error: {e}. Please login manually in the open browser window.")
142
+ logger.info("Waiting 30s for manual login...")
143
+ await asyncio.sleep(30)
144
+ await self.context.storage_state(path="state.json")
145
+ logger.info("Session saved after manual window.")
146
+
147
+ async def enter_game(self):
148
+ """Navigate to Aviator and find the game iframe"""
149
+ logger.info(f"Navigating to: {self.config['BASE_URL']}")
150
+ await self.page.goto(self.config['BASE_URL'], wait_until="domcontentloaded", timeout=60000)
151
+ logger.info("Game page loaded. Waiting 5s for game iframe to initialize...")
152
+ await asyncio.sleep(5)
153
+
154
+ try:
155
+ # Search all frames for any spribegaming/aviator URL
156
+ for f in self.page.frames:
157
+ logger.info(f" Frame URL: {f.url}")
158
+ if any(k in f.url for k in ["spribegaming", "aviator", "launch.spribe"]):
159
+ self.game_frame = f
160
+ logger.info(f"✅ Aviator iframe found: {f.url}")
161
+ return True
162
+
163
+ logger.error("❌ Could not find Aviator game iframe after scanning all frames.")
164
+ return False
165
+ except Exception as e:
166
+ logger.error(f"Error entering game: {e}")
167
+ return False
168
+
169
+ async def monitor_results(self):
170
+ """Monitor game events by polling the DOM inside the Aviator iframe"""
171
+ logger.info("Monitoring results started via DOM polling...")
172
+
173
+ last_result = ""
174
+ last_active = ""
175
+
176
+ while self.is_running:
177
+ try:
178
+ if not self.game_frame:
179
+ await asyncio.sleep(1)
180
+ continue
181
+
182
+ # Execute JS inside the iframe to grab the current state
183
+ state = await self.game_frame.evaluate('''() => {
184
+ let result = null;
185
+ let active = null;
186
+
187
+ // Historical payout bubbles (most recent is usually first or last depending on layout)
188
+ let bubbles = Array.from(document.querySelectorAll('.payouts-block app-bubble-multiplier, .bubble-multiplier, app-bubble-multiplier, .payout'));
189
+ if (bubbles.length > 0) {
190
+ // Usually the first one in the list is the most recent completed
191
+ result = bubbles[0].innerText.trim();
192
+ }
193
+
194
+ // The large active flying multiplier on screen
195
+ let main_text = document.querySelector('.fliers-block .fliers-x, .current-multiplier, [class*="fliers-x"], app-multiplier');
196
+ if (main_text) {
197
+ active = main_text.innerText.trim();
198
+ }
199
+
200
+ return {result: result, active: active};
201
+ }''')
202
+
203
+ result = state.get('result')
204
+ active = state.get('active')
205
+
206
+ # Detect active flight (Round Start)
207
+ if active and active != last_active and "x" in active.lower() and "wait" not in active.lower():
208
+ # We only log round start if it's a small multiplier indicating takeoff
209
+ val_str = active.lower().replace("x", "").strip()
210
+ try:
211
+ if float(val_str) < 1.10 and last_active == "":
212
+ logger.info(">>> NEW ROUND STARTING...")
213
+ await self.on_round_start()
214
+ except:
215
+ pass
216
+ last_active = active
217
+
218
+ # Detect when flight ends (active disappears or says 'flew away')
219
+ if not active and last_active:
220
+ last_active = ""
221
+
222
+ # Detect new completed result
223
+ if result and result != last_result and "x" in result.lower():
224
+ last_result = result
225
+ logger.info(f">>> ROUND RESULT: {result}")
226
+ self.log_result(result)
227
+
228
+ # Poll every 0.3 seconds
229
+ await asyncio.sleep(0.3)
230
+
231
+ except Exception as e:
232
+ # Ignore expected errors like frame detached during reload
233
+ if "Target page, context or browser has been closed" in str(e):
234
+ break
235
+ await asyncio.sleep(1)
236
+
237
+ async def on_round_start(self):
238
+ """Logic to execute when a new round begins"""
239
+ if self.dry_run:
240
+ logger.info("[DRY-RUN] Round started. Skipping bet.")
241
+ return
242
+
243
+ # 1. Place Bet
244
+ bet_success = await self.place_bet(self.config['BASE_BET'])
245
+ if bet_success:
246
+ # 2. Start looking for target multiplier to Cash Out
247
+ asyncio.create_task(self.auto_cash_out())
248
+
249
+ async def place_bet(self, amount):
250
+ """Interact with the Bet button"""
251
+ if self.dry_run:
252
+ return False
253
+
254
+ try:
255
+ if self.game_frame:
256
+ # Set amount in input field first
257
+ # Selector: .input .bet-input
258
+ await self.game_frame.fill(".input .bet-input", str(amount))
259
+
260
+ # Click 'BET' button
261
+ # Selector: button.bet-button.green
262
+ bet_btn = self.game_frame.locator("button.bet-button").first
263
+ await bet_btn.click()
264
+ logger.info(f"Bet placed: {amount}")
265
+ return True
266
+ except Exception as e:
267
+ logger.error(f"Failed to place bet: {e}")
268
+ return False
269
+
270
+ async def auto_cash_out(self):
271
+ """Monitor live multiplier and click Cash Out at target"""
272
+ if self.dry_run:
273
+ return
274
+
275
+ target = self.config['TARGET_MULTIPLIER']
276
+ logger.info(f"Waiting for {target}x to cash out...")
277
+ # ... logic for actual cashout ...
278
+
279
+ def log_result(self, result):
280
+ """Save multiplier results to a local file for strategy analysis"""
281
+ try:
282
+ timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
283
+ # Check if file needs header
284
+ needs_header = not os.path.exists("game_history.csv")
285
+ with open("game_history.csv", "a") as f:
286
+ if needs_header:
287
+ f.write("timestamp,multiplier\n")
288
+ f.write(f"{timestamp},{result}\n")
289
+ logger.info(f"Result logged: {result}")
290
+ except Exception as e:
291
+ logger.error(f"Failed to log result: {e}")
292
+
293
+ async def start(self):
294
+ self.is_running = True
295
+
296
+ logger.info("[START] Initializing Playwright...")
297
+ async with async_playwright() as self.playwright:
298
+ await self.setup()
299
+
300
+ await self.login()
301
+ if await self.enter_game():
302
+ logger.info("Game entered. Starting monitor loop...")
303
+ await self.monitor_results()
304
+
305
+ async def main_bot():
306
+ load_dotenv()
307
+ config = {
308
+ "EMAIL": os.getenv("EMAIL"),
309
+ "PASSWORD": os.getenv("PASSWORD"),
310
+ "BASE_URL": os.getenv("BASE_URL"),
311
+ "DRY_RUN": os.getenv("DRY_RUN", "True").lower() == "true",
312
+ "BASE_BET": float(os.getenv("BASE_BET", 10.0)),
313
+ "TARGET_MULTIPLIER": float(os.getenv("TARGET_MULTIPLIER", 1.5))
314
+ }
315
+ bot = AviatorBot(config)
316
+ await bot.start()
317
+
318
+ if __name__ == "__main__":
319
+ asyncio.run(main_bot())
game_history.csv CHANGED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 4.70x
2
+ 2.38x
3
+ 5.79x
4
+ 1.90x
5
+ 1.17x
6
+ 1.29x
7
+ 48.88x
8
+ 4.14x
9
+ 8.15x
10
+ 1.06x
11
+ 1.31x
12
+ 8.16x
13
+ 1.07x
14
+ 5.23x
15
+ 1.36x
16
+ 3.51x
17
+ 1.21x
18
+ 2.97x
19
+ 1.07x
20
+ 2.54x
21
+ 1.41x
22
+ 1.32x
23
+ 2.39x
24
+ 2.17x
25
+ 1.00x
26
+ 1.92x
27
+ 1.11x
28
+ 1.09x
29
+ 17.63x
30
+ 232.59x
31
+ 2.45x
32
+ 4.16x
33
+ 1.00x
34
+ 1.04x
35
+ 1.43x
36
+ 2.41x
37
+ 3.57x
38
+ 2.09x
39
+ 1.41x
40
+ 28.05x
41
+ 1.03x
42
+ 1.15x
43
+ 3.60x
44
+ 1.78x
45
+ 1.21x
46
+ 1.79x
47
+ 8.77x
48
+ 4.94x
49
+ 2.38x
50
+ 1.00x
51
+ 1.83x
52
+ 751.99x
53
+ 3.80x
54
+ 1.72x
run_bot.py CHANGED
@@ -1,7 +1,7 @@
1
  import asyncio
2
  import logging
3
  import os
4
- from src.bot import main_bot
5
 
6
  if __name__ == "__main__":
7
  try:
 
1
  import asyncio
2
  import logging
3
  import os
4
+ from bot import main_bot
5
 
6
  if __name__ == "__main__":
7
  try: