Q8NddN^>b$}*PU@@*bRk5yDOcc{JD~g$E zB2qvmkV+YmZnR{Yg7-`#h=HfUWQuM?qVRK$00r+~5E{H^XdnjMwD0RvN09DU;H;1U zTS0mXU=+VHT8B#U*Q1M~zXZi10QgVDccJ;D6ndPFrR3(d^mL)7Sc9t2VB7ytf*=t8 EAE5b>asU7T delta 4308 zcmZ`+4RBM}vA*ZtE7|(9B+HV2F~UFC3dTP%0UCbHPl$iO1lt&6j4`q#6B+r3b7f+P zj9p$rWQN3sb%8t*0u-JJBm=3^JSGVV#9`V@I<1Lcc|s*6w6+7C_gZ0^Her&sXRmBX zXWBcX@9x>%vv+sTo;^pyA%6b|AM>eBr{>u6-RHwk*A2WC6UP@AT&5=8X?#vRivsp+ zh;s?f9B1rvueNldVVKdUk&GJR);%M_R`{9!tuP2iY14PKq5 zh0QZx5OiK0T+N9y_{1vCtCMY`x)T1B{uGIVqKw4lag33d_ek9mkG;|5S(J6>zYg9x z{?n}b4kZ&r1|1qk7UYiielX@gKl<*Tsqs&aM_BOd3|mx4AsYZ8vz0s#G;=|I`VU6T zkNeeOUZqM1>J$H0KmDqH`my<+>1W+AnEPA)$B+?>pD}EhF*-SdP-aGQ^3p=?a+U_M zP_@h`TwbVIrWHQYDwY`!q{j#HXKp20I>7v)(-2ZYotYFE$~w)Hfq>_cPegJCin5ZS zBCpxlMy-=UrH#MiAOGPw_-o#07A0p+IaI#CJj{!_jC+F8toEzIhQQ7IS%f46G>;NO zQbAXEjSRqO;Zr0V=FC|VMbEI0Id~wi#*vwUz++9ybUnv)nR%`^!)HEAn3O$TdY_(( zK7H?u?HnchOo2@crx8V&FTPn1mvtt9Me|@{QE8Ocm+TXq+0ePzmT2>(`1HO+=OeF( zQ)UR)Pxi&ayNjohSrA |hk%!F3YJ1a~(j zz@>%Jo1MbeY>unqY+}bs=F|A0W7h3%$=&F2S;yWwI^HulzOQ$z@7(RdAC8@T?#uo? ze|rDO_$$4m7Y0Tzylc&BvU?;~mUX=M#oL2>7-#gtixK7D_w1&xF?*}wx63SK2) to)1(b{0bFs#)rV9(zlp)4sT!z|GQh ma-5L_wI=qts+%5_UEz%a7NMAvz;B-?OhBa$Tt#WFPGp3D%h+D#< zw*K49gbDm{Z9e%3rdL~yxD@m{niCt_+B`G}YpU0i-@)tES>$uLS{-NlBMKF6ud79( zS5f;r4!*9Qo
ok=|k; zVd^%+@pUQCV-exvy0WNzHc7fKLQKtc!b5INx
;+GMp~|K}uFRGZ2Qabs-? yEiRKRYCkqug4}GE`VA7 z<=0J?!|B5_3a%y<1PuwyRGb+GHoQzE$gRub GJj>!qP6r>n`{;qlU+GoGYiV8tg=61e0$ zO-tR?Ubjce{oZ!}3iH2!Nqe}ks8l5f4Y7xm`<4A((E67K^#=crV0>yrf6NV2!eJxK z_doUZ4SfRUBRObF+PfO_k$hh=Fax{e(zrwFH132ZUpPndg;12&-Qr@EOm8!(<3UJl zfM*qs?;T}dKg3}M5}`Xi28K4yxA+v!=$%a1rDT*!rlNcV3fyL$a;oQcMf;*5Piuz1 zXREZ-%5y%E{RGCm)t3VO1<}R_nkt`)@;+4+Y%7R|8{5^&F11hHtild&R!?bt&xCr) zgnLTZHD!X^p-Jbw*n{LJgQEiBYPo&yJi~PgwcInrrtMh4L}PttM&EmB?A-ge{b$CH z_1!-A-dNwk(F^a5A9!=D|HUr{&$I5#j$&-q{`0Ksz}8Jx5tGDPo&`>A8p@GR%x=b^ zA0ps^Zet1E;_~8B%hf _k)M>_n@aRuU<@{=@?I>fV!&SI-caYNHW2C$=Po`GzW`$vf1_NatWz~7om zbR0VKSJ|0xG*oTL>L#k9R|> #y?w~gW;}iO$Sv!bvnEUE`>xYwPkLYs!d{Cq7o7X$9zi{8e$jr*Yb;sBJ zc>PF9?ua4p;mj}e#;^bEB9r~WcwKA2-C(>g8Cht%P0KlMh*K@s3PT#gFmUtf-6URz z+0n`=VoEk4lD#l=Hm~3p@bF>t95IY(BIy>;ZGDx5;q2DAd?_FJ{nl%QT!eR9HW4)> zwpz&wc)YcYujFA*>!*s<%;IY3-e>_&+jZqdW`P#N%J#yvQv6gIgOKR#XlZB1h R73w{P?kZ>^Dk!N-VvQyw*xgQcRy8)$ zOpv^*_~-e+WpA-SDnQk_$54aQ)+69Og4*G=PK$B_lGot9&U|IW2e{i=!@tf4%62$K zQVi$1ij`|o=OTRDl|;%Qeplvn^eW;-JyIvoDFae=gdo)IN= zlVTcd7I`f~Q4xVeF$CI0I44JUA^BjDWR->%dwVE~MJ0c*0qrWn*&ArVh;+Kp^ca=BWm6$9&m9lh23Jw7npn yxT|1;m z*z)Sjv0zlJfcD5=z^L~!O8%fEG7)PXlZVZ2D1K=gzgU2#mkviAK&2)ET^kbu)A}xo zQ`Pwe !Q8r{e!BeD^crV zeaL?@@N}Ujglh;7Yep`{S~kNr*|jEpV@g}lj-2pvq*fESHPFPD3W3reB?-AdLf*4z zO!lf0P4G08z1R)PF1aH2(#h!CVE^GHe!CEO^Y8{< gMRE|SXm1-z z
Pq#TEU7#6tcOvv5 z>_$kzsV^fXXB{sa6vL)(5iXOXZzDO3I`a|aKNZnPAz%-ete}7Wmc^(@I6ZTMW3PJx zr! *CpdghsvsFIQ8MuaAEij1 Ou&%8{`m(UlvEIN^T6yYS)zEMGzz}YwEC;hYQ_oElG?$_aQEoOg4 VzI$VdX@#DwBL1eYIGK=R{0En8)%*Ye diff --git a/Utils/__pycache__/ControlUtils.cpython-312.pyc b/Utils/__pycache__/ControlUtils.cpython-312.pyc index e99a5257afad3eddd54ecc0781a8b6bdd34c240e..75ffa3b3046a920097ac5f42ff520008ad12accb 100644 GIT binary patch delta 1239 zcmZ`%U1$?o6ux)<(m4G~+NQRtnx 2ntYz9jL{ zU*z6guuBgmw9Y_JD16Y0&JyPWHsPznS<3&0UfwzJ@=kvG#;NJMXY8hS4?T2fa5zK; zQ$~mw%nKistaOk*mHRkFp}x|qSfLL~4gWK?MjjP~rOvvd8&!MHc20E`1*fa#gIHQ! zer#iLVPRj{PR_N8@rZ+gDMa=CNzzX5SJdG3^zVvwI7Z!-QwvYC 6UBxQPhMC!Db+|7?GNB-f++bt_3+kMdSQYK7%tT%kM@R ztccswy^Lz9YwhR%Z}R-BTbKPH?u 4SjSzP6oIT{4rvGI z0H}pUS-2SvO$jBIW33^1tsZ}Mh1rsoy34?UkMU8W8#R( zg-1=13ul>TZ+u(vqe4d`k8z@~*1uiA{e|O8s&T6|SR~1`t|}#S9mo-Ac-ZPiOis 2VgI~7hIJ~1J{w(qwJSVkO??(BT Nf&x2G|Pl6Mzmd24G$1BGp>bo))q0lJp)i^MOZ_tH?30 zI6#XnjUFdUEqRZanMpCPIG+ZV-L8an2`E7N08;erGA|yWC9U1{S0K;|(8%C($+|>R ZO~ |UWVzM?v^WeCudvJ{EJW+ z6lo{wp~$ilA}VavgD>GjJ=BZgOI!+kF^eFE67*2e`8H!+IKS^Z=lA=*?|k1qTfGl^ zwC^>VY=(Y+KCcbhg=MXW-}{PgZ8mGSYsl9x4mpLdudi?3x+C wWzQajw=9+050Ou9YNjXhHf4`Ab;`%Hj5#vS@rBFLrlEAk@GVX+$0x6F=mAG zFfn9Nv_ `>ipw9{*%5XDx?8avY!upasAK5CWJ0NLE}RLRGZ2R9SRawarko!!fGd z1WUby3nks`6i3Z2N4-RGlFFo19`dc~r4G8qFaX>E5Ft;iOOThmt9GL$ ZqP7{(W3ta?&z<7X{yE1_1}v_o%D+hH`2+v} diff --git a/Utils/__pycache__/DevDiskImageDeployer.cpython-312.pyc b/Utils/__pycache__/DevDiskImageDeployer.cpython-312.pyc index 6626122c92f5a0fc43b38ba0b30755dab519e258..984f6dbd4b95f802638525fa84c13c663c6e0fea 100644 GIT binary patch delta 20 acmexr{?(lOG%qg~0}#mGy1bG5l{5fJbOzo4 delta 20 acmexr{?(lOG%qg~0}x#DJGYVhl{5fL1_uHF diff --git a/Utils/__pycache__/JsonUtils.cpython-312.pyc b/Utils/__pycache__/JsonUtils.cpython-312.pyc index 5a87e50bc9e1a26f3ee317be7b8125e4732fea31..64e9fcf21206370a97b5ed75c911ea9b0fe7f58d 100644 GIT binary patch delta 298 zcmbOfH9wN~G%qg~0}$*MzM8ROBku(T##NK=DdaJVPWDyQkeJBSBhJE*%v8$~3?v!B zta1iT#>wXuw@YiX7BK^L7CC|lRS;nhBGiDy (1-`|A`2rGQkjf`~E@ z!2=>de*DE@lbfGXnv-f*R0-rV0&%e{kodsN$jEq`f%`TC=UoPg+YB<_+0vOA6+bb6 HNU(MQng&dY delta 371 zcmbOqIVp @$%Q)d{3* B217WS~0%7hIchKRL&vZ*!?iVvROP11tw^iKp(RJHIU7x>$0r%biP;KR!M)v2<-BULxNLVVeA#u!ZtJA&5&~iC%nt zx=wt84?#6czh`S0p-V_Y2DWet7Z`&nusP~0Tbik=jn!f;#$azarl6i$!9hBljS^=u zSI~Ei!IL}} |`#u8m!fAOjVIQ^I&<9mS() zj6H%zaa2v1A=)IrgFI2Zq(hKTf0*eWObsWq$phwA8Zje)ZGi27CO{Oh1E2$%0XqS^ zD59cS4;uQKEdUkZ2Y3K>&1R~yBE^=9nQzda`yOx392m}?9LS|I!|O-M2goh4wt4-( z9g3e*oIyp$CaE7k&}+ZU7FniNJ-5HeqndprsCkGa{k=KTEu%l0LX>l|)E#5zJihKd z?A#uXYVry8;#1R_vjr )#VSs z 1BQ(-ae)_I-IMnH$U){i)#-new>35UZnzs^yEZ z@8liuwgKXRy#N@i*-2W|V|*W#**0=X{S3QEgXZ@=QoR2tp}R_GA<{MznW xr@RF^=j) z6q`Z1Mbt@Fv`+bL*tivNkhJOne4O;@@m+DmyH}YSt{*>RsVv&E_@?a*aARffDg)9? okRS9>{~>6R0Kj81S!y`5_$M 8 z?0ULn<>Z}$RX%D!fg%wQ0doE=?%dRpjQo`N%)GSxA~BG#IFQifE0O@QwLyd)h|mWS z1|Y%^L>Pex^T`u<#3xS^a%Zyya*K5*KNf11;o_`dydk95;e1C_c6#i@*agKKM0b>4 z5q0fwyD@o|u$hA84%Hpv7aXE5#3WshNxvAAemN%dLUh&z+|MFa=fy0LDg>tpET3 diff --git a/Utils/__pycache__/ThreadManager.cpython-312.pyc b/Utils/__pycache__/ThreadManager.cpython-312.pyc index c24066db8add9ef243a9c66b53e40dcc5e8024c5..790ff8f05fd8a598f8f75ca1badd3ff9631a2349 100644 GIT binary patch delta 1139 zcmZ8fZ)jUp6u&nw|6cRb_wwS}+SVpbLf6zSqyHA{@}|uj4#8F`(h+INHZmPLDZZp+ z#jWc^uyk5muCPCytW04-ne8Y@+f`!imoib_qmfr6AA~iD{bbf5$o9p1UYrxpgWoy# zcYo*Hch9-^ePd0PKRTTih}tK$znp&6ai=n4#wqf%Ic!SdX|>**U=n8X*gS+S q~|4sr C&A6Lmj?EYa}d9oq!PUJ_wz{f)HXFdS7NpKgD5y?l1irZiDX zsR0O(5jKdM$(L*dH!c3gp1@s3l9O)cFJ3R)zFAHR1sSCc5<&owA2ch?5Vr$73(!hF zaD;IuS#+GUZD52hBX;_y*U5mBC7*HkFYr`j*gL|L=&4Uw6Sg^1d5uvDjyEh}pQDw) zXlq$NN leNlE8M W&gE}*ZeH>3e17%!`T63tkMff@@-wr4tX}I4 zRuD(!{uhi6TWPN11AwkQX_yTNxQ5b7A!(-IrKY%n+D8T|Jr@qc#tMZoRI>7{jB{d* zF8Valr;8CyjI8!&2A2mn+Bc=wa+0h|J(|>$?Tv3r#~(7-eG-q$B`Xr#Qzs_Q=7c?J z)!wYoyj5Ml#fwvY6KR9mpA|wmp-LCLn&8!iE=}lKJ(!V}r49Gy=|epm?`MU+99N}t zK8^FKerahm%k^$^E>Ga(lPj)qmyqnq7q9=YW0jd#rO&jf*}mF+e^_LJvReu9Zz_ z?YZs%b?YWpZe-RSoLtYW*IQ^zI6j8` {J!w^ORo&f-Xas WjtQ@6Fc)H0>(&F0ZdI@70?;^t)`o94P5=R68 delta 741 zcmXAmZ%7ky7{~9o+ii2ZyLq>1Gj*G8re|tRmUCg0hSiE*NTE!vl@vt;(k-(<%jiY2 zD1_%lK}N0@5ri+q!0ZpRB8Y_8;b0OYjB>uU*}D*We&=)WdA`5ro_}}0u^qpv#YI7| zGH4AA+QrSH88KxfN8p<=1Oe_m$Bq~-*&u1DfH|&{7-5s!RnGaC023#}2Es%cg3k@^ zIvZt%iDH7oCJ#I?eI)= WCYh!sjyDQMYrZm-vzzewtaa zz<_w6OvYpW$yk2+?b@5v+KYQzQ%@>p*kf<>>+{&Hd1lO1X?mPRc)ecLK_ffivE6fP zAC2EZj4^*rjJ+f)x;4?A6 gQ<1DcsQH8X;dL|>n~&)--PN7$=}q^?(mgS)>r8>>Zm^6=S~s9t_rjud zs>#MVA7LAGQ&$Vr=^1|vsPw(mZ8%=xRU2KU4SMC~k`^l4DV!8EK|Kgb*=0S1lHOT7 Q0H5RrAxxYF2BA0l4}gfrU;qFB diff --git a/requirements.txt b/requirements.txt index cfac345..95e0e58 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,13 @@ -facebook_wda==1.5.1 +easyocr==1.7.2 +facebook_wda==1.5.4 Flask==3.1.2 flask_cors==6.0.1 +lxml==6.0.2 +numpy==2.3.3 +opencv_python==4.12.0.88 +opencv_python_headless==4.12.0.88 +portalocker==3.2.0 +psutil==7.1.0 Requests==2.32.5 tidevice==0.12.10 +torch==2.8.0 diff --git a/script/ScriptManager.py b/script/ScriptManager.py index ea40c30..de798d8 100644 --- a/script/ScriptManager.py +++ b/script/ScriptManager.py @@ -1,5 +1,7 @@ +import atexit import random import re +import subprocess import threading import time from enum import Enum @@ -11,6 +13,7 @@ from Utils.IOSAIStorage import IOSAIStorage from Utils.JsonUtils import JsonUtils from Utils.LogManager import LogManager from Entity.Variables import anchorList, removeModelFromAnchorList, anchorWithSession +# from Utils.OCRUtils import OCRUtils from Utils.Requester import Requester import Entity.Variables as ev @@ -42,8 +45,11 @@ class ScriptManager(): # 关闭并重新打开 TikTok ControlUtils.closeTikTok(session, udid) + event.wait(timeout=1) + ControlUtils.openTikTok(session, udid) + event.wait(timeout=3) LogManager.method_info("养号重启tiktok", "养号", udid) AiUtils.makeUdidDir(udid) @@ -124,7 +130,7 @@ class ScriptManager(): ControlUtils.clickLike(session, udid) LogManager.method_info("继续观看视频", "养号", udid) - videoTime = random.randint(10, 30) + videoTime = random.randint(25, 40) for _ in range(videoTime): if event.is_set(): break @@ -137,9 +143,9 @@ class ScriptManager(): else: nextTime = random.randint(1, 5) for _ in range(nextTime): - if event.is_set(): - break - event.wait(timeout=1) + if event.is_set(): + break + event.wait(timeout=1) client.swipe_up() except Exception as e: @@ -172,7 +178,9 @@ class ScriptManager(): # 2) 进入直播 (使用英文) live_button = session( - xpath='//XCUIElementTypeButton[@name="直播"] | //XCUIElementTypeOther[@name="直播"]') + xpath='//XCUIElementTypeButton[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"] ' + '| //XCUIElementTypeOther[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]' + ) if live_button.exists: live_button.click() @@ -186,7 +194,12 @@ class ScriptManager(): break event.wait(timeout=1) - live_button = session(xpath='//XCUIElementTypeButton[@name="直播"]') + # live_button = session(xpath='//XCUIElementTypeButton[@name="直播"]') + live_button = session( + xpath='//XCUIElementTypeButton[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"] ' + '| //XCUIElementTypeOther[@name="LIVE" or @label="LIVE" or @name="直播" or @label="直播"]' + ) + if live_button.exists: continue @@ -880,14 +893,26 @@ class ScriptManager(): print("greetNewFollowers方法执行完毕") + + # 检测消息 def replyMessages(self, udid, event): - client = wda.USBClient(udid) - session = client.session() + + try: + client = wda.USBClient(udid) + session = client.session() + except Exception as e: + LogManager.method_error(f"创建wda会话异常: {e}", "检测消息", udid) + return + + LogManager.method_info("开始重启tiktok", "监控消息") ControlUtils.closeTikTok(session, udid) event.wait(timeout=2) + # time.sleep(1) ControlUtils.openTikTok(session, udid) event.wait(timeout=3) + # time.sleep(1) + LogManager.method_info("重启tiktok成功", "监控消息") while not event.is_set(): try: @@ -895,16 +920,344 @@ class ScriptManager(): self.monitorMessages(session, udid, event) except Exception as e: LogManager.method_error(f"监控消息 出现异常: {e},重新启动监控直播", "检测消息", udid) + + LogManager.method_info(f"出现异常时,稍等再重启 TikTok 并重试 异常是: {e}", "监控消息", udid) + + LogManager.method_info(f"出现异常,重新创建wda", "监控消息", udid) + client = wda.USBClient(udid) + session = client.session() + + LogManager.method_info(f"重启 TikTok", "监控消息", udid) # 出现异常时,稍等再重启 TikTok 并重试 ControlUtils.closeTikTok(session, udid) event.wait(timeout=2) + # time.sleep(1) ControlUtils.openTikTok(session, udid) event.wait(timeout=3) + # time.sleep(1) + LogManager.method_info("TikTok 重启成功", "监控消息", udid) continue # 重新进入 while 循环,调用 monitorMessages # 检查未读消息并回复 + # 此方法暂时只取最后一天进行发送给ai + + # def monitorMessages(self, session, udid, event): + # + # LogManager.method_info("脚本开始执行中", "监控消息") + # + # # 调整节点的深度为 7 + # session.appium_settings({"snapshotMaxDepth": 7}) + # + # el = session.xpath( + # '//XCUIElementTypeButton[@name="a11y_vo_inbox"]' + # ' | ' + # '//XCUIElementTypeButton[contains(@name,"收件箱")]' + # ' | ' + # '//XCUIElementTypeButton[.//XCUIElementTypeStaticText[@value="收件箱"]]' + # ) + # + # # 如果收件箱有消息 则进行点击 + # if el.exists: + # + # try: + # m = re.search(r'(\d+)', el.label) # 抓到的第一个数字串 + # except Exception as e: + # LogManager.method_error(f"解析收件箱数量异常: {e}", "检测消息", udid) + # + # count = int(m.group(1)) if m else 0 + # if count: + # el.click() + # session.appium_settings({"snapshotMaxDepth": 25}) + # event.wait(timeout=3) + # while True: + # info_count = 0 + # + # # 创建新的会话 + # el = session.xpath( + # '//XCUIElementTypeButton[@name="a11y_vo_inbox"]' + # ' | ' + # '//XCUIElementTypeButton[contains(@name,"收件箱")]' + # ' | ' + # '//XCUIElementTypeButton[.//XCUIElementTypeStaticText[@value="收件箱"]]' + # ) + # + # print("el", el) + # if not el.exists: + # LogManager.method_error(f"检测不到收件箱", "检测消息", udid) + # raise Exception("当前页面找不到收件箱,重启") + # # break + # + # # 支持中文“收件箱”和英文“Inbox” + # xpath_query = ( + # "//XCUIElementTypeStaticText" + # "[@value='收件箱' or @label='收件箱' or @name='收件箱'" + # " or @value='Inbox' or @label='Inbox' or @name='Inbox']" + # ) + # + # # 查找所有收件箱节点 + # inbox_nodes = session.xpath(xpath_query).find_elements() + # + # if len(inbox_nodes) < 2: + # LogManager.method_error(f"当前页面不再收件箱页面,重启", "检测消息", udid) + # raise Exception("当前页面不再收件箱页面,重启") + # + # m = re.search(r'(\d+)', el.label) # 抓到的第一个数字串 + # count = int(m.group(1)) if m else 0 + # + # if not count: + # LogManager.method_info(f"当前收件箱的总数量{count}", "检测消息", udid) + # break + # + # # 新粉丝 + # xp_new_fan_badge = ( + # "//XCUIElementTypeCell[.//XCUIElementTypeLink[@name='新粉丝']]" + # "//XCUIElementTypeStaticText[string-length(@value)>0 and translate(@value,'0123456789','')='']" + # ) + # + # # 活动 + # xp_activity_badge = ( + # "//XCUIElementTypeCell[.//XCUIElementTypeLink[@name='活动']]" + # "//XCUIElementTypeStaticText[string-length(@value)>0 and translate(@value,'0123456789','')='']" + # ) + # + # # 系统通知 + # xp_system_badge = ( + # "//XCUIElementTypeCell[.//XCUIElementTypeLink[@name='系统通知']]" + # "//XCUIElementTypeStaticText[string-length(@value)>0 and translate(@value,'0123456789','')='']" + # ) + # + # # 消息请求 + # xp_request_badge = ( + # "//XCUIElementTypeCell" + # "[.//*[self::XCUIElementTypeLink or self::XCUIElementTypeStaticText]" + # " [@name='消息请求' or @label='消息请求' or @value='消息请求']]" + # "//XCUIElementTypeStaticText[string-length(@value)>0 and translate(@value,'0123456789','')='']" + # ) + # + # # 用户消息 + # xp_badge_numeric = ( + # "(" + # # 你的两类未读容器组件 + 数字徽标(value 纯数字) + # "//XCUIElementTypeOther[" + # " @name='AWEIMChatListCellUnreadCountViewComponent'" + # " or @name='TikTokIMImpl.InboxCellUnreadCountViewBuilder'" + # "]//XCUIElementTypeStaticText[@value and translate(@value,'0123456789','')='']" + # ")/ancestor::XCUIElementTypeCell[1]" + # " | " + # # 兜底:任何在 CollectionView 下、value 纯数字的徽标 → 找其最近的 Cell + # "//XCUIElementTypeCollectionView//XCUIElementTypeStaticText" + # "[@value and translate(@value,'0123456789','')='']" + # "/ancestor::XCUIElementTypeCell[1]" + # ) + # + # try: + # # 如果 2 秒内找不到,会抛异常 + # user_text = session.xpath(xp_badge_numeric).get(timeout=2.0) + # val = (user_text.info.get("value") or + # user_text.info.get("label") or + # user_text.info.get("name")) + # LogManager.method_info(f"用户未读数量:{val}", "检测消息", udid) + # except Exception: + # LogManager.method_warning("当前屏幕没有找到 用户 未读徽标数字", "检测消息", udid) + # print("当前屏幕没有找到 用户消息 未读徽标数字", udid) + # user_text = None + # info_count += 1 + # + # if user_text: + # + # user_text.tap() + # event.wait(timeout=3) + # + # xml = session.source() + # msgs = AiUtils.extract_messages_from_xml(xml) + # + # text_list = ['What do you think of my live stream?', + # 'What do you think makes my streams special?', + # 'Do you think I’m one of the most engaging streamers you’ve seen?'] + # + # # 检测出对方发的最后一条信息 + # + # + # last_msg = next((item['text'] for item in reversed(msgs) if item['type'] == 'msg'), + # random.choice(text_list)) + # + # LogManager.method_info(f"检测到对方最后发送的消息:{last_msg}", "检测消息", udid) + # + # isLanguage = AiUtils.is_language(last_msg) + # if isLanguage: + # # LogManager.method_info(f":{last_msg}", "检测消息", udid) + # + # last_msg_text = last_msg + # else: + # LogManager.method_info(f"对方发送的消息不是语言,随机挑选作为最后一条进行回复:{last_msg}", + # "检测消息", udid) + # last_msg_text = random.choice(text_list) + # + # if AiUtils.contains_chinese(last_msg_text): + # LogManager.method_info(f"需要翻译:{last_msg_text}, 即将进行翻译", "检测消息", udid) + # last_msg_text = Requester.translation(last_msg_text) + # LogManager.method_info(f"翻译成功:{last_msg_text}, ", "检测消息", udid) + # + # # 向ai发送信息 + # # 获取主播的名称 + # anchor_name = AiUtils.get_navbar_anchor_name(session) + # + # LogManager.method_info(f"获取主播的名称:{anchor_name}", "检测消息", udid) + # LogManager.method_info(f"获取主播最后发送的消息 进行翻译:{last_msg}", "检测消息", udid) + # last_msg = Requester.translationToChinese(last_msg) + # LogManager.method_info(f"翻译后的内容:{last_msg}", "检测消息", udid) + # + # # 找到输入框 + # last_data = [{ + # "sender": anchor_name, + # "device": udid, + # "text": last_msg, + # "status": 0 + # }] + # print(last_data) + # + # LogManager.method_info(f"主播最后发送的数据,传递给前端进行记录:{last_data}", "检测消息", udid) + # JsonUtils.append_json_items(last_data, "log/last_message.json") + # + # # 从C盘中读取数据 + # anchorWithSession = IOSAIStorage.load() + # + # sel = session.xpath("//TextView") + # if anchor_name not in anchorWithSession: + # # 如果是第一次发消息(没有sessionId的情况) + # LogManager.method_info(f"第一次发消息:{anchor_name},没有记忆 开始请求ai", "检测消息", udid) + # LogManager.method_info(f"向ai发送的参数", "检测消息", udid) + # aiResult, sessionId = Requester.chatToAi({"query": last_msg_text, "user": "1"}) + # IOSAIStorage.save({anchor_name: sessionId}) + # + # # 找到输入框,输入ai返回出来的消息 + # + # if sel.exists: + # sel.click() # 聚焦 + # event.wait(timeout=1) + # sel.clear_text() + # sel.set_text(f"{aiResult or '暂无数据'}\n") + # else: + # LogManager.method_error("找不到输入框,重启", "检测消息", udid) + # raise Exception("找不到输入框,重启") + # else: + # LogManager.method_info(f"不是一次发消息:{anchor_name},有记忆", "检测消息", udid) + # # 如果不是第一次发消息(证明存储的有sessionId) + # sessionId = anchorWithSession[anchor_name] + # + # # TODO: user后续添加,暂时写死 + # + # aiResult, sessionId = Requester.chatToAi( + # {"query": last_msg_text, "conversation_id": sessionId, "user": "1"}) + # # aiResult = response['result'] + # if sel.exists: + # sel.click() # 聚焦 + # event.wait(timeout=1) + # sel.clear_text() + # sel.set_text(f"{aiResult or '暂无数据'}\n") + # + # LogManager.method_info(f"存储的sessionId:{anchorWithSession}", "检测消息", udid) + # event.wait(timeout=1) + # # 返回 + # ControlUtils.clickBack(session) + # + # # 重新回到收件箱页面后,强制刷新节点 + # session.appium_settings({"snapshotMaxDepth": 25}) + # event.wait(timeout=1) + # try: + # # 如果 2 秒内找不到,会抛异常 + # badge_text = session.xpath(xp_new_fan_badge).get(timeout=2.0) + # val = (badge_text.info.get("value") or + # badge_text.info.get("label") or + # badge_text.info.get("name")) + # + # LogManager.method_info(f"新粉丝未读数量:{val}", "检测消息", udid) + # if badge_text: + # badge_text.tap() + # event.wait(timeout=1) + # ControlUtils.clickBack(session) + # event.wait(timeout=1) + # except Exception: + # LogManager.method_warning("当前屏幕没有找到 新粉丝 未读徽标数字", "检测消息", udid) + # print("当前屏幕没有找到 新粉丝 未读徽标数字", udid) + # badge_text = None + # info_count += 1 + # + # try: + # + # # 如果 2 秒内找不到,会抛异常 + # badge_text = session.xpath(xp_activity_badge).get(timeout=2.0) + # val = (badge_text.info.get("value") or + # badge_text.info.get("label") or + # badge_text.info.get("name")) + # LogManager.method_info(f"活动未读数量:{val}", "检测消息", udid) + # if badge_text: + # badge_text.tap() + # event.wait(timeout=1) + # ControlUtils.clickBack(session) + # event.wait(timeout=1) + # except Exception: + # LogManager.method_warning("当前屏幕没有找到 活动 未读徽标数字", "检测消息", udid) + # print("当前屏幕没有找到 活动 未读徽标数字", udid) + # badge_text = None + # info_count += 1 + # + # try: + # # 如果 2 秒内找不到,会抛异常 + # badge_text = session.xpath(xp_system_badge).get(timeout=2.0) + # val = (badge_text.info.get("value") or + # badge_text.info.get("label") or + # badge_text.info.get("name")) + # LogManager.method_info(f"系统通知未读数量:{val}", "检测消息", udid) + # if badge_text: + # badge_text.tap() + # event.wait(timeout=1) + # ControlUtils.clickBack(session) + # event.wait(timeout=1) + # except Exception: + # LogManager.method_warning("当前屏幕没有找到 系统通知 未读徽标数字", "检测消息", udid) + # print("当前屏幕没有找到 系统通知 未读徽标数字", udid) + # badge_text = None + # info_count += 1 + # + # try: + # # 如果 2 秒内找不到,会抛异常 + # badge_text = session.xpath(xp_request_badge).get(timeout=2.0) + # val = (badge_text.info.get("value") or + # badge_text.info.get("label") or + # badge_text.info.get("name")) + # LogManager.method_info(f"消息请求未读数量:{val}", "检测消息", udid) + # if badge_text: + # badge_text.tap() + # event.wait(timeout=1) + # ControlUtils.clickBack(session) + # event.wait(timeout=1) + # except Exception: + # LogManager.method_warning("当前屏幕没有找到 消息请求 未读徽标数字", "检测消息", udid) + # print("当前屏幕没有找到 消息请求 未读徽标数字", udid) + # badge_text = None + # info_count += 1 + # + # # 双击收件箱 定位到消息的位置 + # if info_count == 5: + # r = el.bounds # 可能是命名属性,也可能是 tuple + # cx = int((r.x + r.width / 2) if hasattr(r, "x") else (r[0] + r[2] / 2)) + # cy = int((r.y + r.height / 2) if hasattr(r, "y") else (r[1] + r[3] / 2)) + # session.double_tap(cx, cy) # 可能抛异常:方法不存在 + # LogManager.method_info(f"双击收件箱 定位到信息", "检测消息", udid) + # + # else: + # return + # else: + # LogManager.method_error(f"检测不到收件箱", "检测消息", udid) + # raise Exception("当前页面找不到收件箱,重启") + + # 检测消息进行优化,检测直到我发送的内容,把这些发送给ai + def monitorMessages(self, session, udid, event): + LogManager.method_info("脚本开始执行中", "监控消息") + # 调整节点的深度为 7 session.appium_settings({"snapshotMaxDepth": 7}) @@ -918,7 +1271,12 @@ class ScriptManager(): # 如果收件箱有消息 则进行点击 if el.exists: - m = re.search(r'(\d+)', el.label) # 抓到的第一个数字串 + + try: + m = re.search(r'(\d+)', el.label) # 抓到的第一个数字串 + except Exception as e: + LogManager.method_error(f"解析收件箱数量异常: {e}", "检测消息", udid) + count = int(m.group(1)) if m else 0 if count: el.click() @@ -1019,6 +1377,7 @@ class ScriptManager(): info_count += 1 if user_text: + user_text.tap() event.wait(timeout=3) @@ -1028,46 +1387,46 @@ class ScriptManager(): text_list = ['What do you think of my live stream?', 'What do you think makes my streams special?', 'Do you think I’m one of the most engaging streamers you’ve seen?'] - # 检测出对方发的最后一条信息 + + # 检测出对方发的最后一条信息, + + # 获取了最后一条消息 last_msg = next((item['text'] for item in reversed(msgs) if item['type'] == 'msg'), random.choice(text_list)) LogManager.method_info(f"检测到对方最后发送的消息:{last_msg}", "检测消息", udid) - isLanguage = AiUtils.is_language(last_msg) - if isLanguage: - # LogManager.method_info(f":{last_msg}", "检测消息", udid) + # 如果最后一条消息不是文字,随机取出一条当做最后一条消息,最后一条消息是last_msg_text + isLanguage = AiUtils.is_language(last_msg) + + if isLanguage: last_msg_text = last_msg else: LogManager.method_info(f"对方发送的消息不是语言,随机挑选作为最后一条进行回复:{last_msg}", "检测消息", udid) last_msg_text = random.choice(text_list) - if AiUtils.contains_chinese(last_msg_text): - LogManager.method_info(f"需要翻译:{last_msg_text}, 即将进行翻译", "检测消息", udid) - last_msg_text = Requester.translation(last_msg_text) - LogManager.method_info(f"翻译成功:{last_msg_text}, ", "检测消息", udid) - - # 向ai发送信息 # 获取主播的名称 anchor_name = AiUtils.get_navbar_anchor_name(session) LogManager.method_info(f"获取主播的名称:{anchor_name}", "检测消息", udid) LogManager.method_info(f"获取主播最后发送的消息 进行翻译:{last_msg}", "检测消息", udid) - last_msg = Requester.translation(last_msg, "中国") - LogManager.method_info(f"翻译后的内容:{last_msg}", "检测消息", udid) + chinese_last_msg_text = Requester.translationToChinese(last_msg_text) + LogManager.method_info(f"翻译中文后的内容,交给前端进行展示:{chinese_last_msg_text}", "检测消息", + udid) # 找到输入框 last_data = [{ "sender": anchor_name, "device": udid, - "text": last_msg, + "text": chinese_last_msg_text, "status": 0 }] print(last_data) - LogManager.method_info(f"主播最后发送的数据,传递给前端进行记录:{last_data}", "检测消息", udid) + LogManager.method_info(f"主播最后发送的数据,传递给前端进行记录:{chinese_last_msg_text}", + "检测消息", udid) JsonUtils.append_json_items(last_data, "log/last_message.json") # 从C盘中读取数据 @@ -1075,11 +1434,12 @@ class ScriptManager(): sel = session.xpath("//TextView") if anchor_name not in anchorWithSession: + # 如果是第一次发消息(没有sessionId的情况) LogManager.method_info(f"第一次发消息:{anchor_name},没有记忆 开始请求ai", "检测消息", udid) - LogManager.method_info(f"向ai发送的参数", "检测消息", udid) + LogManager.method_info(f"向ai发送的参数: 文本为:{last_msg_text}", "检测消息", udid) aiResult, sessionId = Requester.chatToAi({"query": last_msg_text, "user": "1"}) - IOSAIStorage.save({anchor_name: sessionId}) + IOSAIStorage.save({anchor_name: sessionId}, mode="merge") # 找到输入框,输入ai返回出来的消息 @@ -1098,6 +1458,8 @@ class ScriptManager(): # TODO: user后续添加,暂时写死 + LogManager.method_info(f"向ai发送的参数: 文本为:{last_msg_text}", "检测消息", udid) + aiResult, sessionId = Requester.chatToAi( {"query": last_msg_text, "conversation_id": sessionId, "user": "1"}) # aiResult = response['result'] @@ -1115,6 +1477,7 @@ class ScriptManager(): # 重新回到收件箱页面后,强制刷新节点 session.appium_settings({"snapshotMaxDepth": 25}) event.wait(timeout=1) + try: # 如果 2 秒内找不到,会抛异常 badge_text = session.xpath(xp_new_fan_badge).get(timeout=2.0) @@ -1203,7 +1566,6 @@ class ScriptManager(): LogManager.method_error(f"检测不到收件箱", "检测消息", udid) raise Exception("当前页面找不到收件箱,重启") - # 放在 ScriptManager 类外面或 utils 里 def interruptible_sleep(self, event: threading.Event, seconds: float, slice_: float = 1.0): """把一次长 sleep 拆成 1 秒一片,随时响应 event""" @@ -1214,3 +1576,82 @@ class ScriptManager(): left -= timeout return not event.is_set() # 返回 True 表示正常睡完,False 被中断 + # 切换账号 + def changeAccount(self, udid, event): + + LogManager.method_info("开始进行切换账号", "切换账号", udid) + + client = wda.USBClient(udid) + session = client.session() + + # 重启进行切换账号 + ControlUtils.closeTikTok(session, udid) + # event.wait(timeout=2) + time.sleep(1) + ControlUtils.openTikTok(session, udid) + + # 使用pop取出列表中的第一个元素, + account_ids = IOSAIStorage.load(f"{udid}/accountId.json") + account_id = account_ids.pop(0) + # 再放到末尾 + account_ids.append(account_id) + # 重新进行保存覆盖 + IOSAIStorage.save(account_ids, f"{udid}/accountId.json") + + LogManager.method_info("重启进行切换账号", "切换账号", udid) + + session.appium_settings({"snapshotMaxDepth": 15}) + + user_home = session.xpath('//XCUIElementTypeButton[@name="a11y_vo_profile" or @label="主页"]') + LogManager.method_info("检测主页按钮", "切换账号", udid) + if user_home.exists: + LogManager.method_info("检测主页按钮成功,点击主页", "切换账号", udid) + user_home.click() + + session.appium_settings({"snapshotMaxDepth": 25}) + + LogManager.method_info("检测切换账号按钮", "切换账号", udid) + check_account_btn = session.xpath('//XCUIElementTypeButton[@name="切换账号" or @label="切换账号"]') + if check_account_btn.exists: + LogManager.method_info("检测切换账号按钮成功,点击切换账号", "切换账号", udid) + check_account_btn.click() + + # 使用截图进行保存,保存进行图像的识别 + + try: + img = client.screenshot() + base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + resource_dir = os.path.join(base_dir, "resources", udid) + os.makedirs(resource_dir, exist_ok=True) + filePath = os.path.join(resource_dir, "home.png") + img.save(filePath) + LogManager.method_info(f"保存屏幕图像成功 -> {filePath}", "养号", udid) + print("保存了背景图:", filePath) + event.wait(timeout=1) + except Exception as e: + LogManager.method_info(f"截图或保存失败,失败原因:{e}", "养号", udid) + raise Exception("截图或保存失败,重启养号功能") + + # 使用EasyOcr进行识别, + + # 取出图片路径 + image_path = AiUtils.imagePathWithName(udid, "home") # 替换为你的图像路径 + + # ocr = OCRUtils(langs=['ch_sim', 'en'], force_gpu=None) + # print(f"image_path:{image_path}") + # + # # 进行识别,切换 + # hit = ocr.find_best(image_path, account_id, match_mode='fuzzy', weight_sim=0.75) + # + # if hit: + # print(f"[BEST] {hit.text} | sim={hit.sim:.2f} conf={hit.conf:.2f} score={hit.score:.2f}") + # print(f"bbox={hit.bbox}, center={hit.center}") + # # 做一次缩放映射 + # mapped_hit = hit.mapped(1 / 6, 1 / 6) + # print(f"center(/6)={mapped_hit.center}") + # + # # 传给 session.tap + # session.tap(mapped_hit.center[0], mapped_hit.center[1]) + # + # else: + # print("未找到目标名称") diff --git a/script/__pycache__/ScriptManager.cpython-312.pyc b/script/__pycache__/ScriptManager.cpython-312.pyc index 1618b9c497907fa27a4ba68796e3047c92d8b43d..a495cb419b73cb1110e7df41fa5bd8769e23960f 100644 GIT binary patch delta 10669 zcmcgy3w%_?)xWdvXLhsCY<4% 5JV`BJ`o(_w}1|Z*E>-?XSPD-5>n# z+?jLEoSA#(%$YN3+9CYaNulOFwOT=u-_~&-mUm7=%^EX$zuA@AfT~1ABATL6mAFVu zo`Nb#wX{fzD7H>kBx9fQA~|^qs}$ABA|=} ^dF0_uK>Ryf0C>p f_gX48B!JSm>&{=Sa?p=w6(A`XJ z%h2CK#$-~Ch&>xUHU5aCw8~4KOg_#w{ufHYe={1Texy$6wb<}yX=a>h(#2oX8uuug z6-P2ciY{$(uh!PBb#-W6C#0>0F74E7ZKb9Ugo~ygK|P77$7dd$`9jWzN$ws;@^Qsc zMQg?p#RrZl15%-V>VS;0Cmvq1Z%LPJ!oVg0Wpx}Lzi<5W=>y4>K4#DO=J9*dL+O9H zUEa1m_MmXLJqgc_yM?0gl=uXs!7JnEAuay7(}??%3|U%Q89=9_PYKCL$PZEec& _|ptw#_`E zZBv2<+Y-h}ErWIE64Lasm1`)9xig|yE6Zmcq)B#8jJNUj!fQE~TH8iZLG_R(ak$wz z3)xvt`%sTdP#j*8xERIbLy7swfj>!16eiG4EKa&3O$ q2uE&tzW*lDaKDTN$ zD4PqA1CR%h4{$$#7+^NQ9Dun13kgKT#Gp$n85vY80(bzR2w)Y!T3kNPp y5Zr3XKswG2&c8^}6lKu_cEEl84ZhJ)W+@IMbf z4FZM@DFa@?kWNau31z#5&W1lsM9zMM*(KydfG#J+%o9N11%O%RK4daBEqVa0ZdF}BgkcJ;!o}guw zXH`yw%6fcq`Sc3*yR%j_tUGHPFuj~TErwDdz!HF^01pBz2UrFG*2b&=cnClYK-T}D zy|4zi78k6Do5Btq%!MfdSPwuGh)8KKv$Vut9zhJlTGq&^IlO{K=i5XNm5OJxiy#l! zSo>$yAJ&}hcs8`MpPgL^?BGODHy ZKASp#8WcR7B@-xS 3R^m z0#ng(ye?24dkBc02Vkx3FqDn}9L4IOQ+X1KF9Ou#89{q`D-_QFyaLb$@G8J-0QUi$ z1vm$gfwu(<1 <0$k4*=u IzXA9yKrg^*fIfhJ02RP>fExg;9U1OvN34;6F)=p*jsax< z4)FH?e*pLt;4^>$fIkA<0{8;pHUMjI`S`1?PV^@n*N}pCwasf-Et5Kkq3@yO7)>FY zqez20cEsU#MGCyIQ7@r|wCI!oFSog{x>1XtwB@nSh%Etc@hX8tN6UbObU=QH3J5C9 zxWgVR^QFkC099eZ`7%BJaifmJ0Hiv`hnkW%Q=6$o$}iz~C@P6!l5lHwLM{r34^W2$ zv>5FH=#|`qr%nHCNxIImmotW2N|^DkdR2h36&<$QAxr8PO@mI zPD86`(TINTHGCPZ=4k%j1T@f5qXbdB!Xeb;;Zz@1*{vz8H AKUI)Y%xAh%=>gJB8`$8>#Z>7JslBK(T zpt=8< vG&QlhWZ?kx#Pn1XIi;_`}dsv{9@Df6Hi}% zX=mRPPjhUJ72e7fwUv&(^JhuLjh1H}gT#AVW;p8O#)u&NgsT6!)}evbs|m>{QT2LK zv%=fJisf23t=#IdE0~QrSQpKPAoB?0JtGzv3A=pUBQ|Qz`3`wj#zTmFU~CTdhu?iN zW7c d2)io7)!!lOdn=kPAWJ%Wj-XdNM0Ed^^l;I-E^YBu?&zT#YCB NUu z&J_sX5r|3o9ff>>TKJBB&cp>W;T4&fyp+Oz$^T%w$rMvxtTN*xJDge?(Si|90^vNb z0p~8%k`-7X#U06pR6d>$iUOhv`1E6bAvyE8oboGiUZYJyO9)XEaVUEHQ=ilj6gP;O zSig~H35bakN|5Rk p=ANVu($I^4@uGTvbQ5T zIw)tmB+gVhG^mProd1k9Ug1yRc>)Taht^dRpUe#Q;?jR@FM6JX9QVFvas&6AT 64a2r0t|r+kDOJH?y_rQ zhvz(~4QO$G$Q^CvJ9-rPvdP!sYHcHGNoIkoRoeO5U^FNbN5=)C?^Y(D4GEdo2+NsJ z0zOu$7s0p2H$!f<&Od>l)QWV_Yr@dEIH)h9=!Ea1purEp5&NT^k5m3E{w<)##myOd z1Fr*b`9UM-qM#E4Mot%O$7cM{7QHE8 >UJCaY7QHq^O|^kChH znBRaBW_mnmPfs9w_(;>?^iL0%2j{MZ%-v7&4Hh#!k)Cu=zzbVL>^^7@#`)*r&!4lK zV*+trL}#4N B5J(qYqvyccMT;Sh4AT{`buU9 PM*ifza#PRmaVISD2Q zB^>uWX+UzfTVfAalC6@EqRB!ISEMw2xEdoVousu9e(R`xW&CIzhqo` Jj(tT|FkUUs}@ vJ!`Cnxa9j| z mf?N@%aA!gQvN3 zZU4^WH;y*-w!g&u4{2eXc-yfb&8~-F`o{5N{m+N`zjwCx^|RMsKGXkf>&+dz_@grO z-#`syT&9W56*=G)&Zf*G_>mXvc>4>NNiKBmfBsa23fgopK8qx5Cgc`=>D0plMvN7w z&*SdXOA96wmg{f5exs$C=+Xb&j=s<{*Y`i(_r!nny?mY=ZF?`gaQ*dteLJ7NeyZih zTL*}`eP^D%{=$VBZc#+y^VHBDCL*Cd8@;6-<`!Q2(t30WUwSDQId{Koo-xBg1d`GA zwqN-C;(jKDbmbOA#NVzbiSLN0-bZ-F)gGqI!yE_2Uj=~dSG^VQDlp=Lm$S_(a!`$E zN^5I2l4Ny>-&
&3kiq5d*)ubn$6NqykhF>j7!T-M z!jzUXh{)s;hj7G>2)*PoOI*g(*4Hs?YHJQ~eG8h@8@x4iv8T#YO)k28T$-#Kzu3BR z!D^s^j8;VH^%Yl@)RfhilzAeiGLOHwrex##5~jGMrnJ14A#Y@eklctE{7gxWuZkVT zirP8l-Wrb&&pI>F_z(yiPN<3Dbw7UWjByq_xXtk12mpSO2@r56a_O>ss7MA_2k;1i z2&!h!{mc{iXJ@XOjHPTp9-^bqUtH}gEB1RH_2ZMTOpCe*)PmAS$^Xs1L~`?xgnw?7 z3YZ(%(&n_?B!^mGHF3t>$n6BuzjbRcH=8Ce0 0_U>D zw ?DgI%_*_|~~J@s*Vy*dD@;_K?Hw>b7Ne*fPUY z?m^p|c^`hI{gOZwjbD6yLtEs0wovMoQD*Bv4Ys_IJb9j+&1)c-D@a+u{bzXG{a4T> zUpV!FE9=5*OS-k|f1zFf)qoI)zPgl z;qNH~D9Wn5Px$*82rwtBYKrg=lM!HcR`o>Tr|AeVGpoie{45m_1OsdP;>{eC51FTk zu-aS0-Wu@fC)WOQU}fdPk`+~XHu#x%LOLS<#O2IC#LT4>fm4F_Un;}7B9mag1QI1O zNR&WU5}#U{0@+w3hcmuV#GcK^;fHN;xVlL}QWm7*sKSO-jZt`y*kK^&CDEawGf+@W zQXu4HlWb58MG2CC7>A}A@WV@4b|d~c$)*p8189(y-v(h@H_(#NNm!CAiHFDpf4)%z z37Q0W8O}rmrEqu|M^1>NQnvwVg_8_^%rAtPi$JcUm1D{AQAZqzXZsRMX_Xhj`O-l0 zu%iVJYM_Z48lEg5t8l`p4!JntnlUUg5H2BS5MhOj%&A>TqBQPTF%Cx`(BnK3-}uK? z-jEu`63!bs_45KK%Ad)Xd4*^#A6W1;LB&0!>viM;J$g6~9aQ?=_{BP#Euf@zr}exH z^1biYjPNRH15XlE1>_ZWHpo-Kq-D~^fQk*EOvB=doN2tKv^k*SnaKfHP%#}xYmLH~ zQwU$)?Mg(nm2VA34W{g8@Vq2Y8y8f=L^sfufchTYz<$jB=vaONNFIw^)NfS}M{I6e zVSNmt;v#{;t+jDS|I^>U@#+uycAcw-Eu(Kl1-s#I(M&8UEv>Du@h_y)Gb? C%vwwj2RCt4GDmd_~L@7bSS~!fJ8(<=yr? uT#Fl9IYQ5;AeOzM}(_1}^ki3=Cr~ zk0J9g;s$vIyh3!sUD~qu1(1J^M0=}A2m-glE4}{mLK1ekH6t$7SO-0HH4sq_+7St- zd=W)S17}s_Eh2}&tQbZei7qYolvWn=r0e}PBQBy#K?fSZ1Klgw?u+Y4|2_?=;I15o zkoEl$b!j;Xl|1=eZ(FR(OBrvSe{o68B%(T#KB{#*nLXc=#5a!$K&H3nT$$V@TZ)%9 z#5RykTyE@^IXYyHBjyuHU9yS2GSx1{PDLoANqtpj=$6HI$l{MkPZ+vn>4eyv*lluk zm|Q0&pGaxdgj4f6P5F(=9;0JVadYvJWt~P>qvD#3+ ?Mh_L*)!77F?+buUp@xD(z7jcP-huq)TPLrqT~yp(Iah z*M*a2cBy9dHo(0K$1 lJ^OYDJI3bzFT^9Zuia>DUxj*?TmOZjI{~8W$UTuy-o1 zHQ{wt;Ton(>+8`bZ~;k|);%CosS6Of|KX;n)8q~buUhSgr|p||ICo#}>CBdCC#SW{ zJUR2C;4SHS>06rfnuhmfo!QICMNVhhs!r?bknC!F!r@K(HXRP^3!GTcT6t-5XZ(VY z>Z(?^N7=00ThKcB18p`hbLPI8CseH!o!0!2tVgTgBWsrJjp@=lhZS|tyHtLuDC}Aq zey}LK=8-OK$yKc}Y)U(^iu~ WFoAk+g1o>X zd~a%UfmQgvRZPl^R!f@QsEi=_4dl8FSbuqP(znSA^CZA-fM)>y4Dcs_{{^^#D=*un zA40JZ;U_OokeEo>$KW?Eo5>y_PWi$wTp$oz23Fu-Uyjq1OH;*`TLsFEBJus`j|^7- z(6a0cN-B2#$uL8lj~FuuiXz~)6CXq3d#|6na@T_EQTXcD6IW0E2++qoNGdotCGynN zS2J)?#oa&_; *kG@OOrY&uG)beb3RNjg}p@Oyagf=Bf;Uwkdi3JB0 z6}9yrlo1$V97hlw(Wt!d1@474Yu)9wmEHkkRk#8^X4Hzf^g7O3cmF3%(=ss5tb56V z@9eYBzxV&|fB*YG`|N(SPqOt sfI*UQDLqFzC-5}(qqs#l56q&~G@Q?C)rGGCHkTdx(%a$mAvSFaPx3SWv}U#}O- zN?)qqP;c-X>y3U>y-BQ7`O^I6db3z2^tq+pnnoCwsbx6zW`@)3XVF52sZZ~^euz8{ z2S^D?fe%QrOwT2GlHnVz$*T3FaLJyO6>?6uSyr##!mK0psrN9h)JQd(Mu-^}OWr5W zNV)XBB+?$~N|{FTH?SooQ5EkAwzcz3p5Ou(i7GwoyunaI+sYpm%bVAftO_)@!%zB5 z(hiPPGuxg6_oZ54UaFQ>EK9vCN6&rkjMN&g5mPQLM`fNho ~=>UN&ACDQnj1FM^*>xsDf4LR$T^Tu%P5~ zC7mTdTwfm9p1xB;lELiAfyK^i;LA|Mf(#4wf(<|JZ)AU?93`u zSO#kg;mIr=v5K U mO0u~eu;qTI$Q2Yj7%nej&eM|-`Ez*;6n+#U z3z3a*Ae@LyiuSu;UVb*blBI6vR}- zJ&3yzi{NNsrluN;^AHo^O5vz%j4Qs5BC7E>u4~{uA>Qi=@_ww283N;qobttZUj+?C zHhl{UOM~7G9)1 1km4}1n>rm1K%c9fX)LcGOU-=~OqS5%Km z`N>c{(`ZK{_R07#)+|S$k7LuA3#(_&BC{hWX8w+l(ZJ1qjs4sTra2x`7 nJum8XICHn)Ua zNm12WIKDtNTJ%Z_iq|9VMJz*c1?T1OY7DhR3D1imH`HrCF5t+4NW;P=S&g_l5(`5d zQ(`zbTOyNDhZHdhF&3ov*2fluIFtZ*@BJ;A56MfbT??py%bW-u{!wfpS^~5bmD8Nw z?hEnH!@8wYZF99SvRWqIUbC|s6HMn&c=dseOd1%2pJP{m(1Thpha zq-e0XQU@cJe}o3(LUt7kvqxpY_LkSlH*Va>r(qqAVnZ1`*pUT$g%t8U91&V-e~C}FA;frl7E8||wj+Lp zz!lB!K 7~(kM1VqAf zJ6r{^?Dv0~U+nH3gbMMWh<6d)h#tgyh-ySE0q6=d{nDcdFC {1dEpaL44vFhb zsu^}{o$UJ#J%07YjaT1#;_BO5u5NnGm2_j|+$ zLzsBdZAMy3xAbXcMGDQ97y(s*8((WA`Rt=MbqRhC>(YM)j=rrut*Mcu1( z3~bAONw;Nc*!@(NLdh5%*BAwS|5OgK!z%Y3)B92^f9cmLwALSkR@(Pzg`uXHIbE!p zS|m9=rn+cqzU1HYW%Q;=N}tAma$2C)FyGELGt6_0K%g<%XygdTdP!nOBal`J^h_}( z#jC {O=;GCn<|jcX8OG#K{i4A9xBN{hD-5KbPJ z3-V4C{}pX1M#%gm)SXIoD~9QofIh=yRvu8r8xuyw(B_J`Rk`s8VQI(|SIemdX?#~s zn3%O}FdP2b>MY?jT#~>h!YgDOY^9C&^$_8b!%9&_m)amv?kBMGu5{QvSLqC^LK*P} z!k|^0ZY9lnyr?0cOSz?nl!S(V+;R4JqqyUOwh*1L*bg@4n9B5md _vn!#q3)*ci`kgR#TSl>du*cM9hI9Ug zgbg9wt%yG^V>1(~h~EjR@QZC_j=?X9_>_l@!);Mz3dWc%@b$K$4C9~%lVIW{f-#&H zD7~RY)?kaeXN$Ch8p3IzVpu)Fo+b=NGe2v1Q%$yzM$=dhO=C&0>3K7Jr8W!^r#%dt zJ58=Ao+pZbz0C}-gbXzEYBc5> Bv|0m7*SIaS0q>l)vpZ|qzD$y9oIvcP%WI=ZpQ^84_jja zrZYH-2OXVGl~~sh{=?ye(w5+$xKbRvz{ZCq Sq;X|j`A#<8)l$07!nUwI zg!@bJ*E$>MBGsD(`>@QBE~Y4cmXJP>VKg+{8lN!h1{W<2;`G*e7>JVe*1NG2VuNG9 zvXVD3QH Iicz7gLBH_OP>iTWXJ2QmsKN;OTCT{~^VvWi1++%&7iYH!RcV(qQ7ilgeLS;fK6 z5FCR!h1&P`#s=^1lELyP(r!KmShUl58|@!|+wD*O_3gBO;%&G8> lG;_TMvP z4G0RZW@Q200Yn68>!SEtN?gOv#GWX$BwjB#xhe4+%G(8JYrU9+oN(zy<04MCK|?>Y zsBAz>1?8AN=v9Z;#Itz%2xr_v!PH?2?n4C 5=-%F>;Sf|G5ayUws;3Dc?E8jf%LZ_#Zul0^1Yf^oEp`m+MY8qM&-CJ` zn{Z1DC){A+jeSr0*}f;;vhPWMw{LdquAl2$UwvcWFzaQjbGVIU>tEvixTeqLvtZB5 zXKb+}BXm9gDk2ks$5{Ll*t^#u &wYuki9Xf!bn?P>FjFsUU|Cf^NpLZ-O~?U z-FA@AqYqqcRJOX^!>^A@+k+mS-%soK1JL!xe6xhmvl(4eTVM@66Ko85+X4+ 4%n_`$rv0k(q<&Exn9TuZatMg}e(sxbGZq&}zWQf)8TUGwstd+t1!QoN=5{SgI` z^21*ease`rjw4D~dUT5Aa-REqUS&^S 1A8?d0Rz~ zt>WOEURzC{J^#GDyvJUCu;Jvglk>XoY3{CQ>26)uz2U)Ldq 6bhr3=?f&aZ`2Ohp${hFkoXVaYs{CZ-xt!VCm3@}%^OlkxOG$Ux z)RT)(&g?Fo*L` dF%r+SbaJh^r!7?i-Z;pP;>f!JPp+QXNzl6TBXc|$|<(iiA t!N!5`J##-CswQ+E&0 6Yl}i3xLXhPZewXCS z0)li`1ac%_WfMxiE^_$aCX=kOIO`I>Hz3A%{1`+Gk#f4I_zJL|$sj#&$C-TguoN24 zG^g?@w2g~2Q_^4mB$ojuyf `o$h{*E44M?h4nYDj9 zq?~gma|(mZdd=M`mDQ4OmcoQ{*0J9+3RyOfn|nT)!ext{*2!@^RR}+=jY>U%c0Z42 zD6xa1T6p=~k#XV)AGYFuLWrHD(bA8~@%}ch-RGIe*U~QNKPFgB(Jvu{e86ZwU{tWD YH`n|jllLLx_@4ePl4M_FkYXSI3qGLi4*&oF