77import base64
88import os
99import re
10- import os
11- from playwright .async_api import async_playwright , Playwright , Browser , BrowserContext
12-
13- _browsers : dict [int , dict [str , any ]] = {}
1410
11+ try :
12+ from playwright .async_api import async_playwright
13+ PLAYWRIGHT_AVAILABLE = True
14+ except ImportError :
15+ PLAYWRIGHT_AVAILABLE = False
1516
1617try :
1718 from cryptography .hazmat .primitives import serialization
@@ -29,14 +30,21 @@ def __init__(self, client: AsyncClient, logger: LoggerAdapter):
2930 self .admin_username = "admin"
3031 self .service_url = str (client .base_url )
3132
33+ def validate (self ) -> None :
34+ if not PLAYWRIGHT_AVAILABLE :
35+ self .logger .warning ("Playwright not available, skipping admin simulation" )
36+ return
37+
38+ self .logger .info ("Starting headless browser admin simulation..." )
39+
3240 async def _solve_admin_challenge (self , encrypted_b64 : str ):
3341 if not CRYPTO_AVAILABLE :
34- raise MumbleException ("Cryptography package missing for admin challenge" )
42+ raise MumbleException ("cryptography package missing for admin challenge" )
3543
3644 try :
3745 ciphertext = base64 .b64decode (encrypted_b64 )
3846 except Exception as e :
39- raise MumbleException (f"Invalid base64 in challenge" )
47+ raise MumbleException (f"Invalid base64 in challenge. " )
4048
4149 pem_path = os .path .join (os .path .dirname (__file__ ), 'admin_private.pem' )
4250 try :
@@ -60,6 +68,8 @@ async def _solve_admin_challenge(self, encrypted_b64: str):
6068
6169
6270 async def load_feedback_page (self ) -> None :
71+ self .validate ()
72+
6373 message_data = generate_admin_message ()
6474 message_text = message_data ["message" ]
6575 message_year = message_data ["year" ]
@@ -84,7 +94,7 @@ async def load_feedback_page(self) -> None:
8494
8595 match = re .search (r'<pre[^>]*>(.*?)</pre>' , response .text , re .DOTALL )
8696 if not match :
87- raise MumbleException ("Could not find encrypted challenge on page (please wrap it in <pre> tags )" )
97+ raise MumbleException ("Could not find encrypted challenge on page (please use <pre> tag )" )
8898
8999 encrypted_b64 = match .group (1 ).strip ()
90100
@@ -110,59 +120,36 @@ async def load_feedback_page(self) -> None:
110120 'secure' : ck .secure
111121 })
112122
113- browser = None
114- context : Optional [BrowserContext ] = None
115- try :
116- pid = os .getpid ()
117- entry = _browsers .get (pid )
118- if not entry or not entry ["browser" ].is_connected ():
119- if entry :
120- await entry ["browser" ].close ()
121- await entry ["playwright" ].stop ()
122-
123- p : Playwright = await async_playwright ().start ()
124- b : Browser = await p .chromium .launch (
125- headless = True ,
126- args = ["--no-sandbox" , "--disable-setuid-sandbox" ]
127- )
128- _browsers [pid ] = {"playwright" : p , "browser" : b }
129- browser = _browsers [pid ]["browser" ]
130- except Exception as e :
131- if browser :
132- await browser .close ()
133- if _browsers [pid ]["playwright" ]:
134- await _browsers [pid ]["playwright" ].stop ()
135- if pid in _browsers :
136- _browsers .pop (pid , None )
137- raise MumbleException (f"checker's side (4)" )
138-
139- try :
140- context : BrowserContext = await browser .new_context ()
141- await context .route ("**/*" , lambda route , request :
142- route .abort () if request .resource_type in ("image" , "stylesheet" , "font" )
143- else route .continue_ ()
123+ async with async_playwright () as p :
124+ browser = await p .chromium .launch (
125+ headless = True ,
126+ args = ['--no-sandbox' , '--disable-setuid-sandbox' ]
144127 )
145- await context .add_cookies (cookies )
146- page = await context .new_page ()
147- page .set_default_timeout (10_000 )
148- await page .goto (f"{ self .service_url } /admin/feedback" )
149- await page .wait_for_url (f"{ self .service_url } /admin/feedback" , timeout = 2000 )
150-
151- if "/admin/feedback" not in page .url :
152- raise MumbleException (f"Admin was redirected to problems page - service unavailable" )
153- except Exception as e :
154- if context :
155- await context .close ()
156- if browser :
128+
129+ try :
130+ context = await browser .new_context ()
131+ await context .add_cookies (cookies )
132+
133+ page = await context .new_page ()
134+
135+ page .set_default_timeout (10000 )
136+
137+ await page .goto (f"{ self .service_url } /admin/feedback" )
138+
139+ await page .wait_for_url (f"{ self .service_url } /admin/feedback" , timeout = 2000 )
140+
141+ if "/admin/feedback" not in page .url :
142+ raise MumbleException (f"Failed to load feedback page" )
143+
144+ except Exception as e :
145+ self .logger .warning (f"Admin simulation failed: { e } " )
146+ raise MumbleException (f"checker's side (4)" )
147+ finally :
157148 await browser .close ()
158- if pid in _browsers :
159- await _browsers [pid ]["playwright" ].stop ()
160- _browsers .pop (pid , None )
161- raise MumbleException (f"checker's side (5)" )
162- else :
163- await context .close ()
164149
165150 async def post_new_message (self ) -> None :
151+ self .validate ()
152+
166153 message_data = generate_admin_message ()
167154 message_text = message_data ["message" ]
168155 message_year = message_data ["year" ]
@@ -200,7 +187,7 @@ async def post_new_message(self) -> None:
200187 )
201188
202189 if response .status_code not in [200 , 201 , 302 ]:
203- raise MumbleException (f"Failed to submit admin challenge solution. " )
190+ raise MumbleException (f"Failed to submit admin challenge solution: { response . text } , { response . status_code } " )
204191
205192 response = await self .client .post (
206193 "/admin/message" ,
0 commit comments