From d2fc0cd7c755f1c92338990b1d9a70f5912ca465 Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Wed, 28 Jan 2026 07:31:28 +0800 Subject: [PATCH] 'commit' --- Apps/TeLaiDian/Config/Setting.py | 2 +- Apps/TeLaiDian/FirstPageKit.py | 18 +- .../__pycache__/FirstPageKit.cpython-310.pyc | Bin 11491 -> 11605 bytes Apps/XinDianTu/Config/Setting.py | 2 +- .../__pycache__/Setting.cpython-310.pyc | Bin 683 -> 683 bytes WeiXin/Output/test_current_screen.jpg | Bin 0 -> 32102 bytes .../Output/test_input_detect_1769473006.jpg | Bin 0 -> 52499 bytes WeiXin/T2_ChatMonitor.py | 225 ++++-------- WeiXin/WxUtil.py | 325 +++++++++++++----- .../T2_ChatMonitor.cpython-310.pyc | Bin 11057 -> 9592 bytes WeiXin/__pycache__/WxUtil.cpython-310.pyc | Bin 27591 -> 31068 bytes clear_sql_cache.py | 35 -- run_log.txt | Bin 7702 -> 0 bytes 13 files changed, 336 insertions(+), 271 deletions(-) create mode 100644 WeiXin/Output/test_current_screen.jpg create mode 100644 WeiXin/Output/test_input_detect_1769473006.jpg delete mode 100644 clear_sql_cache.py delete mode 100644 run_log.txt diff --git a/Apps/TeLaiDian/Config/Setting.py b/Apps/TeLaiDian/Config/Setting.py index c3f44cf..61231eb 100644 --- a/Apps/TeLaiDian/Config/Setting.py +++ b/Apps/TeLaiDian/Config/Setting.py @@ -2,7 +2,7 @@ # 采集配置 SCROLL_DISTANCE_RATIO = 0.3 -MAX_STATIONS_COUNT = 20 +MAX_STATIONS_COUNT = 50 FIRST_RUN_ONLY_ONE_STATION = False REDIS_STATION_EXPIRE = 120 diff --git a/Apps/TeLaiDian/FirstPageKit.py b/Apps/TeLaiDian/FirstPageKit.py index 6f84794..a21c35e 100644 --- a/Apps/TeLaiDian/FirstPageKit.py +++ b/Apps/TeLaiDian/FirstPageKit.py @@ -34,7 +34,6 @@ NON_STATION_KEYWORDS = [ "充电券", "电信积分兑换", "确认", - "广告", "距离/区域", "综合排序", "偏好", @@ -47,6 +46,7 @@ NON_STATION_KEYWORDS = [ "输入", "商城", "推荐", + "新客专享", ] def _load_image(path): @@ -140,11 +140,17 @@ async def run_ocr_rect(image_path, log_path=None): status = "drop" reasons.append("bottom_safe_zone") if status == "keep" and txt: - for kw in NON_STATION_KEYWORDS: - if kw and kw in txt: - status = "drop" - reasons.append("non_station_keyword") - break + # Prevent filter bar items from being treated as stations + if txt.strip() in ["充电站", "快充", "慢充", "超快充", "广告"]: + status = "drop" + reasons.append("exact_filter_keyword") + + if status == "keep": + for kw in NON_STATION_KEYWORDS: + if kw and kw in txt: + status = "drop" + reasons.append("non_station_keyword") + break log_detail( f"OCR[{idx + 1}] text={repr(text)} prob={prob:.3f} " diff --git a/Apps/TeLaiDian/__pycache__/FirstPageKit.cpython-310.pyc b/Apps/TeLaiDian/__pycache__/FirstPageKit.cpython-310.pyc index f9d849561dcdd912d5c026281a41a5d5d6abb284..e5e86e0335eff369b33695ba7ab8817f7643f5a1 100644 GIT binary patch delta 3746 zcmb_fYfN0n6~1%#F1yPsU>*zM&GOpiVKBxv*nk}aegMXfo!DUG^_saR%d&Tu*}E}| z&&?{OmfWbQn@m+T0!xret3?^PQP9=ggT0A5H&xrZ84ekgMSDSJTVk@!_`%zozna^|j(;KI-m&=STP6 zeC^|(@sHnsgAY1>PHg@!M-M6G?b&nCJYHFf)_&DiySi->W}B)c=lDi!cbF~*X?V`ea*N@F zY3I&`3>u~(Q%~5=gc)L{?MfKocw9Ga60{wQS3#@YRZsyVIEkYD1r@0YTSeZ|ZD6^$j6;z>A4OLJf?W-T! z*9d!}7-+rZ0hQK*hq}GWxf41xmGjEHVpW?O+Hjg%k?yEepoVrX=4=%(H*hdQ+GqY8D=18dHG%1VAOPU56; zQL!4Jelw4Mmg`AxA5dr>hFrfztVSI5pqT0%1%qr9o>6m1XiP6Ot`f_KazFTX16*;L zrJEJt!@f-y6=PXb%*u?Knk9-=LzEQ}`T*4ITANQURBaYZS}kdfS)1M>TZA6oQmtlG z)j=~Yqes?MbWEs{R5B`S%yt8TS8djVvPCu<=pY^1Qn6PH9kyBx4G3~atyZ%UTA-C4 zqff0lzEx(m-BGY`axFi!ZTi4Zk9)BUsj@SDcT{PCgVv(S#|*LIy_-FBk;bM)ygtWc|aM?SwJ_fQvfYl=gO;Plwvsav+6 z8Mq!s0b)H5v42a4HddzFYJR}`s_x|PE}pR>J7+! zc?mi&L(|YLH{%TS$o4YqL+uslMLG*zNUy?P45er{6bNJ-iAfAJC^S2pYd-{V!6&Rf z=)*8EbJl^hWJS6h@gSz_ASUX7>Bj|f#0**v2k>8wg%dw)w)6iKR;B0h)Cj;4>aI}mlt8H>ThxxFUz;8{ zjLSLz1J)o6nr?8?=Sr1T62TDJKOV^s(Xd1{L_e3Plo3_w0t{fzBQUsIK1PAGSWJs9 z(j+QDdD0=IQ9~j_f`_3Bh2E$VmcuX%LwG11#So6+_Y{7|#ZYF4v>%bfy3K+LMarX8 zvBqf3I)Ue8fVydXO&!4laMpiCwLI2IIDRs_e{j#76wzC~AT(m{Xz}T=Vx3ya$Fe?! zy7SNr&MHNgdJmpYh4OsTQYOD#)K|BTjtA(HbUJ~S6FA997>CJ-h8bVyZxxkwexJIm z({LIF5gSL$DY3V~DW$;*1<`RBmD&&ZCq*R%PUc4YG2A%F@#11%T4+4k)x_IOW1qvA zxSOmg7`4vAS(rjwjr|jI&SWHymy)(L0#oSYtTk@rqebE?Er!wKQ5c`staBLCIqJd+ zIY)0)DtI5W1T>8V#o$=_(a3=13 z`O1Kn-E#nromc$8qb1{YP2yQbY)3RP|2nZfi?R9OV%X3VyN|QKlw{KNXfuzW_#L&i zh_1(NWxiMb{xocBl*QsJ?l_BGKhoXRJCmzN<7O&o>dWTXrylHc@AmBn-~RD~H(r&} zUw?A@-iL3X?$aEBJ!0?Qehsx3Y&8?$v8AXzm zL%jFly_a8_Db<%lb7t^Hc+u2ZFrue!#u(*l8FksD)2}fvkCxr)7EeevDaaW?rjYoZ zwi>4LartG0*xME5MJ{}v`fGkmO86%=Pm;>*{Ms<74vFs9MfV&wBls(VTovTn_RDqC zq-{>{^4UvlvXfBx?kz*Wb{|;`MX%G)&|$u(;UJmgryDj%AAiuGlNmnQ=*`Iv#TOdu zToLSmi^MaHZM2J(i*X%d3=h*oR3@6WNm)0NQJvuf*jA0W?F=y%O4&J~gqe!#wj*qq zEFkP0(an@abb0PE9e9Pu9S_zWU~7fje(3#zLm;WU(YZo?E1f zCZq8La|z3Se#Q5TqN9Q>7NkUwJ>1{aGGS{=$MK@JOYL=Z!ecLlkH#P;aRb9!6^>UZ0o za5OZp<3B;nc8h(B;pj7{%yNlr4t^^7W(1Kw?g~y$f2R_$7VRRIG=kSd=G=3^up!2K zi{EPRyYhjs9YEr*wOye^%9snsWC<{_sclChm0$&eb7IxSQ!FOzr56yKCCCc`&&Q)- zLC3wxVlgvYcRatN5p%n@g2?LrhT!@Ik+I8}JeqZO>il~h!{nL&0taO& ArT_o{ delta 3712 zcmb^!TWlOjae8*fyM853{7n3cz42q$c4EhI;yfJZ<-Baiao(9Ex3}INZ#KK0-R+q@ zdreJN!9*8=%q1&H{8Ps9Nd%qr)#@ms*p z{FU>**B~2Lntd6PD}lN^VI|gvGayB_LsRt)t}_lCqVT5i`s?NTYNUnWE%l0nj+ zf=nBrnEGm^hZ1eHHbUtFL97YNX!ApbZi6b?f@3RGi_s4?$lVULsIP~%Emc76GBF5k ze<(fPaAE<~LJcj6dyqYt-VMH`QYf`HLD>RB#;Ykk)V-wZIP_aICgK5Xx)M=@SXxrcRW(Lt~^QKv?^~`K?ALzI~QfU33c?M z?uK>Uv=5qUCAzCtvYVrlz0InIW|Z!y67|yEi|Q_EzN}xO(kbbRWVgV!rP3U75x1ES#Hf(I+5xDScuu32RAzias6-j6!tW&TZm*lulK zkdc2KX~(QK=WbcGN)bQqJS=O>`Ju~d7yLU&8-Zf1pc9WhJZgD4eH*%#I_Sw|l9yBW z1%3*n)h)2og6atT3{rapHY%v!6ZjZXcL;1;P=AE!-3i?omiH|`^l-a5+}JN(8a@kM z7;a_FIM!wlnxCNOmaz)$-qrc!>b#G7p;wVSQXIF({!D#hd~RU;jdJAmz2tpV;F%vA z=+;YX=b`6e=_}@(UzhuH7aqy519rkL=%*JS6|Gj$B`n#XID40&8zXcDdc}#m3VmX{ z2D^}|K|jLlumj-@*om%GthoZgyvN*!rf+fzl`cpai`@{!1drJRFo14icj+jk{YdnJ$PAoQW~x0m_3Nxy&}DyRob`HVY0UD-LUy*2JQFvqT zFT{t^xQd29#*H7af=KQd#Vv=j(ZA2k-`<9LhR4BU|IV*3=FDjWwq0;jk$cE6zT zqoAZlVv>D&z6`7MG)hf6>559VF0W)?cfMLNkb4s??Waj$bqxAp4DY*f7=`h;l2_Q9 zFm8|HY^YAM&%hbXqJ-3MQZJvu5Xy3{Dr6sn`ikr*j0o!6F!BoZ1l~WmC-w=hK7r># zk=ZBC@FstblZp%3ipwbQ0#5Mv#h$?B=inSnaAEc##x%9&?8{P7(!DT&8#-r?8f7Sv z{;v|lX!0Mw@7%&)4F<|kIjPN6AV^hIRD(#kvqvLuo)_&vQUjj zLX7-8sHy&_M9oJm_+yn&S%B^I%1;&yMfp^#~$O0 zhu0grGhbqLg9Jz67``}j4T}oFN5VJjZnwu~QsB!@(E_XIDOLzD7DHIWF2$;*!;9 ziT<<-8LsbOB0Mui!-oc(*_ORz(s{4t5$SQpTJpxmMLp`jTTk;^#ikTj!BCf2RYbn-^%#6;kM|EW*rKw@Y!da~-Y+Bir z?s~Y^PA=z}3C}T}Xm`WKk*gO&7bnhz&L6pWdV&=>cm3PiwsV#`ok)f)S7pfzwzh48 zL^CrflX($mz0Pm^zpU8H(FzWD4A~~f+1@^;Dd|l3mAGr3lx1q_T!?q!YPK-oEIcP> zILHzHW!F>Z7GaBRWlYUc+b&feLEUaqWxONTWSY?Asf6ZD##_t&3U`MzvC)5DiS}yF_HZ;mN7!f z2n+En-$1f?%aJ>7mikBqQAi1X@(SxvWb$BTHqcDSi1VG`KoLJ>?2_|AaFEP8rCn!p zJY$Ui_r!RDn8m>^4*EIJIC#K;jsR<6E~2M+U;;j^I5rcG>gXHOeEdYC`#L8UFtZS| zMZR{P17YJ1$AlxRWsG&_R&YqcQj`qi z$KCaqS(B9`rm0wN<{;5M(81M|$Pk~1Wh=`An5Rs9Qkl?w!;GY9X zILNrqSVd6qls=$7x5H=ii_UmVBU;`@pgantWjD0FRmOH9hl z%Fg+bo0nfuQCU@81F5a6Z|~^r>h9_N**7*mF*!9oGds7sw!X2swY{^ucYJbsc7AaQ z`+fC?TnGT-zljBZ{Wrn>7rAiYay>#qLPSFSLoS3zF7SkigM|Ey6$MvB3H5^=-g7p; z$M~Yr8RczgH0;Vy0z>;zbV6E=Rl4IpMEjFuf1hCf|Bz&V7VJOdngcKq5#awkL>xc} zI7WY&>H9C6?8RvlPezl?`@zb#AURnR%S*_`MX+eqNs)|#;Q(;ww4cgy{C)XJ>45;} zY8WF=y8oha%4T?_Ob^Ibl81!86jUxu`AR`5JHaJ^OmH-|TDrCL9V|Il(~?x<^No&$ z4Mp^%BJX zsxT#mxVuZ&JfjNsXQy&AipZ>d?L-!8wfP|QTw3?dZ^kFKAjaflLBG{o8jW#q)u0zA zhV-cRn#tUfG_$|AKA%I?3F{zWU!^gUhRxDQ>EzLb*FowbRW?^uX~`bm*vVFcBH?_~ zc@I2Z&vpNO=!?!cCBTd1gLQ+*9r}7*rPwlL(_)lwBa_m_Xaw3b^YTbvg{HBBBZk$? zl%!CRS zuO6UE)g-?g&z=%6aiXDB6p*XIl79?htup@N@bjlq(l1VaX5;4zu=%y8qp9Ld@uVL6 z47rd#5gYl4a}I>9%X}Tdr285XR(I!HdedRV|b!djJCgz(Gk>Ye(S~3;U zGXY~w!|%#c*WSUn*Bl`lmT0mdQPI8i{NFm!%e1d<7-_odo8t<1AvKg-<5;n4g*;(8 z3p$-z)~@`*G`z|Zf<}Hrk9h&apYQ#3l7kOcB65<{9|wnOC5E&DSlLOl_rNFiPJ=m( zB-=4-(!(JZF4u`iYIa{lP*HhTFk^@K)aAY^fX*A0WWMyCFl0xr5}?swYTl0UZC88Y zl!AttuV?0?dOoi5+1Z&;_i9znc$cu|vc4;>da-X_M~*MMvb;4y z_Q_z{ha@28=!()eT}+L|1mENOhkk1DPX zb7!ogQS0l5IBCXIzQ`4)Qq9S;#YbL}QpbFI+72C71#MuPjRo8=>jpI=rE&Z6*GmBc z)0`zRYt?a2JFonng89V`m#yp{qBYY}V>iB-p7SE~r6d$D5&NtRWTD;V#TJmOpfuC5 zxtSl=pE0w{+vtJsv+NOrUlhsehE+K1UIdxX9v!oJEdV_%!fFP_?3Hth*qXMSu zJs`{G)?(t8i5+f@fd|;l3w}jdb((2|9-cF2Bx_8bMc@x4k6~O#RK$VE%ux}41srHm zV%NwHlbXk$FjU!iJvOlpZ)l@ACdul$bkK+OYp1%f(CGKo(etPo;$y!D5N8xomT#!J zgwFV3Huu0IvU?yT>+UHT)fL4J_dVeEYPFzY?Aico$J zWGr|1z@9GO$RVz-$+z>f5V=}!tw!SFIIDfTz=-Qs$R#LU=Wk+^PE#!lXtnLIE~aZF z4R8zP#>_M6os$xX^w3BgK`xe+YaHW5D`GiV!pPIH>b@2E1@nI+a-?Q8=9Ge{a$MXK zxL&Tg(UKshMdxNkT^A7+pL zXpz#K>v-OI;)w8#Rq3cbsaXGD?H)i}FmHKWR3*nYWYyo*AYCFjh$l6)spIfL!Z{M3 z-0@N4YpIPOjZjtNXyQV~M0F z@8|ENTCH4zFNtZcG*S!MD(cIXbEyOTv5Vb{nz^9{VPG)*ab$ukr>tftm^0(IsnQS@ zF-~l3pr{h8^+-qvvtTZN@1>Gg7p-s_g8$)j_ytB5@NqrAqHRGR)Vo5*YQ?w z7EKmUmF#7)0&!wV4~gF>tsrLgZdr{)f!;}|3(JavU{+p9SdTtWK<5|DU2U~6&7CQX z;Ae*1tlfxQ?BSTcZ0<1%0uD*T!JGz9%Qo<+sQ^f7C@VD%Xux>*&dhwhx1!_kIcw4;Zuwp5tmTE8wczW;GB z4z9>6X$WI|b-oBI4WUw09_U_nEmFxtlEtST=S^z#^cQntkP(F66F}ZO(((+~UKT=9 znPGNovN=$B!0LzsH}w%?+`@(L zcCmh~D*$Q}yd(SzVEz)Gf7ZF)n3KmXwsM%__-pN@L#F*`RW7#20#8L&QZt(nJ4ly5 zM|^kT=Hna=x%uG5ZsWh|QgC7`LLCkU&S)J*&`%i_p`Sx?pb5b%lbH~mC?>Y5ck?cc znB-ho*v(G>msFaG8-6=87jPj=+vbUD%;TH+7gf4$r9(@q2>z{@Up!W}L)Qcp?g2FM zv-aywY5OCan(=|eM+f{a$UKPOx!kr>-HHwHJ*66^Z!}Db=Vv-TepCTB+WWEa(mNOf z^74}8@4YO+8f#u#8993wwpz>$dk##o?{wK2ep`kYaD@}jPnXepjG!W`Q&*<0q2-1@ ziheR6y**Vp`AhEY-0XtR?+=ZeG|qMq8hixaaihrgsU3cHsXY>Tu9et3f|9+$HDW16 zG~aE+g(=C==%Pkw8Z-#e_YPjTT)qZ$47+QXn<;3}K%r z9@=)?A#I*1HCvUiSS`F5^q&2U+zTkW#k&LDy|@Ra2Yc?qELW`snVWi|iW;RHzm)6D zB^%jnZ+_#F2G6^1Tscyn%Xh3f)~MgTk{HLC*L9;+rlS@eAdwRAY(=@)@PHx=!ziYN zD9xE10({Vq_jD0%_DVx0azoGKs0%T6{gB=VY;l^22*gk0EQoH>g>+=GsJ*v;d}9S^ zm}(?ivx+)+Vn3}mUd_u}3l=gGV>gPQTgVp-p!q$5LmO0nQe)$H$N3a*X!F2BNKwFn(t zQRTl48WqDz=9F_}Tg$TuKoY6}0lR8lEgdHB`}1ADzTrg@BNR!;8t+J;WUi|}CKu$A zjm={I-o3KeQ$uGfvCI@5r3+rq_!WAreU^M+)6SQYIcMOS*3HfJD_oqbTc6JuZ+KnH zLy&Zyt&jGYVrx8&I-9l7gWZhUi#HuBX1t5lmF=iqJ8rV5GRqu=Ik-({D>_jDR7;iW zRXBWxjkDnD(TPzsKQ}jQADBqEN)o5l5{&%b9)V}Oi9W9)Mfd4JqAwc>70jh{@M`u+x4L= zX(Le9&tcm;+Kv!CiKdFQVhyTr-SVZ$41Edw<7uiAx|HLUzWB>lBErag-o$X_jesMC z!V*40tT;in&#In^0`xo{!uIxKNw4NCIV%YXS@}4RP%v*7U^psMFw_^ClNkBPFGe9p z@2+#wXNSn+9`Gi+dHE=Uz~dTE?H)+iJGzD0K(qexA|0JWWiwQY&U`B(D_WjJOQ!59 zK*KQtPifi)xg^not~0k}^d#@xbSG)M7P7`_drcZO~+72Fd%;atw(1&z}ND zBWJvn)hfzkrDtJ3Y9^TJYQFhkhJ;@Sj6~UW0V(UpDh%FjWU%O_PHIICr+HQss+v7xfAtj@}+!415v5)`S* z&;jK@K*m=R{^usO6uyByzPOw(anHH&m_#Lc)D~KeuQ5lEkFZ>w;a2rrl{4Y1cp;9M zH@^+__j|5fN=m!DpN%_$o;A{}2B9Eyov*Z6%-07jxJUPV=ytH_=JSV<$x-5tti+?l zAubtI9C(CBhWa#2kUMT*ZTyb=Pc>f6vbt-+EU;6#@rj9+b6GFA2lD1pB4DtIn5|sW7M7VtUkd{ zM;QV=D3Io>D&Jkn)AHrfO-VQQi;mtECRl6qn3t2^epg;rTkoY-oJqv^sl6bd7f4&B zKQ5NeoNigAfJjvxP*h{T`8~qU{iQIzO$EZ=e5AB6h2`gPTfQp23C31Xamr&0!>Ftw z?#n}U>9rM+xTDNHF7aa$-I?>0lpa+Tvn6jWzW!!A;L_>YZ1DYdasvYDMGbSk)}GMl zoVu8HCiN;_ir-t_@%?JprfbGC#B*!aG{Q+0oceqed@d{7gvPwRSZ*qb$yKiM1wXRD zW18UDMy00l*l4!H2uXQveV{&ytb|*Vge;h<9R)@DhB0@fs*noL^VxWn8)ul+E2HQw zXTzdCq|!;**Qf)$>F|M6#m&2WAnhK&x(Aloe2zH{lzc)+t&?4RK!?7g+U=)&nZ23? z`GzQ}m@DqnboT&ymNb04wA6ohQ+f|jl=$~_Oi^M(>4R7G+~+P2rW}Z8kl)2cBv{@I z)^1#+W52GPEOxRXk<51uU0BdrkWCI=!OW?sB@^cT1&KWWwM2Q0l|y%?uFdUlRyopx zd4hm=dLvAZc!)rIH5~yn>P*lln2gLaBUkP-G_UDH)8*n@37Eh-KS-rL7ECb7-i;_; z+gpn$l*!6CW%g6g^hkO3LHB#$vr~$WM#?_+ryt`SFT#7sKA%vazB~9Wk9`dS z$+@M*9uuiSj@2%5DXJmK`Bh5wWrlh9fylzt+a!DL->0)(r#tnj#*cy$i}m;R28k0) zvcBqNe?edn0b!$ZAiM}%0dsc{P1mO;+PUTFm59!JWoRIsR@VWk?vTx-Z6^f+IS1rP zNTdMK%Rn&>ye_h*jTD6~kPme_(ERErEUimVS3tG+VC zW`0FfWO2RGvxW#8&B}3R8MMP6nqTT#kt5j@avb$RFdwfK#zLNwl8m~@1Cp$mHyX%x zwNJgcu{^eXdH+|pEr?8bdf)Ryj^wHjzI2V#Z^v8lI%8`;&D6RThv|%Ml-fZb;TGsp zAze@g2nZ4JP*gLIwKc`Z(jzM z-HtR3<+oCxc#yx#Jt%Q?eh_z&nhRpHB{dNEjVd8yNa&LjiZAFVU6d+hq^LTX-!x6d zjpaqPF=mD+D-bXd(aYu$I>b#>*+@#;f@Q?_)`dsHVx-%*97Q=%(!{GTj0> zsLsy`^=L51MB92nWHBVIMs2*EckzRzlM0`Cr|xdLmRfqtO?;?h$a(#`sXX;7fF>TU4kW@1lK)}C(EV?Rs93%BxvRD)DLEP>VI-_X12U>!B`Iu!_ z<*Pj9m}^ojmuJCma29%heG;39Zm^s0z3gdnq69&gAcC-!>8S0E$s?x39ef^gh}R_!}83z{l`^mtlVai{7B-O zm8DP_r1q`m&zlT1#s!2{FUJ}M>O1Y{USFJOt5EgTw(5St7P!_3vgFITE46>^gpqG; z`uM3pL~S4mwfB!2j$ukznq)=fP>)(P^E&Zc^D()s5kh6*i*&5pM!R*QkPkKpm2Vph z$XYM3rfSjxC__&_s1^O-m#mSb5R&<> zW0m&wtj=-JN(TIBY%;J@`8F}Z!M7h`g%j7ox;~$@uFw@l_?3YrLs^0w!?E{rH)^c% zsQDSJRJl54Zd>z9$MZNRsWGlmhQzD)p^oh`FgSrPOrITX-pzffD6F6P`#OcnyE_q%nGc$#mr~_ zE`ATDlA1MvxPXQ2{xj2;*53`B8@Yx?`6|ya+BlNu=ldn<-uxtGDEc8_y_hO2 z=wD9;!fI!hxncVT&ec2scRbk;2rlY0t3AI=$07`!u7iwZNvkvVPbG>WKS2*<;xj(n zP4e%%RDcAS48=g$tmy}=KFfpNdTRdH;0FU; z?FE@)3&WaFB4k1dubydUVFL3)cW@PvOU7$!^(AC=Gv{Bv?12^-TsTM>UzL=I%dKDr;UO8EG)xpbx~dnjNE#fp%F#bEthj-@z8{bb$Ab`LN!*) zzSb?JwbhjQchY2lwk!s6m_oJY>qM@8=fS$+ihIEE4~JZu(gcf+{55l2r@bYgdr-Za zKNluh{2bT%CJ!p$v{n1-K>bunRVP>Cv9w+}!@AI+Uy}dap$C6<9V9L*QGU}zVEbya zNmi+5!blqVlk=rVNW4@s`uenu(OEkB<0#tD=zKFn0yPO~_|$NV%O=d{U4p!X#)S1( zj2|k|=;t$_VfX&AYF>`8RTs=R{S5hPr(er-;nP5=%OQoaFkkr!>oJbjAuL_d`3jG1 z7i}Qp$34)iJbu@Zd-sW9Iv5`kVl#nRDc_2%k~HkE4oQ9W_0eeUd+c`ssTP+I8!r#e ziGo7fAw7S8fPS0En!lCsg52?JhS7n&$ zp!AmxH`SBsLJx~vi~U4Hh9ZKQ7)@W+lj~6VCXW4?NnVgL&mN;QHrgIHx)v!pLp*Ky z-L8#-$W^Gcz$+he6{%sH8e9?nsKJO^o_pZOM8 znDfJajyNsY#)MP0?jF3ktrrKh&2$h-~7*NF(Ljsto~ z7z%_gL@l%Tnf7H9^m;>=l}V{l*DESRcl7p$655x|_+pZWEAvH8f<(TE=JgV9*j;%O z+Tx1W6T=a=D$A1V`O}fH?y8`=$-jBPj6{kw?IWhS|=knr$2(JB^EuF z6q*MN_qO~jXwZ{b(pA>csgPNz*%V;p0Hy6CF^X6PDOM$urZ6 z89LOMzy4&7f@Rn7Qb;#*EYwnx#vNDE3+vR0D-9fo5qmwPgG0FP%*S|VpnImzUKy$o z(2(t?tfP6bsTAW}Ssw(%C>(PuTYbAmN>CJ`LeLj2@rW7N%RNc-Vc7S6DgVWjuRcUH zKgo3hp{K7X_jj>w&UG){B3}O?m*+hoj;~Q>%_+-9n;JBRAA z7tOoleT2}Zj)pBZ*8gB(z3k=}Cw|JP21uc8E-us}%%AXe3-CJS zY2OTz-pqWwB)@hfp!BoJ=y4qFj;c3*mz?muoN~5DzN-1<1hWA5>NA^%UiFvLh`xrkk2|-HXkW4XYMNo8eM#(DY5U`tk;4OV{kYfDey(sDTu$_$(w2 zYm0}u>hjW*Do_^|XGVX8&tF^R<8j~1LiMR1@A#oZEO*#s_3@W{mbfQ%c zEhzu79Su`k_d9=4uYjFt<&8dh#viO^&BShph>QTSmlFJq?65O)AWXc(0^M+@*kIS< zO^|O;U_)b1<>}jQQ4~4j9MW+5cn{3MABi|z|8|%lSNnuSASF9$XFv!+7d{=U-V#`h zd=y&M;H=m;{ZdP+mw^~ph}tTD>PDw3SPg0zb%#wC)3vL{HflVxyZ107gh9Y1>g z3{MQq6iq@%-TNuud8h7NFb;p@rwzxKfe7;Qat#S$llQhF6cLW3Lcz>c2Za2ulue_Z zcx=$rkey#{*n3~*+PMFmVxgIEYPg<>i{}pup%Y#~K_AdW3_lHk4|Os_ftPV>7E{MPK;7i1_$spdVgfpKX#Q@JsERD715oS3T5hf5dN z#RQcK{^*+3D1+NsSX_nQ7p^J2f`M8p7h)lOA66Aji-BmA`iM9LmDn3eYu2l3uw0>T z8@cJ$tnxx%MzX&klx9alh-sQarkGzi_s14XppAD|es@wR|Jyn>9=fc9o;-pH{h4`v zx{1p%&IM87-SVLc<$&n3g$^oh>$Gf(`sPB+$3qn|vzWHEHy$TeWew0PEsI#<`9bs? z+DML{KZ)rD8@Evj*uH;vixB~)=O6K&c3G70<3(nPBlBN9q z0<({;FLCu7O9}hc)d;HAk5}V&__&eYM)ZJS|GJ^}J#w#&quJ z;u)*8F)E}@s7c5H!yOuSy<(3lS z`R1cNa}0(x9T)#r>dco)&(W!J?gCvH_h#6C+IMl)+w|2|U7 zP+d6TP0b34L$6J)ktS(W6o*L*YlfDZrexE)VUyhiWeOuQ*m<5W=FX^OYHnsOs|;iZ ze-5hvRjlkoRp+(lX+nOkD09s`MD%NM`8>r3Q;UpHe05bD?tk z#%0bY7eT~Bv<7!ga@m^26@8)mvc3pLKU)z?R~Uu5oMo+6bs)5dVBbd$L+3)D-48P; zl^JBVzm19$EErn;PvhzwIqk+hcl~Bi;qQL};?K5~dXCAGPtW*&yOkvicHm4I(BL{8 z9VA1M)GrXX(B3abg5aK=b1F( z2F59=GK2$O*HaV(SYNd(852{8E^f=;4^(dWROAX3Tj;npDEMK$uUqh`FO0fHudQb! zdA}@0|O5XJTUOUzykvh3_LLKz`z3o4-7mo@W8+W z0}l*5Fz~>@0|O5XJTUOUzykvh3_LLKz`z3o4-7mo@W8+W0}l*5Fz~>@0|O5XJTUOU zzykvh4E$FypnXCEUxamcMYSp!O?7Q>IRcN4=YmXW;YM-MlzpJSRPxfQ`*X!A_dq~H4 zzpc};GiWzh9+lUwKzuy+laW_HjAS>NilB zg0D4V|F?`*yI12o=_r5tLQsfI3=hW@GNAV7g}~L0!q z&x@U5W@{UIJ<{|OXlwK*i^Ak71TIQroD?q%SQN`^YYc4#CK$+e+^O306%oYo;Ysl2 zl7%|YL}ewiB7dyjMZ&5Mqm-DQ1OzWSp1H5IjDRgEa+{EyO7?MVYVm&b_Jl@(kMZvE z88*D1Xkom=_c5{f^94adjb0S{?faaYD5yY5=()mvG5?+4Ma$);PkYo~o_}n>_`7nU zUbV^ordn~+BiKuu)pof>Jn%W{y9WZP{_=uy)*1WUKjB2E^`xG0QmCTp=n56S6nr@j z-nsqt6{upL>K8K0hL7K-BU}vlou95+!6Pwi+%U4MLaXBz=!%U9PyON;+acxnlg3Lw zzEm#d?eqd#5$U)KCnF-3ui;fgio(vC9SheR-}J&AQy5LE+n@lsc})lH1fj_sDwEf8NR? zxH82F2(p}qsWUVh{30y+cRK$d^q-WZWu^aPWu`20@!&WC1QM@K@zz{3sxPx-T4N#y zIv5d|_Ty7X5-V*C0jB2J(VocdOT_RP?!Z}E3Hb8JR(~OMu+!Z4bsebto(pTi;(Q;? z>nyS$;k)!($tHUaK=Ea|BC<9-v>5)QP5_PG7uj9>^XDo9q+P|myI0|Qmh7*8fw}#O z7X5zB^R76yaVA5@WopvY>!;2E!H(=sGtY4U89E>8BJXr{!mK=fSWDM)EnT!ydd*CA zAtuYgYK5M{z++m;JF&B`wcT9$OXVW1Bh8Ft{C1W9&_lRSiU@C-( zTS+oF$M-!eqOaunHnha^?VQt%mB&Y&517Z?WS?~v*9{pyEHHbe=`ZETFuEv?WyUJk zfL}|^k+tg6IU|0c5J8390|nKb*iYp0ni`>9n79Owwyh#UB}o7e=d8Gj^?-cFoI-<; z9wj}=;Ht!Dewa9-HO_g!`wov73F#h?fRLiALtGY+N9|l2qrZ0bXWqa7imM zQvQuhzR*yGl~I?_3G1RytI!#8k^4bZd(=poHO$o=9xH+&GIqq#l&b}Wb2e*Dx>Wte zd$;-P5~1fE$EAX$Z=vw^3Wi~KHd96=hcizwE(Xup3gIikcP%2=_Tn2s_3~KZ*ArUJ zpPoB5X<{Z%@0=A5Z*z`PIWzV2a|KX*udK7qGm)Ja)lVKqg;@i8RP@*CPrO|!M zmohsZ6muE#;`=F6DJ3@pM6T6o#~~6%Fz*^9Yk<4wj`~ReM4w_}S@6SYr_)y}>bd|; za5IP}{qY^cn#UsH?}U#KF7)A(mDukmLMsxDFjjR;G)ycJio$AGMqZoSTDciGibf$2 z9E2sY8q)zCT82HbzmiCPU0OJ20rfYojWa^MPES=B!AxuS9^k@6VE98=P<8fB(1K&5Q=OB6?9E81QkxEz#NnJV>ueS|zu8SBp?NKNd z6-+(PvcFiHV#LPL)OCyR_^9zFpW)XzWvkursxUtI9@t1(^qKCxv(h3RioD|NRP1Ny z9VU+5B%VW~K|LwSf2OBT%mikptsH%C!=+_P_U_v@BB&w8=bd3)Dej&{!7pVwl7W{< zc4=x9_W-VRI%X&`J~~hcSHY<1I|E>EOsh)C&RG4Tcde(8OI>gA&+EFNxZ?AH`6BQk zT?`x98ya>N0ZKjxy=8x*YnxVYI_R0_!UyXN{e6jXKUiiI>}i1z2B&LPDRH*Sm+~bE za0ew<`Ll#m#;9&znkmjGa;tjF8H2gyrur{h>VseFC@`Kj-UAHVEoGc(S4hkEzzRFO z`*&V?cH9!-2`bjLnC1=^sCmJ~wHxv@E$%Z#&xSp}y1@;z5TVcTnE?zdZB-b#yKcQO z1^fEBj+aQWjN3==?zTjCFf)YKJGa^DOFYQ|J?kL(mw`_v@V(zsvjTccb)i(DjS6c| zWOPzQkBOwJBs@Ye(F^34h@6F1BxgKAPv>Ll!)|O?X1@mH=|2r1FNj}B%RKRh2V!RV z+rahw2{?oQ4LA_+(N*J4g5Ll}b>};|OR#lEa1TUBPT!%zqcL;3i#w<<{KmWoZa4pE z(~NlSuV)o}Xh-IB^yHIg7z%n@A1R~@ML3h*kiCFtOdD5-2Q!oqAJXL0QYy&wjD66< zmIoW|V1p)#v*jjY_-=)AC)A1=mey6qOan9|A}6I|80(~z!xcd80cp8sWBi&IwxgDm zL)tYZkP3ZcW)t4=5*y|lNsgb7Hp}!xKtZxjoS(s*jSy2gwL=rp+C?&ykH_J$^CZQ? zI6A`?Mo;468hlLCu}oEJW$Y${ckjFkzt_|x9j;RQVf1bT=*purTb=rxG)UDt#o8aI zTgIw(oQP?NVsvz5KUSi~aet4oeN(&7Xz+HN33piKS}^w_&=u2KeKDw2WSeBWBJq`1a6= zj+B#p8$Utrs9;~UcDyR@U~z3~%HmNB-u`|=)Q9iKS_F_hg&%v)%jvnO90t&@uAW}G(hZa!!-#(dMlI1g z>uw7z@}iP!AnGiU9y{l+TE$!_GYW6G)tuNz9Yyvmcvm3==y;PKi*E-}p2!lfT3CNz zMWe7EJ^j+>lPlKkuAzDr)iI2FcXBtiE!JSIRouPv^8)=bT$M;pWl@sBJi{p!2p2d! zQwnhwqpmmL?Fm>3iCHt0-!+N8olOk6mFC=aCrl>lXu`RXEP4+25G_25>>g}}+lZRZ zAod-ey_tgkHF!7{ErlBzQn4j{U(ux{M*~~FJAm2ts=3^!#GvUg!*=}Lf-w}|S&ag? zNJoco<1Q}BI`AhXnVLC+v_gIgCFbp9`cnNdVkFT2!_527?8ovqzl-oPC;QKvR06nI zB5efIu2!9w&yT^?7+qX#@{}KqaYn3c&j1u_9$f^Z!vHFntI z{_E-QbfbP;RFUu7*SXZ|p(~6Y9r~{)1(@yi`^3%3qqvsjm2Qt`uA`=W`50L!ERvd2 z2$R<-cuSGQKDw0KF+_QbTzzrtp+3(Ivk4qw{l07op?0{i&qh2*DTHrbp^~Nqb6Yc5 zGMHP~aRK*p)-I9=wuw-|cCIFjI()FB1AeZ}2SvqJ8}gUKV-%%r3gdf%}M{lD#&2H_G!oaTDwv+j*~=bRJ{DGOeUSDg{1ZYA!P zY#}M?d%5-DV2?I#F}|x%PUfWBh`G9>yXWZr2KNNz6H9C&OS7Zm$~5(0epyfI*)l@2_ru)RixNX8I`=JR7CaJ>YScj<%C0&~taWEBWnD75m7Y0h_hS zN9IrO3FD0_x<&m_!GF93r232c%ZY3WUAD+rm|Ev@0AK$`xI=>6T&t^~q~vE`IeZrE z>LkauJ@A}Q z0VY>X_4^M$h9`W`K_GeaVlL_i3ir-5>)ZpOCDx&!?;fZZ)46Zj@ zU8{=RNQ2qz?}tJamvWXAX%p2Y!)sZKMOF59a8<^xr?wf6X<}f1fvZ zuEE^-^XAY0^I^Df!F(8@BJCzta7<;YVkUcYJs2!GevS7nGZLyisoYB;eeo zg^NthR;^yMd5gu?ZI(NC+1Y=(d(VNtIvsTW;?UtEM?Jl0-afu3P6nO|IvpGmem>&c z$f)QGm#;iJr~?8i@X3X6(`BJnRzODZa#S5?>i_TpuI zLt|63tff`n)!p+)Z(skrfkD;S_{5}o3Y(tM&ub3J;2+b1@BhPjEr)r1px*;S{k-OU z5D5>1<#Xq4*gk*7es{y;XIFl-~cQSJVP_sctnzu$r zy7VI^AQs$KSyk|qZgrRitAaXrU=KM?OJ+Wa61X={M2Ce56P~aYlaiwaoT--ywJD5A zv`j}zp?i`z+nQ?a_se#T79+>sMcOL@<9|;ZPB|y-bz)?ZmB)0X%j9{SkJ)B8PeO?94x|# z^Al2DrC7pQ?|Pqjr6UQ4$%`fwB=%o1Vdw4$@^$l7#YGzX!a8) zI$-2Fl0$C4QCqIs36Iy2f><|^rP?`~eVjHs>Iw&o?tMF&`>V}26x)fZEjy$Uy)Kj) zi}z>~XKJ{RP*bd?isy_8P4ERs`C5)8Qf}3U^>^2`8W9WIl#cx#;@51$h}@{#-BZ`Q z##zFFfD)l_EMq)ioFgS}Tccw`X7E;s)R%~PN6J9GsR4>b?|90CENsic0@yB7EG@(D zkc!u{B@Itj1|4X$*1kAny%`^sYX#H2l=ts5b)+nJ-zpu+hB?ObZWQ{*9Q{SMOFC`K zvK4Ipe(XJCvQSDH4NSRc^~TAVUF%-QsD-Ohrxw(`Cgl$jp`1UZiLb)@nS!3v&G zi?<}Qx~T@lZ@%rK)Ci+HOBL4zHs^WmunU&&VKa$VC&pjl*;YEzX=)2Y{Ux@x3pL@? zk>^ZO5)qN}4Pi{KjbY`Ul&cYH)-sNoVe1X^_Uw&Kq1gEZ{AmkkL)0N zi^`P9K{z{4HJb)~g+F~gecPogF+fI3)sg1#Z8J-Bq~AQp%+dQHk~Pg?JE<#zX?wyR zFn5Y%DXHI{Yda>Fn!$P7hBVRM2mPwJBH6Brd(sc{+KO~`+c2sm*1|ZS^NM)Xbk}tj zU(l^1`B7$eDMka}Xn{@oN_C_&xxc}RJx~<84Yz8X*qc<@mV!p;~ks>GeXQ} zs`#R`+>kv#c-b|tkXq9{8^t?xq?$+W2!9;uO-BAo7RqXN%n)Z5W-IDBHLMtb)bg)v7+7GYv_ls7H%U%joN_pxCCHXtTf*qw~Ty)%m5p5O3T3M+lGf)t9 z_dUzf#3V0eCJS|OJ*DV!-Y`187+{>r_z8_Q8L z+rEous3TQSyQfbU2$e!{t4-+nXQ$Do*c|zw59K}D{{x*h%Ip)!#cB(cd73g+-HBCc zK7WUpY3;jf4ZF?MqBMSOo=a$#+iGurCga_9C$OtjwRfgI0+fuymUw`~R@Xh)hnL-;1Fo6E2we4^3umlvsvhlbr2!HHW<*aS;f zywQ4UzUDMi=R3|jBnU=B8`z(y+^Wf^nl}qmaZg!m2A5$;nHlPJ(UHFV^Wdcs3+pAD z;Pv|*7h>dN1)`@7qlD$@?H#`UI#N!Fka*4YfkgmcQ;d`!BsN6lb|WK`s)yC|kDE4? zHQGunn06g!ilQZ+e12nrZS`w0$w?uXN#2uv0_r2pLP7Sp%e#E~ssntzgj~Qt8IVn37-8xdWlwp94 zcU9ke;u3zs8VwoCED?<(F~)RHVIA0|KQfS-w$(66N2<&qmgz|Ke?tNlT5DT$+VmRI znOH*O=4ZT?tU$`YhQBw)E)6oQT$K~l4>Lt2C8>muQ)r)jntlcymri*Op~@ph*LX#& z;mK|{E_q%l9J;ywZ+23Ry(13G<3w}w4{lg#$4YdPBUu_1g{xmHVO`c6ZEyA zrwZ*%sxoS=>*ffQem&Gnf?~4qJA3q0BW3#gyHJu%1bD>w=@0tSlt{N-fil+{ zYsif9>SHW^x2G!q_LC6BU5m_h0Npr#=)43aFj^%-zEfWpeI5A2089j_AbKe1Qz)(T^4&BbM5!qB2QQiMZ(u z>yhaDX4a0?0cld$oPIthhpA8=6=0iqTI0Rtl9j3?tqDUNt}132JFI{hm5IZuAE6;->P*~@~@<(Z^1I3_|w~6 ztP7$GoI*8<&{t|Kl6W>2IB6aOqBtnxZbf5yolIi4sWuXeSn%O~Fp3-UcZez4`G{OZNt$ru zuCYrpNy&U1wP+kS^Wjopes~TnkYJ{zw53#GRhx1VcDu8TGgm1XIiT6iHiozOHENH> zuOSnA7{tsN>R8z{xh*W9;eCcM#akkUfRd9?D|)I&G)|QFnE($11>aGFSuOqQ+j!FC z2)UA1oLb)QQW1GqorXVrC=YQw$_o*~8Ss#56w^ayGcCFAc$j6K*5p7rPgEDDU9Q>1 zsYMNK*>jYvu1sEPmzz<0t(iPO^TQuAN}i70kkFFN?ijmA1Ni2{X~0L{OU~h$=ozxy zMg6O+IB7Xi&sZfyOb6F2yJLDUt9@__Wx|i42{fC5kE6`Ao;DcOk+59`gquru5PmC7 zfPWf1sp4r@jH+W`*6tcaYlRu945muV%eo&wYPO;F4>mV%3(M3Gy1*}*p>8g7CKe>? z6B3L9%Phz5vyZDHdy=j?9LWv7Fm>^LvCF6IPlA}?;UCJ6s1mij#0EG3B;nn9HiXE(LOl+qk63Y`Ks2av;7l|Rk``)2F6)GO-} ztci9eQ_~6?#qJzOfy3tX1dwJuZF1&gEG$EEzLQa69BELQY+W94AADT*WiekwVCr#eA{Gny5El z_Xg5L$!dRWQJs`%Me)&AG@1#g!UNWkex?Ad$A%4Qa69ceB6ipN0rnBuL#$Hs?SSJ} zNvX1?l}*O^-RteFScOtGL+He$BpWlz#`4*U4q|EwEyG7H@J1A1;{xoC<`~gtz62Mr zjg&EeRhIIW6^|BS)I+k55@`5n8bsr#;{kvY#{qly2$wclHH~=w)8nSJY zlHiLxXJH3Qb7TDNhZ!d|L8F0?CkWDdzc$Q>q7wTEYoz5cU?nFP21S;2np*6yPGj~g zuu%Ttaj5%x=J(`@I&5;B-GYwrqwkSo*J~U&zcEDpBQ%XU+etN5X|%#6Tq)>UA?>z7 zD&Dw&>CW}{Vy%?sMHhtJ@|8x|rL=a*;>3d_i-5iBzOZP zv&xvpn2=5tAPtOm5*vpp3mS*dBiOc}Ut6G4W-4Bi*WZAI zA~!^zv`ItHnpL@8*qw2g3_JkQR3g}`6mx}&x!6NDRn651pEE+&wI19l*Js55^OzCF zk~1D~WK@f@OrZ&2@5aiMb={|O9O_p2Yb`YMUd0RiqZwt-G?0~t zZcZhlo%-OF4^JDcBo-R#QwcTi!c-DPvMXOS2aKfh;WrS2WiZ+b%fNjb28Dqa&BQLOuL;hxRo> zh|FyDcLCdLDQ!CxkopqFOrsiIhJba^G}67JMa#jTbpOHy9G9W&NbIY!A;mt>m|~U0 zqR1X(HRnyb35=>Nn7%c&vt3Xbd%-Rd+NuTq#vV&!}{QQy35HZcGkeDKTID-_8MxT>g0${{%A)(}T zoEnC39ts%{E1teWrag3gU(>6!;O<(x!9F;*Lj*m8+dR09-I9^Qu$qDhdPl<|S|l5B zp~NofdWrb*Fq`u5-OM~eJbPu=KF9_ip5C)GkM7COI|rKruh6^BLMeUUXXFOMKQZRh z9O5Na>0-24`^}{pc};hAmB6sYxl`l1cj~NfyT5N$L_~zKhm-=jN9q)0YcV!?fKd2? zzMH|>r6u3bME{X;=+yX@yNqcj{e|W5=^R<~vj10N zL8->BEYdF1IMUrkBbWrHEtPN#xNl^U!Bz7Ts6UfORx9JxeoDqjOgEqM#qDyA{oTE> z=A&OVc0Wy_>>%4wil^bQDl8cqrZI3z)Qq_w$Zmp3$;t~Izcan8P9dF4bbw$WI9Z8^B zM}F4~^u1piZ_?`)(g5Ub-DANq@EOZE4b+uLaIKsAfKpJIaEJ4k;BbHEm`8M16LG6V zYL-R;m{l?cBp5oFsi(5ig)g+Bn1L!=9ih5cbJ6ir_Kqg4O&$as+qN|p>~SP+pTJU zNyRO^oASFLjef&NdihPiqZ${&SJuQ+eobvN>ognYD{Xs-g)tDzMkQR=W!rlJ+Fo7A z^%E>_N2Gqzejs^lgb;0G3nm$5kdrg5(K|=~-;_J%yk>7!3A9#5T*+E25x*V! z1o_)gbBVaswrf0~L}SzFF|7v`4w+-QGe;XZB<*^Y#btJIeiYfW-lmkIqEyk%RQ`X^ z&%4W~-JLT3DQ|D_nP?+wH9$q?-AjjzhF{vT`^%91*sUDxS4!vSb!4EO3?0p*ZdaKBa?CH?e!sHEifUQ&QV3ru47;sR zgYk!lT=qEX_I|t=?Mh}-Gox6Y&gFY~*T`)vfUrE&mG(?3z?}J^>^6a=Uoc17hXY5A zHp4R51PToCTkKHC6gFs#nduu*{3fxs?NATphMCuEt+i@caq4yPQ+?=S04Tff0?d`j zo;o%}h`{GCJ{wRh>MX_#mB2j$>{NU((({_lSUTLXT3D(hT{piB@YO|)f5aF@I~1HL zppcUMpmG-heN4Bqrn;mhWtuiDOt?6S0*JmE*4HLBkrbS-E{z>T^ZwSWP9dQA1OQW?wtshP@AfjTQkM zb7EREhC(yJK!vIx?YjLcY1h)8L9pQ5SOubFP=OJC7p_nc}+XZz#I8C=D+U~K7A_W z+Jpd*2`v_T0R=Iz9U#%ZDdIyn>{B-)fi?_tzjz^ANBT&%PsIZeaY@Tw39h0p(Q!K_ z*6e;ez*(wnCxlOYgPSir5_`zya%lvZg{Z)(Z`vLB;b{u7VI04M41DZ37dABvoOi0{ zgkc5R(%&i8u0)810?olFuNrl|QhXEbI>EM=J$m!LE3R2un<6`0BGlN3XFO%{UOoCx zIXY)L5+c$SEL`(BN8yXqjJ7@dbqu>*NoI3UM38`aiXLV;$elJ zR1waoH0y{K1zDJMP0pxrYdt_LTr9vWI@ye8rHv7%1C;m_aKVFHfeV8|K$S7nFtw9{ zYG4p(>J}_fGe;YCy9cc(Gy0-QcnAnoi{ZO}?2s~h0lW|CD`5B;z|X$;&$x&hCTOke zN-8JEL97yvK3817YoWD94dsJh%h@|hHO6#SYiJ-0>JZmk?^~08L?ilM&qWu*c}wCI zCY3AN4*1~N+O?|enmr1xO}zRFeSzby`Gea6cf*UICQ?$uJ;oWdx&ut-5TJF1Ih9qH zB;zd?)?MK#kGNr*PVr-voWXHobIHxyS<+l-rX2EuM)CG&$O7T$Adu5RCgj@(#fm-P z@K&KgXlqZoj-+(I-ig&@4RChIB(?X4?z-Ku&1iOPvZi@V7f#dYXBeuHaE-#QOk71( zy1QDGE0iweHJrCPQbGR>ha=r-ti^*~CEwl)QU#EUi7Aa&bMsK`SSELn(S8gDP)p)` z!Kvcxz~dlR7w;Nj^&rEtZi=yXPl=xU%gq zqZepHvf&_&3#WlmOuIJfxY!L``txSOSi897O>aYS?C0xUh~=801!z+=dJ0l^eOhu4 zX#(HQ96z9j0C1^}sHYp@#icB$KJkpSHuw`2XZ&D;(8}d;*dx);+-7f}>E&C2JhFc| zjxz9)Sh?7bW$A;&sQq{yU~R+{ z9xB6U#+m&{5>?#KWiz`3#!-gE;^i>WADVjVZ30T3D#h1}UDphL!ABw}t)ZNDsO+J= zKQTgtK;()%3sjdosoLe0#KP?3%F&)H4cZ`OS3-TsJ1CaTxmEHO$zR_v;~K{a+bq+(Tl?PU6(d*6j2uTpLU ze$)dfbBGf74tykh;-3tWF+A zJMIWw{6BtM8DW3N=>5p1@zD1laUH*(M#f%VbX=D4jM^&gbW7Bcf_0>u9UtO6&idLQ zb8(N+a`}ModGXkfJzPIH#CC@ay>s6|(VeDUvABnD_diNt38yDHMZuPFUzel~Ph ze#N@-v({G$PH25+Q$E|ww1BB2Ej;Q=*2cOp`(Q?bcGnnU(S99i@nZENY#NT@3;Sky zSiCZoF0ALQZLbRIt%_hWO?i~GUZieYV2XDs2wi$Pd>h z0G)ihg0OEyiz5=AmD%~F{Rk1-3Cbw?*Gh{SLjfrlspodK#~i>^U99hr1wc*dNLM+g zPi2mjLEr2|heMqSWp-9+r}eU+2abZ+tZL8&O*2iR-*$YAiMw-gBlYJhvi3uo zYigHyY7dLz`s|s?^GZqJ6*x-9C3#*{h=u7i`yJCjx%z5z2wroAsUTn&sp&=o{-pA^{mfD{TSjUcD7&K)o621fCPWs))*m@C^Zc1)0B&j*RWO{X+BOgH*jP# z*%vu;*}K7EUS&jdfAR2*_C#)om&Ehx6_9nMP=v8L486n(h+OVRFxJ#A?oK$(&!5eNQ8@svcKnw|Dr0WoPIk3?+?e zT>$wWQ#}qfjSMt|3}&e#L0vV6sO9}!C9bZe`d~3kJQHxog7#*RFM6vn$Z~bedYx0a( zv{7TPb!;F^wCnLllU8IS5Vu?i<3&Df31I{#09|R*4!jWf_WMRCh1!`zR(i&|926`0 zQ`+TWef=&%yf>X^U%CE*-)VFp&pnzk0qgRSY`=1S^n@R@{{`EFpN$0(R(JhzycxY!ft3m)DaB-;iTk1ief)}E?<*;n7Z z4L~qtwuXsA3ZwIaOAO&;e}w=$9MCiZ;OirQ3r+Yia*8`><$vI%hL+q%0GQENTLo=( z*tQxdIR>-mV5-b_$Q*s9WT!CGQX{&4$w#`j#FtO`mKFeWt=CBf8$(v50Z*wT?PE@B z?v80ZfPk9`whh~y_WRx7tnt7Uu(V$hrJoVDO;L#~3?>YrqRDuI1hx27g$Cgd?+-+Z zK^KENw9BQ|KyLbyG`GD0(mY1WJ++6JtV||P>GUkEWq89_-iR%+YN#FxGc$pGRY1)! zrWc6=l+Ggw5I6sh+2?TTs6uqO^{i%3lv}y%8!TymbIqkNKGB32+k2sbnq7?r(NLeR z{T(dCf0Xh?Zvrp^7EYvZfu(iS9>=o? zi$U@#_|d7U)V(_~=%xML4t{jsF`&(QVU0mOgZ3szn16tmP|r}}7)6q&nr%@A>O|GS zTgvh0N2FT>T~@9xQc-Gc9VESz+-(pF-8qBD2z6;Xm-I%-O8_6Av?7?;tqc>#JQ`YK z_t4~hT5j;vxwg0Rb~wex1ukZz0U&Vb<$tRq6)Q%rYuq@kbn6D~ag7^0xViMih`s%;3c02ptvwQcy%?7Mm9Sty6rzlP7T+K5jEZIAr zs{VR+SwUv2kKC78KOmvWB~5*VdMmi2ba&co-clVY5V<5btP+^-k3C^~y$fC5I>0ej zvhBaYMc#tReU8n8gWjcpw&B2oS1g{REHfEOw^Jg+!4L;i9M@w{dUEdtG_QR;-1SV^ zp66BH?nLC)rj7u|Ew98*sP=Xum$kd`d&FphsNtjEB}TkT8D)>1TK5Bsrh!)_|Y*w2Wp{==^>3=iE`6F#=?7+ zp6bwarIxp`=C=Wmt$FiAD^qKajdr_T<(Q=Rx$L9RO9~qTpWUU{gFxSsV&_Zheh1WgjAkn)pyQ8D z;_0PlbJ~J;1b-t9?{@vX{&aqTP{@^yfC@=469{s~!2CW2Gn04tVG&lY;m+KdZGpmZkP88D^AF^^Eh6gWz5OFD z4~cqY_Ypw}W1HU4QcGIO7E@iaQ7!^r^#V<^`Is*!%DtYS&Vg(-apyNg+kC z4hTy%uKm}}QEBGtPX(+uS8>)mVKa8vpDSz zq{$!%1X;5zbx8ihnv}BWoN~kD^-X_>sZ(m-Nf=baU0MYr?ptCVMppK<0hd_Pz^N5P zso#%}*w;g$*NQbg@n37jKVh{hq`SSpf``$l3mwj84KkKHnzdGi_efpb76(Ue_jiiW zv>33ZUCp;12G}j4ZEtb4PC-t-4h=08*b`+RCDi6o z=W0J!B~>D;eJ~yAS7PJShKK|oNkdR%u>B}of#>=MRoC6GWsyP62^Wj=B|1GJ`I*~G@K z;_(`?_AgD2bAxHkGR1O-z#wCe!?WVF+JybU68Pafs6wRzbV}9E!Fu!Iq%VJn-Roj3 zdUsd!gc)QbPOxd?m?1ot~_LPJvO^~r!I zq!+nH>JP{#ROroJkTw26KCY~xKWzjIoIQ;)19)fpcPc~muPWLNhqXCT1{I1WrE5EfVlSM2q}n7|*$^m7Jvs)OPvFGB zi`j#Y`LlIMW$eM4uA4>Kugt*W@QV^X9 zQ;t=(sc$HIOYa^`6$FcYJ$YAp$0W56V=pnRUW&l2V})` zmLm;Y9~;E~-`d6Y|NC)2ihC6!eLVcPONUOTuxwX7(3eAos7fdj+uZAT@CXxBisYMn zb^QV@RGGOUKhG{%?|7@ricdYRqEm6t0ZQe+qH`KBeL361}9%+ji5*J#V-o@JLa zJ3@*=~=L-`B$F2p`2LJ>~JEshw{POws#G_ZS(`dPyFFr0ht6$!#gOaTfWMQ z{h>bh#h$q0vA2PYjw4y@2&b6rt7s=EY}c2b2;Qu|jbu!JD?0bKcBlAza2I)E#<&!C z4FDGbk0+Bbi_z-DwFjGbNk@)UB0;ZDrR>hqoJ0rcGk3zroXocb0{Bv%Y&DS{!?7Y_QE)we(R(pinD<${L8;A&ds(bEDp__m#Z5xBqCi zZ>sj0!+Y-5{D(hoxiT3KWqSq4w zlMrNO;5TJca6YEQ&Gk@W-kRHqVjGi(*RZt4;a!i$YmLv=+C71$ znt^<1dyRdniSZ3dHvx?%vMTsQ?JuucVf?HF;sT!f_0K63d`k*y-7QZ>o7X@n?Nt;x z*8GJg%>f=MJKoSfvGiWzqP8$tc>`^UA>l$qQo=Y0YabfhhTdFUMMcD3WC z`iXtZ77sDguAc(bl7tpcdqBse7@BlJuO{5|dgv~zN?=qDY6^8s?%oAmV@L!*K_=6T zXp!2S<7_Lv+=W)&+2u3o$13&?7Ky2o&EEON-Wdsm9FHq4-L)4z6`7O%N|IgA``JzV zmvOeQ_!kVk)`Irpln-^J2bGaoRZubVg8r-mr(O`&wj+N~G=cOxAec%}_TSj7%1VB~ zvv+H9IF$LeRsU>@RwRaLTmU^(dV;M0X=6_KqpQ=;~@nBEi#mRhRuA91XHi>skye3Ma@0IU9Gp49@v`wp%NH zxxvw3sKc%N=`IH5blxR+^P3SQJUFXr9i63h89^pIYs06~9bS)8i?qA**;HkFZFSE-#p5L=bKS4^AhnH`q(BM;&zkXmQR z+x5-04?}aGE$50@m$Tgn&S$+*%-$wTavg5$&L|)oUkIvP`{@Q{a;XxL`ec63(rEnW z9oITS;Q6R-h(aW$i2w6f2MDZGy+4_9c9dQ+njYyx{;-@pJT`ibI<~hX799N>_pj$~ z{$DQN`i}*m(9LWZRY`nEZ6kN}-}qaaRAb2Hhn<2w0nCYhMeTgFGI)=k-P-gCfnmWlGxvIkb!419+So58#@In*p5ma z>A7M!m)RD5W+K#=4BfgQZjkk{kAF2S+!NpOhY8YZdBlN5$Feh>?!V(~%v^P~dCfJj*1)7J7_W358_&U}

HW{%(3wO&ETnlm{R`Ofu|2S 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 literal 0 HcmV?d00001 diff --git a/WeiXin/T2_ChatMonitor.py b/WeiXin/T2_ChatMonitor.py index dc32c34..55559b2 100644 --- a/WeiXin/T2_ChatMonitor.py +++ b/WeiXin/T2_ChatMonitor.py @@ -63,7 +63,8 @@ class ChatMonitorBot: self.input_pos = None self.last_screen_hash = None self.last_processed_msg_hash = None - self.first_run = True # 标记是否为首次运行 + # [User Requested] 移除持久化存储,只在内存中记录,重启即忘 + self.processed_hashes = set() self.check_interval = 3 # 检查频率 (秒) self.persona = ( @@ -76,13 +77,23 @@ class ChatMonitorBot: "3. 仅针对家长明确表达的内容进行回复。\n" "4. 严禁使用列表格式。严禁使用‘首先、其次’等逻辑词。\n" "5. 回复必须简练,字数严格控制在 50 字以内!\n" + "6. 对方问什么就答什么。例如问‘学校叫什么’,就只回答‘少惠林’,不要回复地址和电话!\n" "如果涉及到校区信息,必须且只能使用以下真实数据:\n" - "- 单位:长春市少惠林作文素养培养中心\n" + "- 单位/学校名称:长春市少惠林作文素养培养中心(简称:少惠林)\n" "- 地址:南环城路与临河街交汇,TOUCH12街3楼325号\n" "- 联系人:小张老师(电话:18686619970)\n" "- 每学期开学招收小学三年级至六年级,初中七年级的学生入学,其它年段不招生。\n" ) + def _record_processed_hash(self, msg_hash): + """记录已处理的消息哈希 (仅内存)""" + self.processed_hashes.add(msg_hash) + # 仅保留最近 100 条记录,防止无限增长 + if len(self.processed_hashes) > 100: + # 简单丢弃旧的(转列表切片再转回集合) + temp = list(self.processed_hashes)[-100:] + self.processed_hashes = set(temp) + async def get_reply(self, last_message_text, context_text=""): prompt = ( f"【教师人设】:{self.persona}\n\n" @@ -141,9 +152,30 @@ class ChatMonitorBot: with open(file_path, "rb") as f: return hashlib.md5(f.read()).hexdigest() + def get_stable_message_hash(self, msg): + """ + 计算消息的稳定哈希值(忽略坐标等易变字段) + 仅包含: sender, content, time_display, type + """ + if not msg: + return "" + + stable_data = { + "sender": msg.get("sender", ""), + "content": msg.get("content") or "", # 确保 None 转为空字符串 + "time_display": msg.get("time_display", ""), + "type": msg.get("type", "") + } + + # 序列化并计算哈希 + msg_str = json.dumps(stable_data, sort_keys=True, ensure_ascii=False) + return hashlib.md5(msg_str.encode('utf-8')).hexdigest() + async def run(self): - """主运行循环""" - logger.info("🚀 大张老师自动巡课系统启动 (T2 增强版)...") + """ + 主运行循环 + """ + logger.info("🚀 正在启动 T2_ChatMonitor (Auto-Reply)...") # 定义 JSON 序列化辅助函数 def numpy_serializer(obj): @@ -159,118 +191,11 @@ class ChatMonitorBot: if not self.step_1_prepare_env(): return if not self.step_2_connect_device(): return - # 2. 首次运行:识别所有语音并获取上下文 - logger.info("🔍 [首次运行] 正在进行全量识别,获取对话上下文...") + # [User Requested] 移除首屏概念,直接进入监控循环 + # 以前说过什么都不管了,只关注最后一条 + logger.info("🚀 启动完成,直接进入实时监控阶段...") - # 调用封装好的 get_first_screen - self.dialogue_log, self.input_pos, enter_path, flag_path = await WxUtil.get_first_screen(self.device) - - # 更新 live paths (用于后续监控逻辑的引用) - import shutil - if enter_path and os.path.exists(enter_path): - shutil.copy(enter_path, self.screenshot_path) - - if flag_path and os.path.exists(flag_path): - shutil.copy(flag_path, self.debug_view_path) - logger.info(f"📸 已保存识别标记图: {flag_path}") - - if self.dialogue_log: - logger.info(f"✅ 首次运行识别完成,获取到 {len(self.dialogue_log)} 条消息上下文") - logger.info("\n" + "="*50) - logger.info("【测试模式】最终提取的对话记录:") - for msg in self.dialogue_log: - sender = msg.get('sender', '未知') - content = msg.get('content', '') - time_str = msg.get('time_display', '') - - # 按照用户要求的格式输出: 2026-01-26 10:03 糖豆爸爸 : 老师您好! - log_prefix = f"{time_str} " if time_str else "" - log_line = f"{log_prefix}{sender} : {content}" - logger.info(log_line) - logger.info("="*50 + "\n") - - # --- LLM 总结 --- - logger.info("🤖 正在请求 LLM 生成对话摘要...") - chat_history_text = "" - for msg in self.dialogue_log: - sender = msg.get('sender', '未知') - content = msg.get('content', '') - type_str = "[语音]" if msg.get('type') == 'voice' else "[文字]" - time_str = msg.get('time_display', '') - time_prefix = f"[{time_str}] " if time_str else "" - chat_history_text += f"{time_prefix}{sender}{type_str}: {content}\n" - - prompt = ( - "请根据以下微信对话记录,总结归纳双方交流的主要信息点。\n" - "要求:\n" - "1. 简明扼要,分点列出。\n" - "2. 明确指出双方达成的一致或待解决的问题。\n" - "3. 忽略无关的寒暄。\n\n" - f"对话记录:\n{chat_history_text}" - ) - - try: - full_response = "" - async for chunk in get_llm_response(prompt, stream=True): - full_response += chunk - - logger.info("\n" + "="*20 + " 对话摘要 (LLM) " + "="*20) - logger.info(full_response) - logger.info("="*55 + "\n") - - except Exception as e: - logger.error(f"LLM 摘要生成失败: {e}") - - # 初始化最后处理的消息哈希,避免重复回复第一条 - last_msg = self.dialogue_log[-1] - - # --- 初始回复逻辑 (Added) --- - # 如果最后一条是对方发的消息,说明可能需要回复 - sender = last_msg.get('sender', '') - # 判断逻辑:只要不是"我",就认为是对方 (可能是 "对方", "糖豆爸爸" 等) - if sender != "我": - logger.info(f"💡 [首屏] 最后一条消息来自 '{sender}',尝试生成回复...") - - # 构建上下文 - context_text = "\n".join([f"{m.get('time_display', '') + ' ' if m.get('time_display') else ''}{m.get('sender')}: {m.get('content')}" for m in self.dialogue_log[:-1]]) - last_content = last_msg.get('content', '') - - reply = await self.get_reply(last_content, context_text) - if reply: - logger.info(f"🤖 [首屏] LLM 建议回复: {reply}") - - # 检查输入框位置 - if self.input_pos: - logger.info(f"⚡ [首屏] 执行自动回复...") - perform_input_action(self.device, self.input_pos, reply) - - # 发送后更新 hash,避免进入循环后重复回复 - # 发送后,界面会变,但我们需要标记当前这条已经回过了 - msg_str = json.dumps(last_msg, sort_keys=True, ensure_ascii=False, default=numpy_serializer) - self.last_processed_msg_hash = hashlib.md5(msg_str.encode('utf-8')).hexdigest() - - logger.info("✅ [首屏] 回复已发送") - else: - logger.warning("❌ [首屏] 未找到输入框位置,无法发送") - else: - logger.info("⚪ [首屏] LLM 认为无需回复") - else: - logger.info("⚪ [首屏] 最后一条是自己发的,无需回复") - - # 更新 Hash (如果刚才没发回复,也需要记录当前最后一条,防止循环里重复处理) - if not self.last_processed_msg_hash: - msg_str = json.dumps(last_msg, sort_keys=True, ensure_ascii=False, default=numpy_serializer) - self.last_processed_msg_hash = hashlib.md5(msg_str.encode('utf-8')).hexdigest() - - self.last_screen_hash = self.get_image_hash(self.screenshot_path) - else: - logger.warning("⚠️ 首次运行未识别到有效对话") - - # logger.info("🛑 测试结束:已完成所有语音的转换与读取。停止进入监控循环。") - # return # 测试模式:直接退出,不进入监控循环 - # 3. 进入循环阶段 - logger.info("🔄 进入实时监控阶段...") while True: try: # A. 截图并计算哈希 @@ -306,19 +231,22 @@ class ChatMonitorBot: # D. 只关注最后一条消息 last_msg = dialogue_log[-1] - # last_msg 是字典,需要序列化 - msg_str = json.dumps(last_msg, sort_keys=True, ensure_ascii=False, default=numpy_serializer) - current_msg_hash = hashlib.md5(msg_str.encode('utf-8')).hexdigest() + # 计算稳定哈希(忽略坐标变化) + current_msg_hash = self.get_stable_message_hash(last_msg) # E. 判断是否需要回复 (对方发送且非重复消息) sender = last_msg.get('sender', '') - if sender != "我": - if self.first_run: - # 首次运行时,记录最后一条消息的哈希但不回复,防止重启后重复回复历史消息 - logger.info(f"🚦 [启动] 首次扫描,忽略已存在的最后一条消息: {last_msg}") - self.last_processed_msg_hash = current_msg_hash - self.first_run = False - elif current_msg_hash != self.last_processed_msg_hash: + + # Check if hash is already processed (in-memory only) + is_processed = current_msg_hash in self.processed_hashes + + # Log only if it changed from last *in-memory* check to avoid spam + if is_processed and current_msg_hash != self.last_processed_msg_hash: + # logger.info(f"🚫 [监控] 消息哈希已存在于历史记录中,跳过回复 (Hash: {current_msg_hash})") + self.last_processed_msg_hash = current_msg_hash + + if not is_processed and current_msg_hash != self.last_processed_msg_hash: + if sender != "我": event_shot = WxUtil.get_next_debug_path("event_new_msg") self.device.screenshot(event_shot) logger.info(f"💡 [监控] 发现新消息: {last_msg},保存现场截图: {event_shot}") @@ -345,10 +273,14 @@ class ChatMonitorBot: last_content = last_msg.get('content') or "" logger.info(f"🔄 [重试] 强制转换后内容: {last_content}") - # 重新构建 msg_str 和 hash,确保下次循环不会因为内容变化而再次触发(虽然这里已经处理了) - # 但实际上这里是在处理当前事件,更新 hash 是为了避免重复处理 - msg_str = json.dumps(last_msg, sort_keys=True, ensure_ascii=False, default=numpy_serializer) - current_msg_hash = hashlib.md5(msg_str.encode('utf-8')).hexdigest() + # 重新构建哈希 + current_msg_hash = self.get_stable_message_hash(last_msg) + # 再次检查是否已处理 (因为内容变了,哈希变了) + if current_msg_hash in self.processed_hashes: + logger.info(f"🚫 [重试] 转换后发现该消息已处理,跳过。") + self.last_processed_msg_hash = current_msg_hash + # 跳过本次循环的剩余部分 + continue # 生成回复 reply = await self.get_reply(last_content, context_text) @@ -357,35 +289,35 @@ class ChatMonitorBot: logger.info(f"🤖 [监控] LLM 建议回复: {reply}") if self.input_pos: logger.info(f"⚡ [监控] 执行自动回复...") - perform_input_action(self.device, self.input_pos, reply) + # input_pos 是 ((x,y), box) 格式,取第一个元素坐标点 + target_pos = self.input_pos[0] if isinstance(self.input_pos, (list, tuple)) and len(self.input_pos) == 2 and isinstance(self.input_pos[0], (list, tuple)) else self.input_pos + # 简单兼容处理:如果 input_pos[0] 是 tuple/list 且 input_pos[1] 是 None/box,则取 input_pos[0] + if isinstance(self.input_pos, (list, tuple)) and len(self.input_pos) == 2 and isinstance(self.input_pos[0], (list, tuple)): + target_pos = self.input_pos[0] + + perform_input_action(self.device, target_pos, reply) # 发送后截图留存 reply_sent_shot = WxUtil.get_next_debug_path("event_reply_sent") self.device.screenshot(reply_sent_shot) logger.info(f"✅ [监控] 回复已发送,保存发送后截图: {reply_sent_shot}") + self._record_processed_hash(current_msg_hash) self.last_processed_msg_hash = current_msg_hash else: - logger.warning("❌ [监控] 未找到输入框位置,无法发送") + logger.error("❌ 未找到输入框位置,无法发送回复") else: - logger.warning("⚠️ [监控] LLM 未生成有效回复") + logger.info("⚪ [监控] LLM 认为无需回复") + self._record_processed_hash(current_msg_hash) + self.last_processed_msg_hash = current_msg_hash else: - # 消息已处理过 - pass - else: - # 最后一条是我发送的 - if self.first_run: - logger.info(f"🚦 [启动] 首次扫描,最后一条是自己发的,标记为已处理: {last_msg}") + # 是我发的消息,更新哈希,不再处理 self.last_processed_msg_hash = current_msg_hash - self.first_run = False - elif current_msg_hash != self.last_processed_msg_hash: - logger.info(f"⚪ [监控] 最后一条消息是自己发的,跳过回复: {last_msg}") - self.last_processed_msg_hash = current_msg_hash await asyncio.sleep(self.check_interval) except Exception as e: - logger.error(f"❌ 循环中发生错误: {e}", exc_info=True) + logger.error(f"Error in monitoring loop: {e}", exc_info=True) await asyncio.sleep(self.check_interval) async def run_main(): @@ -398,11 +330,4 @@ async def run_main(): if __name__ == "__main__": # 应用 Win32 补丁 Win32Patch.patch() - - try: - # 运行机器人 - asyncio.run(run_main()) - except KeyboardInterrupt: - logger.info("🛑 用户手动停止程序。") - except Exception as e: - logger.error(f"❌ 程序异常退出: {e}", exc_info=True) + asyncio.run(run_main()) diff --git a/WeiXin/WxUtil.py b/WeiXin/WxUtil.py index 54ef86d..28e25a7 100644 --- a/WeiXin/WxUtil.py +++ b/WeiXin/WxUtil.py @@ -253,6 +253,61 @@ def draw_debug_info(image_path, messages, current_voice_center=None, suffix=""): except Exception as e: logger.warning(f"绘制调试信息失败: {e}") +def _detect_bubble_color(img, bbox): + """ + 检测文本框区域的背景颜色,用于辅助判断发送者。 + :param img: OpenCV 图像 (BGR) + :param bbox: OCR 返回的边界框 4个点 + :return: "green" (我), "white" (对方), or "unknown" + """ + if img is None: return "unknown" + + # 提取 bbox 区域 + h, w = img.shape[:2] + min_x = max(0, int(min(p[0] for p in bbox))) + max_x = min(w, int(max(p[0] for p in bbox))) + min_y = max(0, int(min(p[1] for p in bbox))) + max_y = min(h, int(max(p[1] for p in bbox))) + + if max_x <= min_x or max_y <= min_y: + return "unknown" + + roi = img[min_y:max_y, min_x:max_x] + + # 计算背景颜色 (抗文字干扰) + # 文本是黑色的 (0,0,0),会拉低平均值/中位数 + # 使用 95% 分位数来获取背景色 (偏亮的部分 - 真正的背景) + try: + # axis=(0,1) 对 h,w 维度操作,保留 c 维度 + # percentile 返回 float,需转 int + bg_color = np.percentile(roi, 95, axis=(0, 1)) + b, g, r = bg_color + except Exception: + # Fallback + mean_color = cv2.mean(roi)[:3] + b, g, r = mean_color + + # 调试日志:打印颜色值 + logger.info(f"Color Debug: B={b:.1f}, G={g:.1f}, R={r:.1f} | bbox={bbox}") + + # 绿色气泡特征 (Light Mode): + # R: 152, G: 225, B: 101 (BGR: 101, 225, 152) + # G 显著大于 R 和 B + # 提高阈值以区分白色/灰色背景的噪声 (White: 255, 255, 255) + if g > r + 30 and g > b + 30 and g > 100: + return "green" + + # 白色气泡特征: + # R, G, B 都很高且接近 + # 考虑黑色文字的影响,如果是中位数,应该很高 (>200) + # 放宽对灰色的容忍度 (Dark Mode 可能偏灰) + if abs(r - g) < 30 and abs(g - b) < 30 and abs(r - b) < 30: + # 且亮度不能太低 (太低可能是黑色背景或深色物体) + if g > 150: + return "white" + + return "unknown" + def _scan_chat_messages(image_path): """ 内部函数:扫描图片中的微信消息(语音、文本、红点) @@ -263,6 +318,7 @@ def _scan_chat_messages(image_path): logger.error(f"无法读取图片: {image_path}") return [], None h, w = img.shape[:2] + logger.info(f"DEBUG: Image size w={w}, h={h}") # 3. 模板匹配寻找语音图标和红点 audio_template = os.path.join(TEMPLATE_DIR, "audio.jpg") @@ -383,6 +439,29 @@ def _scan_chat_messages(image_path): if has_intermediate_audio: continue + # [Fix] 检查中间是否有其他气泡消息阻断 (防止跨消息合并) + # 如果遇到一个明确属于另一方的消息气泡,必须停止关联 + if c_y > ay + 60: # 稍微放宽 Y 轴,避免误判紧贴的转换文本 + bubble_color = _detect_bubble_color(img, bbox) + + if voice_is_left: # 语音在左 (对方) + # 如果遇到绿色气泡 (我),或者是明显的右对齐文本,视为阻断 + if bubble_color == "green": + logger.info(f"语音({ax},{ay}) 被中间'我'的消息(绿色气泡)阻断: '{text[:10]}...'") + break + if c_x > w * 0.65: # 右侧明显区域 (short message check) + logger.info(f"语音({ax},{ay}) 被中间'我'的消息(右对齐)阻断: '{text[:10]}...'") + break + + else: # 语音在右 (我) + # 如果遇到白色气泡 (对方),或者是明显的左对齐文本,视为阻断 + if bubble_color == "white": + logger.info(f"语音({ax},{ay}) 被中间'对方'的消息(白色气泡)阻断: '{text[:10]}...'") + break + if c_x < w * 0.35: # 左侧明显区域 + logger.info(f"语音({ax},{ay}) 被中间'对方'的消息(左对齐)阻断: '{text[:10]}...'") + break + clean_text = text.strip() # 判定是否为时间戳 is_timestamp = re.search(r'(\d{1,2}:\d{2})', clean_text) and (len(clean_text) < 15) @@ -456,23 +535,68 @@ def _scan_chat_messages(image_path): if c_y < 150 or c_y > h - 100: continue - # 判定发送者 (增强版几何判定,防止 720p 屏幕下的中心点误判) - # 默认使用中心点判定 - sender = "对方" if c_x < w / 2 else "我" + # 判定发送者 (增强版: 几何 + 颜色) + # 1. 尝试通过背景颜色判定 (最准确) + sender_color = _detect_bubble_color(img, bbox) - # 使用边界特征进行修正 + sender = "unknown" + if sender_color == "green": + sender = "我" + elif sender_color == "white": + sender = "对方" + + # 2. 几何特征强制修正 (Double Check) + # 假设头像+边距约占 15% 宽度 + edge_margin = w * 0.15 min_x = min(p[0] for p in bbox) max_x = max(p[0] for p in bbox) - # 修正阈值:假设头像+边距约占 15% 宽度 - edge_margin = w * 0.15 - - if max_x > w - edge_margin: - # 文本框延伸到了最右侧 -> 肯定是"我" (因为对方的头像在左,文本不会靠右) + # 规则 A: 如果这一行极其靠右 (超过 85% 宽度),那肯定是"我" + # 即使颜色判成了白色 (比如光照问题),也得纠正回来 + if max_x > w - edge_margin: + if sender == "对方": + logger.warning(f"Sender detected as '对方' by color but geometry says '我' (max_x={max_x} > {w-edge_margin}). Correcting to '我'.") sender = "我" - elif min_x < edge_margin: - # 文本框延伸到了最左侧 -> 肯定是"对方" (因为我的头像在右,文本不会靠左) - sender = "对方" + + # 规则 B: 如果这一行极其靠左 (小于 35% 宽度),且不靠右,那肯定是"对方" + # 扩大判定范围,防止因为 OCR 稍微缩进导致判定失效 + # 注意:如果颜色明确为"我"(绿色),则跳过此规则,因为"我"的长消息也可能靠左 + elif min_x < w * 0.35 and max_x < w * 0.85: + if sender == "我": + logger.info(f"Geometry says '对方' (min_x={min_x} < {w*0.35}) but Color is '我' (Green). Trusting Color.") + else: + sender = "对方" + + # 规则 C: 如果颜色是 unknown,且不在极端位置,使用中心点兜底 + if sender == "unknown": + c_x = int((min_x + max_x) / 2) + # 简单中心判断 + if c_x < w / 2: sender = "对方" + else: sender = "我" + + # 规则 D: 强几何中心校验 (Final Geometry Verdict) + # 仅对短消息使用强几何校验 (宽度 < 70% 屏幕宽度) + # 长消息通常铺满屏幕,中心点在中间,容易受字体渲染影响导致误判,应信任颜色检测结果 + box_width = max_x - min_x + if box_width < w * 0.7: + # 如果中心点明显在左半屏 ( < 45% ),判定为"对方" + if c_x < w * 0.45: + # [Fix] 如果颜色明确是绿色,说明是"我"的左对齐文本(长文换行),不应被几何规则强制改为"对方" + if sender == "我" and sender_color == "green": + logger.info(f"Geometry says '对方' (center={c_x} < {w*0.45}) but Color is 'green'. Keeping '我'.") + else: + if sender == "我": + logger.warning(f"Sender detected as '我' by color but center is left ({c_x} < {w*0.45}). Correcting to '对方'.") + sender = "对方" + # 如果中心点明显在右半屏 ( > 55% ),判定为"我" + elif c_x > w * 0.55: + if sender == "对方": + logger.warning(f"Sender detected as '对方' by color but center is right ({c_x} > {w*0.55}). Correcting to '我'.") + sender = "我" + else: + logger.info(f"Message in middle zone ({w*0.45} < {c_x} < {w*0.55}), trusting color detection: {sender}") + else: + logger.info(f"Wide message (width={box_width} > {w*0.7}), skipping geometry check, trusting color: {sender}") time_pattern = r'(\d{4}年|\d{1,2}月|\d{1,2}日|\d{1,2}:\d{2}|昨天|今天|星期|上午|下午|晚上)' # 优先判断是否为独立的时间戳 (行短且符合时间格式) @@ -775,14 +899,15 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name=" dialogue_log = [] # 使用 debug_img 的尺寸,如果 debug_img 未定义(极端情况),默认 1080x1920 if 'debug_img' in locals() and debug_img is not None: - input_field_coordinates = (debug_img.shape[1] // 2, int(debug_img.shape[0] * 0.9)) + # [User Requested] 几何兜底 Y 轴应为 0.88 (避开底部导航条) + input_field_coordinates = (debug_img.shape[1] // 2, int(debug_img.shape[0] * 0.88)) else: # 尝试读取 current_image_path try: tmp_img = cv2.imread(current_image_path) - input_field_coordinates = (tmp_img.shape[1] // 2, int(tmp_img.shape[0] * 0.9)) + input_field_coordinates = (tmp_img.shape[1] // 2, int(tmp_img.shape[0] * 0.88)) except: - input_field_coordinates = (540, 1728) + input_field_coordinates = (540, 1690) # 1920 * 0.88 # 找出最后一条消息 last_msg = None @@ -827,7 +952,7 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name=" except Exception as e: logger.error(f"分析过程发生异常: {e}", exc_info=True) - return [], None + return [], (540, 1690) def clean_screenshots_dir(): @@ -982,41 +1107,119 @@ def perform_input_action(d, center_point, text, auto_send=True): try: # --- 新增逻辑:确保处于文字输入模式 --- logger.info("正在检查输入模式...") - tmp_check_shot = os.path.join(OUTPUT_DIR, "temp_input_check.jpg") - d.screenshot(tmp_check_shot) - wen_zi_template = os.path.join(TEMPLATE_DIR, "wen_zi_input.jpg") - input_text_template = os.path.join(TEMPLATE_DIR, "input_text.jpg") - - # 1. 检查是否存在 '切换到文字' 图标 (表示当前是语音模式) - # 注意:这里假设 wen_zi_input.jpg 是那个“键盘”图标 - wen_zi_pos = find_template_match(tmp_check_shot, wen_zi_template, threshold=0.8) - - if wen_zi_pos: - logger.info(f"检测到语音模式 (找到切换文字图标: {wen_zi_pos}),点击切换...") - d.click(wen_zi_pos[0], wen_zi_pos[1]) + # 优先使用 uiautomator2 的属性检测(比图像识别更稳) + # 1. 检查是否有 "切换到键盘" 按钮(说明当前是语音模式) + voice_mode_btn = d(description="切换到键盘") + if voice_mode_btn.exists: + logger.info("检测到语音模式 (UI树: '切换到键盘'),点击切换...") + voice_mode_btn.click() time.sleep(1.0) # 等待 UI 切换 - else: - # 2. 如果没找到切换图标,假设是文字模式,尝试点击输入区域标识 - logger.info("未检测到语音模式切换图标,尝试寻找文字输入区域...") - input_text_pos = find_template_match(tmp_check_shot, input_text_template, threshold=0.8) - if input_text_pos: - logger.info(f"找到文字输入区域标识 (input_text.jpg): {input_text_pos},点击激活...") - d.click(input_text_pos[0], input_text_pos[1]) - time.sleep(0.5) - else: - logger.info("未找到特定的输入区域标识,将使用默认坐标或控件查找。") - # 清理临时文件 - if os.path.exists(tmp_check_shot): - try: - os.remove(tmp_check_shot) - except: - pass + # 2. 检查是否有 "切换到语音" 按钮(说明当前是文字模式) + # 这一步不是必须的,但可以用来确认状态 + # text_mode_btn = d(description="切换到语音") + # if text_mode_btn.exists: + # logger.info("当前已是文字模式 (UI树: '切换到语音')") + + # 3. 如果 UI 树检测失败,尝试图像兜底 + if not voice_mode_btn.exists: + tmp_check_shot = os.path.join(OUTPUT_DIR, "temp_input_check.jpg") + d.screenshot(tmp_check_shot) + + wen_zi_template = os.path.join(TEMPLATE_DIR, "wen_zi_input.jpg") + + # 检查是否存在 '切换到文字' 图标 + wen_zi_pos = find_template_match(tmp_check_shot, wen_zi_template, threshold=0.8) + + if wen_zi_pos: + logger.info(f"检测到语音模式 (图像: 找到切换文字图标: {wen_zi_pos}),点击切换...") + d.click(wen_zi_pos[0], wen_zi_pos[1]) + time.sleep(1.0) + + # 清理临时文件 + if os.path.exists(tmp_check_shot): + try: + os.remove(tmp_check_shot) + except: + pass # --- 新增逻辑结束 --- # 1. 尝试找到原生输入框并输入 + # 增加多种查找方式 edit_text = d(className="android.widget.EditText") + if not edit_text.exists: + # 尝试通过 resourceId 查找 (微信常见ID) + edit_text = d(resourceId="com.tencent.mm:id/b4a") + + # 1.2 [User Request] 尝试使用 input_text.jpg 模板寻找输入框 + if not edit_text.exists: + input_template_path = os.path.join(TEMPLATE_DIR, "input_text.jpg") + if os.path.exists(input_template_path): + # 截图用于匹配 + tmp_input_search = os.path.join(OUTPUT_DIR, "temp_input_search.jpg") + d.screenshot(tmp_input_search) + + logger.info(f"正在尝试使用模板 {input_template_path} 寻找输入框...") + # [User Request] 降低阈值到 0.6 + input_pos = find_template_match(tmp_input_search, input_template_path, threshold=0.6) + + if input_pos: + logger.info(f"✅ [Template] 通过 input_text.jpg 找到输入框: {input_pos}") + + # 绘制调试图 (蓝框) + try: + debug_img = cv2.imread(tmp_input_search) + if debug_img is not None: + # 读取模板获取宽高 + tmpl = cv2.imread(input_template_path) + if tmpl is not None: + th, tw = tmpl.shape[:2] + cx, cy = input_pos + top_left = (cx - tw//2, cy - th//2) + bottom_right = (cx + tw//2, cy + th//2) + + # 蓝色框 BGR=(255, 0, 0) + cv2.rectangle(debug_img, top_left, bottom_right, (255, 0, 0), 3) + cv2.putText(debug_img, "MATCH: input_text.jpg", (top_left[0], top_left[1]-10), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2) + + debug_save_path = os.path.join(OUTPUT_DIR, "debug_input_box_match.jpg") + cv2.imwrite(debug_save_path, debug_img) + logger.info(f"已保存输入框匹配调试图(蓝框): {debug_save_path}") + except Exception as e: + logger.warning(f"绘制输入框调试图失败: {e}") + + # 更新点击坐标 + center_point = input_pos + else: + logger.info(f"❌ [Template] input_text.jpg 未匹配到输入框") + + + # 1.5 如果找不到原生输入框,尝试通过“切换到语音”按钮定位 Y 轴 + # 输入框通常与左侧的“切换到语音”按钮垂直居中对齐 + if not edit_text.exists: + try: + # 确保在文字模式下,左侧会有“切换到语音”按钮 + # 有时候可能是 "切换到键盘" (如果状态判断出错),都尝试一下作为锚点 + anchor_btn = d(description="切换到语音") + if not anchor_btn.exists: + anchor_btn = d(description="切换到键盘") + + if anchor_btn.exists: + # 获取按钮中心 Y 坐标 + bounds = anchor_btn.info['bounds'] + anchor_y = (bounds['top'] + bounds['bottom']) // 2 + + # 获取屏幕宽度 + w, h = d.window_size() + + # 更新中心点:X居中,Y与按钮对齐 + center_point = (w // 2, anchor_y) + logger.info(f"通过'切换到语音'按钮修正输入框坐标: {center_point}") + except Exception as e: + logger.warning(f"尝试修正坐标失败: {e}") + input_success = False if edit_text.exists: @@ -1126,37 +1329,3 @@ def match_template_center(image_path, template_path, threshold=0.8): -async def get_first_screen(device=None): - """ - 获取刚进入界面的首屏信息: - 1. 截图 - 2. 全量识别 (策略=ALL),包含语音转文字 Peek-and-Restore - 3. 返回识别结果和相关图片路径 - - Returns: - tuple: (dialogue_log, input_pos, enter_path, flag_path) - """ - logger.info("🔍 [get_first_screen] 正在进行首屏全量识别...") - - if not device: - device = connect_device() - - if not device: - logger.error("设备连接失败,无法获取首屏") - return [], None, None, None - - # 1. 截图 - enter_path = get_next_debug_path("enter") - device.screenshot(enter_path) - logger.info(f"📸 已保存进入截图: {enter_path}") - - # 2. 识别 - flag_path = get_next_debug_path("flag") - dialogue_log, input_pos = await analyze_chat_image( - enter_path, - flag_path, - device=device, - process_strategy="ALL" - ) - - return dialogue_log, input_pos, enter_path, flag_path diff --git a/WeiXin/__pycache__/T2_ChatMonitor.cpython-310.pyc b/WeiXin/__pycache__/T2_ChatMonitor.cpython-310.pyc index 623eeffc4ee8a740bf6447a122ec2bc4c4659a37..ee575b98ce0d945a88b2f08a2ce71499d2d6b738 100644 GIT binary patch delta 4131 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 P!;XNe`c>FW`OE(SQbma~ delta 5522 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 diff --git a/WeiXin/__pycache__/WxUtil.cpython-310.pyc b/WeiXin/__pycache__/WxUtil.cpython-310.pyc index a30fca3b56ee5f0f303722c5718259f5e8877551..9973afc116690d68e4e12c7184aae075ba4c7dee 100644 GIT binary patch delta 13291 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 delta 9973 zcma(%33OD)k-z`kBaQC+Vn6~kAPEqcxe^m!|I{8|KmqI^h`cdZ?RD zJSKUk(Mhxtp3`X+oea+!I*(4J)j&}TB@dkjr8+vF*3eog%>Y~-odKnpboRj8#A~tX z=C*K*>3qIzI?;K+_9no3`?mlN+v`ho+aX11r~Yo;8ddx((572kSvVGn>U@r)dYNul zgPn>msxnpt!s-?^476THphNM85UrCoopkClRlQc#7x6RI>*!XZs=|;bzv!5=Ku=Ts ztW{Bc)DP$~WJ9`jLcLDGyVI}2sB#3ov7crdN8 zOgB>{8qn=6!44(l?^Fb#0T%Her&W_R10SXNiM0XxvlV=YYbsgEkGLk~=~mXMGDTt2 zv84tlGd$8+0a=x9ti0mE6$$5&b9XC-V^&;4b zU>5+G05a7{7n_SXWTaa{${`=sWu<Mc zfWW&z{zo^y8U#M92pk=?c&!*a`xnGW()RCG6YG&1;Nt%gT zsqM5WZV@$es%BN?J}0&J6C$Z;ed(IiPvSP};15kGPB@DtnpP}nPHHkrg%UNDN=HfD zrMdu%?eI&rLus@A-p|tuLcrif5{seOWX^%j}og@dN8= zrj~h7ik_ia_+)2+E|X>_p!nE<^@21RQ;FMwTOKW-g_^zJBt)?b1r%xai_&Gmr&XfGPm_37 zYeJ$WP{?kT;yD`W%Llo0RjIE)%cZ5ggqF3)w49pyEuc9A>(pdgQ6#m!4 zw#Zm36tzOBWt~i?gOtYnP|6x;t`)>}qms@LG%%BM?5uGx+fm@wIW1SVXu%M^FU z)8gq`g_d^^IbVYwPyBx?aI`(Og3Y*u`T9wvi8*xcF<7*Xu#jOXyVVJBc#OwP)F!|n zA8Bqn9|euRuT4aA7fC|w4{`8NhQQ4S90(NsU!bjklFXoX;0gMG=F$p^Bx3^rHiIsJ zRud};nTV7S(wD))%`XBG=t83jG%yLxyn8OpxiVe_CRqfgs2XKbueoTY7*J+S$mpDW zpaOI_Nf>ysFz^y#;H6q6n0bj-1#p>`MhUHaR4$gFw)}Ehe*$)dP&;(jCV_N2P#|Mt zo|IsJxM@SL)Gwct!$CE>7e!prF2yH{mX)cNgQ=EAXqlo;JR!l3TLoIQYuPEiPNl1R zB`}v9<|*1WsG%@V2u5p>QM_84a!!uEqD@BFD`{B=2?+FxHdSD&5yA{hwQ|_#E-j;1 zYBAx`)$;l#X%$!!qL0>)4}0q#eXQ%nxv#%$6Fm|*TNsx8?jFJocMu{Sh`|2)9=eV9 z&`kyFrrksL;N5gck)Y#kwdrmWtsGc2YvucM>1J?YlHtN=VV{jYL_Y@$w;UA=Ub>7n z(JifV+@pD5)i~nQw6eZRZ5k~Ey_TIb9heVw^tEMoi3M)0St}b;h*k;%wg{rFHUfrr^0ht(*-ryO>$@zArHq94E_6fPb-fr)*D%wbWExI7d4>xj!!E2!@d zpMqXyuYpOVHV8Tuf1VVtMOUJMUj?RSR*oJsWYgSQEevU9Rn!38fzA&q*|IVv9VuJr;4$~wFGP=%>^e%%O$he5I75QEQ{Tr zMO(!lH?#eMQCn((3Pw2N)V?;e-f{aHYv?(|wg$0#Mc?4veQ}G!u?_?m1i}8h2~w`4 zeY`6H>g$5K<8ErPRytv=2=R?+fw4lD!~rMFlLPxGJZ>M6cwz<|GBdRt*fcZZv$R<> z0_#C(2Su_!s#Q=wPz)O^Ua!^1XNX~$fdh3$wR(sW46T_Dys6EAKL4f7fOd7U6e12u zn+c9;me4A;&xG^U-eTG!d1A3IK&a+)!-qdI{V{hY)OsBL7!8K&_D5QKTuh<9NH`c$ zvC3k+sb;wq!%KFEU#K}+gY?f5mM|U^9v-5TL#ap|s=9q~hre?l^)L1Cb+s!=`oQU0 zJ0TbO`MPS~?ejpvE@3Z|5vXD?DN47Im)F_6`61uxbsL*kHF;#FV&gdk-Pq__vAMZ< z^Mk2&(Rv#L-4R|PUOr>ibU)H_0Jkee+wH@M=pO;P-C)0}kwtm;j8aP%v^4XVN|(*U zd&0A_F4xV}No0Hw4tU+MxK#-$T@kjl1!Vr%3^@64fz&gF|9W->UsX1#_9~#**AR#c z$Ja+o#{C1U&jDzK?(7@9X(r<@&#EN3{N1u5{=uxtvy9={HK5T+`?ExM8n--E=~Bn^ zD|qx8JXY8LrAF9BjPUq<2K)-(o&n;h423KnURS^$neCo~Run8j$;No85k^JTor^ny zQ8f_mj4Zv1qg~){&;G@_PV5*)5J7MN0YeZ3;IXkbc;HPoMBzrY9~;C4XY7(;3xq?e z5>i3o#dYj6s_du6bl)v2U+@`w{7m$K@mpt$&-a({cjrG*p*!%(8&&BeqxBUVL?s4VDlO_2wujl`Kbl9bHv;Qn@_Wi z73}gc+Qu%W#NmHfFnPY7F0SS@7>#uJyFHUx53>I}0&$7MOQddzw)rCpYe4J@1S|Q* zg*DUIR&2C|1I*VRRCNba{M0Abkhn0GAZ00nGJbhsVPYCmn*-rUw{GqTh7=aZHgpoY zDXOUKFjh4LxaW0;5{kjaA2SIPv2N=0cd@CWT)Q;{tlUc5r8*Mp{7u4aB{=rMch z>gqg%9pi6pJf1exB0hu5*y({?4<<-r%w}T~!1G3U3XfH{`J>&TKrpOlVSbLqLZM)& z)z=XYNAygn`Cu_CT}mLPh8YtJMW`?V96UC!J=obQ5@ud|D#3?a82Wn6ozd1PTPn=} z3t3viu@Iz{BAte=MrW>P`o!oy^c1Q;+I~$QIJr42lYRWppIcZkCi-_!V08D*u*=pp zO)UEbA~FHA{>xAB^ZR8U`F0uTF!&EvetJRaZ1x$~T>+$x#JXdb`7fL5$qjDXawf4C z+5a;DJ;xvNcXanCzCfE_^#wcqtqS`iw$>m(KWAvA2TMl~{0PB6ArQ{)Ei9b^;B|I{ zL9>C5V4!`x$HeyUA((_<4FH|zHCHEEu{wm+v;a6zRS_CuyAl0E1hv>^ZH06t9CX4e zlM#)k(XAcffWITkzKfX8Bbbh07XZ&3;Ss%Q;g}kUVZv60jIWVf9L>{)`~{tgo|W2H znB@flTD#di{lYuMF<-ZL_@k;1)(blUNUx>a*RFJrCI28k_zmFMzatRN8F#uFB%&bqSQ1@UbA3C4TeK8{vX~=u-+EzT)IlgbocNgL3>{T^5E=%eQ7L7 zh-FxdKcH;lH+FjD3^FjZb2+JaAI1;Pu|ukQlh`Nh89=mpcAfp+e^&o~8E@EKWkv77 zIB(xQInjW{6$peRVlj(VBi4^t$7Ab#Y$e(5I$OmVq}u}!T)}bIHCh^H&GBk%35F=l zBNF48d(sKzWqUl!o zFO+RLKnbelzYMG>;Mm~`0zgX41zZ2cW@v5^g z4YIp1YzPxGgrlD&Vn83 zvB7QHWz1-s6f$ELl1MuwO`_Q}=QN2sXl~pI`TKPChG3D0?0Rw_%kSt1N+GZq0x%hx z$ko&QxD$Alm?3Z3eS=8xH0Y55y|ZXR0y7{+N`D&+1KH)YFQkPm#`6$41Sw!h-I_E4 znWJ=gqk;1*(#S|3P`^Wiw-|Tos>cTSY-4a37_zf!<~h<0`oymoxERF-szi=Kb`K+C z4pLt(EkV*9kU2Os&K#Nvvd_naIHlO0vNu{iPn7f3f__*pvfo6 zsy%_PM&PX~E{rTK3z)7GOebhrU`zKnTV@GcW{ub~R4L{$(U=Fmn>bDOr$hgJU@B2d z83Z*}g;XIYh>=St3o#Ho1xg7+@^q9AXVl{m_7}2lleR%(bBj#q-+ik(%HWl&AouSMKf=<84Q8JXev*GVRLyUNipVDZ ztI!Gvg-XI+GRR}$GbJu)j%C5%D>24XsC#Pi?3ZpIJ~CD3<&il%i^j!_4M3H;F=I)D zBOhT&M1pwOumA!P6aEQH;$ViHFdPeTCpK==;v~jcg})P7>Jh)J*Jr=iLh07G@@5J_ehVvStnuF>}@t!(k<>rl> zn|v$QG}W(ZYS?l2A^u&QqX?cv6oQ5lF{UTSh@0*>sf~l0zjkoap8K56V+T0gw89Zz zhte|cfWF`HtLKYLysfo1Sy3N8$R=D^6dF!?~}u~E`s+Mdi>Pw%TL3e zzWufH$(PQ)cdLK!$o2PcJSk{r)wu_IpG#i6DQfj~?&PD_Zl5>{jwIRp4R`g>^XG>i zPh4HnuyLaY9l)taljpu+1n9$WeADnj?uXzHF}3~>t!;u#U6?6?)$BU=@U6#^7oRd% z4ZY3>pSqO%@~NSV-%dU{00R!4JTd(0_2kWCf?R+mMUAmgbfKt#Gj^-7NQbg8;jX6e zAC7P<1XTcQ+{T82?NQ@4f@i#y)wo+a{Koy-BN)UmGX3DnW2fCa@v`0$WN>jZcG_~xxC zD=~v7NQYdP6LVnTBSyYi|C30!vAMt=(k4q#uoqDpc@s;=r)d*Ai%2mk!eWRc&7OL1 zdMt=O6!uk^)f@k(5&n4yD0Cax5fXK+kh1KK8rMy{M!|n-Vx5t05y2ZdPrPPTG7pT- zrXiS)(@ck(_I@P*37BwL716!Uv%98{AMgjdCMEFJ0Iwbzwyg3sHZ(VgKNzh99D5jn zI6TC;u^g+55v)Pbj9?pry$D(noI>zx1ZV-~KnXGsh>Mpvc+dr~8pNTO06k3sxDb(J vKzMHZ;?6LQbtp@LOp0#jf9q-}krGnEISHY?((bYsI7^%r&UwzxjA{P|T})F~96xiLim<4!d%BoqtqQDs7a}@?5RFhqTPUPJZQ*he%17FQGB$LP1(Ise z7nki}%a&|0d(k~DzAV{`?qSI^i|)a=&4=y5>?6@+n_B|={m$Xo7AUkRY`MAR+3<*P2<1P^n+!dk_e~a|g{KbD0_wbqMK9<(F*3wY=68W<^AFeW=cWQL zh;0&04}LpDkBADNq&9%3pg1lLNlrmLHQ>F4`HmG)6p650N4l?u?7qgPp;Lxvn5hN= zL-_nJPf+$^!TG%yrRrsM6h2`BRU7W1RgsXkA@<{5oi6oPBrlC@Qv6>oA~F^(>a14U z#Dq0y_i=Fqy6DANuSxz(ZM!fR6CUW=6y4BVFK$!n#1M9P)JlG+bh5n16@hjFo3eW~ z_{4yU_OZlntniCYc(Ebe;5jIfn4(Uc1QHX7Vpz$TNYF8)U(}0Hcv2YBHDeUSD1o~U z^dtBx0?B(oS;@$VI4;IOwH9-OxPqYD173Qy;=$jQWloMgu6&xYXKObC&z#=cb^onP z?t7hc@qga8C{M=lj`@yT6EMub&D;2J?RT0;1LQLmuR{BlLk=>E|e-yBwaw z8c`wrTIG8u-Z(S16LD040&_I2vKo$`0)QZ?I#7qNX$suPvs4MiLqoM|wtJaJ&TMMSv!*ZZu{_u;7@8%EC@968@19smX zjQg6uxo|DM*G-&x^Piu&?Rn(C8{g*2bxoVu`5m=eg*v`gze`13hWzaW&Ig@$o7(($ zX3ge3p}AICA-T#CH8bSWI-VRwtoWt~A)+kw6~(ws@nyz~1{Syi6K3kGV$D0ILYq~)b1g=K61 zYiwWBCtU_S)PSE9!Gcai8hbRm*m`l_Yt(R!oN=Pg@hOuWq>*N-rFtM6m353AqamwW z47IZlHj3hzb)UV=@w+SG{Frm;-Bh-c5>79(0_2TadG`gEQTDpET-wr(2zBxu-dGoAc z-%bW(#AZ?jbX@8T_GEobYDd7_wx#b1ESzdvY9!s>K7CK{G;F(eDMwNBDrI#t9`|UJ z@2r1ENcCh6?3_qHXfe{ZxhN$&v~2C4ImS9v5H~6b>jqs1ETr41RQT*1hphImp?4{l+sHN-A(SnU)j z+w+%QsD9+j{(wA6&_xx?cYI@reG+vKq$P&^k5LgBH8U<{SN&N;c0zW@&W zLmt(EEtcnyl!xa4)Dq7bdN3-5&8V9(d78yjBA&51@Rm43_?ybPV}TEXaOX`|R@mx&HB1jLhU*fK&PYG0$SK0A9`w5QBwFgbBI!*!i`kLKXzN~NP$$NW-y*rEIVm#$ zbEbtah=$QkyazE(&5enZy>yxO$+9fD)@;Buu7?Hfb;BIp!+8>XHAdspoZFNX*+2v&=-y zFELcpGU!}&|GOp4rL%fJqvmbEq&THt$q2H6Lv$fKFdGJeQhisCaabZxUZ*lP^H2o1 zxYtyxS^Y-wtk$YkoF{rMabg#x_a&)&k~gJKEvHdZIw>hB`izg2^~t)DEq{$&p-#3% zDdI=v)k;|{7wcHPU2ps!hI6d#9Uo0z9XZgv$+42}lpsHrNLy6OUTrB}c5vzO{x=@1 zcVUasF7;r>Ad(-`3lM^bvKTpm|B2-3oCDdR92r@y)^V@juQ%4u qyuY}5{rP=UWBk9O-FM&n^@Rh7`SjRJQD0R3(->FAzESy}`TSqj3Ik^V