diff --git a/capmonstercloud_client/CapMonsterCloudClient.py b/capmonstercloud_client/CapMonsterCloudClient.py
index 21de793..8ffa4c1 100644
--- a/capmonstercloud_client/CapMonsterCloudClient.py
+++ b/capmonstercloud_client/CapMonsterCloudClient.py
@@ -17,6 +17,7 @@
((RecaptchaV2Request,), getRecaptchaV2Timeouts),
((RecaptchaV2EnterpriseRequest,), getRecaptchaV2EnterpriseTimeouts),
((RecaptchaV3ProxylessRequest), getRecaptchaV3Timeouts),
+ ((RecaptchaV3EnterpriseRequest), getRecaptchaV3Timeouts),
((ImageToTextRequest), getImage2TextTimeouts),
((FuncaptchaRequest,), getFuncaptchaTimeouts),
((HcaptchaRequest,), getHcaptchaTimeouts),
@@ -35,6 +36,7 @@
((YidunRequest), getYidunTimeouts),
((TemuCustomTaskRequest), getTemuTimeouts),
((ProsopoTaskRequest), getProsopoTimeouts),
+ ((AltchaCustomTaskRequest), getAltchaTimeouts),
)
@@ -44,6 +46,14 @@ def __init__(self,
self.options = options
self._headers = {'User-Agent':
f'Zennolab.CapMonsterCloud.Client.Python/{parseVersion()}'}
+ if self.options.client_proxy:
+ if self.options.client_proxy.proxyType not in ['http', 'https']:
+ raise UnsupportedProxyTypeError(f'Supported client proxy types are: [HTTP, HTTPS]')
+ if self.options.client_proxy.proxyLogin and self.options.client_proxy.proxyPassword:
+ auth = f"{self.options.client_proxy.proxyLogin}:{self.options.client_proxy.proxyPassword}@"
+ self.client_proxy_string = f"{self.options.client_proxy.proxyType}://{auth}{self.options.client_proxy.proxyAddress}:{self.options.client_proxy.proxyPort}"
+ else:
+ self.client_proxy_string = None
@property
def headers(self):
@@ -56,7 +66,8 @@ async def get_balance(self) -> Dict[str, Union[int, float, str]]:
async with aiohttp.ClientSession() as session:
async with session.post(url=self.options.service_url + '/getBalance',
json=body,
- timeout=aiohttp.ClientTimeout(total=self.options.client_timeout)) as resp:
+ timeout=aiohttp.ClientTimeout(total=self.options.client_timeout),
+ proxy=self.client_proxy_string) as resp:
if resp.status != 200:
raise HTTPException(f'Cannot create task. Status code: {resp.status}.')
result = await resp.json(content_type=None)
@@ -69,6 +80,7 @@ async def solve_captcha(self, request: Union[
RecaptchaV2EnterpriseRequest,
RecaptchaV2Request,
RecaptchaV3ProxylessRequest,
+ RecaptchaV3EnterpriseRequest,
RecaptchaComplexImageTaskRequest,
ImageToTextRequest,
FuncaptchaRequest,
@@ -87,7 +99,8 @@ async def solve_captcha(self, request: Union[
MTCaptchaRequest,
YidunRequest,
TemuCustomTaskRequest,
- ProsopoTaskRequest],
+ ProsopoTaskRequest,
+ AltchaCustomTaskRequest],
) -> Dict[str, str]:
'''
Non-blocking method for captcha solving.
@@ -106,6 +119,7 @@ async def _solve(self, request: Union[
RecaptchaV2EnterpriseRequest,
RecaptchaV2Request,
RecaptchaV3ProxylessRequest,
+ RecaptchaV3EnterpriseRequest,
RecaptchaComplexImageTaskRequest,
ImageToTextRequest,
FuncaptchaRequest,
@@ -124,7 +138,8 @@ async def _solve(self, request: Union[
MTCaptchaRequest,
YidunRequest,
TemuCustomTaskRequest,
- ProsopoTaskRequest],
+ ProsopoTaskRequest,
+ AltchaCustomTaskRequest],
timeouts: GetResultTimeouts,
) -> Dict[str, str]:
@@ -168,7 +183,8 @@ async def _getTaskResult(self, task_id: str) -> Dict[str, Union[int, str, None]]
async with session.post(url=self.options.service_url + '/getTaskResult',
json=body,
timeout=aiohttp.ClientTimeout(total=self.options.client_timeout),
- headers=self.headers) as resp:
+ headers=self.headers,
+ proxy=self.client_proxy_string) as resp:
if resp.status != 200:
if resp.status == 500:
return {'errorId': 0, 'status': 'processing'}
@@ -184,11 +200,13 @@ async def _createTask(self, request: BaseRequest) -> Dict[str, Union[str, int]]:
"task": task,
"softId": self.options.default_soft_id
}
+
async with aiohttp.ClientSession() as session:
async with session.post(url=self.options.service_url + '/createTask',
json=body,
timeout=aiohttp.ClientTimeout(total=self.options.client_timeout),
- headers=self.headers) as resp:
+ headers=self.headers,
+ proxy=self.client_proxy_string) as resp:
if resp.status != 200:
raise HTTPException(f'Cannot create task. Status code: {resp.status}.')
else:
diff --git a/capmonstercloud_client/GetResultTimeouts.py b/capmonstercloud_client/GetResultTimeouts.py
index bd86b14..202262b 100644
--- a/capmonstercloud_client/GetResultTimeouts.py
+++ b/capmonstercloud_client/GetResultTimeouts.py
@@ -17,6 +17,9 @@ def getRecaptchaV2EnterpriseTimeouts() -> GetResultTimeouts:
def getRecaptchaV3Timeouts() -> GetResultTimeouts:
return GetResultTimeouts(1, 10, 3, 180)
+def getRecaptchaV3EnterpriseTimeouts() -> GetResultTimeouts:
+ return GetResultTimeouts(1, 10, 3, 150)
+
def getImage2TextTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(0.35, 0, 0.2, 10)
@@ -50,6 +53,9 @@ def getBinanceTimeouts() -> GetResultTimeouts:
def getImpervaTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(1, 0, 1, 20)
+def getAltchaTimeouts() -> GetResultTimeouts:
+ return GetResultTimeouts(1, 0, 1, 50)
+
def getCITTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(0.35, 0, 0.2, 10)
diff --git a/capmonstercloud_client/clientOptions.py b/capmonstercloud_client/clientOptions.py
index 5e58501..5d81077 100644
--- a/capmonstercloud_client/clientOptions.py
+++ b/capmonstercloud_client/clientOptions.py
@@ -1,11 +1,14 @@
from pydantic import BaseModel, validator, Field
-
+from .requests import ClientProxyInfo
+from typing import Optional
class ClientOptions(BaseModel):
api_key: str
+ client_proxy: Optional[ClientProxyInfo] = None
service_url: str = Field(default="https://api.capmonster.cloud")
default_soft_id: int = Field(default=55)
client_timeout: float = Field(default=20.0)
+
@validator('api_key')
def validate_api_key(cls, value):
diff --git a/capmonstercloud_client/exceptions.py b/capmonstercloud_client/exceptions.py
index f523ff8..a6d828d 100644
--- a/capmonstercloud_client/exceptions.py
+++ b/capmonstercloud_client/exceptions.py
@@ -24,6 +24,8 @@ class TaskNotDefinedError(BaseError):
class ExtraParamsError(BaseError):
pass
+class UnsupportedProxyTypeError(BaseError):
+ pass
class UserAgentNotDefinedError(BaseError):
diff --git a/capmonstercloud_client/requests/AltchaCustomTaskRequest.py b/capmonstercloud_client/requests/AltchaCustomTaskRequest.py
new file mode 100644
index 0000000..d7bb9c4
--- /dev/null
+++ b/capmonstercloud_client/requests/AltchaCustomTaskRequest.py
@@ -0,0 +1,36 @@
+from typing import Dict, Union
+from pydantic import Field, validator
+
+from .CustomTaskRequestBase import CustomTaskRequestBase
+
+class AltchaCustomTaskRequest(CustomTaskRequestBase):
+ captchaClass: str = Field(default='altcha')
+ websiteKey: str = Field()
+ metadata : Dict[str, str]
+
+ @validator('metadata')
+ def validate_metadata(cls, value):
+ for key in ['challenge', 'iterations', 'salt', 'signature']:
+ if value.get(key) is None:
+ raise TypeError(f'Expect that {key} will be defined.')
+ else:
+ if not isinstance(value.get(key), str):
+ raise TypeError(f'Expect that {key} will be str.')
+ return value
+
+ def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
+ task = {}
+ task['type'] = self.type
+ task['class'] = self.captchaClass
+ task['websiteURL'] = self.websiteUrl
+ task['websiteKey'] = self.websiteKey
+ task['metadata'] = self.metadata
+ if self.proxy:
+ task['proxyType'] = self.proxy.proxyType
+ task['proxyAddress'] = self.proxy.proxyAddress
+ task['proxyPort'] = self.proxy.proxyPort
+ task['proxyLogin'] = self.proxy.proxyLogin
+ task['proxyPassword'] = self.proxy.proxyPassword
+ if self.userAgent is not None:
+ task['userAgent'] = self.userAgent
+ return task
diff --git a/capmonstercloud_client/requests/CustomTaskRequestBase.py b/capmonstercloud_client/requests/CustomTaskRequestBase.py
index 4508e20..a9a16f2 100644
--- a/capmonstercloud_client/requests/CustomTaskRequestBase.py
+++ b/capmonstercloud_client/requests/CustomTaskRequestBase.py
@@ -6,5 +6,5 @@ class CustomTaskRequestBase(BaseRequestWithProxy):
captchaClass: str # Class(subtype) of ComplexImageTask
type: str = "CustomTask" # Recognition task type
websiteUrl: str # Address of a webpage with captcha
- userAgent: Optional[str] = None # It is required that you use a signature of a modern browser
- domains: Optional[List[str]] = None # Collection with base64 encoded images. Must be populated if not.
+ userAgent: Optional[str] = None
+ domains: Optional[List[str]] = None
diff --git a/capmonstercloud_client/requests/FuncaptchaRequest.py b/capmonstercloud_client/requests/FuncaptchaRequest.py
index 9e44156..9646c8e 100644
--- a/capmonstercloud_client/requests/FuncaptchaRequest.py
+++ b/capmonstercloud_client/requests/FuncaptchaRequest.py
@@ -9,6 +9,7 @@ class FuncaptchaRequest(BaseRequestWithProxy):
websitePublicKey: str
funcaptchaApiJSSubdomain: Optional[str] = Field(default=None)
data: Optional[str] = Field(default=None)
+ cookies: Optional[str] = Field(default=None)
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
task = {}
@@ -26,4 +27,6 @@ def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
task['funcaptchaApiJSSubdomain'] = self.funcaptchaApiJSSubdomain
if self.data is not None:
task['data'] = self.data
+ if self.cookies is not None:
+ task['cookies'] = self.cookies
return task
\ No newline at end of file
diff --git a/capmonstercloud_client/requests/ImpervaCustomTaskRequest.py b/capmonstercloud_client/requests/ImpervaCustomTaskRequest.py
index f103868..2b7ed8b 100644
--- a/capmonstercloud_client/requests/ImpervaCustomTaskRequest.py
+++ b/capmonstercloud_client/requests/ImpervaCustomTaskRequest.py
@@ -1,5 +1,5 @@
from typing import Dict, Union
-from pydantic import Field, validator
+from pydantic import Field, validator, model_validator
from .CustomTaskRequestBase import CustomTaskRequestBase
@@ -23,18 +23,24 @@ def validate_metadata(cls, value):
raise TypeError(f'Expect that reese84UrlEndpoint will be str.')
return value
+ @model_validator(mode='before')
+ def validate_imperva_proxy(cls, values):
+ proxy = values.get('proxy')
+ if proxy is None:
+ raise RuntimeError(f'You are required to use your own proxies.')
+ return values
+
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
task = {}
task['type'] = self.type
task['class'] = self.captchaClass
task['websiteURL'] = self.websiteUrl
task['metadata'] = self.metadata
- if self.proxy:
- task['proxyType'] = self.proxy.proxyType
- task['proxyAddress'] = self.proxy.proxyAddress
- task['proxyPort'] = self.proxy.proxyPort
- task['proxyLogin'] = self.proxy.proxyLogin
- task['proxyPassword'] = self.proxy.proxyPassword
+ task['proxyType'] = self.proxy.proxyType
+ task['proxyAddress'] = self.proxy.proxyAddress
+ task['proxyPort'] = self.proxy.proxyPort
+ task['proxyLogin'] = self.proxy.proxyLogin
+ task['proxyPassword'] = self.proxy.proxyPassword
if self.userAgent is not None:
task['userAgent'] = self.userAgent
return task
\ No newline at end of file
diff --git a/capmonstercloud_client/requests/RecaptchaV2EnterpiseRequest.py b/capmonstercloud_client/requests/RecaptchaV2EnterpiseRequest.py
index e740983..db7dfed 100644
--- a/capmonstercloud_client/requests/RecaptchaV2EnterpiseRequest.py
+++ b/capmonstercloud_client/requests/RecaptchaV2EnterpiseRequest.py
@@ -9,6 +9,7 @@ class RecaptchaV2EnterpriseRequest(BaseRequestWithProxy):
websiteKey: str
enterprisePayload: Optional[str] = Field(default=None)
apiDomain: Optional[str] = Field(default=None)
+ pageAction: Optional[str] = Field(default=None)
def getTaskDict(self) -> Dict[str, Union[str, int]]:
task = {}
@@ -25,4 +26,6 @@ def getTaskDict(self) -> Dict[str, Union[str, int]]:
task['enterprisePayload'] = {'s': self.enterprisePayload}
if self.apiDomain is not None:
task['apiDomain'] = self.apiDomain
+ if self.pageAction is not None:
+ task['pageAction'] = self.pageAction
return task
\ No newline at end of file
diff --git a/capmonstercloud_client/requests/RecaptchaV3EnterpriseRequest.py b/capmonstercloud_client/requests/RecaptchaV3EnterpriseRequest.py
new file mode 100644
index 0000000..32625c5
--- /dev/null
+++ b/capmonstercloud_client/requests/RecaptchaV3EnterpriseRequest.py
@@ -0,0 +1,22 @@
+from typing import Dict, Union, Optional
+from pydantic import Field
+
+from .baseRequest import BaseRequest
+
+class RecaptchaV3EnterpriseRequest(BaseRequest):
+ type: str = Field(default='RecaptchaV3EnterpriseTask')
+ websiteUrl: str
+ websiteKey: str
+ minScore: Optional[float] = Field(default=None)
+ pageAction: Optional[str] = Field(default=None)
+
+ def getTaskDict(self) -> Dict[str, Union[str, int, float]]:
+ task = {}
+ task['type'] = self.type
+ task['websiteURL'] = self.websiteUrl
+ task['websiteKey'] = self.websiteKey
+ if self.minScore is not None:
+ task['minScore'] = self.minScore
+ if self.pageAction is not None:
+ task['pageAction'] = self.pageAction
+ return task
\ No newline at end of file
diff --git a/capmonstercloud_client/requests/TurnstileRequest.py b/capmonstercloud_client/requests/TurnstileRequest.py
index 6a525c2..4bc0776 100644
--- a/capmonstercloud_client/requests/TurnstileRequest.py
+++ b/capmonstercloud_client/requests/TurnstileRequest.py
@@ -19,7 +19,7 @@ class TurnstileRequest(BaseRequestWithProxy):
@validator('cloudflareTaskType')
def validate_cloudflare_task(cls, value):
if value is not None:
- if value not in ['cf_clearance', 'token']:
+ if value not in ['cf_clearance', 'token', 'wait_room']:
raise ValueError(f'cloudflareTaskType could be "cf_clearance" if you need cookie or ' \
f'"token" if required token from Turnstile.')
return value
@@ -28,13 +28,13 @@ def validate_cloudflare_task(cls, value):
def validate_cloudflare_type_token(self):
if self.get('htmlPageBase64') is None:
- if self.get('cloudflareTaskType') == 'cf_clearance':
+ if self.get('cloudflareTaskType') in ['cf_clearance', 'wait_room']:
raise RuntimeError(f'Expect that "htmlPageBase64" will be filled ' \
- f'when cloudflareTaskType is "cf_clearance"')
+ f'when cloudflareTaskType is "cf_clearance" or "wait_room')
if self.get('proxy') is None:
- if self.get('cloudflareTaskType') == 'cf_clearance':
- raise RuntimeError(f'You are working using queries, and you need cf_clearance cookies ' \
+ if self.get('cloudflareTaskType') in ['cf_clearance', 'wait_room']:
+ raise RuntimeError(f'You are working using queries, and you need cf_clearance cookies or wait_room ' \
f'it is required that you need your proxies.')
if self.get('cloudflareTaskType') == 'token':
@@ -44,7 +44,7 @@ def validate_cloudflare_type_token(self):
f'when "cloudflareTaskType" = "token".')
if self.get('cloudflareTaskType') is not None:
- if self.get('cloudflareTaskType') in ['cf_clearance', 'token']:
+ if self.get('cloudflareTaskType') in ['cf_clearance', 'token', 'wait_room']:
if self.get('userAgent') is None:
raise RuntimeError(f'Expect that userAgent will be filled ' \
f'when cloudflareTaskType specified.')
diff --git a/capmonstercloud_client/requests/__init__.py b/capmonstercloud_client/requests/__init__.py
index 2932039..9c224d3 100644
--- a/capmonstercloud_client/requests/__init__.py
+++ b/capmonstercloud_client/requests/__init__.py
@@ -2,6 +2,7 @@
from .RecaptchaV2Request import RecaptchaV2Request
from .RecaptchaV2EnterpiseRequest import RecaptchaV2EnterpriseRequest
from .RecaptchaV3ProxylessRequest import RecaptchaV3ProxylessRequest
+from .RecaptchaV3EnterpriseRequest import RecaptchaV3EnterpriseRequest
from .RecaptchaComplexImageTask import RecaptchaComplexImageTaskRequest
from .HcaptchaRequest import HcaptchaRequest
from .FuncaptchaRequest import FuncaptchaRequest
@@ -22,10 +23,11 @@
from .YidunRequest import YidunRequest
from .ProsopoTaskRequest import ProsopoTaskRequest
from .TemuCustomTaskRequest import TemuCustomTaskRequest
-from .proxy_info import ProxyInfo
+from .AltchaCustomTaskRequest import AltchaCustomTaskRequest
+from .proxy_info import ProxyInfo, ClientProxyInfo
-REQUESTS = ['RecaptchaV2EnterpiseRequest', 'RecaptchaV2Request', 'RecaptchaV3ProxylessRequest', 'RecaptchaComplexImageTaskRequest'
+REQUESTS = ['RecaptchaV2EnterpiseRequest', 'RecaptchaV2Request', 'RecaptchaV3ProxylessRequest', 'RecaptchaComplexImageTaskRequest', 'RecaptchaV3EnterpriseRequest'
'ImageToTextRequest',
'FuncaptchaRequest', 'FunCaptchaComplexImageTaskRequest',
'HcaptchaRequest', 'HcaptchaComplexImageTaskRequest'
@@ -41,5 +43,6 @@
'MTCaptchaRequest',
'YidunRequest',
'ProsopoTaskRequest',
- 'TemuCustomTaskRequest'
+ 'TemuCustomTaskRequest',
+ 'AltchaCustomTaskRequest'
]
diff --git a/capmonstercloud_client/requests/proxy_info.py b/capmonstercloud_client/requests/proxy_info.py
index d7edaf7..3811d34 100644
--- a/capmonstercloud_client/requests/proxy_info.py
+++ b/capmonstercloud_client/requests/proxy_info.py
@@ -1,5 +1,6 @@
from pydantic import BaseModel, validator
from .enums import ProxyTypes
+from typing import Optional
class ProxyInfo(BaseModel):
proxyType: str
@@ -19,3 +20,22 @@ def validate_port(cls, value):
if not isinstance(value, int):
raise TypeError(f'Expect that port value will be type, got {type(value)}')
return value
+
+class ClientProxyInfo(BaseModel):
+ proxyType: str
+ proxyAddress: str
+ proxyPort: int
+ proxyLogin: Optional[str] = None
+ proxyPassword: Optional[str] = None
+
+ @validator('proxyType')
+ def validate_proxy_type(cls, value):
+ if value not in ProxyTypes.list_values():
+ raise ValueError(f'Expected that proxy type will be in {ProxyTypes.list_values()}, got "{value}"')
+ return value
+
+ @validator('proxyPort')
+ def validate_port(cls, value):
+ if not isinstance(value, int):
+ raise TypeError(f'Expect that port value will be type, got {type(value)}')
+ return value
\ No newline at end of file
diff --git a/capmonstercloud_client/version.txt b/capmonstercloud_client/version.txt
index 0fa4ae4..fbcbf73 100644
--- a/capmonstercloud_client/version.txt
+++ b/capmonstercloud_client/version.txt
@@ -1 +1 @@
-3.3.0
\ No newline at end of file
+3.4.0
\ No newline at end of file
diff --git a/examples/altcha.py b/examples/altcha.py
new file mode 100644
index 0000000..1b04a46
--- /dev/null
+++ b/examples/altcha.py
@@ -0,0 +1,49 @@
+import os
+import time
+import asyncio
+
+from capmonstercloudclient.requests import AltchaCustomTaskRequest
+from capmonstercloudclient import ClientOptions, CapMonsterClient
+
+
+async def solve_captcha_sync(num_requests):
+ return [await cap_monster_client.solve_captcha(altcha_request) for _ in range(num_requests)]
+
+
+async def solve_captcha_async(num_requests):
+ tasks = [asyncio.create_task(cap_monster_client.solve_captcha(altcha_request))
+ for _ in range(num_requests)]
+ return await asyncio.gather(*tasks, return_exceptions=True)
+
+
+if __name__ == '__main__':
+ key = os.getenv('API_KEY')
+ client_options = ClientOptions(api_key=key)
+ cap_monster_client = CapMonsterClient(options=client_options)
+
+ metadata = {
+ "challenge": "challenge_string_here",
+ "iterations": "1000",
+ "salt": "salt_string_here",
+ "signature": "signature_string_here"
+ }
+ altcha_request = AltchaCustomTaskRequest(
+ websiteUrl='https://example.com/login',
+ websiteKey='altcha-public-key-123',
+ metadata=metadata,
+ userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
+ )
+
+ nums = 3
+ # Sync test
+ sync_start = time.time()
+ sync_responses = asyncio.run(solve_captcha_sync(nums))
+ print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} ' \
+ f'resp/sec\nsolution: {sync_responses[0]}')
+
+ # Async test
+ async_start = time.time()
+ async_responses = asyncio.run(solve_captcha_async(nums))
+ print(f'average execution time async {1/((time.time()-async_start)/nums):0.2f} ' \
+ f'resp/sec\nsolution: {async_responses[0]}')
+
diff --git a/examples/cf_waitroom.py b/examples/cf_waitroom.py
new file mode 100644
index 0000000..b92ee7a
--- /dev/null
+++ b/examples/cf_waitroom.py
@@ -0,0 +1,37 @@
+import asyncio
+from capmonstercloudclient import CapMonsterClient, ClientOptions
+from capmonstercloudclient.requests import TurnstileRequest
+from capmonstercloudclient.requests import ProxyInfo, ClientProxyInfo
+
+async def main():
+ client_proxy = ClientProxyInfo(
+ proxyType='http',
+ proxyAddress='gw-us.zengw.io', # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyPort=8080, # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyLogin='login', # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyPassword='password' # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ )
+ client_options = ClientOptions(api_key="dac3599143bdfd88dfc41758c6cb8729", client_proxy=client_proxy)
+ cap_monster_client = CapMonsterClient(options=client_options)
+
+ proxy = ProxyInfo(
+ proxyType='http',
+ proxyAddress='gw-us.zengw.io', # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyPort=8080, # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyLogin='login', # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyPassword='password' # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ )
+
+ turnstile_request = TurnstileRequest(
+ websiteURL="https://resa.notredamedeparis.fr/",
+ websiteKey="xxxxx",
+ cloudflareTaskType="wait_room",
+ userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
+ htmlPageBase64="PGh0bWwgbGFuZz0iZW4tVVMiIGRpcj0ibHRyIj48aGVhZD48dGl0bGU+SnVzdCBhIG1vbWVudC4uLjwvdGl0bGU+PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9VVRGLTgiPjxtZXRhIGh0dHAtZXF1aXY9IlgtVUEtQ29tcGF0aWJsZSIgY29udGVudD0iSUU9RWRnZSI+PG1ldGEgbmFtZT0icm9ib3RzIiBjb250ZW50PSJub2luZGV4LG5vZm9sbG93Ij48bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLGluaXRpYWwtc2NhbGU9MSI+PHN0eWxlPip7Ym94LXNpemluZzpib3JkZXItYm94O21hcmdpbjowO3BhZGRpbmc6MH1odG1se2xpbmUtaGVpZ2h0OjEuMTU7LXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OjEwMCU7Y29sb3I6IzMxMzEzMTtmb250LWZhbWlseTpzeXN0ZW0tdWksLWFwcGxlLXN5c3RlbSxCbGlua01hY1N5c3RlbUZvbnQsIlNlZ29lIFVJIixSb2JvdG8sIkhlbHZldGljYSBOZXVlIixBcmlhbCwiTm90byBTYW5zIixzYW5zLXNlcmlmLCJBcHBsZSBDb2xvciBFbW9qaSIsIlNlZ29lIFVJIEVtb2ppIiwiU2Vnb2UgVUkgU3ltYm9sIiwiTm90byBDb2xvciBFbW9qaSJ9Ym9keXtkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2hlaWdodDoxMDB2aDttaW4taGVpZ2h0OjEwMHZofS5tYWluLWNvbnRlbnR7bWFyZ2luOjhyZW0gYXV0bztwYWRkaW5nLWxlZnQ6MS41cmVtO21heC13aWR0aDo2MHJlbX1AbWVkaWEgKHdpZHRoIDw9IDcyMHB4KXsubWFpbi1jb250ZW50e21hcmdpbi10b3A6NHJlbX19Lmgye2xpbmUtaGVpZ2h0OjIuMjVyZW07Zm9udC1zaXplOjEuNXJlbTtmb250LXdlaWdodDo1MDB9QG1lZGlhICh3aWR0aCA8PSA3MjBweCl7Lmgye2xpbmUtaGVpZ2h0OjEuNXJlbTtmb250LXNpemU6MS4yNXJlbX19I2NoYWxsZW5nZS1lcnJvci10ZXh0e2JhY2tncm91bmQtaW1hZ2U6dXJsKCJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSXpNaUlnYUdWcFoyaDBQU0l6TWlJZ1ptbHNiRDBpYm05dVpTSStQSEJoZEdnZ1ptbHNiRDBpSTBJeU1FWXdNeUlnWkQwaVRURTJJRE5oTVRNZ01UTWdNQ0F4SURBZ01UTWdNVE5CTVRNdU1ERTFJREV6TGpBeE5TQXdJREFnTUNBeE5pQXpiVEFnTWpSaE1URWdNVEVnTUNBeElERWdNVEV0TVRFZ01URXVNREVnTVRFdU1ERWdNQ0F3SURFdE1URWdNVEVpTHo0OGNHRjBhQ0JtYVd4c1BTSWpRakl3UmpBeklpQmtQU0pOTVRjdU1ETTRJREU0TGpZeE5VZ3hOQzQ0TjB3eE5DNDFOak1nT1M0MWFESXVOemd6ZW0wdE1TNHdPRFFnTVM0ME1qZHhMalkySURBZ01TNHdOVGN1TXpnNExqUXdOeTR6T0RrdU5EQTNMams1TkNBd0lDNDFPVFl0TGpRd055NDVPRFF0TGpNNU55NHpPUzB4TGpBMU55NHpPRGt0TGpZMUlEQXRNUzR3TlRZdExqTTRPUzB1TXprNExTNHpPRGt0TGpNNU9DMHVPVGcwSURBdExqVTVOeTR6T1RndExqazROUzQwTURZdExqTTVOeUF4TGpBMU5pMHVNemszSWk4K1BDOXpkbWMrIik7YmFja2dyb3VuZC1yZXBlYXQ6bm8tcmVwZWF0O2JhY2tncm91bmQtc2l6ZTpjb250YWluO3BhZGRpbmctbGVmdDozNHB4fUBtZWRpYSAocHJlZmVycy1jb2xvci1zY2hlbWU6IGRhcmspe2JvZHl7YmFja2dyb3VuZC1jb2xvcjojMjIyO2NvbG9yOiNkOWQ5ZDl9fTwvc3R5bGU+PG1ldGEgaHR0cC1lcXVpdj0icmVmcmVzaCIgY29udGVudD0iMzYwIj48c2NyaXB0IHNyYz0iL2Nkbi1jZ2kvY2hhbGxlbmdlLXBsYXRmb3JtL2gvYi9vcmNoZXN0cmF0ZS9jaGxfcGFnZS92MT9yYXk9OTk1MWIxNTVmODE5ZTk0YiI+PC9zY3JpcHQ+PHN0eWxlPip7Ym94LXNpemluZzpib3JkZXItYm94O21hcmdpbjowO3BhZGRpbmc6MH1odG1se2xpbmUtaGVpZ2h0OjEuMTU7LXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OjEwMCU7Y29sb3I6IzMxMzEzMTtmb250LWZhbWlseTpzeXN0ZW0tdWksLWFwcGxlLXN5c3RlbSxCbGlua01hY1N5c3RlbUZvbnQsIlNlZ29lIFVJIixSb2JvdG8sIkhlbHZldGljYSBOZXVlIixBcmlhbCwiTm90byBTYW5zIixzYW5zLXNlcmlmLCJBcHBsZSBDb2xvciBFbW9qaSIsIlNlZ29lIFVJIEVtb2ppIiwiU2Vnb2UgVUkgU3ltYm9sIiwiTm90byBDb2xvciBFbW9qaSJ9YnV0dG9ue2ZvbnQtZmFtaWx5OnN5c3RlbS11aSwtYXBwbGUtc3lzdGVtLEJsaW5rTWFjU3lzdGVtRm9udCwiU2Vnb2UgVUkiLFJvYm90bywiSGVsdmV0aWNhIE5ldWUiLEFyaWFsLCJOb3RvIFNhbnMiLHNhbnMtc2VyaWYsIkFwcGxlIENvbG9yIEVtb2ppIiwiU2Vnb2UgVUkgRW1vamkiLCJTZWdvZSBVSSBTeW1ib2wiLCJOb3RvIENvbG9yIEVtb2ppIn1ib2R5e2Rpc3BsYXk6ZmxleDtmbGV4LWRpcmVjdGlvbjpjb2x1bW47aGVpZ2h0OjEwMHZoO21pbi1oZWlnaHQ6MTAwdmh9Ym9keS50aGVtZS1kYXJre2JhY2tncm91bmQtY29sb3I6IzIyMjtjb2xvcjojZDlkOWQ5fWJvZHkudGhlbWUtZGFyayBhe2NvbG9yOiNmZmZ9Ym9keS50aGVtZS1kYXJrIGE6aG92ZXJ7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZTtjb2xvcjojZWU3MzBhfWJvZHkudGhlbWUtZGFyayAubGRzLXJpbmcgZGl2e2JvcmRlci1jb2xvcjojOTk5IHJnYmEoMCwwLDAsMCkgcmdiYSgwLDAsMCwwKX1ib2R5LnRoZW1lLWRhcmsgLmZvbnQtcmVke2NvbG9yOiNiMjBmMDN9Ym9keS50aGVtZS1kYXJrIC5jdHAtYnV0dG9ue2JhY2tncm91bmQtY29sb3I6IzQ2OTNmZjtjb2xvcjojMWQxZDFkfWJvZHkudGhlbWUtZGFyayAjY2hhbGxlbmdlLXN1Y2Nlc3MtdGV4dHtiYWNrZ3JvdW5kLWltYWdlOnVybCgiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l6TWlJZ2FHVnBaMmgwUFNJek1pSWdabWxzYkQwaWJtOXVaU0lnZG1sbGQwSnZlRDBpTUNBd0lESTJJREkySWo0OGNHRjBhQ0JtYVd4c1BTSWpaRGxrT1dRNUlpQmtQU0pOTVRNZ01HRXhNeUF4TXlBd0lERWdNQ0F3SURJMklERXpJREV6SURBZ01DQXdJREF0TWpadE1DQXlOR0V4TVNBeE1TQXdJREVnTVNBd0xUSXlJREV4SURFeElEQWdNQ0F4SURBZ01qSWlMejQ4Y0dGMGFDQm1hV3hzUFNJalpEbGtPV1E1SWlCa1BTSnRNVEF1T1RVMUlERTJMakExTlMwekxqazFMVFF1TVRJMUxURXVORFExSURFdU16ZzFJRFV1TXpjZ05TNDJNU0E1TGpRNU5TMDVMall0TVM0ME1pMHhMalF3TlhvaUx6NDhMM04yWno0Iil9Ym9keS50aGVtZS1kYXJrICNjaGFsbGVuZ2UtZXJyb3ItdGV4dHtiYWNrZ3JvdW5kLWltYWdlOnVybCgiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l6TWlJZ2FHVnBaMmgwUFNJek1pSWdabWxzYkQwaWJtOXVaU0krUEhCaGRHZ2dabWxzYkQwaUkwSXlNRVl3TXlJZ1pEMGlUVEUySUROaE1UTWdNVE1nTUNBeElEQWdNVE1nTVROQk1UTXVNREUxSURFekxqQXhOU0F3SURBZ01DQXhOaUF6YlRBZ01qUmhNVEVnTVRFZ01DQXhJREVnTVRFdE1URWdNVEV1TURFZ01URXVNREVnTUNBd0lERXRNVEVnTVRFaUx6NDhjR0YwYUNCbWFXeHNQU0lqUWpJd1JqQXpJaUJrUFNKTk1UY3VNRE00SURFNExqWXhOVWd4TkM0NE4wd3hOQzQxTmpNZ09TNDFhREl1TnpnemVtMHRNUzR3T0RRZ01TNDBNamR4TGpZMklEQWdNUzR3TlRjdU16ZzRMalF3Tnk0ek9Ea3VOREEzTGprNU5DQXdJQzQxT1RZdExqUXdOeTQ1T0RRdExqTTVOeTR6T1MweExqQTFOeTR6T0RrdExqWTFJREF0TVM0d05UWXRMak00T1MwdU16azRMUzR6T0RrdExqTTVPQzB1T1RnMElEQXRMalU1Tnk0ek9UZ3RMams0TlM0ME1EWXRMak01TnlBeExqQTFOaTB1TXprM0lpOCtQQzl6ZG1jKyIpfWJvZHkudGhlbWUtbGlnaHR7YmFja2dyb3VuZC1jb2xvcjojZmZmO2NvbG9yOiMzMTMxMzF9Ym9keS50aGVtZS1saWdodCBhe2NvbG9yOiMwMDUxYzN9Ym9keS50aGVtZS1saWdodCBhOmhvdmVye3RleHQtZGVjb3JhdGlvbjp1bmRlcmxpbmU7Y29sb3I6I2VlNzMwYX1ib2R5LnRoZW1lLWxpZ2h0IC5sZHMtcmluZyBkaXZ7Ym9yZGVyLWNvbG9yOiM1OTU5NTkgcmdiYSgwLDAsMCwwKSByZ2JhKDAsMCwwLDApfWJvZHkudGhlbWUtbGlnaHQgLmZvbnQtcmVke2NvbG9yOiNmYzU3NGF9Ym9keS50aGVtZS1saWdodCAuY3RwLWJ1dHRvbntib3JkZXItY29sb3I6IzAwMzY4MTtiYWNrZ3JvdW5kLWNvbG9yOiMwMDM2ODE7Y29sb3I6I2ZmZn1ib2R5LnRoZW1lLWxpZ2h0ICNjaGFsbGVuZ2Utc3VjY2Vzcy10ZXh0e2JhY2tncm91bmQtaW1hZ2U6dXJsKCJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSXpNaUlnYUdWcFoyaDBQU0l6TWlJZ1ptbHNiRDBpYm05dVpTSWdkbWxsZDBKdmVEMGlNQ0F3SURJMklESTJJajQ4Y0dGMGFDQm1hV3hzUFNJak16RXpNVE14SWlCa1BTSk5NVE1nTUdFeE15QXhNeUF3SURFZ01DQXdJREkySURFeklERXpJREFnTUNBd0lEQXRNalp0TUNBeU5HRXhNU0F4TVNBd0lERWdNU0F3TFRJeUlERXhJREV4SURBZ01DQXhJREFnTWpJaUx6NDhjR0YwYUNCbWFXeHNQU0lqTXpFek1UTXhJaUJrUFNKdE1UQXVPVFUxSURFMkxqQTFOUzB6TGprMUxUUXVNVEkxTFRFdU5EUTFJREV1TXpnMUlEVXVNemNnTlM0Mk1TQTVMalE1TlMwNUxqWXRNUzQwTWkweExqUXdOWG9pTHo0OEwzTjJaejQ9Iil9Ym9keS50aGVtZS1saWdodCAjY2hhbGxlbmdlLWVycm9yLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJK1BIQmhkR2dnWm1sc2JEMGlJMlpqTlRjMFlTSWdaRDBpVFRFMklETmhNVE1nTVRNZ01DQXhJREFnTVRNZ01UTkJNVE11TURFMUlERXpMakF4TlNBd0lEQWdNQ0F4TmlBemJUQWdNalJoTVRFZ01URWdNQ0F4SURFZ01URXRNVEVnTVRFdU1ERWdNVEV1TURFZ01DQXdJREV0TVRFZ01URWlMejQ4Y0dGMGFDQm1hV3hzUFNJalptTTFOelJoSWlCa1BTSk5NVGN1TURNNElERTRMall4TlVneE5DNDROMHd4TkM0MU5qTWdPUzQxYURJdU56Z3plbTB0TVM0d09EUWdNUzQwTWpkeExqWTJJREFnTVM0d05UY3VNemc0TGpRd055NHpPRGt1TkRBM0xqazVOQ0F3SUM0MU9UWXRMalF3Tnk0NU9EUXRMak01Tnk0ek9TMHhMakExTnk0ek9Ea3RMalkxSURBdE1TNHdOVFl0TGpNNE9TMHVNems0TFM0ek9Ea3RMak01T0MwdU9UZzBJREF0TGpVNU55NHpPVGd0TGprNE5TNDBNRFl0TGpNNU55QXhMakExTmkwdU16azNJaTgrUEM5emRtYysiKX1he3RyYW5zaXRpb246Y29sb3IgMTUwbXMgZWFzZTtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMCwwLDAsMCk7dGV4dC1kZWNvcmF0aW9uOm5vbmU7Y29sb3I6IzAwNTFjM31hOmhvdmVye3RleHQtZGVjb3JhdGlvbjp1bmRlcmxpbmU7Y29sb3I6I2VlNzMwYX0ubWFpbi1jb250ZW50e21hcmdpbjo4cmVtIGF1dG87cGFkZGluZy1yaWdodDoxLjVyZW07cGFkZGluZy1sZWZ0OjEuNXJlbTt3aWR0aDoxMDAlO21heC13aWR0aDo2MHJlbX0ubWFpbi1jb250ZW50IC5sb2FkaW5nLXZlcmlmeWluZ3toZWlnaHQ6NzYuMzkxcHh9LnNwYWNlcnttYXJnaW46MnJlbSAwfS5zcGFjZXItdG9we21hcmdpbi10b3A6NHJlbX0uc3BhY2VyLWJvdHRvbXttYXJnaW4tYm90dG9tOjJyZW19LmhlYWRpbmctZmF2aWNvbnttYXJnaW4tcmlnaHQ6LjVyZW07d2lkdGg6MnJlbTtoZWlnaHQ6MnJlbX1AbWVkaWEgKHdpZHRoIDw9IDcyMHB4KXsubWFpbi1jb250ZW50e21hcmdpbi10b3A6NHJlbX0uaGVhZGluZy1mYXZpY29ue3dpZHRoOjEuNXJlbTtoZWlnaHQ6MS41cmVtfX0ubWFpbi13cmFwcGVye2Rpc3BsYXk6ZmxleDtmbGV4OjE7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2FsaWduLWl0ZW1zOmNlbnRlcn0uZm9udC1yZWR7Y29sb3I6I2IyMGYwM30uaDF7bGluZS1oZWlnaHQ6My43NXJlbTtmb250LXNpemU6Mi41cmVtO2ZvbnQtd2VpZ2h0OjUwMH0uaDJ7bGluZS1oZWlnaHQ6Mi4yNXJlbTtmb250LXNpemU6MS41cmVtO2ZvbnQtd2VpZ2h0OjUwMH0uY29yZS1tc2d7bGluZS1oZWlnaHQ6Mi4yNXJlbTtmb250LXNpemU6MS41cmVtO2ZvbnQtd2VpZ2h0OjQwMH0uYm9keS10ZXh0e2xpbmUtaGVpZ2h0OjEuMjVyZW07Zm9udC1zaXplOjFyZW07Zm9udC13ZWlnaHQ6NDAwfUBtZWRpYSAod2lkdGggPD0gNzIwcHgpey5oMXtsaW5lLWhlaWdodDoxLjc1cmVtO2ZvbnQtc2l6ZToxLjVyZW19Lmgye2xpbmUtaGVpZ2h0OjEuNXJlbTtmb250LXNpemU6MS4yNXJlbX0uY29yZS1tc2d7bGluZS1oZWlnaHQ6MS41cmVtO2ZvbnQtc2l6ZToxcmVtfX0jY2hhbGxlbmdlLWVycm9yLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJK1BIQmhkR2dnWm1sc2JEMGlJMlpqTlRjMFlTSWdaRDBpVFRFMklETmhNVE1nTVRNZ01DQXhJREFnTVRNZ01UTkJNVE11TURFMUlERXpMakF4TlNBd0lEQWdNQ0F4TmlBemJUQWdNalJoTVRFZ01URWdNQ0F4SURFZ01URXRNVEVnTVRFdU1ERWdNVEV1TURFZ01DQXdJREV0TVRFZ01URWlMejQ4Y0dGMGFDQm1hV3hzUFNJalptTTFOelJoSWlCa1BTSk5NVGN1TURNNElERTRMall4TlVneE5DNDROMHd4TkM0MU5qTWdPUzQxYURJdU56Z3plbTB0TVM0d09EUWdNUzQwTWpkeExqWTJJREFnTVM0d05UY3VNemc0TGpRd055NHpPRGt1TkRBM0xqazVOQ0F3SUM0MU9UWXRMalF3Tnk0NU9EUXRMak01Tnk0ek9TMHhMakExTnk0ek9Ea3RMalkxSURBdE1TNHdOVFl0TGpNNE9TMHVNems0TFM0ek9Ea3RMak01T0MwdU9UZzBJREF0TGpVNU55NHpPVGd0TGprNE5TNDBNRFl0TGpNNU55QXhMakExTmkwdU16azNJaTgrUEM5emRtYysiKTtiYWNrZ3JvdW5kLXJlcGVhdDpuby1yZXBlYXQ7YmFja2dyb3VuZC1zaXplOmNvbnRhaW47cGFkZGluZy1sZWZ0OjM0cHh9I2NoYWxsZW5nZS1zdWNjZXNzLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJZ2RtbGxkMEp2ZUQwaU1DQXdJREkySURJMklqNDhjR0YwYUNCbWFXeHNQU0lqTXpFek1UTXhJaUJrUFNKTk1UTWdNR0V4TXlBeE15QXdJREVnTUNBd0lESTJJREV6SURFeklEQWdNQ0F3SURBdE1qWnRNQ0F5TkdFeE1TQXhNU0F3SURFZ01TQXdMVEl5SURFeElERXhJREFnTUNBeElEQWdNaklpTHo0OGNHRjBhQ0JtYVd4c1BTSWpNekV6TVRNeElpQmtQU0p0TVRBdU9UVTFJREUyTGpBMU5TMHpMamsxTFRRdU1USTFMVEV1TkRRMUlERXVNemcxSURVdU16Y2dOUzQyTVNBNUxqUTVOUzA1TGpZdE1TNDBNaTB4TGpRd05Yb2lMejQ4TDNOMlp6ND0iKTtiYWNrZ3JvdW5kLXJlcGVhdDpuby1yZXBlYXQ7YmFja2dyb3VuZC1zaXplOmNvbnRhaW47cGFkZGluZy1sZWZ0OjQycHh9LnRleHQtY2VudGVye3RleHQtYWxpZ246Y2VudGVyfS5jdHAtYnV0dG9ue3RyYW5zaXRpb24tZHVyYXRpb246MjAwbXM7dHJhbnNpdGlvbi1wcm9wZXJ0eTpiYWNrZ3JvdW5kLWNvbG9yLGJvcmRlci1jb2xvcixjb2xvcjt0cmFuc2l0aW9uLXRpbWluZy1mdW5jdGlvbjplYXNlO21hcmdpbjoycmVtIDA7Ym9yZGVyOi4wNjNyZW0gc29saWQgIzAwNTFjMztib3JkZXItcmFkaXVzOi4zMTNyZW07YmFja2dyb3VuZC1jb2xvcjojMDA1MWMzO2N1cnNvcjpwb2ludGVyO3BhZGRpbmc6LjM3NXJlbSAxcmVtO2xpbmUtaGVpZ2h0OjEuMzEzcmVtO2NvbG9yOiNmZmY7Zm9udC1zaXplOi44NzVyZW19LmN0cC1idXR0b246aG92ZXJ7Ym9yZGVyLWNvbG9yOiMwMDM2ODE7YmFja2dyb3VuZC1jb2xvcjojMDAzNjgxO2N1cnNvcjpwb2ludGVyO2NvbG9yOiNmZmZ9LmZvb3RlcnttYXJnaW46MCBhdXRvO3BhZGRpbmctcmlnaHQ6MS41cmVtO3BhZGRpbmctbGVmdDoxLjVyZW07d2lkdGg6MTAwJTttYXgtd2lkdGg6NjByZW07bGluZS1oZWlnaHQ6MS4xMjVyZW07Zm9udC1zaXplOi43NXJlbX0uZm9vdGVyLWlubmVye2JvcmRlci10b3A6MXB4IHNvbGlkICNkOWQ5ZDk7cGFkZGluZy10b3A6MXJlbTtwYWRkaW5nLWJvdHRvbToxcmVtfS5jbGVhcmZpeDo6YWZ0ZXJ7ZGlzcGxheTp0YWJsZTtjbGVhcjpib3RoO2NvbnRlbnQ6IiJ9LmNsZWFyZml4IC5jb2x1bW57ZmxvYXQ6bGVmdDtwYWRkaW5nLXJpZ2h0OjEuNXJlbTt3aWR0aDo1MCV9LmRpYWdub3N0aWMtd3JhcHBlcnttYXJnaW4tYm90dG9tOi41cmVtfS5mb290ZXIgLnJheS1pZHt0ZXh0LWFsaWduOmNlbnRlcn0uZm9vdGVyIC5yYXktaWQgY29kZXtmb250LWZhbWlseTptb25hY28sY291cmllcixtb25vc3BhY2V9LmNvcmUtbXNnLC56b25lLW5hbWUtdGl0bGV7b3ZlcmZsb3ctd3JhcDpicmVhay13b3JkfUBtZWRpYSAod2lkdGggPD0gNzIwcHgpey5kaWFnbm9zdGljLXdyYXBwZXJ7ZGlzcGxheTpmbGV4O2ZsZXgtd3JhcDp3cmFwO2p1c3RpZnktY29udGVudDpjZW50ZXJ9LmNsZWFyZml4OjphZnRlcntkaXNwbGF5OmluaXRpYWw7Y2xlYXI6bm9uZTt0ZXh0LWFsaWduOmNlbnRlcjtjb250ZW50Om5vbmV9LmNvbHVtbntwYWRkaW5nLWJvdHRvbToycmVtfS5jbGVhcmZpeCAuY29sdW1ue2Zsb2F0Om5vbmU7cGFkZGluZzowO3dpZHRoOmF1dG87d29yZC1icmVhazprZWVwLWFsbH0uem9uZS1uYW1lLXRpdGxle21hcmdpbi1ib3R0b206MXJlbX19LmxvYWRpbmctdmVyaWZ5aW5ne2hlaWdodDo3Ni4zOTFweH0ubGRzLXJpbmd7ZGlzcGxheTppbmxpbmUtYmxvY2s7cG9zaXRpb246cmVsYXRpdmU7d2lkdGg6MS44NzVyZW07aGVpZ2h0OjEuODc1cmVtfS5sZHMtcmluZyBkaXZ7Ym94LXNpemluZzpib3JkZXItYm94O2Rpc3BsYXk6YmxvY2s7cG9zaXRpb246YWJzb2x1dGU7Ym9yZGVyOi4zcmVtIHNvbGlkICM1OTU5NTk7Ym9yZGVyLXJhZGl1czo1MCU7Ym9yZGVyLWNvbG9yOiMzMTMxMzEgcmdiYSgwLDAsMCwwKSByZ2JhKDAsMCwwLDApO3dpZHRoOjEuODc1cmVtO2hlaWdodDoxLjg3NXJlbTthbmltYXRpb246bGRzLXJpbmcgMS4ycyBjdWJpYy1iZXppZXIoLjUsIDAsIC41LCAxKSBpbmZpbml0ZX0ubGRzLXJpbmcgZGl2Om50aC1jaGlsZCgxKXthbmltYXRpb24tZGVsYXk6LS40NXN9Lmxkcy1yaW5nIGRpdjpudGgtY2hpbGQoMil7YW5pbWF0aW9uLWRlbGF5Oi0uM3N9Lmxkcy1yaW5nIGRpdjpudGgtY2hpbGQoMyl7YW5pbWF0aW9uLWRlbGF5Oi0uMTVzfUBrZXlmcmFtZXMgbGRzLXJpbmd7MCV7dHJhbnNmb3JtOnJvdGF0ZSgwZGVnKX0xMDAle3RyYW5zZm9ybTpyb3RhdGUoMzYwZGVnKX19LnJ0bCAuaGVhZGluZy1mYXZpY29ue21hcmdpbi1yaWdodDowO21hcmdpbi1sZWZ0Oi41cmVtfS5ydGwgI2NoYWxsZW5nZS1zdWNjZXNzLXRleHR7YmFja2dyb3VuZC1wb3NpdGlvbjpyaWdodDtwYWRkaW5nLXJpZ2h0OjQycHg7cGFkZGluZy1sZWZ0OjB9LnJ0bCAjY2hhbGxlbmdlLWVycm9yLXRleHR7YmFja2dyb3VuZC1wb3NpdGlvbjpyaWdodDtwYWRkaW5nLXJpZ2h0OjM0cHg7cGFkZGluZy1sZWZ0OjB9LmNoYWxsZW5nZS1jb250ZW50IC5sb2FkaW5nLXZlcmlmeWluZ3toZWlnaHQ6NzYuMzkxcHh9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyayl7Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMyMjI7Y29sb3I6I2Q5ZDlkOX1ib2R5IGF7Y29sb3I6I2ZmZn1ib2R5IGE6aG92ZXJ7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZTtjb2xvcjojZWU3MzBhfWJvZHkgLmxkcy1yaW5nIGRpdntib3JkZXItY29sb3I6Izk5OSByZ2JhKDAsMCwwLDApIHJnYmEoMCwwLDAsMCl9Ym9keSAuZm9udC1yZWR7Y29sb3I6I2IyMGYwM31ib2R5IC5jdHAtYnV0dG9ue2JhY2tncm91bmQtY29sb3I6IzQ2OTNmZjtjb2xvcjojMWQxZDFkfWJvZHkgI2NoYWxsZW5nZS1zdWNjZXNzLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJZ2RtbGxkMEp2ZUQwaU1DQXdJREkySURJMklqNDhjR0YwYUNCbWFXeHNQU0lqWkRsa09XUTVJaUJrUFNKTk1UTWdNR0V4TXlBeE15QXdJREVnTUNBd0lESTJJREV6SURFeklEQWdNQ0F3SURBdE1qWnRNQ0F5TkdFeE1TQXhNU0F3SURFZ01TQXdMVEl5SURFeElERXhJREFnTUNBeElEQWdNaklpTHo0OGNHRjBhQ0JtYVd4c1BTSWpaRGxrT1dRNUlpQmtQU0p0TVRBdU9UVTFJREUyTGpBMU5TMHpMamsxTFRRdU1USTFMVEV1TkRRMUlERXVNemcxSURVdU16Y2dOUzQyTVNBNUxqUTVOUzA1TGpZdE1TNDBNaTB4TGpRd05Yb2lMejQ4TDNOMlp6NCIpfWJvZHkgI2NoYWxsZW5nZS1lcnJvci10ZXh0e2JhY2tncm91bmQtaW1hZ2U6dXJsKCJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSXpNaUlnYUdWcFoyaDBQU0l6TWlJZ1ptbHNiRDBpYm05dVpTSStQSEJoZEdnZ1ptbHNiRDBpSTBJeU1FWXdNeUlnWkQwaVRURTJJRE5oTVRNZ01UTWdNQ0F4SURBZ01UTWdNVE5CTVRNdU1ERTFJREV6TGpBeE5TQXdJREFnTUNBeE5pQXpiVEFnTWpSaE1URWdNVEVnTUNBeElERWdNVEV0TVRFZ01URXVNREVnTVRFdU1ERWdNQ0F3SURFdE1URWdNVEVpTHo0OGNHRjBhQ0JtYVd4c1BTSWpRakl3UmpBeklpQmtQU0pOTVRjdU1ETTRJREU0TGpZeE5VZ3hOQzQ0TjB3eE5DNDFOak1nT1M0MWFESXVOemd6ZW0wdE1TNHdPRFFnTVM0ME1qZHhMalkySURBZ01TNHdOVGN1TXpnNExqUXdOeTR6T0RrdU5EQTNMams1TkNBd0lDNDFPVFl0TGpRd055NDVPRFF0TGpNNU55NHpPUzB4TGpBMU55NHpPRGt0TGpZMUlEQXRNUzR3TlRZdExqTTRPUzB1TXprNExTNHpPRGt0TGpNNU9DMHVPVGcwSURBdExqVTVOeTR6T1RndExqazROUzQwTURZdExqTTVOeUF4TGpBMU5pMHVNemszSWk4K1BDOXpkbWMrIil9fTwvc3R5bGU+PHNjcmlwdCBzcmM9Imh0dHBzOi8vY2hhbGxlbmdlcy5jbG91ZGZsYXJlLmNvbS90dXJuc3RpbGUvdjAvYi9jODg3NTViMGNkZGMvYXBpLmpzP29ubG9hZD10Sk5jNiZhbXA7cmVuZGVyPWV4cGxpY2l0IiBhc3luYz0iIiBkZWZlcj0iIiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj48L3NjcmlwdD48L2hlYWQ+PGJvZHk+PGRpdiBjbGFzcz0ibWFpbi13cmFwcGVyIiByb2xlPSJtYWluIj48ZGl2IGNsYXNzPSJtYWluLWNvbnRlbnQiPjxoMSBjbGFzcz0iem9uZS1uYW1lLXRpdGxlIGgxIj5yZXNhLm5vdHJlZGFtZWRlcGFyaXMuZnI8L2gxPjxwIGlkPSJHRnB3azUiIGNsYXNzPSJoMiBzcGFjZXItYm90dG9tIj5WZXJpZnkgeW91IGFyZSBodW1hbiBieSBjb21wbGV0aW5nIHRoZSBhY3Rpb24gYmVsb3cuPC9wPjxkaXYgaWQ9IlRrdFJZMSIgc3R5bGU9ImRpc3BsYXk6IGdyaWQ7Ij48ZGl2PjxkaXY+PGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iY2YtdHVybnN0aWxlLXJlc3BvbnNlIiBpZD0iY2YtY2hsLXdpZGdldC1lMnM4c19yZXNwb25zZSI+PC9kaXY+PC9kaXY+PC9kaXY+PGRpdiBpZD0iZkd0Y1M3IiBjbGFzcz0ic3BhY2VyIGxvYWRpbmctdmVyaWZ5aW5nIiBzdHlsZT0iZGlzcGxheTogbm9uZTsgdmlzaWJpbGl0eTogaGlkZGVuOyI+PGRpdiBjbGFzcz0ibGRzLXJpbmciPjxkaXY+PC9kaXY+PGRpdj48L2Rpdj48ZGl2PjwvZGl2PjxkaXY+PC9kaXY+PC9kaXY+PC9kaXY+PGRpdiBpZD0iRGNIc08yIiBjbGFzcz0iY29yZS1tc2cgc3BhY2VyIHNwYWNlci10b3AiPnJlc2Eubm90cmVkYW1lZGVwYXJpcy5mciBuZWVkcyB0byByZXZpZXcgdGhlIHNlY3VyaXR5IG9mIHlvdXIgY29ubmVjdGlvbiBiZWZvcmUgcHJvY2VlZGluZy48L2Rpdj48ZGl2IGlkPSJOdU10MyIgc3R5bGU9ImRpc3BsYXk6IG5vbmU7Ij48ZGl2IGlkPSJjaGFsbGVuZ2Utc3VjY2Vzcy10ZXh0IiBjbGFzcz0iaDIiPlZlcmlmaWNhdGlvbiBzdWNjZXNzZnVsPC9kaXY+PGRpdiBjbGFzcz0iY29yZS1tc2cgc3BhY2VyIj5XYWl0aW5nIGZvciByZXNhLm5vdHJlZGFtZWRlcGFyaXMuZnIgdG8gcmVzcG9uZC4uLjwvZGl2PjwvZGl2Pjxub3NjcmlwdD48ZGl2IGNsYXNzPSJoMiI+PHNwYW4gaWQ9ImNoYWxsZW5nZS1lcnJvci10ZXh0Ij5FbmFibGUgSmF2YVNjcmlwdCBhbmQgY29va2llcyB0byBjb250aW51ZTwvc3Bhbj48L2Rpdj48L25vc2NyaXB0PjwvZGl2PjwvZGl2PjxzY3JpcHQ+KGZ1bmN0aW9uKCl7d2luZG93Ll9jZl9jaGxfb3B0ID0ge2N2SWQ6ICczJyxjWm9uZTogJ3Jlc2Eubm90cmVkYW1lZGVwYXJpcy5mcicsY1R5cGU6ICdtYW5hZ2VkJyxjUmF5OiAnOTk1MWIxNTVmODE5ZTk0YicsY0g6ICdZTVBjcU5HVm1oSGptNmk4YXdqLllxWmRiZmJ5dDZfRWlIaXR2aHZMcnlRLTE3NjE1NjI4NTctMS4yLjEuMS1WOE90alI2el80QmFCdjlkaUQ4eWowMkVhUkpQYWNjVDZ3WGE4cVpaRjlyUno4a2d4eXp1WG9tVmxDbk0xWFpyJyxjVVBNRFRrOiJcLz9fX2NmX2NobF90az0xaERpMjR5VWxpN0FZSDFPWVA3b3hnWngycHF2Wlp5a0RjRXIxOU9JUHJrLTE3NjE1NjI4NTctMS4wLjEuMS1wYUZTd25ZYkxkVmVqSXhUbk02RUlwTTJmSGZVVEJ4cmw5ZWR4RERnUnh3IixjRlBXdjogJ2InLGNJVGltZVM6ICcxNzYxNTYyODU3JyxjVHBsQzowLGNUcGxWOjUsY1RwbEI6ICcwJyxmYToiXC8/X19jZl9jaGxfZl90az0xaERpMjR5VWxpN0FZSDFPWVA3b3hnWngycHF2Wlp5a0RjRXIxOU9JUHJrLTE3NjE1NjI4NTctMS4wLjEuMS1wYUZTd25ZYkxkVmVqSXhUbk02RUlwTTJmSGZVVEJ4cmw5ZWR4RERnUnh3IixtZDogJ1ZIemtCN0xxS0dENEhaNUVRYV9hQUpueVQuX3h2V2g0WGtyUUc1WGhkaEEtMTc2MTU2Mjg1Ny0xLjIuMS4xLXJmRWRJOXV2ckY3cFI3NEd1QmRwM2VRalBkM2pWY0NtRkNGS3RfWVh3blExTlVxa2VJbkouclBJS0VTSmNhaTd5ZTJQcUdZeDRLa1ZMYVhpMHltWHBLS01xT3k0VGxQaEd1V1h1a1BmUjVYZG5pdkdnRFE4WUhzNloweGRBTHlhcHduNjdNYWtRSE9iRXFQd05udEhFck9uT1RneGdIZzBrS3dqeTZ5d1hhS25QdGwuNGl4Snl4Z3M5R2pxMnEzN3JMUUpkeU90ejBkTWVVcWd0V3Zpa0dLZWNqZ3Z2eTRiR3JGanNIbTdSNHRqbGI2UnIuT3psV3VzUjdZVG9fcXZ0NUZyTkFJNUxaSzdoMnlZUEFhVFFZRGhvN2xkRG0zdHp6UEkxTHdHUDZQWmRsRmtIYURyUW9KTVVCZThQLk82b1VsVUV0UXoxQ3c5YktOYXJJSXN5Mk0zRkI0b3YzQWZfZDZVdTVlMDVaSHhUajdQQW53a201cFRIOVh4NlhacW5HZ2JWSHBIN2FxYnhQd1FQblllNHFVVDZVY0JDMHVPOW5ibzJyMldndTdJOG0zSHVjRTAwbnBRNXhBZE9GMlJPVlp0LlVqcktrdDRFN21lUmJkWE5jcVpPR3JfVXcxUkxxNzEwQnVJSDFONWllc3pzMDFiVUliVVhrV1ZyV3RhLi5lMHY4N0t0U0JEUmU4eWpndHFTQjV0YW1rdENtdldvalpVNDBuSTlfbF9jLk5INnFPUTdla19jT0xhNHBFbFdFUWtuZ0MueG9EdnNuTnF5RFJiZ1VHdUtUUlNET0xxOHlRUG9SdmpXUDh0ckFmVm9NTUdNSG14UVFSYzlQdEZPXzBtekIuVG1XQWJqeVBrQXZtOWs5OTg5S0RKU2Z0WFNIdXRJOER3cHhFalZhWmt1Tl85SXpFakxERkNXQkRCYmFCdC5tRy44aDk3RHVvVEh2aDM2bS5DSUlvZjR5MEd6TDhmQWx4MHdQQ3h0U0hVZjdVZ0U2XzhkSjl3S0FQTU1xeEhWVnJHTEJHblZMOHl2NFJHY3VWTklQd1ByOTV4RFd4UjlWaS5QbnNwdnVxSFFvbmVoSlZSMURNUmVlNWxCU1RvNHMzNy5MLnl3QnFTVHM3OVpoeVZ0el81NlhhZjEzbExGZFFhWDF0WFoyclcuSURKdnNnWnhOYVU4MFlJOEdrbjNCZ21VUmhXWFl1Q1ppMk4ueU1paHFSZXBrVnh5Z3dXWWxtdWVZMm1udW9JU21qVXhKMG0xZzQxZU5FUHQ5dXJWVjlsbVVuYU1vQlI1a0wzMW5pUFh6QWt3QmlpeTc5aW9wYWpnU2R0eVZXdGtUamtscjh1MFRsUnV2OUJVOGRMNTBvWjVTUmVVdE5hdXZvajJYTS5zcGZvYlpiLjBLYWFMakwuM3g0JyxtZHJkOiAnbkd3eHl2Y2lIQU1MTnFWYU1sdjQ1X1NKN3hZRlpYOGhPcnFWazlWZU1ZRS0xNzYxNTYyODU3LTEuMi4xLjEtdWVzYzZHbkJlMHJJZ2oxTlk4TlhpRm01a0R2OW9HX0ExTFR2RWsuSHgxWV9LbkJ6dkZhdDhHWjI3d0xOOWt4ZWxESnNPMklOdWt4Y2Q3V1RYN1JiRzkyTU1WRFZmTG5HRDBQaXdtcE1GREVsNzNBMUdhU2tUX1JNNjIyaWVMMkpCUmdYeEtTamtQckhNWmZXZVNBbnkubEMubGtDM0NwaXBoQ3N6Wk5VV0pGTUVNcW9RZVJLbXkwWmtPUE4yaTcxZ0I3eE81REZteHBTWGlsckREdGpTcG1jZW8wWFpMMTVkLlFzMFkySlpncDlicC5Oc3hSZUJOallPR0s4dnhyU1pncmZ0YlhBUHNDejNXR3ZVbVFyczRwNWczajJ0X0ROVU14cUVxdm55cUVlRDhod2dDZG9OUXMxZHBRZTh0dWpSdGpLcnZadDFXRFFGcHBtcHZYRU1nMzZYVHFHNWgzWnFXdmJ6QVBxbVYyMElsOGdoc3ZZdEhUWl94aUZPY3RSMlh0VEFPWnJJeDc3SEpiQUU3UDRXcjBCSkFwNU5BVTRTZ2IwbmdsU05wVXd4LmNDbndZZGVlXzJZMzZneWRGT3F2SEJkRDRNSWpOeU8yVnB2RzczdkRndUdMdVNuUU5aSE9NbndTSlZoVFFQQk9aUVFfV3EwZ1lBMU1NTFYzSkUzLnh6Zmd6cUpwNDVNQ3BfYU5SRmFYUWo5NGZkb3hwdlJkWVdseVVZSzlwdlM4N3dDc3kucFpsX25ENllncXdBLjlFRGVsSFJfTHNjaVh4S19WbWZnUl9kWmJ6OGYwQnp3UEJITEJZTEk4VUFIbjdoaDFWUlBucUlOUW43X290aXBTaHVOSlAwd25BOHBmQUpxMWxXZGNEczFoUE82bWg3bFNmUkl3dml4dXguWmk1Y2FOUkR1cS5pc2JfNkc5eV83aldSbzJfbFB1akgxWlVBNDBFck5wdjRmOGhjRG1uTFR3R0RHVGw1cHhBc1d5M1pBSU5qeThiTG5CbmJrNFJPUEU3WEQ1MUM4ZkVPNHFfVGdaVzJWUGlyQnNtQnFabEdPWnhyMS5fWTNOdzZHWEd0d01WN0p3N3k4bk5ja0lGdk1POW5PRnBrZkRzNWFsZUlpWkNSOGp5RTk3WjBaX3NiajdJU2lLVXBLM3pRTzlpdDhydXdST2EyelpUZ3ZZWDcyWUMxdzZ0M1VFWXhoeTFha2pVbHc1QVFPQkZhODhMVlV6X0VZRE9vMzViMzVFYl8yYjJheFVwV3EwQ3hwRnlPQlN5VlFXNnhvNVV4TklsSmdDZHZCQzBaWGVBUmpkaDQ0V240dURjRmt2eGIzaFFMTkVkUm92R0t3QlBmR2JRdk0wUHlLbUw0SzY2RUNIT2pPM2NCWFNDNmYxRGdCS0hzV2ZKUFFHVGV3VHhMMEF6cUFsOUNjUXcxOVU3LmFBM2VhMnA4eDhqWUFpU3g3ZGNKbEVRMUswS1hyaDl3NVcuS041SFB3RzY2YmYyS0dyUm1Mc2RZd2hpYlBoNlhvcDVJRThhZVluUnFvYmpsZEl2a1o0RGNfSWRXT3ppQUtYM01YSGlWMkJUWFpSeERLbWl2YjF2N2RCZEpDM19jSE5LcUZVWDI3ejBKOFVXRlQ0T01pbVFhQ3hRd3JQV0tCdE9LMmR4TncyZjFHSlBUQmhxVlVSOF8xaGtQTElwdzFvaUFOczcxaGpWUU5EZG9tWlZ1bVAuelFXQjN0N2U0TU9icUJIeThsLnFxeXo0WWV3SWVlSk1Ja1lCZlFMdVpwUV9MZmY4OWZ1cUVpMnA0MHJ4NEEycnVvc1lMeGZRWGVHM1FTSWxrdk96Qm4wemNmMU55TG4uR2tfZVFvYzlYN1lDR2JxTUFMS2hDR3pXM1l0VlZaeU1MSmUxYlN1d0NMc0RvVUpxcnZkUWh4dHhvVU9HZDF1dnBoUmRmOTdYbTRFdmFkWmFDVkI4bnRYb181MmpYUnltYTk3cGZEajU4U25kR21ld3BReWthYUdrem5tRncxajlnRk4ySG9qZnFtZFBVRW44eDM2VFZJRm1sNzJjVXhZWG5mU1EwNTNHYXhuTVR2T3NrZzkuMjJOWXp5cWRuNnNPaHdnMnVvU3lpanAuYnNoYklPYlFzUmxOY05IY1prNHZCa2w2RFVjaEc4WklJRTNObWRsTi45czZSeEk0UGlfUEtKaXJnR0duTUxXWUZrd2lESEc3TGtFdUQ5TkdoYU5WNVNLZVFXY0M3MnlZcHp0MU04VTduVDZ3S1ZKWmJMd2dGQTFXeXFNNXYzajJhYl9ZdEgySFNINGNkeDFRYWxqUkpTcWgzNXRFU1ViLi5vVmppZDh1UFJQdUxVODg3TG45MEx1WENWUnpCN0xWaEpoUG00ZENaWDgyejBnbmluUlkwcFZNOVhaYU90VXdkTlBIN0JKanoyUlo1N2xDN3VEbVQ4M1VoY1R1TjZCNFBlTlJhek9ZZGVOaG1FN0cxZU9sVnROaEoucUlRdHdWc3Zick9lWTBoTjM1ZklkUGU4TEhhQkxyUzVwdUg2ZzRHbWZLVUF4Q29NeVRIcnNqTnlYbGV3cG9HOEdNX0JzazRyMUhpWmFMSk9pYkVPM3hqMGRlV0lIZ3BaRnFoaHNZX2RZLndwTWxaR04zeHF6clFKekdFa191UkdPVFNaa0NpM1oxd21HZmFDVmFpVnhTd2NoRk9oOUVWN25EYUJHRFBCYVd6cGhhNXZoSnRVU0toRWxpNVpBQXE2ZndQUDRHY0xFR2xCSU52VGlMZ2FnZTRUbGpPbGU0azJpUDRzWHJqUy5NcnJLUHQxTjZ6RW85R0xfSGlEY0Q3WjBWMnlDeU5uMjlRUUhzNnJBeWdxVmlQVGNjR3Fscl9UdF80eWljc3ZDYW5nVFM0X3VrWWtlclhhdkZudVQyeklCeTNOVmt4SkpoSllQeEtpbElJb1hBLm1RSGZWRHVCcUNXRTR0MC5oLkpDakdXMXpyOHd2ek5xR1dZX283aGthbUpac0NENlZ5VU8ua3RPSVRqQll6blZjUTdWYndjWDVZRjh3YTNvMktLTF83TGtVT3FwSmZQelZ1VkFhYXFiekcxQ3pzaG9yZDFxdVJHbXA1eWVqelRDYmREaU9XZnpVTjdxOFlSbm9JenlKZ1dNOHF5OGVhdGVWUkhaT2VKWHpLQUo0VmYuWEZkQ0hENDNGX1FVcnVjYjRQQ1J4YUtfemhwemFjZVFIYTd4UlZ3d3RTTkVWS01xWEJEV3FSdnhqMk94WGM0YVJtZ3pIYUE2dUY0SDBYaFhSODdQYXZsTnBnQ29lV0Q3R2F5V0FNOW9JSXpNdFBabnZYMGlOaDBvbTlSeUlGV1N5cW1zd1dtMC5tdDV0Lk9SQ3VKYm1QRVBGbHppcnN5Qjc1dDROZlRtUEJ6UlZCY2tkSEtXMVdEaUp1ZGRISTJMcm5QSXdjOWZ2Z2hybU5xN0Uxc3ZIM3ZBXzg4U3JoQzEnLH07dmFyIGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTthLnNyYyA9ICcvY2RuLWNnaS9jaGFsbGVuZ2UtcGxhdGZvcm0vaC9iL29yY2hlc3RyYXRlL2NobF9wYWdlL3YxP3JheT05OTUxYjE1NWY4MTllOTRiJzt3aW5kb3cuX2NmX2NobF9vcHQuY09nVUhhc2ggPSBsb2NhdGlvbi5oYXNoID09PSAnJyAmJiBsb2NhdGlvbi5ocmVmLmluZGV4T2YoJyMnKSAhPT0gLTEgPyAnIycgOiBsb2NhdGlvbi5oYXNoO3dpbmRvdy5fY2ZfY2hsX29wdC5jT2dVUXVlcnkgPSBsb2NhdGlvbi5zZWFyY2ggPT09ICcnICYmIGxvY2F0aW9uLmhyZWYuc2xpY2UoMCwgbG9jYXRpb24uaHJlZi5sZW5ndGggLSB3aW5kb3cuX2NmX2NobF9vcHQuY09nVUhhc2gubGVuZ3RoKS5pbmRleE9mKCc/JykgIT09IC0xID8gJz8nIDogbG9jYXRpb24uc2VhcmNoO2lmICh3aW5kb3cuaGlzdG9yeSAmJiB3aW5kb3cuaGlzdG9yeS5yZXBsYWNlU3RhdGUpIHt2YXIgb2dVID0gbG9jYXRpb24ucGF0aG5hbWUgKyB3aW5kb3cuX2NmX2NobF9vcHQuY09nVVF1ZXJ5ICsgd2luZG93Ll9jZl9jaGxfb3B0LmNPZ1VIYXNoO2hpc3RvcnkucmVwbGFjZVN0YXRlKG51bGwsIG51bGwsIlwvP19fY2ZfY2hsX3J0X3RrPTFoRGkyNHlVbGk3QVlIMU9ZUDdveGdaeDJwcXZaWnlrRGNFcjE5T0lQcmstMTc2MTU2Mjg1Ny0xLjAuMS4xLXBhRlN3blliTGRWZWpJeFRuTTZFSXBNMmZIZlVUQnhybDllZHhERGdSeHciKyB3aW5kb3cuX2NmX2NobF9vcHQuY09nVUhhc2gpO2Eub25sb2FkID0gZnVuY3Rpb24oKSB7aGlzdG9yeS5yZXBsYWNlU3RhdGUobnVsbCwgbnVsbCwgb2dVKTt9fWRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJylbMF0uYXBwZW5kQ2hpbGQoYSk7fSgpKTs8L3NjcmlwdD48c2NyaXB0IGRlZmVyPSIiIHNyYz0iaHR0cHM6Ly9zdGF0aWMuY2xvdWRmbGFyZWluc2lnaHRzLmNvbS9iZWFjb24ubWluLmpzL3ZjZDE1Y2JlNzc3MmY0OWMzOTljNmE1YmFiZjIyYzEyNDE3MTc2ODkxNzYwMTUiIGludGVncml0eT0ic2hhNTEyLVpwc09tbFJRVjZ5OTA3VEkwZEtCSHE5TWQyOW5uYUVJUGxrZjg0cm5hRVJucTZ6dld2UFVxcjJmdDhNMWFTMjhvTjcyUGRyQ3pTalk0VTZWYUF3MUVRPT0iIGRhdGEtY2YtYmVhY29uPSJ7JnF1b3Q7cmF5SWQmcXVvdDs6JnF1b3Q7OTk1MWIxNTVmODE5ZTk0YiZxdW90OywmcXVvdDtzZXJ2ZXJUaW1pbmcmcXVvdDs6eyZxdW90O25hbWUmcXVvdDs6eyZxdW90O2NmRXh0UHJpJnF1b3Q7OnRydWUsJnF1b3Q7Y2ZFZGdlJnF1b3Q7OnRydWUsJnF1b3Q7Y2ZPcmlnaW4mcXVvdDs6dHJ1ZSwmcXVvdDtjZkw0JnF1b3Q7OnRydWUsJnF1b3Q7Y2ZTcGVlZEJyYWluJnF1b3Q7OnRydWUsJnF1b3Q7Y2ZDYWNoZVN0YXR1cyZxdW90Ozp0cnVlfX0sJnF1b3Q7dmVyc2lvbiZxdW90OzomcXVvdDsyMDI1LjkuMSZxdW90OywmcXVvdDt0b2tlbiZxdW90OzomcXVvdDtjMzgxZTcwZWFlNDQ0MzE4YmQzMjk2Y2M4ODlmNjdiNiZxdW90O30iIGNyb3Nzb3JpZ2luPSJhbm9ueW1vdXMiPjwvc2NyaXB0Pgo8ZGl2IGNsYXNzPSJmb290ZXIiIHJvbGU9ImNvbnRlbnRpbmZvIj48ZGl2IGNsYXNzPSJmb290ZXItaW5uZXIiPjxkaXYgY2xhc3M9ImNsZWFyZml4IGRpYWdub3N0aWMtd3JhcHBlciI+PGRpdiBjbGFzcz0icmF5LWlkIj5SYXkgSUQ6IDxjb2RlPjk5NTFiMTU1ZjgxOWU5NGI8L2NvZGU+PC9kaXY+PC9kaXY+PGRpdiBjbGFzcz0idGV4dC1jZW50ZXIiIGlkPSJmb290ZXItdGV4dCI+UGVyZm9ybWFuY2UgJmFtcDsgc2VjdXJpdHkgYnkgPGEgcmVsPSJub29wZW5lciBub3JlZmVycmVyIiBocmVmPSJodHRwczovL3d3dy5jbG91ZGZsYXJlLmNvbT91dG1fc291cmNlPWNoYWxsZW5nZSZhbXA7dXRtX2NhbXBhaWduPW0iIHRhcmdldD0iX2JsYW5rIj5DbG91ZGZsYXJlPC9hPjwvZGl2PjwvZGl2PjwvZGl2PjwvYm9keT48L2h0bWw+",
+ proxy=proxy
+ )
+
+ solution = await cap_monster_client.solve_captcha(turnstile_request)
+ print("Solution:", solution)
+
+asyncio.run(main())
diff --git a/examples/client_proxy.py b/examples/client_proxy.py
new file mode 100644
index 0000000..320fca2
--- /dev/null
+++ b/examples/client_proxy.py
@@ -0,0 +1,50 @@
+import asyncio
+import os
+import urllib
+import base64
+import time
+
+from capmonstercloudclient import CapMonsterClient, ClientOptions
+from capmonstercloudclient.requests import RecaptchaComplexImageTaskRequest, ClientProxyInfo
+
+async def solve_captcha_sync(num_requests,
+ client,
+ request):
+ return [await client.solve_captcha(request) for _ in range(num_requests)]
+
+
+if __name__ == '__main__':
+
+ api_key = os.getenv('API_KEY')
+ userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36."
+ metadata = {
+ "Task": "Click on traffic lights",
+ "Grid": "3x3",
+ "TaskDefinition": "/m/015qff"
+ }
+ imagesUrls = ["https://i.postimg.cc/yYjg75Kv/payloadtraffic.jpg"]
+ client_proxy = ClientProxyInfo(
+ proxyType='http',
+ proxyAddress='gw-us.zengw.io', # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyPort=8080, # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyLogin='login', # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ proxyPassword='password' # get at https://docs.zennolab.com/zennoproxy/introduction or your own proxy
+ )
+
+ options = ClientOptions(api_key=api_key, client_proxy=client_proxy)
+ client = CapMonsterClient(options)
+
+ # urls example
+ request_with_urls = RecaptchaComplexImageTaskRequest(metadata=metadata,
+ imagesUrls=imagesUrls)
+
+ nums = 3
+
+ print(' Test with urls '.center(120, '#'))
+
+ # Sync test
+ sync_start = time.time()
+ sync_responses = asyncio.run(solve_captcha_sync(nums, client, request_with_urls))
+ print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} ' \
+ f'resp/sec\nsolution: {sync_responses[0]}')
+
diff --git a/examples/imperva.py b/examples/imperva.py
index 6eba08b..cf87af4 100644
--- a/examples/imperva.py
+++ b/examples/imperva.py
@@ -18,12 +18,21 @@ async def solve_captcha_async(num_requests):
key = os.getenv('API_KEY')
client_options = ClientOptions(api_key=key)
cap_monster_client = CapMonsterClient(options=client_options)
+ from capmonstercloudclient.requests import ProxyInfo
+ proxy = ProxyInfo(
+ proxyType="http",
+ proxyAddress="8.8.8.8",
+ proxyPort=8000,
+ proxyLogin="proxyLoginHere",
+ proxyPassword="proxyPasswordHere"
+ )
metadata = {"incapsulaScriptUrl": "_Incapsula_Resource?SWJIYLWA=719d34d31c8e3a6e6fffd425f7e032f3",
"incapsulaCookie": "incap_ses_1166_2930313=br7iX33ZNCtf3HlpEXcuEDzz72cAAAAA0suDnBGrq/iA0J4oERYzjQ==; visid_incap_2930313=P3hgPVm9S8Oond1L0sXhZqfK72cAAAAAQUIPAAAAAABoMSY9xZ34RvRseJRiY6s+;",
"reese84UrlEndpoint": "Built-with-the-For-hopence-Hurleysurfecting-the-"}
imperva_request = ImpervaCustomTaskRequest(
websiteUrl='https://example.com/login',
- metadata=metadata
+ metadata=metadata,
+ proxy=proxy
)
nums = 3
diff --git a/examples/recaptchaV3Enterprise.py b/examples/recaptchaV3Enterprise.py
new file mode 100644
index 0000000..6135b19
--- /dev/null
+++ b/examples/recaptchaV3Enterprise.py
@@ -0,0 +1,41 @@
+import os
+import time
+import asyncio
+
+from capmonstercloudclient import CapMonsterClient, ClientOptions
+from capmonstercloudclient.requests import RecaptchaV3EnterpriseRequest
+
+async def solve_captcha_sync(num_requests):
+ return [await cap_monster_client.solve_captcha(recaptcha3enterprise_request) for _ in range(num_requests)]
+
+async def solve_captcha_async(num_requests):
+ tasks = [asyncio.create_task(cap_monster_client.solve_captcha(recaptcha3enterprise_request))
+ for _ in range(num_requests)]
+ return await asyncio.gather(*tasks, return_exceptions=True)
+
+
+if __name__ == '__main__':
+ key = os.getenv('API_KEY')
+ client_options = ClientOptions(api_key=key)
+ cap_monster_client = CapMonsterClient(options=client_options)
+
+ recaptcha3enterprise_request = RecaptchaV3EnterpriseRequest(
+ websiteUrl="https://lessons.zennolab.com/captchas/recaptcha/v3.php?level=beta",
+ websiteKey="6Le0xVgUAAAAAIt20XEB4rVhYOODgTl00d8juDob",
+ minScore=0.9,
+ pageAction="submit"
+ )
+
+ nums = 3
+ # Sync test
+ sync_start = time.time()
+ sync_responses = asyncio.run(solve_captcha_sync(nums))
+ print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} ' \
+ f'resp/sec\nsolution: {sync_responses[0]}')
+
+ # Async test
+ async_start = time.time()
+ async_responses = asyncio.run(solve_captcha_async(nums))
+ print(f'average execution time async {1/((time.time()-async_start)/nums):0.2f} ' \
+ f'resp/sec\nsolution: {async_responses[0]}')
+
diff --git a/examples/turnstile_cf.py b/examples/turnstile_cf.py
new file mode 100644
index 0000000..d24df7c
--- /dev/null
+++ b/examples/turnstile_cf.py
@@ -0,0 +1,30 @@
+import asyncio
+from capmonstercloudclient import CapMonsterClient, ClientOptions
+from capmonstercloudclient.requests import TurnstileRequest
+from capmonstercloudclient.requests.baseRequestWithProxy import ProxyInfo
+
+async def main():
+ client_options = ClientOptions(api_key="dac3599143bdfd88dfc41758c6cb8729")
+ cap_monster_client = CapMonsterClient(options=client_options)
+
+ proxy = ProxyInfo(
+ proxyType="https",
+ proxyAddress="proxyAddress",
+ proxyPort=8080,
+ proxyLogin="login",
+ proxyPassword="password"
+ )
+
+ turnstile_request = TurnstileRequest(
+ websiteURL="https://resa.notredamedeparis.fr/",
+ websiteKey="xxxxx",
+ cloudflareTaskType="cf_clearance",
+ userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
+ htmlPageBase64="PGh0bWwgbGFuZz0iZW4tVVMiIGRpcj0ibHRyIj48aGVhZD48dGl0bGU+SnVzdCBhIG1vbWVudC4uLjwvdGl0bGU+PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9VVRGLTgiPjxtZXRhIGh0dHAtZXF1aXY9IlgtVUEtQ29tcGF0aWJsZSIgY29udGVudD0iSUU9RWRnZSI+PG1ldGEgbmFtZT0icm9ib3RzIiBjb250ZW50PSJub2luZGV4LG5vZm9sbG93Ij48bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLGluaXRpYWwtc2NhbGU9MSI+PHN0eWxlPip7Ym94LXNpemluZzpib3JkZXItYm94O21hcmdpbjowO3BhZGRpbmc6MH1odG1se2xpbmUtaGVpZ2h0OjEuMTU7LXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OjEwMCU7Y29sb3I6IzMxMzEzMTtmb250LWZhbWlseTpzeXN0ZW0tdWksLWFwcGxlLXN5c3RlbSxCbGlua01hY1N5c3RlbUZvbnQsIlNlZ29lIFVJIixSb2JvdG8sIkhlbHZldGljYSBOZXVlIixBcmlhbCwiTm90byBTYW5zIixzYW5zLXNlcmlmLCJBcHBsZSBDb2xvciBFbW9qaSIsIlNlZ29lIFVJIEVtb2ppIiwiU2Vnb2UgVUkgU3ltYm9sIiwiTm90byBDb2xvciBFbW9qaSJ9Ym9keXtkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2hlaWdodDoxMDB2aDttaW4taGVpZ2h0OjEwMHZofS5tYWluLWNvbnRlbnR7bWFyZ2luOjhyZW0gYXV0bztwYWRkaW5nLWxlZnQ6MS41cmVtO21heC13aWR0aDo2MHJlbX1AbWVkaWEgKHdpZHRoIDw9IDcyMHB4KXsubWFpbi1jb250ZW50e21hcmdpbi10b3A6NHJlbX19Lmgye2xpbmUtaGVpZ2h0OjIuMjVyZW07Zm9udC1zaXplOjEuNXJlbTtmb250LXdlaWdodDo1MDB9QG1lZGlhICh3aWR0aCA8PSA3MjBweCl7Lmgye2xpbmUtaGVpZ2h0OjEuNXJlbTtmb250LXNpemU6MS4yNXJlbX19I2NoYWxsZW5nZS1lcnJvci10ZXh0e2JhY2tncm91bmQtaW1hZ2U6dXJsKCJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSXpNaUlnYUdWcFoyaDBQU0l6TWlJZ1ptbHNiRDBpYm05dVpTSStQSEJoZEdnZ1ptbHNiRDBpSTBJeU1FWXdNeUlnWkQwaVRURTJJRE5oTVRNZ01UTWdNQ0F4SURBZ01UTWdNVE5CTVRNdU1ERTFJREV6TGpBeE5TQXdJREFnTUNBeE5pQXpiVEFnTWpSaE1URWdNVEVnTUNBeElERWdNVEV0TVRFZ01URXVNREVnTVRFdU1ERWdNQ0F3SURFdE1URWdNVEVpTHo0OGNHRjBhQ0JtYVd4c1BTSWpRakl3UmpBeklpQmtQU0pOTVRjdU1ETTRJREU0TGpZeE5VZ3hOQzQ0TjB3eE5DNDFOak1nT1M0MWFESXVOemd6ZW0wdE1TNHdPRFFnTVM0ME1qZHhMalkySURBZ01TNHdOVGN1TXpnNExqUXdOeTR6T0RrdU5EQTNMams1TkNBd0lDNDFPVFl0TGpRd055NDVPRFF0TGpNNU55NHpPUzB4TGpBMU55NHpPRGt0TGpZMUlEQXRNUzR3TlRZdExqTTRPUzB1TXprNExTNHpPRGt0TGpNNU9DMHVPVGcwSURBdExqVTVOeTR6T1RndExqazROUzQwTURZdExqTTVOeUF4TGpBMU5pMHVNemszSWk4K1BDOXpkbWMrIik7YmFja2dyb3VuZC1yZXBlYXQ6bm8tcmVwZWF0O2JhY2tncm91bmQtc2l6ZTpjb250YWluO3BhZGRpbmctbGVmdDozNHB4fUBtZWRpYSAocHJlZmVycy1jb2xvci1zY2hlbWU6IGRhcmspe2JvZHl7YmFja2dyb3VuZC1jb2xvcjojMjIyO2NvbG9yOiNkOWQ5ZDl9fTwvc3R5bGU+PG1ldGEgaHR0cC1lcXVpdj0icmVmcmVzaCIgY29udGVudD0iMzYwIj48c2NyaXB0IHNyYz0iL2Nkbi1jZ2kvY2hhbGxlbmdlLXBsYXRmb3JtL2gvYi9vcmNoZXN0cmF0ZS9jaGxfcGFnZS92MT9yYXk9OTk1MWIxNTVmODE5ZTk0YiI+PC9zY3JpcHQ+PHN0eWxlPip7Ym94LXNpemluZzpib3JkZXItYm94O21hcmdpbjowO3BhZGRpbmc6MH1odG1se2xpbmUtaGVpZ2h0OjEuMTU7LXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OjEwMCU7Y29sb3I6IzMxMzEzMTtmb250LWZhbWlseTpzeXN0ZW0tdWksLWFwcGxlLXN5c3RlbSxCbGlua01hY1N5c3RlbUZvbnQsIlNlZ29lIFVJIixSb2JvdG8sIkhlbHZldGljYSBOZXVlIixBcmlhbCwiTm90byBTYW5zIixzYW5zLXNlcmlmLCJBcHBsZSBDb2xvciBFbW9qaSIsIlNlZ29lIFVJIEVtb2ppIiwiU2Vnb2UgVUkgU3ltYm9sIiwiTm90byBDb2xvciBFbW9qaSJ9YnV0dG9ue2ZvbnQtZmFtaWx5OnN5c3RlbS11aSwtYXBwbGUtc3lzdGVtLEJsaW5rTWFjU3lzdGVtRm9udCwiU2Vnb2UgVUkiLFJvYm90bywiSGVsdmV0aWNhIE5ldWUiLEFyaWFsLCJOb3RvIFNhbnMiLHNhbnMtc2VyaWYsIkFwcGxlIENvbG9yIEVtb2ppIiwiU2Vnb2UgVUkgRW1vamkiLCJTZWdvZSBVSSBTeW1ib2wiLCJOb3RvIENvbG9yIEVtb2ppIn1ib2R5e2Rpc3BsYXk6ZmxleDtmbGV4LWRpcmVjdGlvbjpjb2x1bW47aGVpZ2h0OjEwMHZoO21pbi1oZWlnaHQ6MTAwdmh9Ym9keS50aGVtZS1kYXJre2JhY2tncm91bmQtY29sb3I6IzIyMjtjb2xvcjojZDlkOWQ5fWJvZHkudGhlbWUtZGFyayBhe2NvbG9yOiNmZmZ9Ym9keS50aGVtZS1kYXJrIGE6aG92ZXJ7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZTtjb2xvcjojZWU3MzBhfWJvZHkudGhlbWUtZGFyayAubGRzLXJpbmcgZGl2e2JvcmRlci1jb2xvcjojOTk5IHJnYmEoMCwwLDAsMCkgcmdiYSgwLDAsMCwwKX1ib2R5LnRoZW1lLWRhcmsgLmZvbnQtcmVke2NvbG9yOiNiMjBmMDN9Ym9keS50aGVtZS1kYXJrIC5jdHAtYnV0dG9ue2JhY2tncm91bmQtY29sb3I6IzQ2OTNmZjtjb2xvcjojMWQxZDFkfWJvZHkudGhlbWUtZGFyayAjY2hhbGxlbmdlLXN1Y2Nlc3MtdGV4dHtiYWNrZ3JvdW5kLWltYWdlOnVybCgiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l6TWlJZ2FHVnBaMmgwUFNJek1pSWdabWxzYkQwaWJtOXVaU0lnZG1sbGQwSnZlRDBpTUNBd0lESTJJREkySWo0OGNHRjBhQ0JtYVd4c1BTSWpaRGxrT1dRNUlpQmtQU0pOTVRNZ01HRXhNeUF4TXlBd0lERWdNQ0F3SURJMklERXpJREV6SURBZ01DQXdJREF0TWpadE1DQXlOR0V4TVNBeE1TQXdJREVnTVNBd0xUSXlJREV4SURFeElEQWdNQ0F4SURBZ01qSWlMejQ4Y0dGMGFDQm1hV3hzUFNJalpEbGtPV1E1SWlCa1BTSnRNVEF1T1RVMUlERTJMakExTlMwekxqazFMVFF1TVRJMUxURXVORFExSURFdU16ZzFJRFV1TXpjZ05TNDJNU0E1TGpRNU5TMDVMall0TVM0ME1pMHhMalF3TlhvaUx6NDhMM04yWno0Iil9Ym9keS50aGVtZS1kYXJrICNjaGFsbGVuZ2UtZXJyb3ItdGV4dHtiYWNrZ3JvdW5kLWltYWdlOnVybCgiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhkcFpIUm9QU0l6TWlJZ2FHVnBaMmgwUFNJek1pSWdabWxzYkQwaWJtOXVaU0krUEhCaGRHZ2dabWxzYkQwaUkwSXlNRVl3TXlJZ1pEMGlUVEUySUROaE1UTWdNVE1nTUNBeElEQWdNVE1nTVROQk1UTXVNREUxSURFekxqQXhOU0F3SURBZ01DQXhOaUF6YlRBZ01qUmhNVEVnTVRFZ01DQXhJREVnTVRFdE1URWdNVEV1TURFZ01URXVNREVnTUNBd0lERXRNVEVnTVRFaUx6NDhjR0YwYUNCbWFXeHNQU0lqUWpJd1JqQXpJaUJrUFNKTk1UY3VNRE00SURFNExqWXhOVWd4TkM0NE4wd3hOQzQxTmpNZ09TNDFhREl1TnpnemVtMHRNUzR3T0RRZ01TNDBNamR4TGpZMklEQWdNUzR3TlRjdU16ZzRMalF3Tnk0ek9Ea3VOREEzTGprNU5DQXdJQzQxT1RZdExqUXdOeTQ1T0RRdExqTTVOeTR6T1MweExqQTFOeTR6T0RrdExqWTFJREF0TVM0d05UWXRMak00T1MwdU16azRMUzR6T0RrdExqTTVPQzB1T1RnMElEQXRMalU1Tnk0ek9UZ3RMams0TlM0ME1EWXRMak01TnlBeExqQTFOaTB1TXprM0lpOCtQQzl6ZG1jKyIpfWJvZHkudGhlbWUtbGlnaHR7YmFja2dyb3VuZC1jb2xvcjojZmZmO2NvbG9yOiMzMTMxMzF9Ym9keS50aGVtZS1saWdodCBhe2NvbG9yOiMwMDUxYzN9Ym9keS50aGVtZS1saWdodCBhOmhvdmVye3RleHQtZGVjb3JhdGlvbjp1bmRlcmxpbmU7Y29sb3I6I2VlNzMwYX1ib2R5LnRoZW1lLWxpZ2h0IC5sZHMtcmluZyBkaXZ7Ym9yZGVyLWNvbG9yOiM1OTU5NTkgcmdiYSgwLDAsMCwwKSByZ2JhKDAsMCwwLDApfWJvZHkudGhlbWUtbGlnaHQgLmZvbnQtcmVke2NvbG9yOiNmYzU3NGF9Ym9keS50aGVtZS1saWdodCAuY3RwLWJ1dHRvbntib3JkZXItY29sb3I6IzAwMzY4MTtiYWNrZ3JvdW5kLWNvbG9yOiMwMDM2ODE7Y29sb3I6I2ZmZn1ib2R5LnRoZW1lLWxpZ2h0ICNjaGFsbGVuZ2Utc3VjY2Vzcy10ZXh0e2JhY2tncm91bmQtaW1hZ2U6dXJsKCJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSXpNaUlnYUdWcFoyaDBQU0l6TWlJZ1ptbHNiRDBpYm05dVpTSWdkbWxsZDBKdmVEMGlNQ0F3SURJMklESTJJajQ4Y0dGMGFDQm1hV3hzUFNJak16RXpNVE14SWlCa1BTSk5NVE1nTUdFeE15QXhNeUF3SURFZ01DQXdJREkySURFeklERXpJREFnTUNBd0lEQXRNalp0TUNBeU5HRXhNU0F4TVNBd0lERWdNU0F3TFRJeUlERXhJREV4SURBZ01DQXhJREFnTWpJaUx6NDhjR0YwYUNCbWFXeHNQU0lqTXpFek1UTXhJaUJrUFNKdE1UQXVPVFUxSURFMkxqQTFOUzB6TGprMUxUUXVNVEkxTFRFdU5EUTFJREV1TXpnMUlEVXVNemNnTlM0Mk1TQTVMalE1TlMwNUxqWXRNUzQwTWkweExqUXdOWG9pTHo0OEwzTjJaejQ9Iil9Ym9keS50aGVtZS1saWdodCAjY2hhbGxlbmdlLWVycm9yLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJK1BIQmhkR2dnWm1sc2JEMGlJMlpqTlRjMFlTSWdaRDBpVFRFMklETmhNVE1nTVRNZ01DQXhJREFnTVRNZ01UTkJNVE11TURFMUlERXpMakF4TlNBd0lEQWdNQ0F4TmlBemJUQWdNalJoTVRFZ01URWdNQ0F4SURFZ01URXRNVEVnTVRFdU1ERWdNVEV1TURFZ01DQXdJREV0TVRFZ01URWlMejQ4Y0dGMGFDQm1hV3hzUFNJalptTTFOelJoSWlCa1BTSk5NVGN1TURNNElERTRMall4TlVneE5DNDROMHd4TkM0MU5qTWdPUzQxYURJdU56Z3plbTB0TVM0d09EUWdNUzQwTWpkeExqWTJJREFnTVM0d05UY3VNemc0TGpRd055NHpPRGt1TkRBM0xqazVOQ0F3SUM0MU9UWXRMalF3Tnk0NU9EUXRMak01Tnk0ek9TMHhMakExTnk0ek9Ea3RMalkxSURBdE1TNHdOVFl0TGpNNE9TMHVNems0TFM0ek9Ea3RMak01T0MwdU9UZzBJREF0TGpVNU55NHpPVGd0TGprNE5TNDBNRFl0TGpNNU55QXhMakExTmkwdU16azNJaTgrUEM5emRtYysiKX1he3RyYW5zaXRpb246Y29sb3IgMTUwbXMgZWFzZTtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMCwwLDAsMCk7dGV4dC1kZWNvcmF0aW9uOm5vbmU7Y29sb3I6IzAwNTFjM31hOmhvdmVye3RleHQtZGVjb3JhdGlvbjp1bmRlcmxpbmU7Y29sb3I6I2VlNzMwYX0ubWFpbi1jb250ZW50e21hcmdpbjo4cmVtIGF1dG87cGFkZGluZy1yaWdodDoxLjVyZW07cGFkZGluZy1sZWZ0OjEuNXJlbTt3aWR0aDoxMDAlO21heC13aWR0aDo2MHJlbX0ubWFpbi1jb250ZW50IC5sb2FkaW5nLXZlcmlmeWluZ3toZWlnaHQ6NzYuMzkxcHh9LnNwYWNlcnttYXJnaW46MnJlbSAwfS5zcGFjZXItdG9we21hcmdpbi10b3A6NHJlbX0uc3BhY2VyLWJvdHRvbXttYXJnaW4tYm90dG9tOjJyZW19LmhlYWRpbmctZmF2aWNvbnttYXJnaW4tcmlnaHQ6LjVyZW07d2lkdGg6MnJlbTtoZWlnaHQ6MnJlbX1AbWVkaWEgKHdpZHRoIDw9IDcyMHB4KXsubWFpbi1jb250ZW50e21hcmdpbi10b3A6NHJlbX0uaGVhZGluZy1mYXZpY29ue3dpZHRoOjEuNXJlbTtoZWlnaHQ6MS41cmVtfX0ubWFpbi13cmFwcGVye2Rpc3BsYXk6ZmxleDtmbGV4OjE7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2FsaWduLWl0ZW1zOmNlbnRlcn0uZm9udC1yZWR7Y29sb3I6I2IyMGYwM30uaDF7bGluZS1oZWlnaHQ6My43NXJlbTtmb250LXNpemU6Mi41cmVtO2ZvbnQtd2VpZ2h0OjUwMH0uaDJ7bGluZS1oZWlnaHQ6Mi4yNXJlbTtmb250LXNpemU6MS41cmVtO2ZvbnQtd2VpZ2h0OjUwMH0uY29yZS1tc2d7bGluZS1oZWlnaHQ6Mi4yNXJlbTtmb250LXNpemU6MS41cmVtO2ZvbnQtd2VpZ2h0OjQwMH0uYm9keS10ZXh0e2xpbmUtaGVpZ2h0OjEuMjVyZW07Zm9udC1zaXplOjFyZW07Zm9udC13ZWlnaHQ6NDAwfUBtZWRpYSAod2lkdGggPD0gNzIwcHgpey5oMXtsaW5lLWhlaWdodDoxLjc1cmVtO2ZvbnQtc2l6ZToxLjVyZW19Lmgye2xpbmUtaGVpZ2h0OjEuNXJlbTtmb250LXNpemU6MS4yNXJlbX0uY29yZS1tc2d7bGluZS1oZWlnaHQ6MS41cmVtO2ZvbnQtc2l6ZToxcmVtfX0jY2hhbGxlbmdlLWVycm9yLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJK1BIQmhkR2dnWm1sc2JEMGlJMlpqTlRjMFlTSWdaRDBpVFRFMklETmhNVE1nTVRNZ01DQXhJREFnTVRNZ01UTkJNVE11TURFMUlERXpMakF4TlNBd0lEQWdNQ0F4TmlBemJUQWdNalJoTVRFZ01URWdNQ0F4SURFZ01URXRNVEVnTVRFdU1ERWdNVEV1TURFZ01DQXdJREV0TVRFZ01URWlMejQ4Y0dGMGFDQm1hV3hzUFNJalptTTFOelJoSWlCa1BTSk5NVGN1TURNNElERTRMall4TlVneE5DNDROMHd4TkM0MU5qTWdPUzQxYURJdU56Z3plbTB0TVM0d09EUWdNUzQwTWpkeExqWTJJREFnTVM0d05UY3VNemc0TGpRd055NHpPRGt1TkRBM0xqazVOQ0F3SUM0MU9UWXRMalF3Tnk0NU9EUXRMak01Tnk0ek9TMHhMakExTnk0ek9Ea3RMalkxSURBdE1TNHdOVFl0TGpNNE9TMHVNems0TFM0ek9Ea3RMak01T0MwdU9UZzBJREF0TGpVNU55NHpPVGd0TGprNE5TNDBNRFl0TGpNNU55QXhMakExTmkwdU16azNJaTgrUEM5emRtYysiKTtiYWNrZ3JvdW5kLXJlcGVhdDpuby1yZXBlYXQ7YmFja2dyb3VuZC1zaXplOmNvbnRhaW47cGFkZGluZy1sZWZ0OjM0cHh9I2NoYWxsZW5nZS1zdWNjZXNzLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJZ2RtbGxkMEp2ZUQwaU1DQXdJREkySURJMklqNDhjR0YwYUNCbWFXeHNQU0lqTXpFek1UTXhJaUJrUFNKTk1UTWdNR0V4TXlBeE15QXdJREVnTUNBd0lESTJJREV6SURFeklEQWdNQ0F3SURBdE1qWnRNQ0F5TkdFeE1TQXhNU0F3SURFZ01TQXdMVEl5SURFeElERXhJREFnTUNBeElEQWdNaklpTHo0OGNHRjBhQ0JtYVd4c1BTSWpNekV6TVRNeElpQmtQU0p0TVRBdU9UVTFJREUyTGpBMU5TMHpMamsxTFRRdU1USTFMVEV1TkRRMUlERXVNemcxSURVdU16Y2dOUzQyTVNBNUxqUTVOUzA1TGpZdE1TNDBNaTB4TGpRd05Yb2lMejQ4TDNOMlp6ND0iKTtiYWNrZ3JvdW5kLXJlcGVhdDpuby1yZXBlYXQ7YmFja2dyb3VuZC1zaXplOmNvbnRhaW47cGFkZGluZy1sZWZ0OjQycHh9LnRleHQtY2VudGVye3RleHQtYWxpZ246Y2VudGVyfS5jdHAtYnV0dG9ue3RyYW5zaXRpb24tZHVyYXRpb246MjAwbXM7dHJhbnNpdGlvbi1wcm9wZXJ0eTpiYWNrZ3JvdW5kLWNvbG9yLGJvcmRlci1jb2xvcixjb2xvcjt0cmFuc2l0aW9uLXRpbWluZy1mdW5jdGlvbjplYXNlO21hcmdpbjoycmVtIDA7Ym9yZGVyOi4wNjNyZW0gc29saWQgIzAwNTFjMztib3JkZXItcmFkaXVzOi4zMTNyZW07YmFja2dyb3VuZC1jb2xvcjojMDA1MWMzO2N1cnNvcjpwb2ludGVyO3BhZGRpbmc6LjM3NXJlbSAxcmVtO2xpbmUtaGVpZ2h0OjEuMzEzcmVtO2NvbG9yOiNmZmY7Zm9udC1zaXplOi44NzVyZW19LmN0cC1idXR0b246aG92ZXJ7Ym9yZGVyLWNvbG9yOiMwMDM2ODE7YmFja2dyb3VuZC1jb2xvcjojMDAzNjgxO2N1cnNvcjpwb2ludGVyO2NvbG9yOiNmZmZ9LmZvb3RlcnttYXJnaW46MCBhdXRvO3BhZGRpbmctcmlnaHQ6MS41cmVtO3BhZGRpbmctbGVmdDoxLjVyZW07d2lkdGg6MTAwJTttYXgtd2lkdGg6NjByZW07bGluZS1oZWlnaHQ6MS4xMjVyZW07Zm9udC1zaXplOi43NXJlbX0uZm9vdGVyLWlubmVye2JvcmRlci10b3A6MXB4IHNvbGlkICNkOWQ5ZDk7cGFkZGluZy10b3A6MXJlbTtwYWRkaW5nLWJvdHRvbToxcmVtfS5jbGVhcmZpeDo6YWZ0ZXJ7ZGlzcGxheTp0YWJsZTtjbGVhcjpib3RoO2NvbnRlbnQ6IiJ9LmNsZWFyZml4IC5jb2x1bW57ZmxvYXQ6bGVmdDtwYWRkaW5nLXJpZ2h0OjEuNXJlbTt3aWR0aDo1MCV9LmRpYWdub3N0aWMtd3JhcHBlcnttYXJnaW4tYm90dG9tOi41cmVtfS5mb290ZXIgLnJheS1pZHt0ZXh0LWFsaWduOmNlbnRlcn0uZm9vdGVyIC5yYXktaWQgY29kZXtmb250LWZhbWlseTptb25hY28sY291cmllcixtb25vc3BhY2V9LmNvcmUtbXNnLC56b25lLW5hbWUtdGl0bGV7b3ZlcmZsb3ctd3JhcDpicmVhay13b3JkfUBtZWRpYSAod2lkdGggPD0gNzIwcHgpey5kaWFnbm9zdGljLXdyYXBwZXJ7ZGlzcGxheTpmbGV4O2ZsZXgtd3JhcDp3cmFwO2p1c3RpZnktY29udGVudDpjZW50ZXJ9LmNsZWFyZml4OjphZnRlcntkaXNwbGF5OmluaXRpYWw7Y2xlYXI6bm9uZTt0ZXh0LWFsaWduOmNlbnRlcjtjb250ZW50Om5vbmV9LmNvbHVtbntwYWRkaW5nLWJvdHRvbToycmVtfS5jbGVhcmZpeCAuY29sdW1ue2Zsb2F0Om5vbmU7cGFkZGluZzowO3dpZHRoOmF1dG87d29yZC1icmVhazprZWVwLWFsbH0uem9uZS1uYW1lLXRpdGxle21hcmdpbi1ib3R0b206MXJlbX19LmxvYWRpbmctdmVyaWZ5aW5ne2hlaWdodDo3Ni4zOTFweH0ubGRzLXJpbmd7ZGlzcGxheTppbmxpbmUtYmxvY2s7cG9zaXRpb246cmVsYXRpdmU7d2lkdGg6MS44NzVyZW07aGVpZ2h0OjEuODc1cmVtfS5sZHMtcmluZyBkaXZ7Ym94LXNpemluZzpib3JkZXItYm94O2Rpc3BsYXk6YmxvY2s7cG9zaXRpb246YWJzb2x1dGU7Ym9yZGVyOi4zcmVtIHNvbGlkICM1OTU5NTk7Ym9yZGVyLXJhZGl1czo1MCU7Ym9yZGVyLWNvbG9yOiMzMTMxMzEgcmdiYSgwLDAsMCwwKSByZ2JhKDAsMCwwLDApO3dpZHRoOjEuODc1cmVtO2hlaWdodDoxLjg3NXJlbTthbmltYXRpb246bGRzLXJpbmcgMS4ycyBjdWJpYy1iZXppZXIoLjUsIDAsIC41LCAxKSBpbmZpbml0ZX0ubGRzLXJpbmcgZGl2Om50aC1jaGlsZCgxKXthbmltYXRpb24tZGVsYXk6LS40NXN9Lmxkcy1yaW5nIGRpdjpudGgtY2hpbGQoMil7YW5pbWF0aW9uLWRlbGF5Oi0uM3N9Lmxkcy1yaW5nIGRpdjpudGgtY2hpbGQoMyl7YW5pbWF0aW9uLWRlbGF5Oi0uMTVzfUBrZXlmcmFtZXMgbGRzLXJpbmd7MCV7dHJhbnNmb3JtOnJvdGF0ZSgwZGVnKX0xMDAle3RyYW5zZm9ybTpyb3RhdGUoMzYwZGVnKX19LnJ0bCAuaGVhZGluZy1mYXZpY29ue21hcmdpbi1yaWdodDowO21hcmdpbi1sZWZ0Oi41cmVtfS5ydGwgI2NoYWxsZW5nZS1zdWNjZXNzLXRleHR7YmFja2dyb3VuZC1wb3NpdGlvbjpyaWdodDtwYWRkaW5nLXJpZ2h0OjQycHg7cGFkZGluZy1sZWZ0OjB9LnJ0bCAjY2hhbGxlbmdlLWVycm9yLXRleHR7YmFja2dyb3VuZC1wb3NpdGlvbjpyaWdodDtwYWRkaW5nLXJpZ2h0OjM0cHg7cGFkZGluZy1sZWZ0OjB9LmNoYWxsZW5nZS1jb250ZW50IC5sb2FkaW5nLXZlcmlmeWluZ3toZWlnaHQ6NzYuMzkxcHh9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyayl7Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMyMjI7Y29sb3I6I2Q5ZDlkOX1ib2R5IGF7Y29sb3I6I2ZmZn1ib2R5IGE6aG92ZXJ7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZTtjb2xvcjojZWU3MzBhfWJvZHkgLmxkcy1yaW5nIGRpdntib3JkZXItY29sb3I6Izk5OSByZ2JhKDAsMCwwLDApIHJnYmEoMCwwLDAsMCl9Ym9keSAuZm9udC1yZWR7Y29sb3I6I2IyMGYwM31ib2R5IC5jdHAtYnV0dG9ue2JhY2tncm91bmQtY29sb3I6IzQ2OTNmZjtjb2xvcjojMWQxZDFkfWJvZHkgI2NoYWxsZW5nZS1zdWNjZXNzLXRleHR7YmFja2dyb3VuZC1pbWFnZTp1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jaUlIZHBaSFJvUFNJek1pSWdhR1ZwWjJoMFBTSXpNaUlnWm1sc2JEMGlibTl1WlNJZ2RtbGxkMEp2ZUQwaU1DQXdJREkySURJMklqNDhjR0YwYUNCbWFXeHNQU0lqWkRsa09XUTVJaUJrUFNKTk1UTWdNR0V4TXlBeE15QXdJREVnTUNBd0lESTJJREV6SURFeklEQWdNQ0F3SURBdE1qWnRNQ0F5TkdFeE1TQXhNU0F3SURFZ01TQXdMVEl5SURFeElERXhJREFnTUNBeElEQWdNaklpTHo0OGNHRjBhQ0JtYVd4c1BTSWpaRGxrT1dRNUlpQmtQU0p0TVRBdU9UVTFJREUyTGpBMU5TMHpMamsxTFRRdU1USTFMVEV1TkRRMUlERXVNemcxSURVdU16Y2dOUzQyTVNBNUxqUTVOUzA1TGpZdE1TNDBNaTB4TGpRd05Yb2lMejQ4TDNOMlp6NCIpfWJvZHkgI2NoYWxsZW5nZS1lcnJvci10ZXh0e2JhY2tncm91bmQtaW1hZ2U6dXJsKCJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUI0Yld4dWN6MGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TUM5emRtY2lJSGRwWkhSb1BTSXpNaUlnYUdWcFoyaDBQU0l6TWlJZ1ptbHNiRDBpYm05dVpTSStQSEJoZEdnZ1ptbHNiRDBpSTBJeU1FWXdNeUlnWkQwaVRURTJJRE5oTVRNZ01UTWdNQ0F4SURBZ01UTWdNVE5CTVRNdU1ERTFJREV6TGpBeE5TQXdJREFnTUNBeE5pQXpiVEFnTWpSaE1URWdNVEVnTUNBeElERWdNVEV0TVRFZ01URXVNREVnTVRFdU1ERWdNQ0F3SURFdE1URWdNVEVpTHo0OGNHRjBhQ0JtYVd4c1BTSWpRakl3UmpBeklpQmtQU0pOTVRjdU1ETTRJREU0TGpZeE5VZ3hOQzQ0TjB3eE5DNDFOak1nT1M0MWFESXVOemd6ZW0wdE1TNHdPRFFnTVM0ME1qZHhMalkySURBZ01TNHdOVGN1TXpnNExqUXdOeTR6T0RrdU5EQTNMams1TkNBd0lDNDFPVFl0TGpRd055NDVPRFF0TGpNNU55NHpPUzB4TGpBMU55NHpPRGt0TGpZMUlEQXRNUzR3TlRZdExqTTRPUzB1TXprNExTNHpPRGt0TGpNNU9DMHVPVGcwSURBdExqVTVOeTR6T1RndExqazROUzQwTURZdExqTTVOeUF4TGpBMU5pMHVNemszSWk4K1BDOXpkbWMrIil9fTwvc3R5bGU+PHNjcmlwdCBzcmM9Imh0dHBzOi8vY2hhbGxlbmdlcy5jbG91ZGZsYXJlLmNvbS90dXJuc3RpbGUvdjAvYi9jODg3NTViMGNkZGMvYXBpLmpzP29ubG9hZD10Sk5jNiZhbXA7cmVuZGVyPWV4cGxpY2l0IiBhc3luYz0iIiBkZWZlcj0iIiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj48L3NjcmlwdD48L2hlYWQ+PGJvZHk+PGRpdiBjbGFzcz0ibWFpbi13cmFwcGVyIiByb2xlPSJtYWluIj48ZGl2IGNsYXNzPSJtYWluLWNvbnRlbnQiPjxoMSBjbGFzcz0iem9uZS1uYW1lLXRpdGxlIGgxIj5yZXNhLm5vdHJlZGFtZWRlcGFyaXMuZnI8L2gxPjxwIGlkPSJHRnB3azUiIGNsYXNzPSJoMiBzcGFjZXItYm90dG9tIj5WZXJpZnkgeW91IGFyZSBodW1hbiBieSBjb21wbGV0aW5nIHRoZSBhY3Rpb24gYmVsb3cuPC9wPjxkaXYgaWQ9IlRrdFJZMSIgc3R5bGU9ImRpc3BsYXk6IGdyaWQ7Ij48ZGl2PjxkaXY+PGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iY2YtdHVybnN0aWxlLXJlc3BvbnNlIiBpZD0iY2YtY2hsLXdpZGdldC1lMnM4c19yZXNwb25zZSI+PC9kaXY+PC9kaXY+PC9kaXY+PGRpdiBpZD0iZkd0Y1M3IiBjbGFzcz0ic3BhY2VyIGxvYWRpbmctdmVyaWZ5aW5nIiBzdHlsZT0iZGlzcGxheTogbm9uZTsgdmlzaWJpbGl0eTogaGlkZGVuOyI+PGRpdiBjbGFzcz0ibGRzLXJpbmciPjxkaXY+PC9kaXY+PGRpdj48L2Rpdj48ZGl2PjwvZGl2PjxkaXY+PC9kaXY+PC9kaXY+PC9kaXY+PGRpdiBpZD0iRGNIc08yIiBjbGFzcz0iY29yZS1tc2cgc3BhY2VyIHNwYWNlci10b3AiPnJlc2Eubm90cmVkYW1lZGVwYXJpcy5mciBuZWVkcyB0byByZXZpZXcgdGhlIHNlY3VyaXR5IG9mIHlvdXIgY29ubmVjdGlvbiBiZWZvcmUgcHJvY2VlZGluZy48L2Rpdj48ZGl2IGlkPSJOdU10MyIgc3R5bGU9ImRpc3BsYXk6IG5vbmU7Ij48ZGl2IGlkPSJjaGFsbGVuZ2Utc3VjY2Vzcy10ZXh0IiBjbGFzcz0iaDIiPlZlcmlmaWNhdGlvbiBzdWNjZXNzZnVsPC9kaXY+PGRpdiBjbGFzcz0iY29yZS1tc2cgc3BhY2VyIj5XYWl0aW5nIGZvciByZXNhLm5vdHJlZGFtZWRlcGFyaXMuZnIgdG8gcmVzcG9uZC4uLjwvZGl2PjwvZGl2Pjxub3NjcmlwdD48ZGl2IGNsYXNzPSJoMiI+PHNwYW4gaWQ9ImNoYWxsZW5nZS1lcnJvci10ZXh0Ij5FbmFibGUgSmF2YVNjcmlwdCBhbmQgY29va2llcyB0byBjb250aW51ZTwvc3Bhbj48L2Rpdj48L25vc2NyaXB0PjwvZGl2PjwvZGl2PjxzY3JpcHQ+KGZ1bmN0aW9uKCl7d2luZG93Ll9jZl9jaGxfb3B0ID0ge2N2SWQ6ICczJyxjWm9uZTogJ3Jlc2Eubm90cmVkYW1lZGVwYXJpcy5mcicsY1R5cGU6ICdtYW5hZ2VkJyxjUmF5OiAnOTk1MWIxNTVmODE5ZTk0YicsY0g6ICdZTVBjcU5HVm1oSGptNmk4YXdqLllxWmRiZmJ5dDZfRWlIaXR2aHZMcnlRLTE3NjE1NjI4NTctMS4yLjEuMS1WOE90alI2el80QmFCdjlkaUQ4eWowMkVhUkpQYWNjVDZ3WGE4cVpaRjlyUno4a2d4eXp1WG9tVmxDbk0xWFpyJyxjVVBNRFRrOiJcLz9fX2NmX2NobF90az0xaERpMjR5VWxpN0FZSDFPWVA3b3hnWngycHF2Wlp5a0RjRXIxOU9JUHJrLTE3NjE1NjI4NTctMS4wLjEuMS1wYUZTd25ZYkxkVmVqSXhUbk02RUlwTTJmSGZVVEJ4cmw5ZWR4RERnUnh3IixjRlBXdjogJ2InLGNJVGltZVM6ICcxNzYxNTYyODU3JyxjVHBsQzowLGNUcGxWOjUsY1RwbEI6ICcwJyxmYToiXC8/X19jZl9jaGxfZl90az0xaERpMjR5VWxpN0FZSDFPWVA3b3hnWngycHF2Wlp5a0RjRXIxOU9JUHJrLTE3NjE1NjI4NTctMS4wLjEuMS1wYUZTd25ZYkxkVmVqSXhUbk02RUlwTTJmSGZVVEJ4cmw5ZWR4RERnUnh3IixtZDogJ1ZIemtCN0xxS0dENEhaNUVRYV9hQUpueVQuX3h2V2g0WGtyUUc1WGhkaEEtMTc2MTU2Mjg1Ny0xLjIuMS4xLXJmRWRJOXV2ckY3cFI3NEd1QmRwM2VRalBkM2pWY0NtRkNGS3RfWVh3blExTlVxa2VJbkouclBJS0VTSmNhaTd5ZTJQcUdZeDRLa1ZMYVhpMHltWHBLS01xT3k0VGxQaEd1V1h1a1BmUjVYZG5pdkdnRFE4WUhzNloweGRBTHlhcHduNjdNYWtRSE9iRXFQd05udEhFck9uT1RneGdIZzBrS3dqeTZ5d1hhS25QdGwuNGl4Snl4Z3M5R2pxMnEzN3JMUUpkeU90ejBkTWVVcWd0V3Zpa0dLZWNqZ3Z2eTRiR3JGanNIbTdSNHRqbGI2UnIuT3psV3VzUjdZVG9fcXZ0NUZyTkFJNUxaSzdoMnlZUEFhVFFZRGhvN2xkRG0zdHp6UEkxTHdHUDZQWmRsRmtIYURyUW9KTVVCZThQLk82b1VsVUV0UXoxQ3c5YktOYXJJSXN5Mk0zRkI0b3YzQWZfZDZVdTVlMDVaSHhUajdQQW53a201cFRIOVh4NlhacW5HZ2JWSHBIN2FxYnhQd1FQblllNHFVVDZVY0JDMHVPOW5ibzJyMldndTdJOG0zSHVjRTAwbnBRNXhBZE9GMlJPVlp0LlVqcktrdDRFN21lUmJkWE5jcVpPR3JfVXcxUkxxNzEwQnVJSDFONWllc3pzMDFiVUliVVhrV1ZyV3RhLi5lMHY4N0t0U0JEUmU4eWpndHFTQjV0YW1rdENtdldvalpVNDBuSTlfbF9jLk5INnFPUTdla19jT0xhNHBFbFdFUWtuZ0MueG9EdnNuTnF5RFJiZ1VHdUtUUlNET0xxOHlRUG9SdmpXUDh0ckFmVm9NTUdNSG14UVFSYzlQdEZPXzBtekIuVG1XQWJqeVBrQXZtOWs5OTg5S0RKU2Z0WFNIdXRJOER3cHhFalZhWmt1Tl85SXpFakxERkNXQkRCYmFCdC5tRy44aDk3RHVvVEh2aDM2bS5DSUlvZjR5MEd6TDhmQWx4MHdQQ3h0U0hVZjdVZ0U2XzhkSjl3S0FQTU1xeEhWVnJHTEJHblZMOHl2NFJHY3VWTklQd1ByOTV4RFd4UjlWaS5QbnNwdnVxSFFvbmVoSlZSMURNUmVlNWxCU1RvNHMzNy5MLnl3QnFTVHM3OVpoeVZ0el81NlhhZjEzbExGZFFhWDF0WFoyclcuSURKdnNnWnhOYVU4MFlJOEdrbjNCZ21VUmhXWFl1Q1ppMk4ueU1paHFSZXBrVnh5Z3dXWWxtdWVZMm1udW9JU21qVXhKMG0xZzQxZU5FUHQ5dXJWVjlsbVVuYU1vQlI1a0wzMW5pUFh6QWt3QmlpeTc5aW9wYWpnU2R0eVZXdGtUamtscjh1MFRsUnV2OUJVOGRMNTBvWjVTUmVVdE5hdXZvajJYTS5zcGZvYlpiLjBLYWFMakwuM3g0JyxtZHJkOiAnbkd3eHl2Y2lIQU1MTnFWYU1sdjQ1X1NKN3hZRlpYOGhPcnFWazlWZU1ZRS0xNzYxNTYyODU3LTEuMi4xLjEtdWVzYzZHbkJlMHJJZ2oxTlk4TlhpRm01a0R2OW9HX0ExTFR2RWsuSHgxWV9LbkJ6dkZhdDhHWjI3d0xOOWt4ZWxESnNPMklOdWt4Y2Q3V1RYN1JiRzkyTU1WRFZmTG5HRDBQaXdtcE1GREVsNzNBMUdhU2tUX1JNNjIyaWVMMkpCUmdYeEtTamtQckhNWmZXZVNBbnkubEMubGtDM0NwaXBoQ3N6Wk5VV0pGTUVNcW9RZVJLbXkwWmtPUE4yaTcxZ0I3eE81REZteHBTWGlsckREdGpTcG1jZW8wWFpMMTVkLlFzMFkySlpncDlicC5Oc3hSZUJOallPR0s4dnhyU1pncmZ0YlhBUHNDejNXR3ZVbVFyczRwNWczajJ0X0ROVU14cUVxdm55cUVlRDhod2dDZG9OUXMxZHBRZTh0dWpSdGpLcnZadDFXRFFGcHBtcHZYRU1nMzZYVHFHNWgzWnFXdmJ6QVBxbVYyMElsOGdoc3ZZdEhUWl94aUZPY3RSMlh0VEFPWnJJeDc3SEpiQUU3UDRXcjBCSkFwNU5BVTRTZ2IwbmdsU05wVXd4LmNDbndZZGVlXzJZMzZneWRGT3F2SEJkRDRNSWpOeU8yVnB2RzczdkRndUdMdVNuUU5aSE9NbndTSlZoVFFQQk9aUVFfV3EwZ1lBMU1NTFYzSkUzLnh6Zmd6cUpwNDVNQ3BfYU5SRmFYUWo5NGZkb3hwdlJkWVdseVVZSzlwdlM4N3dDc3kucFpsX25ENllncXdBLjlFRGVsSFJfTHNjaVh4S19WbWZnUl9kWmJ6OGYwQnp3UEJITEJZTEk4VUFIbjdoaDFWUlBucUlOUW43X290aXBTaHVOSlAwd25BOHBmQUpxMWxXZGNEczFoUE82bWg3bFNmUkl3dml4dXguWmk1Y2FOUkR1cS5pc2JfNkc5eV83aldSbzJfbFB1akgxWlVBNDBFck5wdjRmOGhjRG1uTFR3R0RHVGw1cHhBc1d5M1pBSU5qeThiTG5CbmJrNFJPUEU3WEQ1MUM4ZkVPNHFfVGdaVzJWUGlyQnNtQnFabEdPWnhyMS5fWTNOdzZHWEd0d01WN0p3N3k4bk5ja0lGdk1POW5PRnBrZkRzNWFsZUlpWkNSOGp5RTk3WjBaX3NiajdJU2lLVXBLM3pRTzlpdDhydXdST2EyelpUZ3ZZWDcyWUMxdzZ0M1VFWXhoeTFha2pVbHc1QVFPQkZhODhMVlV6X0VZRE9vMzViMzVFYl8yYjJheFVwV3EwQ3hwRnlPQlN5VlFXNnhvNVV4TklsSmdDZHZCQzBaWGVBUmpkaDQ0V240dURjRmt2eGIzaFFMTkVkUm92R0t3QlBmR2JRdk0wUHlLbUw0SzY2RUNIT2pPM2NCWFNDNmYxRGdCS0hzV2ZKUFFHVGV3VHhMMEF6cUFsOUNjUXcxOVU3LmFBM2VhMnA4eDhqWUFpU3g3ZGNKbEVRMUswS1hyaDl3NVcuS041SFB3RzY2YmYyS0dyUm1Mc2RZd2hpYlBoNlhvcDVJRThhZVluUnFvYmpsZEl2a1o0RGNfSWRXT3ppQUtYM01YSGlWMkJUWFpSeERLbWl2YjF2N2RCZEpDM19jSE5LcUZVWDI3ejBKOFVXRlQ0T01pbVFhQ3hRd3JQV0tCdE9LMmR4TncyZjFHSlBUQmhxVlVSOF8xaGtQTElwdzFvaUFOczcxaGpWUU5EZG9tWlZ1bVAuelFXQjN0N2U0TU9icUJIeThsLnFxeXo0WWV3SWVlSk1Ja1lCZlFMdVpwUV9MZmY4OWZ1cUVpMnA0MHJ4NEEycnVvc1lMeGZRWGVHM1FTSWxrdk96Qm4wemNmMU55TG4uR2tfZVFvYzlYN1lDR2JxTUFMS2hDR3pXM1l0VlZaeU1MSmUxYlN1d0NMc0RvVUpxcnZkUWh4dHhvVU9HZDF1dnBoUmRmOTdYbTRFdmFkWmFDVkI4bnRYb181MmpYUnltYTk3cGZEajU4U25kR21ld3BReWthYUdrem5tRncxajlnRk4ySG9qZnFtZFBVRW44eDM2VFZJRm1sNzJjVXhZWG5mU1EwNTNHYXhuTVR2T3NrZzkuMjJOWXp5cWRuNnNPaHdnMnVvU3lpanAuYnNoYklPYlFzUmxOY05IY1prNHZCa2w2RFVjaEc4WklJRTNObWRsTi45czZSeEk0UGlfUEtKaXJnR0duTUxXWUZrd2lESEc3TGtFdUQ5TkdoYU5WNVNLZVFXY0M3MnlZcHp0MU04VTduVDZ3S1ZKWmJMd2dGQTFXeXFNNXYzajJhYl9ZdEgySFNINGNkeDFRYWxqUkpTcWgzNXRFU1ViLi5vVmppZDh1UFJQdUxVODg3TG45MEx1WENWUnpCN0xWaEpoUG00ZENaWDgyejBnbmluUlkwcFZNOVhaYU90VXdkTlBIN0JKanoyUlo1N2xDN3VEbVQ4M1VoY1R1TjZCNFBlTlJhek9ZZGVOaG1FN0cxZU9sVnROaEoucUlRdHdWc3Zick9lWTBoTjM1ZklkUGU4TEhhQkxyUzVwdUg2ZzRHbWZLVUF4Q29NeVRIcnNqTnlYbGV3cG9HOEdNX0JzazRyMUhpWmFMSk9pYkVPM3hqMGRlV0lIZ3BaRnFoaHNZX2RZLndwTWxaR04zeHF6clFKekdFa191UkdPVFNaa0NpM1oxd21HZmFDVmFpVnhTd2NoRk9oOUVWN25EYUJHRFBCYVd6cGhhNXZoSnRVU0toRWxpNVpBQXE2ZndQUDRHY0xFR2xCSU52VGlMZ2FnZTRUbGpPbGU0azJpUDRzWHJqUy5NcnJLUHQxTjZ6RW85R0xfSGlEY0Q3WjBWMnlDeU5uMjlRUUhzNnJBeWdxVmlQVGNjR3Fscl9UdF80eWljc3ZDYW5nVFM0X3VrWWtlclhhdkZudVQyeklCeTNOVmt4SkpoSllQeEtpbElJb1hBLm1RSGZWRHVCcUNXRTR0MC5oLkpDakdXMXpyOHd2ek5xR1dZX283aGthbUpac0NENlZ5VU8ua3RPSVRqQll6blZjUTdWYndjWDVZRjh3YTNvMktLTF83TGtVT3FwSmZQelZ1VkFhYXFiekcxQ3pzaG9yZDFxdVJHbXA1eWVqelRDYmREaU9XZnpVTjdxOFlSbm9JenlKZ1dNOHF5OGVhdGVWUkhaT2VKWHpLQUo0VmYuWEZkQ0hENDNGX1FVcnVjYjRQQ1J4YUtfemhwemFjZVFIYTd4UlZ3d3RTTkVWS01xWEJEV3FSdnhqMk94WGM0YVJtZ3pIYUE2dUY0SDBYaFhSODdQYXZsTnBnQ29lV0Q3R2F5V0FNOW9JSXpNdFBabnZYMGlOaDBvbTlSeUlGV1N5cW1zd1dtMC5tdDV0Lk9SQ3VKYm1QRVBGbHppcnN5Qjc1dDROZlRtUEJ6UlZCY2tkSEtXMVdEaUp1ZGRISTJMcm5QSXdjOWZ2Z2hybU5xN0Uxc3ZIM3ZBXzg4U3JoQzEnLH07dmFyIGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTthLnNyYyA9ICcvY2RuLWNnaS9jaGFsbGVuZ2UtcGxhdGZvcm0vaC9iL29yY2hlc3RyYXRlL2NobF9wYWdlL3YxP3JheT05OTUxYjE1NWY4MTllOTRiJzt3aW5kb3cuX2NmX2NobF9vcHQuY09nVUhhc2ggPSBsb2NhdGlvbi5oYXNoID09PSAnJyAmJiBsb2NhdGlvbi5ocmVmLmluZGV4T2YoJyMnKSAhPT0gLTEgPyAnIycgOiBsb2NhdGlvbi5oYXNoO3dpbmRvdy5fY2ZfY2hsX29wdC5jT2dVUXVlcnkgPSBsb2NhdGlvbi5zZWFyY2ggPT09ICcnICYmIGxvY2F0aW9uLmhyZWYuc2xpY2UoMCwgbG9jYXRpb24uaHJlZi5sZW5ndGggLSB3aW5kb3cuX2NmX2NobF9vcHQuY09nVUhhc2gubGVuZ3RoKS5pbmRleE9mKCc/JykgIT09IC0xID8gJz8nIDogbG9jYXRpb24uc2VhcmNoO2lmICh3aW5kb3cuaGlzdG9yeSAmJiB3aW5kb3cuaGlzdG9yeS5yZXBsYWNlU3RhdGUpIHt2YXIgb2dVID0gbG9jYXRpb24ucGF0aG5hbWUgKyB3aW5kb3cuX2NmX2NobF9vcHQuY09nVVF1ZXJ5ICsgd2luZG93Ll9jZl9jaGxfb3B0LmNPZ1VIYXNoO2hpc3RvcnkucmVwbGFjZVN0YXRlKG51bGwsIG51bGwsIlwvP19fY2ZfY2hsX3J0X3RrPTFoRGkyNHlVbGk3QVlIMU9ZUDdveGdaeDJwcXZaWnlrRGNFcjE5T0lQcmstMTc2MTU2Mjg1Ny0xLjAuMS4xLXBhRlN3blliTGRWZWpJeFRuTTZFSXBNMmZIZlVUQnhybDllZHhERGdSeHciKyB3aW5kb3cuX2NmX2NobF9vcHQuY09nVUhhc2gpO2Eub25sb2FkID0gZnVuY3Rpb24oKSB7aGlzdG9yeS5yZXBsYWNlU3RhdGUobnVsbCwgbnVsbCwgb2dVKTt9fWRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJylbMF0uYXBwZW5kQ2hpbGQoYSk7fSgpKTs8L3NjcmlwdD48c2NyaXB0IGRlZmVyPSIiIHNyYz0iaHR0cHM6Ly9zdGF0aWMuY2xvdWRmbGFyZWluc2lnaHRzLmNvbS9iZWFjb24ubWluLmpzL3ZjZDE1Y2JlNzc3MmY0OWMzOTljNmE1YmFiZjIyYzEyNDE3MTc2ODkxNzYwMTUiIGludGVncml0eT0ic2hhNTEyLVpwc09tbFJRVjZ5OTA3VEkwZEtCSHE5TWQyOW5uYUVJUGxrZjg0cm5hRVJucTZ6dld2UFVxcjJmdDhNMWFTMjhvTjcyUGRyQ3pTalk0VTZWYUF3MUVRPT0iIGRhdGEtY2YtYmVhY29uPSJ7JnF1b3Q7cmF5SWQmcXVvdDs6JnF1b3Q7OTk1MWIxNTVmODE5ZTk0YiZxdW90OywmcXVvdDtzZXJ2ZXJUaW1pbmcmcXVvdDs6eyZxdW90O25hbWUmcXVvdDs6eyZxdW90O2NmRXh0UHJpJnF1b3Q7OnRydWUsJnF1b3Q7Y2ZFZGdlJnF1b3Q7OnRydWUsJnF1b3Q7Y2ZPcmlnaW4mcXVvdDs6dHJ1ZSwmcXVvdDtjZkw0JnF1b3Q7OnRydWUsJnF1b3Q7Y2ZTcGVlZEJyYWluJnF1b3Q7OnRydWUsJnF1b3Q7Y2ZDYWNoZVN0YXR1cyZxdW90Ozp0cnVlfX0sJnF1b3Q7dmVyc2lvbiZxdW90OzomcXVvdDsyMDI1LjkuMSZxdW90OywmcXVvdDt0b2tlbiZxdW90OzomcXVvdDtjMzgxZTcwZWFlNDQ0MzE4YmQzMjk2Y2M4ODlmNjdiNiZxdW90O30iIGNyb3Nzb3JpZ2luPSJhbm9ueW1vdXMiPjwvc2NyaXB0Pgo8ZGl2IGNsYXNzPSJmb290ZXIiIHJvbGU9ImNvbnRlbnRpbmZvIj48ZGl2IGNsYXNzPSJmb290ZXItaW5uZXIiPjxkaXYgY2xhc3M9ImNsZWFyZml4IGRpYWdub3N0aWMtd3JhcHBlciI+PGRpdiBjbGFzcz0icmF5LWlkIj5SYXkgSUQ6IDxjb2RlPjk5NTFiMTU1ZjgxOWU5NGI8L2NvZGU+PC9kaXY+PC9kaXY+PGRpdiBjbGFzcz0idGV4dC1jZW50ZXIiIGlkPSJmb290ZXItdGV4dCI+UGVyZm9ybWFuY2UgJmFtcDsgc2VjdXJpdHkgYnkgPGEgcmVsPSJub29wZW5lciBub3JlZmVycmVyIiBocmVmPSJodHRwczovL3d3dy5jbG91ZGZsYXJlLmNvbT91dG1fc291cmNlPWNoYWxsZW5nZSZhbXA7dXRtX2NhbXBhaWduPW0iIHRhcmdldD0iX2JsYW5rIj5DbG91ZGZsYXJlPC9hPjwvZGl2PjwvZGl2PjwvZGl2PjwvYm9keT48L2h0bWw+",
+ proxy=proxy
+ )
+
+ solution = await cap_monster_client.solve_captcha(turnstile_request)
+ print("Solution:", solution)
+
+asyncio.run(main())
diff --git a/test/altcha_test.py b/test/altcha_test.py
new file mode 100644
index 0000000..728b89a
--- /dev/null
+++ b/test/altcha_test.py
@@ -0,0 +1,79 @@
+import unittest
+
+from pydantic.error_wrappers import ValidationError
+from capmonstercloudclient.requests import AltchaCustomTaskRequest
+
+
+class AltchaCustomTaskRequestTest(unittest.TestCase):
+ websiteUrlExample = "https://example.com"
+ websiteKeyExample = "altcha-public-key-123"
+ userAgentExample = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
+ challengeExample = "challenge_string_here"
+ iterationsExample = "1000"
+ saltExample = "salt_string_here"
+ signatureExample = "signature_string_here"
+
+ def test_altcha_request_required_fields(self):
+ required_fields = ["type", "class", "websiteURL", "websiteKey", "metadata"]
+ metadata_required_fields = ["challenge", "iterations", "salt", "signature"]
+ metadata_example = {
+ "challenge": self.challengeExample,
+ "iterations": self.iterationsExample,
+ "salt": self.saltExample,
+ "signature": self.signatureExample,
+ }
+ request = AltchaCustomTaskRequest(
+ websiteUrl=self.websiteUrlExample,
+ websiteKey=self.websiteKeyExample,
+ metadata=metadata_example,
+ )
+ task_dictionary = request.getTaskDict()
+ for f in required_fields:
+ self.assertTrue(
+ f in list(task_dictionary.keys()),
+ msg=f'Required captcha input key "{f}" does not include to request.',
+ )
+ for f in metadata_required_fields:
+ self.assertTrue(
+ f in list(task_dictionary["metadata"].keys()),
+ msg=f'Required captcha input key "{f}" does not include to request.',
+ )
+ self.assertEqual(task_dictionary["class"], "altcha")
+ self.assertEqual(task_dictionary["type"], "CustomTask")
+
+ def test_altcha_metadata_validation(self):
+ base_kwargs = {
+ "websiteUrl": self.websiteUrlExample,
+ "websiteKey": self.websiteKeyExample,
+ "metadata": {}
+ }
+ self.assertRaises(TypeError, AltchaCustomTaskRequest, **base_kwargs)
+ base_kwargs["metadata"]["challenge"] = self.challengeExample
+ self.assertRaises(TypeError, AltchaCustomTaskRequest, **base_kwargs)
+ base_kwargs["metadata"]["iterations"] = self.iterationsExample
+ self.assertRaises(TypeError, AltchaCustomTaskRequest, **base_kwargs)
+ base_kwargs["metadata"]["salt"] = self.saltExample
+ self.assertRaises(TypeError, AltchaCustomTaskRequest, **base_kwargs)
+ base_kwargs["metadata"]["signature"] = self.signatureExample
+ AltchaCustomTaskRequest(**base_kwargs)
+
+ def test_altcha_missing_fields(self):
+ base_kwargs = {}
+ self.assertRaises(ValidationError, AltchaCustomTaskRequest, **base_kwargs)
+ base_kwargs.update({"websiteUrl": self.websiteUrlExample})
+ self.assertRaises(ValidationError, AltchaCustomTaskRequest, **base_kwargs)
+ base_kwargs.update({"websiteKey": self.websiteKeyExample})
+ self.assertRaises(ValidationError, AltchaCustomTaskRequest, **base_kwargs)
+ metadata_example = {
+ "challenge": self.challengeExample,
+ "iterations": self.iterationsExample,
+ "salt": self.saltExample,
+ "signature": self.signatureExample,
+ }
+ base_kwargs.update({"metadata": metadata_example})
+ AltchaCustomTaskRequest(**base_kwargs)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/test/imperva_request_test.py b/test/imperva_request_test.py
index 16ac86b..0edf1ea 100644
--- a/test/imperva_request_test.py
+++ b/test/imperva_request_test.py
@@ -2,7 +2,7 @@
from copy import deepcopy
from pydantic import ValidationError
-from capmonstercloudclient.requests import ImpervaCustomTaskRequest
+from capmonstercloudclient.requests import ImpervaCustomTaskRequest, ProxyInfo
class ImpervaRequestTest(unittest.TestCase):
@@ -14,17 +14,26 @@ class ImpervaRequestTest(unittest.TestCase):
incapsulaCookieExample = "l/LsGnrvyB9lNhXI8borDKa2IGcAAAAAX0qAEHheCWuNDquzwb44cw="
reese84UrlEndpointExample = "Built-with-the-For-hopence-Hurleysurfecting-the-"
+ def setUp(self):
+ self.proxy = ProxyInfo(
+ proxyType="http",
+ proxyAddress="8.8.8.8",
+ proxyPort=8000,
+ proxyLogin="proxyLoginHere",
+ proxyPassword="proxyPasswordHere"
+ )
+
def test_imperva(
self,
):
- required_fields = ["type", "websiteURL", "metadata"]
+ required_fields = ["type", "websiteURL", "metadata", "proxyType", "proxyAddress", "proxyPort", "proxyLogin", "proxyPassword"]
metadata_required_fields = ["incapsulaScriptUrl", "incapsulaCookie"]
metadata_example = {
"incapsulaScriptUrl": self.incapsulaScriptUrlExample,
"incapsulaCookie": self.incapsulaCookieExample,
}
request = ImpervaCustomTaskRequest(
- websiteUrl=self.websiteUrlExample, metadata=metadata_example
+ websiteUrl=self.websiteUrlExample, metadata=metadata_example, proxy=self.proxy
)
task_dictionary = request.getTaskDict()
for f in required_fields:
@@ -41,7 +50,7 @@ def test_imperva(
def test_imperva_metadata(
self,
):
- base_kwargs = {"websiteUrl": self.websiteUrlExample, "metadata": {}}
+ base_kwargs = {"websiteUrl": self.websiteUrlExample, "metadata": {}, "proxy": self.proxy}
self.assertRaises(TypeError, ImpervaCustomTaskRequest, **base_kwargs)
base_kwargs["metadata"]["incapsulaScriptUrl"] = self.incapsulaScriptUrlExample
self.assertRaises(TypeError, ImpervaCustomTaskRequest, **base_kwargs)
@@ -59,10 +68,13 @@ def test_imperva_missing(
"incapsulaScriptUrl": self.incapsulaScriptUrlExample,
"incapsulaCookie": self.incapsulaCookieExample,
}
+ self.assertRaises(RuntimeError, ImpervaCustomTaskRequest, **base_kwargs)
+ base_kwargs.update({"proxy": self.proxy})
self.assertRaises(ValidationError, ImpervaCustomTaskRequest, **base_kwargs)
base_kwargs.update({"websiteUrl": self.websiteUrlExample})
self.assertRaises(ValidationError, ImpervaCustomTaskRequest, **base_kwargs)
base_kwargs.update({"metadata": metadata_example})
+
ImpervaCustomTaskRequest(**base_kwargs)
diff --git a/test/recaptchaV3Enterprise_test.py b/test/recaptchaV3Enterprise_test.py
new file mode 100644
index 0000000..2c7ce32
--- /dev/null
+++ b/test/recaptchaV3Enterprise_test.py
@@ -0,0 +1,62 @@
+import unittest
+
+from pydantic.error_wrappers import ValidationError
+from capmonstercloudclient.requests import RecaptchaV3EnterpriseRequest
+
+
+class RecaptchaV3EnterpriseRequestTest(unittest.TestCase):
+ websiteUrlExample = "https://example.com"
+ websiteKeyExample = "6Le0xVgUAAAAAIt20XEB4rVhYOODgTl00d8juDob"
+ minScoreExample = 0.9
+ pageActionExample = "submit"
+
+ def test_captcha_input_types(self):
+ with self.assertRaises(ValidationError):
+ request = RecaptchaV3EnterpriseRequest(
+ websiteUrl=RecaptchaV3EnterpriseRequestTest.websiteUrlExample
+ )
+
+ with self.assertRaises(ValidationError):
+ request = RecaptchaV3EnterpriseRequest(
+ websiteKey=RecaptchaV3EnterpriseRequestTest.websiteKeyExample,
+ )
+
+ request = RecaptchaV3EnterpriseRequest(
+ websiteUrl=RecaptchaV3EnterpriseRequestTest.websiteUrlExample,
+ websiteKey=RecaptchaV3EnterpriseRequestTest.websiteKeyExample,
+ minScore=RecaptchaV3EnterpriseRequestTest.minScoreExample,
+ pageAction=RecaptchaV3EnterpriseRequestTest.pageActionExample,
+ )
+
+ def test_all_required_fields_filling(self):
+ required_fields = ["type", "websiteURL", "websiteKey"]
+ request = RecaptchaV3EnterpriseRequest(
+ websiteUrl=RecaptchaV3EnterpriseRequestTest.websiteUrlExample,
+ websiteKey=RecaptchaV3EnterpriseRequestTest.websiteKeyExample,
+ minScore=RecaptchaV3EnterpriseRequestTest.minScoreExample,
+ pageAction=RecaptchaV3EnterpriseRequestTest.pageActionExample,
+ )
+ request_dict = request.getTaskDict()
+ for i in required_fields:
+ self.assertTrue(
+ i in list(request_dict.keys()),
+ msg=f"Required field {i} not in {request_dict}",
+ )
+
+ self.assertEqual(request_dict["type"], "RecaptchaV3EnterpriseTask")
+ self.assertEqual(request_dict["minScore"], self.minScoreExample)
+ self.assertEqual(request_dict["pageAction"], self.pageActionExample)
+
+ def test_optional_fields(self):
+ request = RecaptchaV3EnterpriseRequest(
+ websiteUrl=RecaptchaV3EnterpriseRequestTest.websiteUrlExample,
+ websiteKey=RecaptchaV3EnterpriseRequestTest.websiteKeyExample,
+ )
+ request_dict = request.getTaskDict()
+ self.assertNotIn("minScore", request_dict)
+ self.assertNotIn("pageAction", request_dict)
+
+
+if __name__ == "__main__":
+ unittest.main()
+