From b6bd78dc6601c7bb79687449b3a6312faf9c0b87 Mon Sep 17 00:00:00 2001 From: zhl Date: Sun, 4 Oct 2026 17:58:53 +0800 Subject: [PATCH] feat: add offer reward claim workflow --- .gitignore | 8 + README.md | 92 +++++++++++ assets/claim.png | Bin 0 -> 3825 bytes assets/home.png | Bin 0 -> 5650 bytes assets/idle_title.png | Bin 0 -> 5215 bytes assets/offer_back.png | Bin 0 -> 2375 bytes assets/offer_claim.png | Bin 0 -> 3950 bytes assets/offer_claimed.png | Bin 0 -> 8028 bytes assets/offer_dismiss.png | Bin 0 -> 8564 bytes assets/offer_monthcard.png | Bin 0 -> 14591 bytes assets/offer_page.png | Bin 0 -> 17671 bytes assets/offer_reward.png | Bin 0 -> 19199 bytes automation/__init__.py | 1 + automation/app.py | 170 +++++++++++++++++++++ automation/engine.py | 169 ++++++++++++++++++++ automation/offers.py | 111 ++++++++++++++ automation/permissions.py | 15 ++ claim_offer.py | 51 +++++++ docs/261004_1607-首页功能与日常任务梳理.md | 42 ++++- main.py | 4 + requirements.txt | 6 + tests/test_engine.py | 126 +++++++++++++++ tests/test_offers.py | 82 ++++++++++ workflows/offer_claim.json | 128 ++++++++++++++++ workflows/pig_claim.json | 28 ++++ 25 files changed, 1028 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/claim.png create mode 100644 assets/home.png create mode 100644 assets/idle_title.png create mode 100644 assets/offer_back.png create mode 100644 assets/offer_claim.png create mode 100644 assets/offer_claimed.png create mode 100644 assets/offer_dismiss.png create mode 100644 assets/offer_monthcard.png create mode 100644 assets/offer_page.png create mode 100644 assets/offer_reward.png create mode 100644 automation/__init__.py create mode 100644 automation/app.py create mode 100644 automation/engine.py create mode 100644 automation/offers.py create mode 100644 automation/permissions.py create mode 100644 claim_offer.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 tests/test_engine.py create mode 100644 tests/test_offers.py create mode 100644 workflows/offer_claim.json create mode 100644 workflows/pig_claim.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a38df2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.venv/ +__pycache__/ +*.pyc +.DS_Store +local/ +build/ +dist/ +*.spec diff --git a/README.md b/README.md new file mode 100644 index 0000000..51c5bbb --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# 蔚蓝星球本地自动化 + +当前是本地 Python 桌面原型。先完成并验证单条流程,再扩展任务和打包成 macOS / Windows 独立程序。 + +## 已实现 + +- 桌面界面:主屏幕截图、框选游戏内容区域、流程预览、执行日志与停止。 +- 小金猪流程:首页识别 → 打开挂机奖励 → 检查标题和领取按钮 → 点击领取一次 → 等待按钮变化 → 关闭弹框 → 确认回到首页。 +- 脚本与识别模板分离,后续任务通过 `workflows/` 扩展。 +- 截图像素与鼠标坐标分别换算,覆盖主屏幕 Retina 缩放的坐标情况。 + +小金猪流程只领取挂机奖励,不执行消耗体力的快速扫荡。完成日志只表示已点击领取、按钮变化并返回首页,奖励实际到账仍需首次运行时人工核对。当前模板来自用户提供的界面截图,尚需实机验证。 + +红点规则:按钮右上角有红点表示其内部有奖励可领取。后续入口任务可以据此进入,但领取成功还需验证内部状态。当前第一条小金猪流程不依赖红点,红点检测尚未实现。 + +## 本地启动 + +### 已验证的特惠一键领取脚本 + +在首页执行: + +```sh +.venv/bin/python claim_offer.py +``` + +只检查首页和显示计划: + +```sh +.venv/bin/python claim_offer.py --dry-run +``` + +流程为:首页 → 特惠 → 若打开“超值月卡”则点击底部“战令” → 赏金战令 → 一键领取 → 确认“恭喜获得”弹框 → 关闭弹框 → 确认领取按钮变灰 → 返回首页。已于 2026-10-04 实际领取并返回首页,领取后奖励出现绿色勾且一键领取变灰。完整脚本再次运行时,已验证从月卡切换战令、识别灰色按钮并返回首页。 + +当前仅适配本次“赏金战令”领取,支持从“超值月卡”切换到“战令”,不会购买战令或遍历其他特惠标签。无法识别的页面样式或标签会使流程停止。macOS 自动读取已打开游戏窗口位置,每次识别重新定位,识别后窗口移动则停止;窗口标题栏高度按本次验证的 44 逻辑点处理,需收起微信侧栏。运行时保持游戏完整可见。Windows 使用 `main.py` 已校准的区域或 `--region X Y WIDTH HEIGHT`,实机尚未验证。 + +需要 Python 3.9 或更新版本,以及带 Tk 支持的 Python。macOS 若当前 Python 缺少 Tk,可使用 python.org 官方安装包。 + +macOS: + +```sh +python3 -m venv .venv +.venv/bin/python -m pip install -r requirements.txt +.venv/bin/python main.py +``` + +Windows PowerShell: + +```powershell +py -m venv .venv +.venv\Scripts\python.exe -m pip install -r requirements.txt +.venv\Scripts\python.exe main.py +``` + +## 首次运行 + +1. 将游戏放在主屏幕,打开首页,保持窗口完整可见。 +2. 点击“校准游戏区域”。程序会最小化并在 1.5 秒后截图,在预览图中拖动框选**游戏内容**,不包含微信标题栏及边框。 +3. 点击“预览流程”,在 3 秒内切回游戏。只验证当前首页并显示计划,后续页面尚未验证。 +4. 预览通过后点击“执行领取”,在 3 秒内切回游戏,观察首次实际结果。 +5. 可以点击程序中的“停止”,或把鼠标移到主屏幕左上角触发 PyAutoGUI 紧急停止。系统截图调用期间停止可能延迟到调用结束。 + +macOS 实际截图和点击需要为运行程序的宿主启用屏幕录制和辅助功能权限。程序不会自行改变系统权限。Windows 的主屏幕缩放与窗口尺寸、macOS Retina 坐标映射均需实机验证;当前没有完成双平台验证。 + +“导入截图校准”需要导入当前**主屏幕完整截图**,不能导入单独的游戏裁剪图,否则无法得到桌面位置。校准配置保存到忽略版本管理的 `local/region.json`。游戏移动、大小改变、屏幕配置改变后请重新校准。当前不支持跨屏或遮挡运行,界面样式不匹配时停止,不强制点击。 + +## 验证与后续 + +### 实机操作排查记录(2026-10-04) + +通过 Codex 窗口控制工具尝试领取时,可以读取微信下的游戏画面,但点击小金猪未打开弹框。随后尝试直接绑定 `com.tencent.flue.WeApp` 超时,系统记录了 16:50:34 的 `WeApp` 崩溃:辅助进程启动后约 39 毫秒,在 `_libsecinit_appsandbox` 初始化中发生 `EXC_BREAKPOINT / SIGTRAP`。该记录支持异常发生在小游戏辅助进程启动阶段,尚不能证明微信主进程崩溃,也不能证明由点击动作直接导致。 + +本次操作使用窗口控制工具,未启动本项目 `main.py`,因此当前不能将该异常归因于本地原型。后续由用户从微信正常打开游戏,不再通过工具直接启动或绑定 WeApp 辅助程序;先验证单次截图和点击,再进行完整流程测试。本地原型只操作已打开的游戏区域,不负责启动微信或其辅助进程。 + +用户授权后进行了单次本地 PyAutoGUI 商城点击测试:沙盒外可读取主屏幕截图,但页面未切换;只读查询 `AXIsProcessTrusted()` 返回 `False`,确认本地 Python 进程缺少辅助功能权限。程序已增加点击前权限检查,权限缺失时停止并提示,不继续报告“已点击”。这不证明此前窗口工具不响应也出于相同原因。 + +用户授予权限后,查询返回已授予。普通点击将游戏窗口激活,但商城仍未打开;随后使用 PyAutoGUI 移动鼠标、按下保持 0.2 秒、释放,成功打开商城,游戏保持运行。本地原型已采用此方式,异常时也确保释放鼠标;这次验证仅证明商城切换有效,小金猪领奖流程仍待实机测试。 + +需要用户在“系统设置 → 隐私与安全性 → 辅助功能”中,为运行程序的宿主(当前为 ChatGPT/Codex;从终端启动则为对应终端)启用权限,之后重启宿主再检查。程序不自动授予或请求权限。可用以下命令只读确认: + +```sh +.venv/bin/python -m automation.permissions +``` + +```sh +.venv/bin/python -m unittest discover -s tests -v +``` + +测试使用模拟桌面,不操作游戏,覆盖坐标换算、遮罩识别、领取流程、预览、超时和停止。真实领取后的弹框变化尚未采集,如未回到首页则会超时停止,不重复领取。 + +后续依次补充红点检测、各任务具体步骤与结果识别、资源策略、模型设置与 Key 存储,再准备独立应用打包。目前不调用模型 API,也不保存模型 Key。 + +实现参考:[PyAutoGUI 官方文档](https://pyautogui.readthedocs.io/en/latest/quickstart.html)、[OpenCV 模板匹配文档](https://docs.opencv.org/4.x/d4/dc6/tutorial_py_template_matching.html)。 diff --git a/assets/claim.png b/assets/claim.png new file mode 100644 index 0000000000000000000000000000000000000000..a6dd9f89afb7e0e82491f03816c1034559950675 GIT binary patch literal 3825 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 Z`9HvMJJT^p2G0Nh002ovPDHLkV1j^<=cNDu literal 0 HcmV?d00001 diff --git a/assets/offer_back.png b/assets/offer_back.png new file mode 100644 index 0000000000000000000000000000000000000000..ca5b5b64a8e700b27988ea5f3ce3e18a1f19e0ed GIT binary patch literal 2375 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 IM6N<$f_5Qt?*IS* literal 0 HcmV?d00001 diff --git a/assets/offer_claimed.png b/assets/offer_claimed.png new file mode 100644 index 0000000000000000000000000000000000000000..c82fadbc603457b936d2224b3d8fbb27b48e7abf GIT binary patch literal 8028 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 literal 0 HcmV?d00001 diff --git a/assets/offer_page.png b/assets/offer_page.png new file mode 100644 index 0000000000000000000000000000000000000000..43e9e31e9934a380b68f549e5e1313d30e41131c GIT binary patch literal 17671 zcmV)UK(N1wP)o)j0003~X+uL$Nkc;* zP;zf(X>4Tx0C=2rkiAPnQ5eR5mL+``wB;bt(1%G_M6{WN1Px+})pos_25Oh971Ges z(Ap9N%|T5~xi$n5jeXM4AJ7^}U~+q}R%+3K_dGuy9?p4rpaDhGl2#y<&guc*jALsWNs#xS7Hy_v}BpiE_@%8X7rtR*91!D~)-?(MS>#o+FKl3#YJ~ zF_}rT-`~Aez3n}i-jJ_Gs$Osl&U&ElvFi0-Lsw4xVNKDMpEfN8Yi4EKUE#JoH0>M4 zYZz=pPxWMPl^QU^MFh07Gv{L_t(|oVC4olwHSpCs^U8ms95m zG|-Kl2oN9$k^qA!QluzSlr4(N!DG!>8hh7!>|@WaJw863S?~NYJ2NYLWXVagMpUpU zOQb0=lVSou5IHuw8=b?;;pSUad%k;LH(p18l3fRf1m1i1-m0&@`r_~VErH48PDbBqus1b1fS)|4`pcc$|;;{vY07-1|^_!C@?5mFVBjLMmq z91YQ$>6LZQbQlsi8kWK#DHf6_AzZ-a?{5hI&%z+fJ_#Q0PXuFRyL={ZP3LXhauMSK z(`ZC$YYjCeLy-s#g(Xds2u6$vF5vgS`oI`5PuLDa2vLeDB?Mzkk^g@h{DvdJxnN8* zKG87QrAzs-iE4eBOYaLSQWO%2NF8mF&W?B}EF%PW5CYjYvu%ci2!&-?CgNXWEdu5V z|E*Tp%uKCVFfpc*Ou|t)6p~a;Rzs4aND9I-AymSuOp@(Ur)1>^%bv@ZP5@)fncdUd z_i!>4+kjtw152bcPpU?l@)1i@(Q2C2#IXj8JjT-hYQD%Lt%`F zKxn$)>NYn_P6^U9SupOHw&%JwLas#X?1GSKNR^sXsVVE*!(+2nzUq38jR{c{Ijl*| zNhK9iBASE==01zx6t#i{H!hnm`y|*%48O0LY%*ue6+O@8ni`fA$t-8}3L7zQKq^KpGQ5G4(YI#)JT6g+EC}rqQsnp)Zk0D3roSjG=7K00BGW>!z7LM_eVkh&NN-rD6s)v&uDZ29LlxwiyuRl7I|_^? zL$8fmZjY?WAXkJy&CS}q4{!bUU%v6)(JNhjozfbzQwT%|;#|y>tZOqmVm zzQ4P(KfTfuaCxfy;@8fMpUo12Cnl@;yg52qr3gucgqt)WkY02rr>jDUJv*DRFNJ@( z0c3Rf@NhX>u)!kHqI)0v{Knfpn7ejyX7Eg{FlScso@F|wW$7hHFZ+s75`vFhEoNFH zZK_1<A3g%sT^DJkK3t=0MTdQITWG%ut+1?bGR<_|F zA;WPI!WF=pU0b(of8g;*(wn`!_tNX{PG31UclA8?tU`sAb*Ck%wq~?sRN;J)DF%E= z3F2_0`b6i2{-VE7jOH(_8s1#doGZGH%Y^49d-``j^yw}4JT8Yc*RmLA?ftvj|F>P` z?CkXUx2Mh@&yNpR=O%0US<5tr$JqF^zG*|euQw+7*4tt#1@;2+wbF_^id{zpwz#Cw zT@P(!o?9)L%;9F$buCw~xDuG7h%;eSUFNaziE4XWL{SLAD49@8MU#7C=#9y?uBhj6 zpS!LfT7giaHG2D}wtVMzXXTJoDw$bN5P>8WuiMkQ>2POmtoH7YhPV*ff^p?)X~X&? zmEN}j>PslhIUgM@PtWRt35hDZAO7sN`=8LF@kB@OhT9(iVei>`p4KI+v}CG^dhClIqVgEw=Bj?x73Wm;U+Pp1uzM zV}H^#FmgZvQ1E*sK9RM@W=+nCRWsW9yAD2a&-#6v;1mkXTo!Lm-S+7ZVwn!TdB*cN zxJiT(N(C6Jh`Sy_u0WiDqM4tTz@cRF&}TmxNz4k*ZrgmQsqeo0=#{~@p1*eD)zaiu z*EVO1;Jzd_$OL1tSQ27Y61Im}!-rzAx~JIpI5^|HP;w^bteWXC&m~yy+;(X1N59Z_ z;0Qv3GbU>q_z@gz{bX}%^2nq82Op?r#|vYFSKfSndhndBmt4yppEQzjrKKg@D3b-k zAQ=hvS(dG`G&x6}=`ojEriWelonEw#{_tvHO7}c&n69Fz z7^6r;0mc_1KU}5~@iifwyvBtKf#MmhYg=Y+xCBZ{h*(o->)l;j?&(T)CYsh)3zPcg zUyNal#wV)bu(W<%!f_ZO?}IgcBLt6+*M_f^{1eXm4n4f%gHLFYIA{LZ5@7ofNr|_# z$6MN4`!<0~S1rH%mw&`uk5J+nTxp4UMb|a-<*E;J&m1h)wzMRprvGoS#J28kx#eKj zi~oH2jh9Xa2MV-|VX>+Ov?iEHgxDUNoY7sEYhfkU-2Q>DJl?*km$`QE41x(~Tn>e9 z{rJ7EVV7P$=^JR*AC3bm2sIRMkGHe{_25Ii;{%6VHmnoe#B{6RDM=Qs{X1LxclOyY>EqPkuGt)+szj>Rw$ibDxGn2<9=y z-utI-UVHad?r~L-;wg=lEv+MBmmOw$i<~o`{gs)i>~M2rmm(ua2n3cUrnepG9=w!$ z_4)UJi7-Z{!>>*mXNRh%hpVSXD(6NkSEqCab~%bQCm#8OPo{fY98(Wy7YsrOXDpP6 zZaQ*HI2Bv$K5&p@xS5b00`KC!kdF;OVngX8+>oZ~V?DNXnE@B8R)9{J5b z@>$a*vb4;5^#ue4_meZuT*s>FR<$a)2fU7vp}VH;5ML_u`M`?L9%A0HS$S(Y=h27+ zeu7MA2J^!6ITCCe#9ZV#{#JU-u^8B+{%{$0UHBtO;Nv4i1jpq$YekP6{AfaAnUEHi z*X?e;<1<_P54BsmBLtx`Wsa9<3my98mQ-5=aaJsuXU=8~!@Eh=1kNEipSLbtD%1>) z1Z(Qse&-keFxlC|Z5Pi692O|(s}e+Ib>!XGFTehC?lFv|SWHc(G*EXERuVD@tYtnX z_FVVqNMTOnKBN1hQuJ@n-2aj7aKZ%=62-G+CtvZjkmTsPXLu$hgb+*#XTsL)xzTAY z711+u3;A^bGB~JGj0&qiDJ@;$$BJW+%r7Kb1gI8(z5;j4@R5%5o+_DCtnr? zPAozmL&v`V|6P6SB_v$O@uq5sP$j3T>8RA+97;yPa`nl!A|2r`y2a*-_FT!aJ;FVQ z5VHQ@eY+lbvSZt=aF947ioDi_V1lVaFrFJ8I{uGepE`e3Qlkr@foLRN=Mf1|- z;?}KcRat6AFDEIbSl7MLv5I4JSt8+d_nlw-gG}#6!8uV^CL@?12syQ~TdTOHC5OVy z6O$7qiQps>p|t9jY|rvAYINQq&M8$&#R-zkP?Z36Au?@vdk^)UJvAc{f=ruN4d%IC zb8lO`eS>Fvwr<&m&HPg$IG(z2t#xB(I1+|1$HHtl*tOZ=@zIlmiIy}lJ#&y0#iSeI zc8ppAD_#gsOEe`rdv-qfU~}K$XsRnv8H<#n5T&WHYsX*7^lkHOgL^I#!mbt_OV5@C zLn0E2$#f-)2}7`h2{BW!XA5>!_n0pL6V2V5jy$~cfse;qx_!L^0UdO6Bi_<>%SXNt z&9sf5dCRPpT~~5ko-aDB&7s!zXezBziD2-)ZyZ2a*ahBbD=%F^odTXCCMRq2Ua5cN z1a?e!{A_OH!8TvzFC7r5l1SU8bTqBG7T9@>Y{xuAB+;{{dC$WG@BQSmQMHRDt5mc) zIwG#Ss(u$j$TGEUHaJw8%UhmnOImo}W1sKdzMnHjR<~ONr)SgQhlF5fo_%U|_(I7e zHQlO|?aiB;dp4$=q@vHxYr=)WIgiAW?VR}TbTHJAWI5=gpV=>g+~S<`AOeSP|ICA# zj_yi6Tgg|Ps)?}(#UhSjNy@zRY8a9v$%=COlOK>($*wtqBrx5ANMFNGiUv%GxSp2%9ZBQRoAR@w%3-u~oo_v}98o0Qxa>Nn*J1Pl;KHTU1~(E3~N9(?oJ zbI(6LJAA=&%$g@^W8O?w?^_pdY6?fg5>+VYU~UChtt3is(!K*O6!RgU`ZcgJ0fqoV zwY+JRtVlYv^g`d|k~ESVtqi^~wdt--*YpVN=lqQc;tVNasrQzq(UViPvyjiTEs)8+ zid%Vllwj9kmj{bu6IGxbrCX0Y(trDdRMrAZarJe;SF#%3`QVeS8@9dq?f*V;_HBqo zOzOJsGQ?YwVVbu+hhsmiTcsctYubXec4Z_ErYHfmBT2=XVtFE; zYH5!qV)0CBegTlnM8e~YFPV;nY4?te{y*XfWf1B{C^a8xsR=QQ_!UBmEq!eW^&`~n zuWkhyh6(8y*tz7xo=ux)F1|H==`>@inGb%dd8zf1NWV$xq zcHgdtKAmXo@?4uritqQl&jBpWs1n+8&#$!)?7#ZX^TVg!$Pb^j%XwXwFAi2xvu1m1 zINcPE##JO?=95KQ!3Oo`dZVGmu-Xj(OT9RJ~3{VElROOv7*w|)Orx%c3pA#N?~NYN-34J@Ycf* z9(dxnwOC4UhHpauU_zK{w+`&O|9AfL(eM6`t8c$#mW!3D{q`FZTer2e$dV+1;<-LJ zr>zN$!6gdjz`1z+rO~T{_KsV-z!?-lk9G|9wau4NQ(q*^T}XN}g5cvDL#9F~CG%Y@NQhVUavW$#BfG{!Q6 zNf3f<(>?d%NUdO+6*u0#(jz!hLUQx^_}QOdd*}O?AN;*rRgE$~s(~p;dwxc*KA6YD5&6p5p|@YTvM5wBe~HhD zYo%tv96(6IxD`u1nDinvZz&5~h--*n7RW}B09cRcW{lEx0lfNH5ONC-_31Oe#r~(n zccBc+o6cMLif6fkxeoKJwt+o6A9!NR;g87S2=F#%gs$QM!M7Y!60FVFo~!eyBy$gZ zmdyH%sosqPM;fIV3{ z2Z0GvRS=J6)h$lxZ3C&5lrhBBB#kODdGd1c-5(C_|Mg9j!aa#ZCeG*I_}0alDb;hYV{x8GFU$q4pohX+>Qg3s-Ii?Ye0s2EJ8=kTbbVp<>NOCAD zx$B-@;qmYN{pqz)AlK;#j%A(pV7Oou!n7OVeu1KbG0?Wc6{sTkpy1d1X{!jOv2;5) zHNeWNvN{;rD=xC(L)blm98Xkqd!}e*%M3Gu1k<9a&Rw_N_Ur$)rEfFmEU+?HJLN)f zu&gPuie=9+qOI-00Tg0Yp-v`SAeRA~48-LA>Tj^*F^$HBr#Syn*>NeVG5uIq_%*|BXV%TnD;nx`~pVtl-M{GC+iMo{Q0vS$6_z&l)F`!EI}EjUkiZ@T+8{y3WH9yf5e)D~1Rl zAS01vb3|6CRdq4JBgZE@`{T8uHFRty99Oy?>O(@LJHnKbcYi!wm@%0Uj>9m9Oj#q$ zZ+yIZdARI2oKTeP-f+)v{87um4(2&jTF#nqm>tu|j}JEW4#;ZA*ZV7+9FP>0a>f#! z-S>X^|4MfBoO}Ks@?)1Qt_{vsESGntqp^@8xMv(6%_VzVTIwnutWVGT?7!Q4L%zX+ zD}-tjXU`d>3MPu@M3BfJd!zK8A?`3qh1B0#%RtzylqSy}J^Qoo zOkO;0Th{Q2*E=`w>Ob_M-aU6FTDlN;$O|H2r8_|0ig3?U!tqT<9_!k9$JnXY2jBYn z=zDKidZk99!(+8<-sbdWX9DjA=!Ta|<`USwzt!^U`N;$yo$gT&UNO$#~ zd;Y2M({Fp8JDRW6EVnxoZA(U!e7^8rB61`i)%4)NNHXs1j;_I-!ILQWVpQpR^IQLQ z_}H0PQ){xLTT&n#38oSCDzkYo&M=24D_kVkBkh8%i7B!XXcJb<&3@|t`BC?-Ly$LG zQ9=0JSv4HM_ApnCXV&li(7wmM(6V8NPb^sWB6z{dTa9NgpFjHaUymGr-mB)R8kPuQ zMs4Kii(_YwHVy3EcHg6$ZvS97nq2cfKCLAQEF2*s>EJl=QU{&O$<;M&PIrZ2t6 zT)S9xE?&s(+>#E*HILk^W3+-34eAPcwr847SVP6B+U!`htv9iJI-FrylW%=|qa30> zuMx=10h9=H7mP8^fpWFHSvGW+56MHLb1j>~x5|XM^FS@W8g~lc5(?5e3?WBWWl51+HVhp8jsFmDj&siv zB1EL)b3<1k0YKvZR(P1U`+EO|k^_ua!Pxm%pIJQ}czs}u)M&hE-KL&fj_iHpb5v0o za{)ee?_49(eM^kG_84g&$azLe*rZ;Yt_@he8bQCL}1pI+r>{5I&O?DbXBm>I_Aa z5Nro@_6k{Y2$fQWLRbR^IM@xx#E{jRMC z{HLv~)cCE(Uzgk`}af+qzc%ynYTnRr_w6iXwX^L0&V-M($UwH`yL)0cGZzR`KJgGwPT zTp|2wiN-AgI}gea=BLGC&26`T=Ktu}yw^t?FuIyozKkfrUa#MG=eBzvWuD`Be$sW} zdqRjTxsJmfM=*w^n>CSr9;HAmfvid~*3>Gin#mcZendbB&-R4NeZbagqyC+3uqDYP zj0B;mc|+p9FK&yqMZyUs+8h>Z066OKk@;ADP^uDq6KGt5C7A&H(2vmh$X!${I)hh> z*@BIQh&H$1_KAPrci?``e08*(H39NQMvlGs`rrInyt)0zZ~jrHXY%fi+FF$?l2&HG)EH#%yq$E2^=rZxS%p2Dwh;WJA55YDUBnN!X(5zGn$bI8Wnt&vl#@q22dM5 zZYY6CW!TrC&MAc|t? z=7XR3%BH(M%$bK*m6~Mu*o&|J&p(d0_1yjCKS_7@bLOqP<(@=ScdB#a$)A4fm9PKD z;^gq=dmm4=b|WT$I~K>Sg7g&_shmri+JDDGP3yNsfBLs)fA&2jNUmm^w!02r7MaMp z!y%O#b?&Se5&*b`mS>Bz6BEo-6yhNyV?hOqDABZMS)f4xeugog!q`F}cL7q0Nf#!o zm(Hy|1m=`V6BpkzYZX7dN*3X;Edmnm{qU22_UrM* z6~3(_7}nCli!u|}@5(tp{mi#6z49YSdCN*zprII~(wUjG3oG~>L+x_rowj6#G zgAe3KHWsfj5H8o=d-a9?_WR*v=E%SK<8*gFz(FXasnY8P4u9znlu+o@Gk^c`*Z#xI z<b zD@!D#z)9zfpLzDp3$GkUjMO!|UkRY7;nK{wq-dNue(;j|e>g%BLb^EX)eF;}V+JY4 z<#ZDWgY=3nHR>VD>ez36?=J*rGLh&~BhvG1OJJhNI2w`*Wq97$sdu*A^-!i~V3B>} z2iDfIK^u{OYwqoegL`fu#XZ-Rq&mdh&#)6)FBis#5r(8`A}Y5vg@izpS<`g|!Kh|< zb*5OofR0k6MkOuUV5kV>axpzv-hOwNtgaFaUUAeJasDU~L@5DgM98eT0h(BnNV#lJ zPU~}73lS7arFTE{>0KZCB$mO|U1BmWDD>f@FTD26KTWiC-uqksscGG2a7KZNLCyv! zG{8JA`i!alJ777ENToo7>eI>#` z?vm|@^pvN@UjODF7bb>Qj%gFXbnSZh6MH`V`Q<;bY89tiq>^k2z&HA-O1g>j&cCs*it!>lpP%Ozf^P>v{4kxDNnNg!$ z;6kVrkbbtq#%2u1<#nh&L6U^2M3@s`!xlT++!PhNnWyY}9z zZ-4iHBs(|U`T5^#>fKUj&EU)sL}iG35n0}H_hXSv%c-CI-Pq})uYB`AZX5ZvJ&$}| zjm5dkAyc)YFAuySBsH}A!=G>6xbwt6ee?W_Ps`!h+}O-J-@)zvntkmJJ0a=GJjHKc_T8lg6%PEd%DEeLX$NYd1n{@vkp+bYf)5dz0sx>m^T zBE%e50ij7EAz6aNOv85=sS-)WdLkN;tGeqLMrCd?xNkk`fhqUgkrS^e(Pa0w zeSSc0)u{Sf8@v)Y!jJjknW6JXpZZ!;@8&J{J+@)*o%3IDf{9Ve8s#i!TvH_5VH0zP z?Rdfyt_3cggWQUvV&G8?ITtZma{+8Gwtz&$~iNah)Km33)%hf{CCi^<1w! zsTap;j>Qp2g;}$fGwiBWoT&LFH=?GyOi<>zp;&6~M?Sax1HTp+g3G05;3^Csf9aLK z`cs6&;V=DRQ|}heJn(oLDH+sqG01H9>^wB`&I=f^^4!$1AAGHvpF8ktUx~N3uaS`R z5!vA8wr|;cM;4Q`^B${to)(hM#Q3LZoAITa(sUi~&(3nPLKoB7#L!qaYo)XO!}u zZBa>vaI0hGu3a2C{@R%rzQ5x`pYGheTL`{9hTO1?YdaPz(hgO#Q2vSRUOqK< z-(%}6<^W}4mJ5JJMOcz?&G489RaMOcna+U(l?m{c1hJluZJE`cti_~_w{^5` zsux~j43N3vwBaw-w^@95p86ifYVaZ~Vb5lxr?S^hXNyzS%8c2*CDXO5*|XWP9}j67 zRNg^U+;8i^nXAQuNNENgpOXs1EF?f6ke=b`?7I z3*wf4?lOC!Oed^RP-Xs>1l$R}gbltjW{@Oh4&AG_ihx|Yi1MlW4`^SP^UKc5>J z45gZydp7{K2s#k}Fn-Z&z~Q?=Ng~wnilsmc<~p(#HuBT1S&6rI)(SJPe)CU<-nn(d zf&13)JJPgnb9v^PXPbx+O(g+*T?o|c*HQQ$C#d(JgqqpTvf6myiD*;kwoh!X`{Tad zUYXWs21}OdaS1~aWEfmPPq%^BeH;u}3`@u>A#RJozr z>ED%SCii~i3oZTI8q@0mbK6Gs^^*wtgt=dPXIoBPVZT_4h- z$)Fj^a@q@l%2Oj(kN#}%jpt^sUc^*ZB@zzF*SjqQS20lAlE*|vcgwmfxS#|Byb>Wf zq$!b3OnL-Q1>y7sE9z#D=L;CQ>z~xmAUxRhM4{vm35OI4@dD;^O2a}_nMO2f=%!UI z6sND1&c1Q!h3`*aI_0?@WK)rtI{S`rT)mt#OZj?6C)hbe7_%eE=Dz*+f`PFlpAm%X z^~%sYFBB$+{0+N4PvJSCSZdN+y9p)MuU8B2A z*S4l*wK>r4Mh?-QUCo6_Gi+))563ckyTbL5=xw!b?$|yE7|N_pZ#k4*8L0NfWUcp?Q129qBS{ZTCU$>K|nwUVWP$2@zz#M0$=`Lzzcyg6r=3Neo4vjDh8(% zgm*kbGYLhe^m;r-K!rvks_2Gf6m!R(`tP~1OO@FvA}K!3%`?lz_kQwiA-)~Nh8p(| zIzHG|vb`tP+!k-?@(rNtlMmF5T`Qe?;fF)VUe;ns&KKZ995d*PKqQ6AvS(XCk^su< zJa^*U@yYWipniu+ibPbELUSm8ca^9tsf5ZTDC@iNSR8lq9LABbs?NJl4_Q?wKa|%? zwl5|ql}Y!`X0U~pNFe{Yom&#=_ARX&l78_xz?X!_V@;vGk8Ci?R({fiz8J1k&CS?G zMGXh(y~Ps|3{#pNym;!Tf9E=GbKed?Hb6BD7^alSdST|hpL}bdq_qs}1j8S|iqh!G zH-?YBENLM*6!xV7l;F;T&_$S1+x5oJ9#g~7-48s`)V+RQ8ZKx6>^au(yRW*YA!(s> zMC(XJS`uNr96dx3HeGM7Xqg^^4kysv2f2pnd9J00i_>#jbX*8|ZLsD|4U7b1`H>5r z#}MYW4e&mJP=QF2M0(wDM}y)3KISf-O|s$gy%NW9=k zEFhu#Q!5m0 zv*w!iwRhi{y?j{!!sAyTAcqj+nI6CPgZtT4!S!O+b=;|(smi1^6%wEbP&5* zn08S7K6T~Xx#zx9E6v3svDS!lg&LlAO18XZ>jHp`DB5*HcB(S z8#28cZf2T+JJq(V>Spb=m1o8z6-QD{{<$n8j4r(NJt&#Pv?CRbYLX2F$9}AR`4fW_ zybwr>sJkCN5=kF%Ou?C1naf$VT7aSYmm2GCy_@>3jIVH^kIm)RpS$vlpL)>$$W6u6 zg`P@&T_x&n3t=KAW=)Qhy& zE6!-1W3ET$*DOB}0?P9l6Va&D)fqc?F)KJKP1S7O)zaFERck@fm4ETld&Qg7TOVET zRP9qg8wtfio^4H>JG%MsqjEGBa3n-#-hEw-#5%X^@zKz0#5ep#C=G6YfP7uN)TNg! zEQ(6f#QAqePQGx*r~ZAiZIi8go>QJ09D)2kM$HN60JT7u75XSi&_E1ed8LUGLcL13 z-FF*Nyv0XJ{VR`HrepNP%a#E>W*nDY@A&1bKbewbM4qCKXS=gk&P`u>uWex0s<@a% z!w*BO0Z(o$yjXKE&!v(){@%-GDTk>V(x^ni0$E5%`E_v1ZpA?ULpB(CZThSV-4=ph zt~GfADG3a1MGUW!H>+hk*&PRz{|%=h^7?bGUfXo%;qIN?rRg!( zs0uD3suGgrAOad(v%(~TOp)I!D~#|uA)rRYe|(Su`Bfz6b^W;7SMkpWs{me1X{QL)RfA?!zG!aX++%T{EOAYP=PdTC5wfFvc=;(8b z5*~j0mF)0)J0G}%nJ$NBTRa_we0rm0we}I=2sw3^dKkp)LoAt zWU)Bf0$pt8%{l;}+yHm{uQ52Gl5JZIU=Wf5n3y2#j@+P;4#tsG2uiS*kOC94;+%YD zq%iB(i4)>EUhke}S&|lM#U&SEh_y}UEz4$<63=oAqg8ZA(2#YlSq1@hDJ;8H=giNC z&%QcVp3&uytjIJRlCybkRSK1vQP(V;eD3?lfBdalso2)PE0S)h*Rx%Z$JOnTHEFVj zjS@&SBDjmmba`4o`ON>ykBuNfrBKW$I`2LG29+rHQRMNE-+-@S`0>ug47<1xdv#UV z$TW@QT!Uob`uM+E9i8cawaQJFG~R9qxvl=l#;VR6D#Pm*;c7qO95RjhYgf!-4q>Xu zSWzHW0C{WQ+XwSQ4G^A7=H_{hqnC`ztm$VvuxGmsg|e$n37M8-*pMqRYHo8+ti3Pc zV~`sR%%CH8{h##+Ip|s{&0Z`X|Iy&&rJ_}Hh^#0Q-Lk2PQe3Nfdd-=>eCD-p{NI<~ ze!(_$iaE5uRYJ>Azw>#5_dDi$uYn&q0);)I_~y5snmBV36IGK*IIQFfrfE2VuI_4z zrelzPYowZlL?Da&oyYG(cdvR}R^b>hoVkMo+{s$^UF8SCN`t6T5H8`Idmi^(Avk=z za+beA^!jb^R(w>0#TgD8h^Z2-U3=@JCIu<+2th5Q@;aAEk}xoC5Q z5UDAtu3MMrXb(FMYiS7&n_g~oaOTPxDoLuUS(eLO+cIh)Cl&MytS`lP%@zp0T!;b` zpD{TUp>;5BBVP){O9IVtc9whkndeUp9XrlE$o%!LOLli9M@Gu!ib*L=HET_rgjH!t zG{}#}1C{2XG7EQJgK8L;XU3;5pJ1LxB*ihRN+>~9%}=`2T-dgyI|D&%Ne+c&t5!w` z4czh3aAJ9t%=MoytbN(Tg%^}AV=RZlkxa`xLV1-t-2gh*_^5_hX@K}#eEdAel@wVD zL8~*@u^q=k1j7p%mYs^FX`!HvrzBVNuA@VCxc-Q`;j*%akNq~?7$ZyP(X_hjLw(!s z=uGrP-I`0327AWQxt_z3W}-%d1yf4(l70S#@!=CQGlRuMYX~G$XS8p9A{J9zm&-Ek z?2MMncBSe-w-DLy$SNs%VdmV=zbz}_c`FW5l#z66Wo~?KwGaZCmbGojV)%b)^YF_yP)B5f(%5y)km)b&PP=jss2WH^K4s z4^NN*s^+Gyy!E_o)Q}KrG`@cCUFpvCORxxjZV57(S0R&mO;Qw0Xz#8=i(+{GJw3V!}QIxEiOhx~)UA$bAf{JO*u;(-PG$~1@f&`h3vhk>X_W;V6)2B*0c<&> zR_3awpLx?Llo?0yh}74YR234G)hCi6?g}DNIib3>IvjRkNF*!?k0SurKo4gB%-lHG z&RpB9R*U0<-COtE`q3}8Z##gg9AKfBf~$QHwOJ^<{Ea^;Pmc#ZbGh(t_=x4^pWmkj zy2BC*RbkcK#PJ{eMRQmG<|7{t!u5-P3rO4S;K_@>cn%>FZ*EVvb)-AjH-OUo!2u&^ z>!nLC{b1zX*CbVI-ms;6$F0FGHuABosIsb+^K%zo_?~N2hy=PK9P-mR{+aRo8c1k9 zlaS#FL3bTOgjo_sj+<6k^DxAouD44T%moM%xB?4A^S6LIwYj3>;2rn2?6^1Hw7CUh z?KY#(9VGaT-ypP>6|N0_qFv*9p2XTPMV9W2pPh3ZAt_QirEXZ4NTsv@**TBqfvYna z=!)+LAaSu^xeU!^4M>y|Kl%yKFy6jx@78-Ci>6!q4}B;%dL=(Lc;$_sjh+03AGK5f zCT8llfAXvQKKgk-W&*~F5W|9xAyEb-2B+Mmay0So(?1<~_aZ_f8y8=DMvF#e6>8#cw)s{=9t)7(kl}-OS!5FGY{7K&awwMG{m^Fy4nNp% zNj6Rm z3>|-QZs^k7(1r5M_=bIlHsAFyCaQ3CM8^c0MSQ~VVx&TbO27#@`}`{xUVMi$CMk0N zhE#i73}XFthy|X&r>*KxzURoy!)B!$(pj5|G*9F*m_{&fqPH<^gF4Jp0(W_A}h7x+{W8KxaG)4 z36YoBoqoU{8^zqk=f7(fXQ`|y5>_QrC^5QL_Dq}lbYkOp`lVPZ!y>I?AzbD zb>9L&81C=!WLNLrt}p6^e13c|cWtn3gZ%^WSx2}2^)W?kwEQKUFEcvGA^#P#PV4gvOLzeumQ;QZ81a9iUv%m>x z>)+M3X^#?$_{h`1VQjbnpkvt5E0VJ>@ZaPrcLaCmWH zq))DTXN883pdbSR-N&HOP6PFcfX3m? zw|?v^vqOVct++;Y2Sgt&Bi7Wm;{%_FWLg%Id)Eixh{?s5{%PvsNkU~zh|5I29264N zcNkt0LV*=AGljuXK!RvEl)|Be4?JMp%nPGH z36DmprE_@w0zf8QUSTBCjKTZitda#}LL=3Ed%!a)YBZi`f`*OE1wqK`!1?Qup%u@l z=Lt%Ax$Mkl4cm4?(b&fQ_hdTzu4CFUr3wZA9id1%6G>-!b{*i}2twzDsQ3{Hj*vE= z1z?gAn!Yr0^oK8T)526rB}081(s*%u#K7qH(XRqJL8ua1&YcVU>AUZXrKX?u3XXi^ zWlF?nujR1nd2Q}g%O`*Ijd!2^=DNLiuG@2`tcAm=W`xj&{rA>#GoeIg@ijio*e~~# zWX10h2blR%)l=c!0HjcMW(pR}gu}~I0)STFT9&6rFTC*m4Y%H-h9gTXMNFw@*~N)r z=2%d!z6@a6$b8&$cYW}ay}Ry&a_q(MUV!LIbmG*}Ge7^HRW4FhVU88}-ocLh{fxjr z0#PQwr9no`S1G<^Sl}LnQZ2GZ>qW4hD=_j9!@~7{>{&#Oa-%5BqCj}$wC>n^_mPC_ zg1rbF;uxIIV$m#IiwKMR@QtMl0U?J&Q7M{KP17?iuT-=`Q3ch(0MHG5wU%mJz+1dZ z6^Glk%+@{Y_T3#6bu_TESV(^eHYd;!Mp=P;U||E>gASo`d20694_?fT%^)tKQMtP_ z9*e6`CD%}u&ma^Cq=xnvDLCEhGb&57S5BY$ z`S*t3et}?o;NxGFwQx{E6@Il|zLuMv8@pU9=54*I&rO)c0?2woXG>1m@IneU z9cZsENpf@FuGS5Em}f(eKfh?4$~2U2F>4hq8eg(L!FgqRJU4QwYx}LueVdwk`xnCs zgC)Y8K9CY!OHqd}r6O^({vpa?QILgLi^0+yTffsk2w!%?+R9 zoGBp*0Aux@%Ztbvj*;6xoDx2SJ}_Ybf*Fv7xuQ15?V4}_X^%Oo7QGWMl~c`f@Ak0I z$Nn*JETYfKGB1_67J*j*F5WN!KxAf9na5icW5n}ZR?8V0MYjLC9$BUor2Fy~^g*L| zw@|cP$Bm>i8*aNl*3{7eU|0x#xromRR1|0u@ggXTItw)--GGVDJbz;N=tX!>p{622 zrswpkZg+IVVsVhK^Sxvj(CdB(ScMd@FAZ1WPLB&s^<@V}R;6_IXWtFQQ=Vg`JJzq? zf1eVn2i2Cw;^D0f&=-??eE8@;AOFX%&kkSmTzCItU+NgxRj-nszhG$r6ee;g5^wJ0 zo^W-8F|Vm_>)g=AN^w?_kNwH{c+0qnGXiLf?n9C&YDH+9_HNdBuA4|H+!kcR? z6SO;(s8y?+e)c<_ZQc6#mr_0bR9>Z;%&*MCggAzA{6~Lz;id0YbF-2bZt2^x_p#pu zg|Rp~y&H1`g0kGYOfnH$fKUCj<(EKXdVz4Tsg{AxJa5|mC?XGZO6F5bSV z0AMw95)%?iW^S@?4yII#ry8f#_$~1<@IlkOrK|T#bE?@%*D^5Gln|8ZuL$xK0$#S) zxSPH=xoF090}^_Kcvy(Jar^Ac<$WJYQ5h>?njN!`|ExH2K~J^G8+J#V!qCuVvB}T4 z#}oxMw@a6NDFE3 z(NstM6##-&g}Cgu8MOT8EtQvxD2cIPV&L}ey}LK$Zr@&-%8#A8GJa;z(#?Fq93Cm{ z*wG@(P=e=sk^3RZB42l0%{OyAE%=@3Hj< zK1d~H#d>5YD88(Nn+hi|50f@l;z^IFWW8!>=^~S1AC%k?Zvpi)dQ6 z(dNToz6nKRt?jYab~zM3{oHrJ=iuBnjKcV!5DzYI_p=I}ZW^3?!ym5Hpb|1?RGPbT z(lM%3lKlcj^e-~VyjZOk-IFhtILDKh>`Kv9Lf|-CIF-PE7yYSp4p3hdb~c(7{(t%H{|G6peyW`vSaDWnPkFI3P}{{?;vzk}o==o#(_ zEC^K-vF1yX9~KA=|T^>oujT zbprrgFhqezj$YD>@bKsV-40LY0B{Q(AeciyKhav3N_5CM&dF9RJJS9j+DE|L*zCI3pq2nhrsw z`M4vHsN|rzgD=BYumGX2Q!6xj@!QwnYR(-!ZxA9R39_#MV&a%NjC&?i6iHL5f`tUt z;pw!N$!M)DUa8H_=FMzYs?}^m*C#KGOkEfmJ#}U9jnl3Dy&Lb?*}SgXcWdBPL94yw zKmg)cz4+SE+-Mfu00pYZP)4Tx0C=2rkiAPnQ5eR5mL+``wB;bt(1%G_M6{WN1Px+})pos_25Oh971Ges z(Ap9N%|T5~xi$n5jeXM4AJ7^}U~+q}R%+3K_dGuy9?p4rpaDhGl2#y<&guc*jALsWNs#xS7Hy_v}BpiE_@%8X7rtR*91!D~)-?(MS>#o+FKl3#YJ~ zF_}rT-`~Aez3n}i-jJ_Gs$Osl&U&ElvFi0-Lsw4xVNKDMpEfN8Yi4EKUE#JoH0>M4 zYZz=pPxWMPl^QU^MFh07+*_L_t(|oVC3Nm?YPA9(q%y&eJnJ zdBe=+#V&TS2n>J-f+R?g6dxr@A{A&`*7sSG^(4#ovlVS6+Iq5O1zWcCEK9O2TcSu& zA}I!g00|<4NQ=ehJUcsidOCMky6JuAR`*QL>@2yI->YxF-I?y{>Rb1od%}PIa|-|D zckQ$op$w&58!KrO#29vL{LsCndmkDP6+%j4tP#fMM3BoJ$yxhXE#*AW% zU|ZU|N~w2zpH>zk3~7R7Su8w*XaRA_FE4h^&*^|Gg|S7DZ6k{!%Cu!^rL@+HV3zT6 z`^Mc}qbip0Ef^uBkw$P3^J88@7)gbQi%wrfzx5k4Z{B2hAhrMQcZ^LPh~kdM$igI! z{l%HfJc`0*Sw?;siNF88|W4R1_^)El+RnL2jYK#e`gi(YrW;P}SV;sh!-U?eN zznnW>6@yw61b1(S()|S|BkBze{!no>p~-E)k8RO~ z<>tTtlfOB%fB#2+;@=__zHx2wpZ&q(E~Ad)y7{71Jd-c&*nbQWt)wKBXrY!apPiY# zl&h}&&ciPp=7IprG_`*pYEOA(Z{#Gf-xly#m_&r z@YNT}VT^>-vBDD5AmoB8R>)&Ukz+B(V^}DLvBmU#cNLG_JM42!kTTcAFxT3wx59{G zB&4`}vwn5~D~))}D8&>LWr?-Gz3*$-n+5^FLM)x_d~G%TuqW$*fBJ^xuv{Xj^A1sn zYK65z3RA7MMp`O_v%V)Wy8n;sRkxk=B?pjF3`_ zU^L>mqqv+`XRo-&6|rEHeRFF@VFuPWG^L0y+)&sSk_c1F$rB~7-6)p^Gs)Q)AqnzJ z2eV!v$tszcEKx4<(_uKhb8Km81ye#SW>I&1VjsgysDNT*TUKT5=HlF?R2(!rL8UN( zU^Lxt?_1?>N9=ArSt~>_$z^pA<2cqrffSmhzm0I+GJq}TN$pE?|64|aP(S->#e4!3 zlM)h1%`jXZYo&D@V~s*7-+HWc;K(qim>8k&et|OETVqf*TI(S4=WCiUH=lPW??$bv z*0FBfkezbymDkeWJ*tt=?MC?cWz`_^%A$y+Xh$@c_x8Spj2{y9Ww|!5!a#;0uP-YC zid2X=ilXLx-M+e%-ajE>rEyQm^={ILf8eX^ZtIJG+4=HI?T_8ldT4yv!W_vsAFL6xWueh;7#;%xW%O zk6N_>zIIT*fhgSYEtA?+w;M8tu&?Z8wsS6?av9ls?_(EV_=~5$ap5OE{>W;jP6*Lj z+YS>_{q++sXR`S>m(_3l!KZ7iRBN;wjTX`xzTI{vb|3PxX~9EEvC=pUM5|VfI*md) zr^ZHq|MQ)L)9v?sSE@NDQr`Mr=o5oGl)PmoNpB^{H?CGaRSVm zP2cji5kl~npI){sA_b>NGlrS#SOh7XAgV#G9=w0#u45(QGEhArT%grJ6Jm3jWWy4y zv<%xEE6MUB`S*Q72eC$42X*DQFhy8TB8*gYHD3H0&JBpghNQ&Y19tbk-8%9UNHlb( zsYR>>mwp439bx|LtIJ>cHI1kYgq9lFEHS1aF;apUf$l_{>+C3f;aUGT{zvovfz==0 zwKPB?F2J_wU=0PmeF8fKVN5WAPZ^7$RcvI|vbo^i$dTM5KZCS|q6R`(b{a|vP}nR@ zpZdh|YhTb(<)f85TSFLT5u-s`kyteBy9Ousr3Z#*m)1V})sr83! z*RS8qxI=Rd>;L)kDdl7swF`y3lg(ImYG7zAozF*cn=po?v@EYyTV0wtJ1{gdI(3N0 zoeQs@`se?t_S;$V$RloRk$WBivD5z>lGlG`;yTzCp;(8p4kCDW+Y9#TqV3+;7>b*- z-ulpp#Q1B^tx&973tnZm;~>eg2B|nWl|6pEL_MaIQEM163!x~K6(9D25qu3^9a5_` z0WKH9mV+~gac1jRgJhCPk|iE5VQ6-x)wrAc*By;FI1m54ztAOLP-PgxVGg z>F7~}hhQh93NVb+`bZR`w(B`kgTHrYtB15!DwY}1JZhi6QhHcxw&>oqzVfz4*%6 zDC9ybN@?u-r_Wz)WOlFFNAAAoQ57_$;6e#P7`2&~&e@(PxK9byQd+j-x2iYKy{O_S zGcqwSI2^^{^uD#Tubug&-?e|kK@Yw))mavfW1LG64*j>0JUCPJISoC9=Q$E-SWk_o9(Z7wcvOLPhmTRgmrq`g zsy+&&Su!53T50Aw{%U9Lf1Q(3c9oSy3j2l&$M)!SMnp1QU@v^3^_5qe58ay| zEN0{8qLUvQedHfFlgD)2GMBy0+;)E>ggHpY_QVN<`G&u|F}c#a?|aAZdIVwetyitL z<7H)T@r%EyFwNjFo5ozpTxt5?+rGbiW0klsN#sgF<{-kO z6fs3ve!@tYjj&nl^&3{4&z*{Wp{-a60W&GMO;a>K4kv(_-WVvx48;w(G@~@qoJT$n zDiD4edngbh;Yy9TcKX=i^cZ{Y>EJ(nx?CI{DouA>OAqZ+9gA z0Yb=!NMw^HOJ29_iAV0>I{k~MIRB*NF~T@xIj${?gqEZ@dCbd=4DC7rn@4NM_L{8J zIQhb$^!-MItAJ4kd)Qn@3Rp%^t#(#~(e<+@*A}nm^MgC~9@BJX7G@uppbZ#AIX+yd-+Opx(1;kDhqME*rMb}Jh zEN|k#U}Pc0SXa;zZL>wzL0s{`So+=X8LNM8Zh0+co=x(3=fQVQ42)$YgpR$D`r+Eu zRi&_%b7@W7b>BEmk2E`Y zlu#vN(OgEVfslo;xHCD?utB$91Znn|+}5VtvUCa|qGeor^^-=hVk>Z{Q zY}?f$unAU}aEywiJi3Pt+qvsk=Kto)qyONCN7DoFNsW*aN(qAnz&OoP_OHpNd+pg# z&@q^K4vs?PGH|A2iKH-yfikd}Lv2SlTOf9ckFrIA0l3h}QpV{gN-3>CL^k3ZEs;`^ zkdfWl5BwiH|MJr}SlXj+J5d_nSrkIy^##LNbwVQ=${XCh<0HSqypgW(8-3T^RfLkl zj=me}lRRu&(;vwXKh%8@>@UJ@7O@etoPA@53kjSa%)axVWOb-fqO0K}8e6({W%+Br z+g_RtYJnZL-#n9j?)CDqV^f8Zy$)JUlNf2uEZ55JKqQS+v{`T6=2%z8w|l$$CcXo( zz-Vl_R=xBpp+r#2&)-zsXf}xzM{6%!YTm41O4Gxc;@&YbRFqN?ikXa}`Dz?Q zI*wH+v;Z7+i|85<4HgX(q7X?JQ+6(u2nW1@33xk{NeBz+^uFQ;9|_svgQbb-AtAfo z^~UsC%s5j>O5_b6K<@6_tdPOVw%!wLHEa#xfuUQ@7k$9by08jtd35mbEmy|v>Xpv? z#VD}uK9qmuvi%2tu{5=JVr1$NcCEZz&C4dz+Q|%AsWAW!jd8dG=hYt-gIIw|=-vaym;$~F@`o&`NY1g}wsY{s`S#$< zi^ouPI=tkl(8GdhNexH|F?yOI9T2qUw`RR`^9B2ZT(V>Gl?VKn91(LVEXySsfVQGp z8X`>yYFW7MlA)vZ{D}i9?Sl7i@J?__*UjRF&eI?Q4vptlR$jYayH<``F}7HHmb9)d z4>isBb&tc@1)pfQETDe z&YMzTDZ(h?0W2!A(pF~jfEKYrEDU11wYXC(oYBjc2#!auktn3&cV&l$TXS#HOd(iX zjB6F*BB2msP8_)M^=mAf${*P+!ic0(D3z9eY$CSAN9)=UCA8(T`k8BMuU_da2QuPr zJ`=XeahMXzD0=47P&;nVlB&uh?!`z`x<0+h^Dg@z><#c z(~2tFW=__DXepVWM1YJdi=xo@l*rDNuvhGpmE@Oo6V|UV@NLw|Ejwb{U%)KFIL!+R z6PK@^_HVqx951%%)0fDFtF625+MOOKaUMG^F2prQ#Y$@{oyUAxExn>yTHCp(1y?>*JNsPN(n~L1t)5;4 zUzm|p$t~W0hz$?uDC&7E;Px?>)=ynodiFx(E3H(jG(7g+pQPF0g)jVCdu2B4q*q?P zCLyIs@0RR^ zyAHw5fgBYJu`4vT?abP1Uua!^iC1R_@B6{*;dcw}C+X2`j$}{SHqA~Tl)BCEI`FgA zTZTi$TGtIK37VZ<{qvu*a0o_+0O%6sMuc|yZ~_oITeSun&*axmSu#}0N}_*6Fi~jO zrJV?+v)RE1zK2eK-`25|&xpoNO;S{a4I-2+j^y^;Px5=c{9bT>`bHklKOZ%H z>I{VS$S()rWg*$VE)f?)?>tU2X)QQ{v^HWy*I&Q1^5P{P60L+YFf{pHKa)H9aYQ_O zaIpN;AC*r(B?O_49k**^kNuPMt_Mw?W$TpQs_Fo|k6Q{b_U?4c_QK7-`9x4&2^(>= zTDK=gATogyySA~Sy;Rfc}&hNfe;F4>uzb+w= zix#8#QvjVX`%;zp&ghQ4dmPW@JfsY)$sC(HWSyOo%YJ1puFOUCPVpTBZ0v1V`eZ#< zw(UU(qUmjHD=1oG?RhsSxoxFZDWs$UAf;-|HfJw59wAz}HVv#yGsIA(NIvstW-Kd!Zl5KcQ$_P_)7 zxzj1jfvu8YgXi9NKQZ}yC*IvJ} zc5YsBDms4imw#Wx4s+Aqj>D|D*Ekh@68_DO zE%T-d=b!M)pVvZ#^|n0;ktpk4s%If#@d?68w^r)lJZg;{Pb0T06UmeT47k1~>v+ee zFEdUv?Fw}F?Y`)#`@!{J6JMccTLFJai~7aZwKpjuHSJw&Feg2n9~_K>z{E$IB3Y-C zf6d=@GaYLd+Dm^C2Qpr3 zq~e&B2HIyXtvz?Xv(lD|B2gQE_y>j_`U!jdUIXS!z(-U-GrI=g`48Rchv&ce>yGD9 z%SF0vZn-zSO(=T*+;pM2zV`^yR%huFAj|}%M{*E)5%BnW%V3~iN`W+jIzeaEhYzJZ zuXNaeEn*{72piy&G6&!Zc&Qh}ZLDaFNq~5SEUT9e#|U}mXFJ>r3mmsW2q;K12&@j` zrITU(ro~v3lh8^PiUeb=;S3;dh&b%Dn|_d6#1DJ=#c_Qzl#*ofW@%!Kx8YuFCq1_w z3Zdwme-Z)S+p4|sIcDp2Ds?*C*S068^M?VL>dHD7QbjR0=sh(_Lromd8ar{D3&uu~ z^gAjN+gWS$-tA5fHp5cj2N7?;_BKNVx`l)<;}|ol2@nSAFP&L={(M*srB=4hhu`t{ zhu`}PIJMh+JD}MhRm1{M+TE2saHw$M2`ppm3?iH|=k(?cgNLx)dTD1CbdVeCS7M@m{3)=D>m=&4a6tS6}%Qq&FEgHe6{P zg{!mDZ)-wFmH$J-OA-D0Qc$^I9T&=r!*;7t(}LRu7jlWRWi0YVd*~o+02LT)Xx$n3 zTOV8YM$g&RLd?Gsx5}8JCUKgmP#P#KQZb;^0nWgYZUO8INrNqy zO?LAZ{{+}t+brz{(%*joPT3u^(ujC+;pJfV43A}YG@BZ9YbUQPzj%d*2-$dGcxvb^ zAI;wT_pm#GbO;#E`dUEV(;@`bU;3@e*{3HT`~hrbd$@#2%VMG0%P0nY)IV0O6doAG z;IV9anOCpCPQ=JcF%35kc@}2kXW+ykq-tkp7hb;3gIHq`X1iM7Aby$ORj+o4mKc*r*;913#2RtTv%Bwciu)iR%hhKEZXZ9hE zuy%;}tLLNoGS(zDm?J!{o}Z5ZjA@mo-a7LBpS5IA9$;)|9Bi2cDI_|( zunj_juU%SX`(|+fPBH4~EewD`@^Qu+?VIl*m z$99pCw`zow8vafIT2h5BtDU?w$eP`1AAwu2lG#_c!i7oOKr#)$f8aIi20 z>UsUHFy~U4*a>4Tm|w$O5!)^7x*s72gW*o{<^5PeZ`Hfg(zho!rhOd&-F({S83XMA zenq==?aipRg0yE3W}`Nbn=w-U@V(zV`smNnk-Lo&HO|udrIgl0Vt;Pw@qfGe>Ssh4 zWk<(JI*A=OMvGZKb?8xurTUWoW^@$0{l>kkxjPG|WE>GpGGjU9STL;+Lu>?#0<0k; z%yXtc{I6K)Sl6Czmjc3=Si^hFe1U@OcSI1Osk=UQ_!l2BL?AP}N>HzW>>So%{|%VA zj!ABMz2GljhwWlp!;|~9RqCZ~O>zHgSz;QNfY-oZK_fS=X-m1!(^3#8?^2!&Lh%qJ1%h1%ALBGvD zNe<##5XNpdV}O>gZveyK7ww=j4-lnG++x=758yr4<)T2!j8O~J9-%b9_w9J#b`Ed< z?QL;5y3j5w9%RLXx11ju0Zrz2!==Mk(HZMca%^*m<)>9x#R&VvYm;oU{~?u(WE~A& z6QxO~CM&b~>H9JV9%Unk=}3&QeFu2q?GCe?vES7+-F4?lrX*LI4!`*8QL_vb5HA~R z)(ho;JQBnpb7mT_Lp1j5aF5Khcie+W#UwctLT%(%HIG#k;Or3bCXJiXGZ)En=#K8J zfkx3%+b&L(u(o^QHb%rTfuxT+H+2|c!ySa&l;k8oT0i@Qzjy&5C$ppI4i{9*wlfZ? zx%I0rWX^phf8UQltdN8S+R(8HSBn=HpZM41Gf!cXrnVz_Xy=9~U}T%81z4|%;^;pJ z!~(bMaM@Z5nhgkA8Fk%MH$@7zbv>Cu2}@aE8@cYxp3D>uev38Ra0PBzFx}Z~aFcwR zv6I^z-L>4tHHK?j8ns)Op72+%6Kd6Tx~?)l?h@trc}!I!WTYa65h6ogScH6 zkw3WqMDD;Ns9vykVPcQ;!hdCqqZ`gVUU~B0Nr`nFq{a`GKJbeuePCl8daY=f+X_bZ;27X=tTR5YZv{+tCX^`PT!P6N?VX|N+?jsDx%5=X=o976MLQE zySskZ7V*W*ph|asNYK7pw?H>A43=&rQWbdPNeaK0|3*;Q(L0e6aJD+>z1jR}(x_Oe z!q7b*$UN}X&}GdiEO{D?~jghD5~Mcb|Lz z4}%T^Auy&1M4J#Nh4O!x*}13moxiB1!e9RN#@VkUoDotMcH{;QP9WRTp+L5Pa5|Y6 zu)-K9ZdQsfJ-7DsAG=e>)BE2K@~4~FEoScj!Rd)3i=X{DI--mF zr)f}+jcFl_S>yX?dNcD#%l6!xGUm)-X()aJcqDnp_#(;H6mit#NFpIP8&A?5-|lgT z=1kx5$H7Fl7tXa7E)Aru)A8^PIpz>3q)9+-j_81_re&K8oheS(u$J9Vt=~CN%!4^O ze-<+Ln>OgSL%>K1vhxVd?zsgsOa9_v?RsKlJ1pT2CNU~RJ8`_BO)W;T)`DbsZo+A- zDIRIju}6;^eP-ZO9UH^|t)GfnhMHa|Bt$M zGk@Y;-o6iEk~7}6nNe)n;}1`K>~~n>iHFE}V6pSKoN8cOpl8{N31tid7Nj9P4Kwu&3dm1R=XZ%dc*2FZXO;EwHaCQL#C7^(e8 z6G58L!ZCy#q=nW#9eucP&qq3Qmv}5XE6vnu%^n+4LgE;1+Y~fmB@~St^Pl={?5_?# z_A}NPC|46tfLTva2(3bE=zcSve!ZhGOIwA3+o**`!UZf4KFEiEv z7O}O|Fd~dHBU6M87|q{rkN7Q7>MaLA9U5@vZH{EIqivK&Z!vrSf@Z=LNNgCPaN*U~ zrDr|IyN-wF>6oqfdZyQaDB-x(N~6fn7v1r_khurNwsB1(WRbh)VQc&Wd`IRLJPn#P zp3~NZEbIY(<4dB`LIej^UQ;`%AxH*q>}-uM*c`wv*a<6y^47CCs*8pSlF4H{K*5ZAQ;3KYP!|2Ifwd zzww!%Mx(WkJzUgWYnt%X6|wBr;*Bf+?f*;{221b%MQiLqV9$8=LV8Z)g!p-B< z{%xl17S>5zYt3CXP#Ync6qMq0dm#W~)Wv*d+!ZMq)EX!Mv-wH@a4}_)i|m1S8gui_ zLji($were;N5Z6Hx@8%Lv&DVt6$c;sNv-&%B=^Q#ucyL7|KbJ{5XNh#pRJyGu8V?VB7)}d@rQGFe_*?Cq3QY6~}cAKlJ#hXvtL4``8A|XXwJa{a3??;>ikAc%hZruP0Js^cC z;`5K!PJP}Q7!!>$$xhIb@6f`>(Agp(>S}~-UB4#ho~IKJkm51$3!z#uqbg@s9{=UW z^*6Ddfpt-&WwY>1bSkl`>Ch9=4)rZpj`kb=_>O87LlOUG2gAz|KPTdR6$)${^t}W6XEF z`yUQH^y9=HyUoQ-?f^5*n|$V#($oWkJMT61_DMKLqUKWhAk;%?eU%iw{go_YRpQNul>u#ulyIzGWJvd!O9e+-$0?irFME-t+VAtjN|2( zJ}Ke=Gb?HYi?7e@{>~vJ3=E+W4$giUK*+>OG+H-aywY51V@xEEQ^n%chkqq=_)%<6 zB*k-m`98H#s+M$h&{+9pVy8EHi@-tJXTEs(Pkxl8if(Cq_`#1B?)y8M7N9t;YhnAL zyLIeQ7KeDlihd`Ls-0+!5MUo(vv_tLfE@fw|Ktd#ZI z`Q!9Vnh55mM;`pa)cqf4xjjbOpyIN}G25c)v8k0G6u zjcrP)>uAZp@&wK8a(BK5_@25I-S`Ht%#utIcY@Uy{3u(+ z+V?@?WP6#k_2x@v34@ijXMb<$>wl^>nt1f5Q@ii(5=r{2sc)H}zVUAsLBA^FPF!1D zckq{mYS~50AdgXZkx3%9Hf?EejT@=1m0!7rec2G5+$ukCv~Dy!T(A)NU=CCQ#^4Z?F5~ zN!~V}Iq~D}*s;~G|6cj@a|IL)+Yy!+x-RwdL`s2sOj#O-Le4w~!7d>>Xd^dGi{msq ziD<5WlPI~Y7B1>I?sX9`E}G=l;)8a*q98DcBbL26SKV}@GAmOENcfhTP`eo5rN#FD14r=#YH)6u@ zTPRgXVrL|MJCZ$Y73ANKN?R#MOv43Xmc)zjWW*B|B zT?lAsuJOhvSDyb>a zT0e4QAoVzin+t$LVoWVp`0bT%d@fqOIr^S|YEM6=M4K@Cn^js6$|fEd{m5_Hr9W7G z?!R-com}3_6uR`|ZmF$C*a_#(@RiF3h_$@C9(H%%h3uk9ZmzpQhQ`)duD$#j-6|8y zPW&@Ws0>{TS(|Vi!wXcko6VO#1C<1z%mwdMv%_P??@l7SZp#HtXlG?6C0he} zdEU=OgvNkEOCEN@>o2B8-VH8SH=Ng9y>jbqi&#k$#=*{64;Qw$r~R{(bmU9S8qkIT z7N(@V_;Rp%5wi?oG^ni(AA4l%BmbHVzQrt1(zjrX^hktgxOPK?0irfE$bl|32{g8u zSa91T@l3iM0?q1ag#7l>Y3}<3)69@((t{?(Qj#$)=HIwhovlb{!-I)#ANu}J^{d~YZaS%s#F%QStCwEs zto+pQ!#^_kj(?gYEpKxtP@)8$Z&(#;*Wpa<4?g(!@+W>2w&v!(B!KdCd*NV`%40}Q z_iNB3ERZBOJ@DS2g+=Y>nJP)Lqg#Cv;|{VN59N#Z7=lm_0ojm-E+ZP)8rL50eCAgO zWd#PfLnl_;Ng)kgr$lSDC-y*4Loju2&znCX3?$}&b4CHbP@d{tL ztfZtai~YvfJ?|d-@V~-?_jhUDT^a2&EL{rY95g~O-9_BO6!Vgjt96`pvku$v#S<`6 zX;eX|iFBZK8E#FLzwCBqzS=DCZtr8-8ImT__aiOCQD3vcWJ zM7$nhIyn*%)&NTqjQqLAsn69;f2Ff@!z)g()M%IAyeZ?BOkNyT%ZdxzbG+eQTJqM? z9I;qfTU`0tf5dU4_|T6dcR0!A^?%U3Vj0gFi6xt{=5W z?>Bn2XFK|)8x$2u$;z9vpZw>gV~>rz`~Sk2HI}cYr;cq?Ljd35hcnNppta{iKgos; zI%5agm!IwY)xRV_J;iaO9F?#1h|${`4%NQ&xuq|BBCJ89Mr;a4Wo-_{%V0Obo4i9B zZNEE`lsnv7incAmKRJILWa*Ee)-&IsY~QF|dKFR949QX_O|Z79W+Fxu5!iaEP8?Gjl+T50 z^O#uFhB}o_Z7o`S108K*obO%A)2NJ4!>n>0;fD63@X9yB$^s$;8uDt6!kfrfC~5;o ziCMwiSt(kCfo>)?wMs6Wqcu*&uzcMb0F1(j#F9=7!S+r^E3aN>mSZwX>gEfVcD!xB z_EJfoB?g}@4ex&MZ)K0Xi$EPmzq(q-IKh3x#A%=U@3pf}PJHaw?c5l41}BOWg;Srw zo?#?!b#-B3(gzO4DLZRk5I}BTB*^g*5a(?W+uP;mvZ<0kY*X7b|~!;rXx$Z~bK3ald~k(b5>%!*bn@lJyao74umW7VYA8GAwqQN)Vap&&jImS!^e?@dDt;HgGuX9HFRQMdip3- z=qJ|C5O6R>3txS`dZ~|SA7w2#3*I7cC(r;EfppwKmyHGO)4%sLY%}0>Q_8* zPrW%MAs+q>QrhvmjxZwQS$ zyFdPKtdX}NlIr=$Ngt%7nQKyUPDJ&TfAd9697yXx*}SF^ zB)}A5BDLc6(~tXK|C4z6k~=tD`kr5~2H$F|cAsw!G7ri8+Ec$-zH%lzhDIOzPCEW; zNYxM->?zs^lt(mI{_1abW?!Fp?0t0lQCXRGvEYo&(8v=vv;t-MWoRlfUg!f#i*M0X8b( zv{J3m;5}3M>9MZ4rwlV_liIvAzjX4Zmb$&#C`^na#jxpGg~Dp(@}K|X;lpnkf9&UJ zVORewYo#&CsKwXjpZPD0u?Y~kqlOTXtcgTcA!$ux{=6kYndGf|!zJmV;Sc^8W;tu@ z9cKBBPYrLuFqD55$r{Ptt08+MqS~dPItz^+TpMB3S*;ZI9nDM~(RdY+y4fwsmr|r! zItCIl#!(`38o|VYe-pIfEyw~}$Z~-+nJOBnERjXqusjk@*Dbni>!CNh5MTl2>P3I` zI(TvdFE;Fir?+Du%F$Ru8%KZbjlW3^42{0)?>dJcwFV#TdB&#Bv+q(eSZvL{94}tw zh5dBo9<9$Jw3>iTS}y4T5C>hmXo=Scj!)wPie|n_pyO{#w&&uQ!Sb6}9#b0$J#=sa zQCyi>H6_L!gj<@pnCs5TS(?qb`^I43K^U9RJR*c5#;~_%j21GQCw$7jquULmJ1+!3A&E;zGl7Ya6iD?mvU!=&eTVHFE%w^>{V-a zFycB|>Ae1RT6!k6=P`foY~`gti~Z2DDR(R_5YowgU2R^-+Ex_rAsp6a0l5m&J^%Mx$^9})?8)e-N&KgwcBXW$N)#Y z#VwxH(?gGfts8BN2;L?Gt92rdi9o_Rq7}5tEnqz}oXMxK5E$x`G;2en%!(DYHAgdF z`NRC-_mlC%2rG;|gr1-I)bHxBIq}{9#u>WLC<6oT8R1VzU>ct>^ut8et!dSSvTrO6 zQ@5=Zm@_Tgu-l+3K*9hs0u7zUlD=R@vhZ zC)HWFavh3aO!o!=Gr2cZO65!LBylE3i9Mlt9$VwsajA5`XX?fxy*<#KlAfI)2xnOr z&ZOO*y1nV`YnBTr3{v51{)=B)eRClx1_NF^vjDxJ0dnF0)k=mdy`#Y2GFqn?DIu9< z?fBSz_TD0KJ}dd>yYLuFev%z449rPYB%&*qc_eb$+ zabyvWc(nA=f8Fu^AJaUjp8VAOYfq#Gyq)iRznwp;qSH#Yk*=(Vf!&HZZB$OuZkS0# z6a>;5nO;vxPy=cKnsFqmW#ewvy#%8sq0g+9+B4L-Tv&Pbq*L%ZYraA_JDA?Nchs-d zD^Z7sVAH!jOey7ICqI2+@O?jv2adwi9SGB!0%2(*yhsdaf7WPY^LFK~t8~|mW|Gm0 zdM*J+fDs6i-{~Gm>vO*=T1}7w=n4-_WogdRgwCv4mzF$+^++nT38XFu_6!vtyVoAh zAttn3=_~NYn}cOTK@d{40%{R&IBk_OfCgHoQGGX2Vi0+WFqA!lye4`W6X@-cnapJ; zi?u6jnVgb1#M-jsK&(g#NmznyJG?O4el@D!tij0>gH{f@1aE{*1nXF+h`R?y3D^?@ zb9R$Kn|O$Q>B-w9rZ?sDAPi{DtpSh5BGR&3Z3z#OQNSo+ZJ$$ulvgaYS1GesU;d)< zuUM&k{mQBI(D3N}_v=uI`YG&~-3d+q)_q@t!BPV4UTIhCx>X8^l7P--iDdTr&35;K zMwaPv(*?Ve`&Xb*(zI}_6f7*)t~8j1Q(31l=xNSv*D388j#_@u2-~41O4+dNoR#*` z(IW_z%vZ>sXW73!*Lf3LC&|Pa9Sw;liBv=DmMI-FF^z8Ogeazc9W|ltBi5>Z3EBq| zs}Yl%E#fi6sBSgra)Y&lv}x&$Y^oBgMJ!?uXAMOxf%tblb)88{1iyIXb3hVez?Tg+-nt6*Q87idrkF;)9p*BQ@aR;f63^~Nbm=WTj+J$Av zZCX@El6o$&SbwWQ!U$h&Iw|Um7a@Q!IacV*Dou0k-qwJCAo&Dz3EJ*nC^a22*59I7 zsG!ZJFG<>*hnDBvmh1}>!msx;AGbTY@qVlbIhi@-f3L z;g&3dAPPx}&(F_|>rG+BVS`w>y#HemhRys&KCo#Y4nLD)w@oGn4`+NvAQ+b#=zZAs`oy zE+aH+-U^_?&xo29HLXIUdjP;Rp^PvoQD}bCliIGw(+hI2@@GT@zU*w1yfBwBxo<5L zv6)RV!|-SzwB}nZuerjF8};xf6!7yY#+k{cCCQmFckDiG%osmT>XX?Jd(ZsqH=vvDjFNE;y zFN*hm*8|@42%tL}`oJZV1@0UppxDeyVs1rqY3&_1JXN5rf_o-45L1Dgm6XqR`|KIX z(@+JevntfGDJ<&<_TGZ@ZjARNprFnu%3R&K-gxDiunj3kg-OJ*=7t0VrF+;Hq~@cH zr5(*v!=vdj6Di@#8v3X#AiV^NO;=QHMF)nN%HZK&=F<^~oxz;2n|PDGmD{j&Nua$s zYNZsOWFRDO1m?M9xExSlz>Psf2ED}@7<|)dcc$h{q`c`|sJIL(t+eTn&=t7E2&h{s zy@iyWz)o31cTeJx0bp5nm0Y55Pfo% zc%@m_6uBvQ!iSe-$cg<>izqQ8Yaz0+p=(bC|k3ud5ik}lgP_=XJQ;T2u{a|$w zBX|NGXATbxUIVBaZbz!xR#N%3Tj?;C+(uEC#n9@RkS;tM2m`Qq1 zB^mAnZwAi6rdfmT1}^fP&8(rmBj8EXAf@%qm3XZT$s3H3O;J!Yy1eJ`WFoQm1n%}{ zUcVS2YOSF<0UH|xzheC>2-Xpoa}_$Aa}P}dPO(8sFz@<2oLP%nTuEJfV}5wDgj0qP zzy5#~c&3w8II-Jbs8z15F^1xpYg>sB9sxrgr&E)*h-?RdPU38?d^X9_V5u#9L0zCV zQXB1j&!OCjy(r~$ZK&BN?*7T4rP|VKH|fqH*o3B_X5%uPcb40NOK7OS;VdIYaUDAg zybuf6ky;_HP%Jg*IYJO{tUNE~zDsp;F}(VvGgA-lw2mCs!tm~Gj3oFG7(gKuzj8Ks zv~3C)G@fdrv6t~a0# zP(d4U=tmRxT0AG}(GUBWEYQ0Q7A<_`QvKXwcB;g74x4ab17nRUez+D69-Bl1DHO#0 z>_g0GT&uP(FTif1IGnFWSC^f;r+U`E`;|HtZ0Bg|(0FrZRTHXO9bZ~8^&pr@YP_)& z0UtRuk}72+H+XOIX+m|atwLz*Vl%NFY9F?LDx%*mDKt~FYb_PmKh@7H>Wup_2qC30qzPB^|*rt(+7QhiF^CIA2!hlXIwU7 zdM4ind{SW?)B<>}h((Sc&Nn+M?cnJh4s2JmUeLPJ>~ar>GOR(Y30;|(DpzVxywRAc zF`E`2NE?RUMq(aBd8Ll?sq{TNH8k~E5711L0zYVMJvD-coVgfhhn_*+as@60n#9)<@S zHAL;@D?^zK%XK?VCD1R8#lo`}1}27*jBMXb!?)M3ubOtBxW?(38YDuc0&hBPGwqv4 zBN<9f;*CJJZAOVjkwpQrTy3W_1vIKc2V)V+=#4Y>fxD4qgO+p-P}~k*{9l)!xXMGJ z8O~3PVdg1Sll*h0PBzxjC73Dcip8)P>`e%d zA$J84U;Fx6_Qr~R*Di2?`ym&tuw|zv^Y(emkO$%@7E*z-aU~S>fx@uNWBz*gpHxJO zkV7#dMxnwM){K&+*A}unN7)JUG*r`wn{cuG^)uzuO9Q)xjfc5~g@qxnYHok2!+dD; z2(Ayvw1+vc%MHO_!TluZg?*hG7Oe4sVLUX2Fj%1>XZ^Gg28Pt(Dia12Y zemwic1tp}La|Wh|-Mu3ym)&s1jP(%hNX2pb-f1mluST;M(nj3BS!*t}?VQ_)Nv!c8 zQ=Mw3admlk@3@8?Vq_F!Rd0*yORZ|Vvrsor4Fk{0mskposbW+!=<$xNT_v1N=}ypX z2&*-h1^ZCHx}4GEU*66i|w0rjZpE>h-Ir4?QJnX&&^8I30X`CVxMz01iR5kV;zAN;N}lGhA=ho<6&BW+{P169#vUTXhal7{f7hIXsyyZCNg) zgS&b;$$zvu(OMHwO_#=+&4ejR+9|?yAm+bxa_5Kcu`rr>@?2}KA-F8=DG=A%T#sWK zpLcjv51{2ACNXhr^8ju0@Dj|;(|8hT1AnfU6=O!(!@71OA&p|8pFA($xXCW= zIp{;1qNXOzh)%cy9BhqFRh)j|t4XY-~5|p&{8UQckcA+wqO3ucWV*$G`Wj zDC3&)Cxj{C)lP7AHGO!*?0-Y|NZ8JVpmyZHIq$Ew3BfJw)Nrod>-X_czWHyWYNy_B7;OQG15NQToHns z5l*G-#g2Q8c>D0Ghec=Azx+R6);QTVc;dk7{(w+tlwjMEoH>b)qBw<3@g1END z&6CHHZ-9rbCQsur57O##r4yNh5?%1}a6447G|zV8 zH=a5>_x#1oaHh0xB)e+}WgL?6;A@S43g)~MwpZF#+FgEQaruoU#UX$q7zYyF?6Bp) z36S07l!!QOsanJ$Mc|2x7^NJR_n1~V>?67xGAl$&HbI`JY*VVrh6^~GR(^ELW6!?L ze-B__HPSu=57@bJV?XEcpPq{NLX|I=qj@0+rZ zPC+QHw5a=`(8c3Ze6fm}P|}A}8OR8FL^lI}t{y5(EmGsG9wLsMHIymDb(>LtuIgV| z${gEMefHe)SI=n|n)X+;yBK-RkhKJr3jD4}k&-J?RPs<0&mxXlX5*CE`?#DVHX@ zlswa5{TX+rJ9@7}pFv0!JTywjQiU?`3x3H zlOUE?%B`6-D`lrfGr93XcIS{iX2NVKRp=)ko|cNhzQvUoU2m{!4X4dntV!Bou1zX| zt_HviQ5stWr&2WSQqTfsuXGcWJysjkmmp4D;6Hc+DaR&ap{5c9_5YLOc9)Sjy0MmY zux)6|=LX{IKEr6Uj_W>d)<_;kA;;x9sfU@vg?N7n*h74!BfffWV5yqFZ@8oC@KDQ1=sT}f!h8k{+{HoF?MI#g(7p*E)x z2WdFh^884=eI!a5M0Tc$M^oAoy0zGDulNhE+)S4|SnkO|XS75Ha$&_3z+%*rG?ow& zBr2_&fYs2C+&FtcJL%?Zf9yjCO`0kp^l#K9P~dF@c`MwRjg`W)O=m5%Tp~vc`sfZl zY=tg(VqNHxP;8|wC-3HV43zdy)Ml1vzjh^N*5xW#_hsK&3M{T@1l}Tuz2neA)@ldB z55T;p9G3N?=!fbL+zA;Uk(A_V`sAZb0-+|cNkvUyw{w9C+B_(CV z;|@He@(nSvP%|X39$U2A#;=E-c7HLq5yfJy8M`(s<>_z^?Y0{wn**PR;7lo#8p^hp z>%_G>D{TdkX&->nCBfi;S~uIN%Z+rB', self.press) + self.canvas.bind('', self.drag) + self.canvas.bind('', self.release) + + def press(self, event): + self.start = (event.x, event.y) + if self.rect: + self.canvas.delete(self.rect) + self.rect = self.canvas.create_rectangle(event.x, event.y, event.x, event.y, outline='#ff3333', width=3) + + def drag(self, event): + if self.start: + self.canvas.coords(self.rect, *self.start, event.x, event.y) + + def release(self, event): + if not self.start: + return + x1, x2 = sorted((self.start[0], event.x)) + y1, y2 = sorted((self.start[1], event.y)) + sx = self.original.width / self.preview_size[0] + sy = self.original.height / self.preview_size[1] + region = Region(round(x1 * sx), round(y1 * sy), round((x2 - x1) * sx), round((y2 - y1) * sy)) + try: + region.validate(self.original.size) + except Exception as exc: + messagebox.showerror('重新框选', str(exc), parent=self) + return + self.on_select(region) + self.destroy() + + +class App(tk.Tk): + def __init__(self): + super().__init__() + self.title('蔚蓝星球 · 本地自动化') + self.geometry('720x520') + self.region = None + self.events = queue.Queue() + self.stop = threading.Event() + self.busy = False + self.flow = load_flow(ROOT / 'workflows' / 'pig_claim.json') + self.matcher = Matcher(ROOT / 'assets') + self.status = tk.StringVar(value='请先将游戏放在主屏幕,打开首页并校准游戏内容区域。') + ttk.Label(self, text='第一条流程:小金猪挂机奖励领取', font=('', 18)).pack(pady=16) + ttk.Label(self, text='开始前请保持游戏窗口可见且位置不变。鼠标移到主屏幕左上角可紧急停止。').pack() + bar = ttk.Frame(self) + bar.pack(pady=14) + self.buttons = [] + for name, cmd in [('校准游戏区域', self.capture), ('导入截图校准', self.import_image), ('预览流程', lambda: self.launch(True)), ('执行领取', lambda: self.launch(False))]: + button = ttk.Button(bar, text=name, command=cmd) + button.pack(side='left', padx=4) + self.buttons.append(button) + ttk.Button(self, text='停止', command=self.stop.set).pack() + ttk.Label(self, textvariable=self.status, wraplength=680).pack(pady=10) + self.output = tk.Text(self, height=16, wrap='word', state='disabled') + self.output.pack(fill='both', expand=True, padx=16, pady=12) + self.protocol('WM_DELETE_WINDOW', self.close) + config = LOCAL / 'region.json' + if config.exists(): + try: + self.region = Region(**json.loads(config.read_text())) + self.status.set('已加载游戏区域;移动游戏窗口或更换屏幕后请重新校准。') + except (ValueError, TypeError): + pass + self.after(100, self.poll) + + def log(self, text): + self.events.put(('log', text)) + + def set_region(self, region): + LOCAL.mkdir(exist_ok=True) + (LOCAL / 'region.json').write_text(json.dumps(asdict(region)), encoding='utf-8') + self.region = region + self.status.set('校准完成。先预览,确认首页识别通过后再执行。') + + def capture(self): + self.iconify() + self.after(1500, self.take_capture) + + def take_capture(self): + try: + screenshot = Desktop().screenshot() + except Exception as exc: + self.deiconify() + messagebox.showerror('截图失败', '请检查系统屏幕录制权限。\n' + str(exc)) + return + self.deiconify() + Selector(self, screenshot, self.set_region) + + def import_image(self): + name = filedialog.askopenfilename(filetypes=[('主屏幕完整截图', '*.png *.jpg *.jpeg')]) + if name: + with Image.open(name) as image: + Selector(self, image.convert('RGB').copy(), self.set_region) + + def launch(self, dry_run): + if self.busy: + return + if self.region is None: + messagebox.showinfo('需要校准', '请先校准游戏内容区域。') + return + self.busy = True + self.stop.clear() + for button in self.buttons: + button.configure(state='disabled') + self.status.set('正在预览…' if dry_run else '3 秒后执行,请将游戏放到前台。') + region = self.region + def work(): + try: + if self.stop.wait(3): + return + Runner(Desktop(), self.matcher, region, self.log, self.stop).run(self.flow, dry_run) + except Exception as exc: + self.log('流程停止:' + str(exc)) + finally: + self.events.put(('done', None)) + threading.Thread(target=work, daemon=True).start() + + def poll(self): + while not self.events.empty(): + kind, value = self.events.get_nowait() + if kind == 'log': + self.output.configure(state='normal') + self.output.insert('end', value + '\n') + self.output.see('end') + self.output.configure(state='disabled') + elif kind == 'done': + self.busy = False + for button in self.buttons: + button.configure(state='normal') + self.status.set('本轮已结束,请查看日志中的实际结果。') + self.after(100, self.poll) + + def close(self): + self.stop.set() + self.destroy() + + +def main(): + App().mainloop() diff --git a/automation/engine.py b/automation/engine.py new file mode 100644 index 0000000..4b1f1ab --- /dev/null +++ b/automation/engine.py @@ -0,0 +1,169 @@ +import json +import math +import threading +import time +from dataclasses import dataclass +from pathlib import Path + +import cv2 +import numpy as np +from PIL import Image + + +class WorkflowError(RuntimeError): + pass + + +@dataclass(frozen=True) +class Region: + # Coordinates in primary-screen screenshot pixels, not mouse points. + x: int + y: int + width: int + height: int + + def validate(self, screen_size): + if min(self.x, self.y) < 0 or min(self.width, self.height) <= 0: + raise WorkflowError('请先框选有效的游戏内容区域。') + if self.x + self.width > screen_size[0] or self.y + self.height > screen_size[1]: + raise WorkflowError('屏幕尺寸已变化,请重新校准游戏区域。') + if abs(self.width / self.height - 562 / 1002) > 0.035: + raise WorkflowError('区域比例不符合竖屏游戏,请只框选游戏内容,不包含标题栏。') + + def mouse_point(self, point, screenshot_size, mouse_size): + if len(point) != 2 or not all(math.isfinite(v) and 0 < v < 1 for v in point): + raise WorkflowError('点击位置必须位于游戏区域内部。') + self.validate(screenshot_size) + return ( + round((self.x + point[0] * self.width) * mouse_size[0] / screenshot_size[0]), + round((self.y + point[1] * self.height) * mouse_size[1] / screenshot_size[1]), + ) + + +class Matcher: + def __init__(self, assets): + self.assets = Path(assets) + self.cache = {} + + def frame(self, screenshot, region): + region.validate(screenshot.size) + crop = screenshot.crop((region.x, region.y, region.x + region.width, region.y + region.height)) + return np.asarray(crop.convert('RGB').resize((562, 1002), Image.Resampling.LANCZOS)) + + def score(self, frame, spec): + name = spec['template'] + if name not in self.cache: + p = self.assets / name + if not p.is_file(): + raise WorkflowError('缺少识别模板:' + name) + self.cache[name] = np.asarray(Image.open(p).convert('RGB')) + template = self.cache[name] + x1, y1, x2, y2 = spec['search'] + area = frame[y1:y2, x1:x2] + h, w = template.shape[:2] + if area.shape[0] < h or area.shape[1] < w: + raise WorkflowError('模板尺寸超出识别区域。') + result = cv2.matchTemplate(area, template, cv2.TM_CCOEFF_NORMED) + _, score, _, (x, y) = cv2.minMaxLoc(result) + candidate = area[y:y + h, x:x + w] + # Correlation alone can mistake a darkened background behind a popup + # for the active home page. Also require similar absolute colors. + difference = float(np.abs(candidate.astype(float) - template.astype(float)).mean()) + if difference > spec.get('max_color_difference', 28): + return 0.0 + return float(score) if math.isfinite(score) else 0.0 + + def matches(self, frame, specs): + return all(self.score(frame, s) >= s.get('threshold', 0.86) for s in specs) + + +class Desktop: + def __init__(self): + import pyautogui + self.api = pyautogui + self.api.FAILSAFE = True + self.api.PAUSE = 0.1 + + def screenshot(self): + return self.api.screenshot() + + def click(self, point, region, screenshot_size): + from .permissions import mouse_permission_granted + if not mouse_permission_granted(): + raise WorkflowError( + 'macOS 辅助功能权限未授予,无法发送有效鼠标点击。' + '请在系统设置 → 隐私与安全性 → 辅助功能中启用运行程序的宿主' + '(如 ChatGPT/Codex 或终端),重启宿主后再测试。' + ) + x, y = region.mouse_point(point, screenshot_size, tuple(self.api.size())) + self.api.moveTo(x, y, duration=0.2) + self.api.mouseDown() + try: + # The game did not react to instant down/up in the live test. + time.sleep(0.2) + finally: + # Always release, including if the pointer moved to a failsafe + # corner during the hold. Restore the guard immediately afterward. + saved_failsafe = self.api.FAILSAFE + self.api.FAILSAFE = False + try: + self.api.mouseUp() + finally: + self.api.FAILSAFE = saved_failsafe + + +class Runner: + def __init__(self, desktop, matcher, region, log=print, stop=None): + self.desktop = desktop + self.matcher = matcher + self.region = region + self.log = log + self.stop = stop or threading.Event() + + def check_stop(self): + if self.stop.is_set(): + raise WorkflowError('已停止。') + + def wait_state(self, specs, timeout=10, absent=False): + end = time.monotonic() + timeout + stable = 0 + while time.monotonic() < end: + self.check_stop() + screenshot = self.desktop.screenshot() + frame = self.matcher.frame(screenshot, self.region) + matched = self.matcher.matches(frame, specs) + stable = stable + 1 if matched != absent else 0 + if stable >= 2: + return screenshot + self.stop.wait(0.35) + raise WorkflowError('等待页面状态超时,流程停止,请检查当前界面。') + + def run(self, flow, dry_run=False): + self.check_stop() + # A dry run validates the CURRENT page and lists later actions; + # it cannot verify pages that have not actually been opened. + initial = self.wait_state(flow['steps'][0]['expect']) + if dry_run: + for step in flow['steps']: + self.log('计划:' + step['name']) + self.log('预览完成:首页识别通过,后续页面尚未验证;未执行点击。') + return + for step in flow['steps']: + self.log('检查:' + step['name']) + screenshot = self.wait_state(step['expect'], step.get('timeout', 10)) + self.check_stop() + if 'click' in step: + # One click only; never retry a claim after an uncertain result. + self.desktop.click(step['click'], self.region, screenshot.size) + self.log('已点击:' + step['name']) + if 'wait_absent' in step: + self.wait_state(step['wait_absent'], step.get('timeout', 10), absent=True) + if step.get('settle'): + self.stop.wait(step['settle']) + self.check_stop() + self.wait_state(flow['finish']) + self.log(flow['result']) + + +def load_flow(path): + return json.loads(Path(path).read_text(encoding='utf-8')) diff --git a/automation/offers.py b/automation/offers.py new file mode 100644 index 0000000..e9ff50b --- /dev/null +++ b/automation/offers.py @@ -0,0 +1,111 @@ +"""Verified offer-claim workflow, with an explicit already-claimed branch.""" +import time + +from .engine import Desktop, Region, WorkflowError +from .permissions import mouse_permission_granted + + +class MacGameDesktop(Desktop): + def window_bounds(self): + import Quartz + windows = Quartz.CGWindowListCopyWindowInfo( + Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID) + matches = [w for w in windows + if w.get('kCGWindowName') == '永远的蔚蓝星球' + and w.get('kCGWindowOwnerName') == '微信'] + if len(matches) != 1: + raise WorkflowError('请在微信中正常打开一个游戏窗口,不要直接启动 WeApp。') + return dict(matches[0]['kCGWindowBounds']) + + def region_for(self, screenshot): + b = self.window_bounds() + width, height = self.api.size() + sx, sy = screenshot.width / width, screenshot.height / height + # This observed WeChat header is 44 logical points high. Expanded + # sidebars change aspect ratio and are rejected by Region.validate. + region = Region(round(b['X'] * sx), round((b['Y'] + 44) * sy), + round(b['Width'] * sx), round((b['Height'] - 44) * sy)) + region.validate(screenshot.size) + return region + + def activate(self): + if not mouse_permission_granted(): + raise WorkflowError('请先为运行宿主授予 macOS 辅助功能权限。') + b = self.window_bounds() + self.region_for(self.screenshot()) + # Focus with a click in the observed empty part of the header, so + # the first game-button click is not consumed by window activation. + self.api.moveTo(b['X'] + 80, b['Y'] + 22, duration=0.2) + self.api.mouseDown() + try: + time.sleep(0.2) + finally: + saved = self.api.FAILSAFE + self.api.FAILSAFE = False + try: + self.api.mouseUp() + finally: + self.api.FAILSAFE = saved + time.sleep(0.3) + + +class OfferRunner: + def __init__(self, desktop, matcher, flow, region_provider, log=print, timeout=12): + self.desktop = desktop + self.matcher = matcher + self.flow = flow + self.region_provider = region_provider + self.log = log + self.timeout = timeout + + def wait(self, *names): + deadline = time.monotonic() + self.timeout + previous, count = None, 0 + while time.monotonic() < deadline: + screenshot = self.desktop.screenshot() + region = self.region_provider(screenshot) + frame = self.matcher.frame(screenshot, region) + detected = next((name for name in names + if self.matcher.matches(frame, self.flow['states'][name])), None) + count = count + 1 if detected and detected == previous else 1 if detected else 0 + previous = detected + if count >= 2: + return detected, screenshot, region + time.sleep(0.25) + raise WorkflowError('未识别到预期页面 ' + '/'.join(names) + ',已停止,不重复领取。') + + def click(self, action, observation): + _, screenshot, region = observation + # Stop on movement after the recognition frame instead of clicking + # stale coordinates. A future run can re-locate the moved window. + if self.region_provider(screenshot) != region: + raise WorkflowError('识别后游戏窗口移动,已停止,请重新运行。') + self.desktop.click(self.flow['clicks'][action], region, screenshot.size) + self.log('已执行:' + action) + + def run(self, dry_run=False): + home = self.wait('home') + if dry_run: + self.log('首页识别通过。计划:特惠 → 一键领取 → 关闭奖励 → 验证按钮变灰 → 返回首页。未点击。') + return 'preview' + self.click('open', home) + offer = self.wait('available', 'claimed', 'menu') + if offer[0] == 'menu': + self.click('battlepass', offer) + offer = self.wait('available', 'claimed') + if offer[0] == 'claimed': + self.log('一键领取已变灰,当前没有可领取奖励;跳过领取。') + self.click('back', offer) + self.wait('home') + self.log('已确认返回首页。') + return 'already_claimed' + self.click('claim', offer) + reward = self.wait('reward') + self.log('已确认“恭喜获得”奖励弹框。') + self.click('dismiss', reward) + completed = self.wait('claimed') + self.log('已确认一键领取按钮变灰。') + self.click('back', completed) + self.wait('home') + self.log('领取完成,已确认返回首页。') + return 'claimed' diff --git a/automation/permissions.py b/automation/permissions.py new file mode 100644 index 0000000..6cfce3c --- /dev/null +++ b/automation/permissions.py @@ -0,0 +1,15 @@ +"""Read-only permission checks; never open prompts or change system settings.""" +import ctypes +import sys + + +def mouse_permission_granted(): + if sys.platform != 'darwin': + return True + framework = ctypes.CDLL('/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices') + framework.AXIsProcessTrusted.restype = ctypes.c_bool + return bool(framework.AXIsProcessTrusted()) + + +if __name__ == '__main__': + print('辅助功能权限已授予' if mouse_permission_granted() else '辅助功能权限未授予') diff --git a/claim_offer.py b/claim_offer.py new file mode 100644 index 0000000..c9317db --- /dev/null +++ b/claim_offer.py @@ -0,0 +1,51 @@ +"""Run from the project root: python claim_offer.py [--dry-run].""" +import argparse +import json +import sys +import time +from pathlib import Path + +from automation.engine import Desktop, Matcher, Region, WorkflowError, load_flow +from automation.offers import MacGameDesktop, OfferRunner + +ROOT = Path(__file__).resolve().parent + + +def main(): + parser = argparse.ArgumentParser(description='首页 → 特惠战令 → 一键领取 → 返回首页') + parser.add_argument('--dry-run', action='store_true', help='只验证首页并显示计划,不点击') + parser.add_argument('--region', nargs=4, type=int, metavar=('X', 'Y', 'WIDTH', 'HEIGHT'), + help='游戏内容区域在主屏幕截图中的像素坐标,不含标题栏') + args = parser.parse_args() + try: + if sys.platform == 'darwin' and not args.region: + desktop = MacGameDesktop() + provider = desktop.region_for + if not args.dry_run: + desktop.activate() + else: + desktop = Desktop() + if args.region: + region = Region(*args.region) + else: + config = ROOT / 'local/region.json' + if not config.is_file(): + raise WorkflowError('请先用 main.py 校准游戏区域,或提供 --region。') + region = Region(**json.loads(config.read_text(encoding='utf-8'))) + provider = lambda screenshot: region + print('3 秒后开始;请保持游戏在前台。鼠标移到主屏幕左上角可紧急停止。', flush=True) + time.sleep(3) + runner = OfferRunner(desktop, Matcher(ROOT / 'assets'), + load_flow(ROOT / 'workflows/offer_claim.json'), provider) + runner.run(args.dry_run) + return 0 + except KeyboardInterrupt: + print('已停止。', file=sys.stderr) + return 130 + except Exception as exc: + print('流程停止:' + str(exc), file=sys.stderr) + return 1 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/docs/261004_1607-首页功能与日常任务梳理.md b/docs/261004_1607-首页功能与日常任务梳理.md index 46075f0..9264047 100644 --- a/docs/261004_1607-首页功能与日常任务梳理.md +++ b/docs/261004_1607-首页功能与日常任务梳理.md @@ -1,7 +1,7 @@ --- created_at: 2026-10-04 16:07:56 CST type: 永远的蔚蓝星球首页、商城、领地与公会入口、日常任务与自动化适配记录 -revision: 第5次 +revision: 第7次 revisions: - time: 2026-10-04 16:07:56 CST version: 第1次 @@ -23,18 +23,34 @@ revisions: version: 第5次 author: GPT-6 content: 补充公会八个入口及位置,右下角红色角色入口暂标名称待确认,记录用户确认各入口均有固定自动化流程。 -summary: 记录首页日常入口、商城六个区域、领地八个栏目及公会八个入口。商城关卡礼包与钻石暂不处理;领地和公会各入口均有用户确认的固定流程,具体步骤、消耗和完成判定待完善,公会右下角红色角色入口名称待确认。 + - time: 2026-10-04 16:47:04 CST + version: 第6次 + author: GPT-6 + content: 补充用户确认的入口红点规则,并记录先本地实现流程、验证后再打包为独立程序的开发顺序。 + - time: 2026-10-04 17:45:37 CST + version: 第7次 + author: GPT-6 + content: 实机完成特惠赏金战令一键领取及返回首页,新增脚本、奖励结果和已领取状态的识别逻辑。 +summary: 记录首页、商城、领地和公会的日常任务入口及待完善流程。入口红点表示内部有奖励可领取。特惠赏金战令一键领取已实机验证并写成脚本;开发先验证本地流程,再打包独立程序,商城关卡礼包和钻石暂不处理。 --- # 首页、商城、领地与公会功能及日常任务梳理 ## 记录范围与依据 -本记录针对《永远的蔚蓝星球》的首页、商城、领地与公会,依据用户对高级账号的说明、两张首页截图、八张商城截图、一张领地截图及一张公会截图。当前阶段先熟悉页面和任务流程,再决定自动化方案;尚未编写程序,也未验证各活动内部操作。 +本记录针对《永远的蔚蓝星球》的首页、商城、领地与公会,依据用户对高级账号的说明、两张首页截图、八张商城截图、一张领地截图及一张公会截图。当前已开始本地自动化原型开发;各活动内部流程继续逐项补充和实机验证。 主界面截图显示账号等级 95,当前关卡为“144. 寒霜之径”,底部导航包括商城、英雄、国王、领地、公会。这里描述的页面统一称为“首页”;底部“国王”是独立入口,其内部功能待后续梳理。此前低等级账号的功能锁定情况不代表当前账号。 -下文区分三类信息:**用户确认**的规律、**截图可见**的内容,以及**待验证**的内部流程。入口带红点只能作为检查线索,目前不能直接判定为可领取奖励。 +下文区分三类信息:**用户确认**的规律、**截图可见**的内容,以及**待验证**的内部流程。 + +## 通用规则与开发顺序 + +用户确认:**当某个按钮进入的页面内有奖励可以领取时,按钮右上角会出现红点。** 红点可作为进入该页面领取奖励的依据;进入后仍需识别具体奖励按钮与领取条件,领取成功通过内部状态变化确认。红点消失可以作为观察线索,但页面内仍可能存在其他奖励,不能只凭一次点击判定整页任务完成。没有红点时是否能跳过整页,需结合具体栏目流程确认。 + +用户确定的开发顺序:**先在本地实现部分自动化流程,逐项验证后再打包成独立程序**,最终目标仍是 macOS 与 Windows。模型 Key 由用户以后在程序设置中填写。 + +第一条本地原型流程为“小金猪 → 挂机奖励 → 领取 → 返回首页”,使用局部截图模板识别,执行前校准游戏区域,提供预览、日志和停止功能。当前不执行快速扫荡,也尚未加入红点检测或模型 API。奖励到账数量、领取后弹框和双平台操作需实机验证;超时停止,不重复领取。启动方法与验证命令见项目 README。 ## 首页 @@ -43,7 +59,7 @@ summary: 记录首页日常入口、商城六个区域、领地八个栏目及 | 入口 | 出现规律(用户确认) | 功能和日常逻辑(用户确认) | 后续需要核实 | | --- | --- | --- | --- | | 金沙游记 | 季节性大活动,不定期出现;每次名称可能不同;可有两个大活动同时出现,入口都在左上角 | 进入后包含多个分活动 | 分活动清单、各任务入口、签到与领奖步骤、完成标志 | -| 特惠 | 固定入口 | 内部有每天按固定流程领取的奖励 | 免费奖励位置、领取顺序、领取后状态、跨日重置规律 | +| 特惠 | 固定入口 | 内部有每天按固定流程领取的奖励;当前赏金战令一键领取已实机验证 | 其他子页面、跨日重置规律及活动更换后的样式 | | 活动 | 固定入口 | 内部有每天按固定流程领取的奖励 | 子页面结构、领取顺序、领取后状态、跨日重置规律 | | 返利 | 当前左侧存在,是否固定尚未说明 | 内部功能尚未说明 | 领取条件、是否与充值相关、是否存在日常免费内容 | | 观影 | 不定期出现 | 出现后流程固定,按按钮操作即可 | 按钮顺序、是否涉及广告播放、等待时间、领奖和结束状态 | @@ -53,6 +69,22 @@ summary: 记录首页日常入口、商城六个区域、领地八个栏目及 截图中左侧入口为纵向排列,较下方按钮部分受折叠区域影响。后续需要确认列表的展开、收起或滚动方式,不能假定所有入口始终同时可见或保持同一位置。 +### 特惠战令一键领取(已实机验证) + +2026-10-04 在当前账号实际执行并观察到完整流程:**首页 → 左侧特惠 → 赏金战令 → 一键领取 → 恭喜获得弹框 → 点击空白处关闭 → 一键领取按钮变灰、已领奖励出现绿色勾选 → 左下角返回 → 首页**。 + +对应脚本为项目根目录的 `claim_offer.py`,步骤与页面模板配置保存在 `workflows/offer_claim.json`。在项目目录运行: + +```bash +.venv/bin/python claim_offer.py +``` + +脚本连续两次识别预期页面后才点击。领取后必须识别奖励弹框和灰色领取按钮,最后确认首页。如果进入时按钮已经变灰,则跳过领取并返回首页;页面不匹配或超时会停止,不重复尝试领取。可用 `--dry-run` 只验证首页并显示计划。 + +特惠也可能默认打开“超值月卡”;脚本识别该页后点击底部“战令”,再进入领取流程。完整脚本复跑已验证从月卡切换战令、识别已领取并返回首页,16 项自动化测试通过。 + +当前流程只覆盖本次“赏金战令”奖励,不购买付费战令,也不遍历其他特惠子页面。macOS 使用 PyAutoGUI 系统鼠标事件,读取微信游戏窗口位置,排除标题栏并处理 Retina 缩放;窗口在识别后移动会停止。Windows 的校准区域入口已提供,仍需在 Windows 实机验证。跨日刷新、活动更换和不同尺寸下的模板适配后续完善。 + ### 右侧与右上方入口 | 区域 | 入口 | 出现规律与用途 | 当前记录状态 | diff --git a/main.py b/main.py new file mode 100644 index 0000000..7de4f38 --- /dev/null +++ b/main.py @@ -0,0 +1,4 @@ +from automation.app import main + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3068d0a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +Pillow>=10.4,<12 +numpy>=1.26,<3 +opencv-python-headless>=4.10,<5 +PyAutoGUI==0.9.54 +pyobjc-core>=10,<12; sys_platform == "darwin" +pyobjc-framework-quartz>=10,<12; sys_platform == "darwin" diff --git a/tests/test_engine.py b/tests/test_engine.py new file mode 100644 index 0000000..8a0adc1 --- /dev/null +++ b/tests/test_engine.py @@ -0,0 +1,126 @@ +import threading +import unittest +from unittest.mock import Mock, patch +from pathlib import Path + +import numpy as np +from PIL import Image + +from automation.engine import Desktop, Matcher, Region, Runner, WorkflowError, load_flow + +ROOT = Path(__file__).resolve().parent.parent + + +class FakeDesktop: + def __init__(self, images): + self.images = images + self.index = 0 + self.clicks = [] + + def screenshot(self): + return self.images[min(self.index, len(self.images) - 1)] + + def click(self, point, region, size): + self.clicks.append(point) + self.index += 1 + + +class EngineTests(unittest.TestCase): + def setUp(self): + self.matcher = Matcher(ROOT / 'assets') + self.flow = load_flow(ROOT / 'workflows/pig_claim.json') + self.region = Region(0, 0, 562, 1002) + + def state(self, specs): + image = Image.new('RGB', (562, 1002), (45, 62, 78)) + for spec in specs: + template = Image.open(ROOT / 'assets' / spec['template']).convert('RGB') + x, y, _, _ = spec['search'] + image.paste(template, (x + 5, y + 5)) + return image + + def test_retina_coordinate_mapping(self): + region = Region(400, 100, 562, 1002) + self.assertEqual(region.mouse_point((0.5, 0.5), (2000, 1400), (1000, 700)), (340, 300)) + + def test_invalid_region_and_click(self): + with self.assertRaises(WorkflowError): + Region(0, 0, 800, 400).validate((1000, 1000)) + with self.assertRaises(WorkflowError): + self.region.mouse_point((1.5, 0.5), (562, 1002), (562, 1002)) + + def test_home_not_detected_behind_dark_popup(self): + spec = self.flow['finish'] + frame = self.matcher.frame(self.state(spec), self.region) + self.assertTrue(self.matcher.matches(frame, spec)) + self.assertFalse(self.matcher.matches((frame * 0.45).astype(np.uint8), spec)) + + def test_claim_workflow_and_no_sweep(self): + home = self.state(self.flow['finish']) + modal = self.state(self.flow['steps'][1]['expect']) + outcome = Image.new('RGB', (562, 1002), (80, 90, 120)) + desktop = FakeDesktop([home, modal, outcome, home]) + logs = [] + Runner(desktop, self.matcher, self.region, logs.append).run(self.flow) + self.assertEqual(desktop.clicks, [[0.135, 0.607], [0.747, 0.738], [0.5, 0.875]]) + self.assertIn('到账数量尚未验证', logs[-1]) + + def test_preview_does_not_click(self): + desktop = FakeDesktop([self.state(self.flow['finish'])]) + Runner(desktop, self.matcher, self.region, lambda _: None).run(self.flow, True) + self.assertEqual(desktop.clicks, []) + + def test_unchanged_claim_stops_without_retry(self): + home = self.state(self.flow['finish']) + modal = self.state(self.flow['steps'][1]['expect']) + desktop = FakeDesktop([home, modal, modal]) + steps = [dict(step) for step in self.flow['steps']] + steps[1]['timeout'] = 0.8 + flow = dict(self.flow, steps=steps) + with self.assertRaises(WorkflowError): + Runner(desktop, self.matcher, self.region, lambda _: None).run(flow) + self.assertEqual(desktop.clicks, [[0.135, 0.607], [0.747, 0.738]]) + + def test_unrecognized_screen_stops_without_click(self): + desktop = FakeDesktop([Image.new('RGB', (562, 1002))]) + flow = dict(self.flow, steps=[dict(self.flow['steps'][0], timeout=0.05)]) + runner = Runner(desktop, self.matcher, self.region, lambda _: None) + # Directly check the same initial guard with a short timeout. + with self.assertRaises(WorkflowError): + runner.wait_state(flow['steps'][0]['expect'], timeout=0.05) + self.assertEqual(desktop.clicks, []) + + def test_stop_before_actions(self): + stop = threading.Event() + stop.set() + desktop = FakeDesktop([]) + with self.assertRaises(WorkflowError): + Runner(desktop, self.matcher, self.region, stop=stop).run(self.flow) + self.assertEqual(desktop.clicks, []) + + def test_permission_denied_blocks_mouse_event(self): + desktop = Desktop.__new__(Desktop) + desktop.api = Mock() + with patch('automation.permissions.mouse_permission_granted', return_value=False): + with self.assertRaisesRegex(WorkflowError, '辅助功能权限未授予'): + desktop.click((0.5, 0.5), self.region, (562, 1002)) + desktop.api.click.assert_not_called() + desktop.api.mouseDown.assert_not_called() + + def test_held_click_releases_after_interruption(self): + desktop = Desktop.__new__(Desktop) + desktop.api = Mock() + desktop.api.size.return_value = (562, 1002) + desktop.api.FAILSAFE = True + with patch('automation.permissions.mouse_permission_granted', return_value=True): + with patch('automation.engine.time.sleep', side_effect=RuntimeError('interrupted')): + with self.assertRaises(RuntimeError): + desktop.click((0.5, 0.5), self.region, (562, 1002)) + desktop.api.moveTo.assert_called_once_with(281, 501, duration=0.2) + desktop.api.mouseDown.assert_called_once() + desktop.api.mouseUp.assert_called_once() + self.assertTrue(desktop.api.FAILSAFE) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_offers.py b/tests/test_offers.py new file mode 100644 index 0000000..fb3d6c6 --- /dev/null +++ b/tests/test_offers.py @@ -0,0 +1,82 @@ +import unittest +from pathlib import Path + +from PIL import Image + +from automation.engine import Matcher, Region, WorkflowError, load_flow +from automation.offers import OfferRunner + +ROOT = Path(__file__).resolve().parent.parent + + +class OfferDesktop: + def __init__(self, images): + self.images = images + self.index = 0 + self.clicks = [] + + def screenshot(self): + return self.images[min(self.index, len(self.images) - 1)] + + def click(self, point, region, size): + self.clicks.append(point) + self.index += 1 + + +class OfferTests(unittest.TestCase): + def setUp(self): + self.flow = load_flow(ROOT / 'workflows/offer_claim.json') + self.matcher = Matcher(ROOT / 'assets') + self.region = Region(0, 0, 562, 1002) + + def image(self, name): + image = Image.new('RGB', (562, 1002), (45, 62, 78)) + for spec in self.flow['states'][name]: + template = Image.open(ROOT / 'assets' / spec['template']).convert('RGB') + x, y, _, _ = spec['search'] + image.paste(template, (x + 5, y + 5)) + return image + + def runner(self, names): + desktop = OfferDesktop([self.image(n) for n in names]) + runner = OfferRunner(desktop, self.matcher, self.flow, lambda _: self.region, + log=lambda _: None, timeout=0.8) + return desktop, runner + + def test_full_claim_and_return(self): + desktop, runner = self.runner(['home', 'available', 'reward', 'claimed', 'home']) + self.assertEqual(runner.run(), 'claimed') + self.assertEqual(desktop.clicks, [self.flow['clicks'][n] for n in ('open', 'claim', 'dismiss', 'back')]) + + def test_already_claimed_does_not_claim_again(self): + desktop, runner = self.runner(['home', 'claimed', 'home']) + self.assertEqual(runner.run(), 'already_claimed') + self.assertEqual(desktop.clicks, [self.flow['clicks'][n] for n in ('open', 'back')]) + + def test_monthcard_entry_selects_battlepass(self): + desktop, runner = self.runner(['home', 'menu', 'claimed', 'home']) + self.assertEqual(runner.run(), 'already_claimed') + self.assertEqual(desktop.clicks, [self.flow['clicks'][n] for n in ('open', 'battlepass', 'back')]) + + def test_missing_reward_stops_without_retry(self): + desktop, runner = self.runner(['home', 'available', 'available']) + with self.assertRaises(WorkflowError): + runner.run() + self.assertEqual(desktop.clicks, [self.flow['clicks'][n] for n in ('open', 'claim')]) + + def test_preview_never_clicks(self): + desktop, runner = self.runner(['home']) + self.assertEqual(runner.run(True), 'preview') + self.assertEqual(desktop.clicks, []) + + def test_window_movement_blocks_stale_click(self): + desktop, runner = self.runner(['home']) + observation = ('home', desktop.screenshot(), self.region) + runner.region_provider = lambda _: Region(2, 0, 562, 1002) + with self.assertRaises(WorkflowError): + runner.click('open', observation) + self.assertEqual(desktop.clicks, []) + + +if __name__ == '__main__': + unittest.main() diff --git a/workflows/offer_claim.json b/workflows/offer_claim.json new file mode 100644 index 0000000..ddda936 --- /dev/null +++ b/workflows/offer_claim.json @@ -0,0 +1,128 @@ +{ + "name": "特惠战令一键领取", + "states": { + "home": [ + { + "template": "home.png", + "search": [ + 150, + 730, + 410, + 865 + ] + } + ], + "available": [ + { + "template": "offer_page.png", + "search": [ + 20, + 20, + 320, + 125 + ] + }, + { + "template": "offer_claim.png", + "search": [ + 20, + 795, + 195, + 885 + ] + } + ], + "claimed": [ + { + "template": "offer_page.png", + "search": [ + 20, + 20, + 320, + 125 + ] + }, + { + "template": "offer_claimed.png", + "search": [ + 10, + 790, + 190, + 892 + ] + }, + { + "template": "offer_back.png", + "search": [ + 15, + 905, + 125, + 985 + ] + } + ], + "reward": [ + { + "template": "offer_reward.png", + "search": [ + 130, + 235, + 440, + 355 + ], + "threshold": 0.8 + }, + { + "template": "offer_dismiss.png", + "search": [ + 160, + 625, + 410, + 705 + ] + } + ], + "menu": [ + { + "template": "offer_monthcard.png", + "search": [ + 150, + 0, + 430, + 110 + ] + }, + { + "template": "offer_back.png", + "search": [ + 15, + 905, + 125, + 985 + ] + } + ] + }, + "clicks": { + "open": [ + 0.08, + 0.233 + ], + "claim": [ + 0.178, + 0.839 + ], + "dismiss": [ + 0.5, + 0.664 + ], + "back": [ + 0.12, + 0.94 + ], + "battlepass": [ + 0.49, + 0.94 + ] + } +} diff --git a/workflows/pig_claim.json b/workflows/pig_claim.json new file mode 100644 index 0000000..3a5364f --- /dev/null +++ b/workflows/pig_claim.json @@ -0,0 +1,28 @@ +{ + "name": "小金猪挂机奖励领取", + "steps": [ + { + "name": "首页 → 打开小金猪", + "expect": [{"template": "home.png", "search": [150, 730, 410, 865]}], + "click": [0.135, 0.607] + }, + { + "name": "挂机奖励 → 领取(不执行快速扫荡)", + "expect": [ + {"template": "idle_title.png", "search": [170, 285, 390, 365]}, + {"template": "claim.png", "search": [300, 680, 535, 805]} + ], + "click": [0.747, 0.738], + "wait_absent": [{"template": "claim.png", "search": [300, 680, 535, 805]}], + "settle": 1.0 + }, + { + "name": "关闭奖励弹框并返回首页", + "expect": [], + "click": [0.5, 0.875], + "settle": 0.7 + } + ], + "finish": [{"template": "home.png", "search": [150, 730, 410, 865]}], + "result": "已点击领取,领取按钮状态已变化,并确认返回首页。奖励到账数量尚未验证,请首次实机运行时核对。" +}