dev 02011812

This commit is contained in:
Julian Freeman
2024-02-01 18:12:45 -04:00
parent 3e17def627
commit 38d98dee94
14 changed files with 536 additions and 406 deletions

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# DailyCheck 日常检查工具

View File

@@ -1,9 +1,9 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>images/none_128.png</file>
<file>images/blank_128.png</file> <file>images/blank_128.png</file>
<file>images/browsers/chrome_32.png</file> <file>images/browsers/chrome_32.png</file>
<file>images/browsers/edge_32.png</file> <file>images/browsers/edge_32.png</file>
<file>images/browsers/brave_32.png</file> <file>images/browsers/brave_32.png</file>
<file>images/dailycheck_128.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -36,289 +36,239 @@ p\x0d\x80k\x00\x5c\x03\xe0\x1a\x00\xd7\x00\xb8\x06\xc05\
\x06\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x80\ \x06\xc05\x00\xae\x01p\x0d\x80k\x00\x5c\x03\xe0\x1a\x80\
6\xf3\x00\xedw\x03\xfd\xcd\xd11\xb4\x00\x00\x00\x00I\ 6\xf3\x00\xedw\x03\xfd\xcd\xd11\xb4\x00\x00\x00\x00I\
END\xaeB`\x82\ END\xaeB`\x82\
\x00\x00\x11\x82\ \x00\x00\x0el\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\ \x00\x00\x80\x00\x00\x00\x80\x08\x03\x00\x00\x00\xf4\xe0\x91\xf9\
\x00\x00\x00\x09pHYs\x00\x00^|\x00\x00^|\ \x00\x00\x03\x00PLTEGpL\xd3\xb0\x22\xfb\xd1\
\x01\x00\xc7|\xb4\x00\x00\x00\x19tEXtSof\ *\xe5\xbf%\xd3\xc1'\xf9\xd1(\xdf\xba$\xe8\xca*\
tware\x00www.inksca\ \xd7\xc4)\xd8\xc2'\xd8\xb4$\xd2\xaf#\xfa\xd1'\xfd\
pe.org\x9b\xee<\x1a\x00\x00\x11\x0fID\ \xd4-\xf4\xcd&\xd1\xaf#\xcf\xad#\xd0\xae\x22\xf9\xcf\
ATx\x9c\xed\x9d\x7fp\x5c\xd5u\xc7?\xe7I\xf2\ '\xfa\xd0(\xf6\xcf&\xca\xb0#\xcd\xab\x1f>\xb56\
\xbe]\xd9\x0d\xe0X\xde\x15\xb61\x8e\x81\x00\xa1m\x0c\ \xfa\xd1'\xfb\xd1'\xfb\xd2)\xfa\xd0&\xd3\xb0#\xd0\
I\x1a0\x04\x13~9\x09?Z\xb7\x04\x08N\x1b\x97\ \xae&\xf8\xce%\xf9\xcf'\xf8\xcf&\xf5\xcb#O\xcc\
I\x93\xd0\xa4\x1dp;\x04\x1ahH\xe2$\xd8PJ\ ?\xd0\xae\x22\xd5\xb2%\xd3\xb0&\xcf\xac!\xfb\xd1(\
:\x19\x06&\x93\x940\x86\xd0P\x87\xf2\xc3.P~\ \xfb\xd1(\xfd\xd3+\xfb\xd2)\xfd\xd4-\xf7\xcd\x22\xf9\
\x19(`2L\x0a\x18\x1c\x036\xd8\xd6[\xcb&6\ \xcf%A\xbe9\xd3\xb0$I\xca@O\xbf7\xfd\xd3\
\xd8\xd2>E\xbb{\xfa\xc7[\xc9\x92\xd0j\xf7\xde}\ ,\xf6\xcc \xf7\xce(\xfa\xd1'\xd1\xaf#\xd3\xb0%\
o\x7fh\xf73\xa3\x19k\xf5\xce=\xd7\xba_\xdd{\ \xcf\xad \xd3\xb0#\xd1\xaf'\xd3\xb0&@\xbc9\xfb\
\xdf\xbd\xe7\x9e+\xaa\xca\xa4BDz]\x0eW\xe5\x98\ \xd1(\xf9\xcf$\xfd\xd3,\xf8\xce\x22\xda\xb7'\xd4\xb2\
\x1c\x1c.0\x17\x98\x830\x03\x98^\xf8r\x81)@\ '\xd8\xb5&\xd3\xb1'\xd0\xae\x22\xcc\xaa\x1e\xca\xb3\x22\
g\xc1\xaa\x0f\xf8=\xe0\x03\xef\x14\xbezQ\xb6)l\ \xfe\xd4-\xf3\xca#\xfd\xd4,\xfd\xd3,U\xdeK\xe2\
u\xe0MGx\xb5\xcbg\x0b\x93\xec\x17&\x8d\xfe\xff\ \xbd(\xfd\xd4-P\xd7F\xe0\xba%S\xdcHZ\xdc\
\xd9\x9e\x90C\xdb\xe1$\x85\x13E\xf9\xb8\xc2G\x80i\ J=\xb46D\xc4;=\xb75<\xb25\xf7\xcd!\
\x11\xb9\xdb\x07\xbc\x84\xb0A\xe0\xe9A\xe1\xe9\xd9}\xda\ L\xd6B\xef\xc6 T\xddJO\xdbF\xf6\xcc \xd9\
\x13\x91\xaf\xaa\xd0p\x02x]$\xd6\x19\xe3T\x1c\x16\ \xb5$C\xc0;A\xbc:\xf3\xc9!\xf7\xcd\x22\xd8\xb5\
\xa3,\x06\x8e\xacq\x956\xa1\xac\x05\xd6\xf6\x0d\xf0\xc4\ &<\xb35W\xdfM\xd6\xb4*X\xdfN\xdd\xba*\
|\xd5\x81\x1a\xd7\xc7\x88\xc6\x10\x80HG:\xc6\x99*\ \xf1\xc8$C\xc1;\xf7\xcd\x22V\xddJ\xb7\xbd(\xfd\
\x5c\x80p\x1e\xca\x07j]\xa5qQ\xf6\x22\xdc+\xca\ \xd4-G\xc7>>\xb56V\xddKd\xdcF\xed\xc4\
\xdd\xc9\x01\x1eB5[\xeb*\x95\xa2\xae\x05\xd0\x93\x90\ \x1f\xff\xcf'\xff\xcf'\xfb\xd1)\xfb\xd2*\xff\xff\xff\
\xd9\xc0\xc5\xa2\x5c\x06\xcc\xaeu}\x0cI\x0b\xfc\xbb\xa3\ \xf9\xcf&\xfa\xd0'\xfa\xd1(\xfc\xd3,\xf7\xcd#\xfc\
\xdc\xd6\xe5\xeb\x1b\xb5\xaeL1\xeaR\x00^B\x16\xa2\ \xd3+\xfc\xd2+\xf7\xcd\x22\xf8\xce$\xf6\xcc!\xf5\xca\
|\x03\xf83\xa0\xad\xd6\xf5\xa9\x10\x05\xfeG\x95\x9b\xbb\ \x1e\xfa\xd0(\xf6\xcb\x1f\xf9\xcf%\xf9\xd0'T\xddJ\
}\xbd\xaf\xd6\x95\x19K\xfd\x08@Dzb\x9c#p\ \xf5\xcb\x1f\xf4\xca\x1d\xfd\xd4-\xf4\xc9\x1c\xf6\xcc \xf8\
-\xc2\x82ZW'\x22^P\xe5\xbaz\x12B]\x08\ \xcf%P\xdcGW\xdfNV\xdeLK\xd9AI\xd8\
\xa0\xc7\x953D\xf8\x1e\xf0\xb1Z\xd7\xa5J<\xafp\ ?\xf3\xc8\x1a\xf8\xce#S\xddIR\xdcHE\xd6<\
uwF\x1f\xa9uEj*\x80m\x9d\xd2\xdd\x9e\xe7\ U\xdeKP\xdbFF\xd7=G\xd7>D\xd6;O\
\x07\xc0\xd2\x9aU\xa2\x86\xa8p\xbf\xc0e\xa9~}\xbb\ \xdbEH\xd8?\xf4\xc9\x1dN\xdbEC\xd5:B\xd5\
Vu\xa8\x8d\x00D\xda\xbd\x18\x97!|\x87\xe8\xde\xd9\ 9\xfd\xd3,\xfd\xd4.\xf7\xcc\x22\xf7\xcc!L\xdaC\
\x1b\x85~\x85\x95{}V\x1c\xa3\xfa\xfbj;\xaf\xba\ \xd1\xaf'\xfd\xd3-G\xd7=C\xd6:\xcb\xa9\x1eM\
\x00\xbc\xb8\x9c\x02\xfc\x188\xb6\xaa\x8e\xeb\x1d\xe1%\xe0\ \xdaD\xfe\xd5.M\xdaCY\xe0OY\xdfO\xcf\xac\
k\xa9~}\xaa\x9an\x9d\xaay\x12i\xef\x89\xcb?\ !@\xd47A\xd58\xdf\xba&\xf5\xcc&F\xca>\
\x03\x8f\xd1j\xfc\xf7\xa3\x1c\x87\xf2\xa4\x97\x90\x7f}]\ A\xca8\xfe\xd4.\xeb\xc4'\xda\xb6$\xd9\xb4\x1e=\
$V-\xb7U\xe9\x01z\x122[\x94;\x81\x93\x22\ \xb65\xd4\xb0\x1e\xfc\xfe\xfc\xe6\xc0(\xe9\xc1\x1d\xef\xc5\
wV\x9a=\x04\xeb\xfe}\x08\xfb\x00\x07\xa5\x138\x98\ \x1cK\xd6BC\xd0:\xdf\xb8\x1f\xef\xc8$\xd4\xb1&\
`o`J-+\x07\x80\xf0\x1br\x5c\x94\x1a\xd0W\ \xfa\xfc\xfa\xf0\xc9)N\xc9F\x8d\xd36H\xd1?\xe2\
#w\x15\xb5\x00v\xba\xf2\xb9\xbc\xf03\x82M\x98j\ \xbb\x1eB\xc58\xe7\xce%\xed\xed\xed\x9c\xeb\x97z\xe4\
\xb1\x1bx\x1exQ\xe1M\x07\xb6\x00[\x92>\xdbK\ s\xe2\xe6\xe2\xf7\xf7\xf7>\xc06\xc2\xf2\xbe\x9b\xd69\
\xae\xce\x89tl\x8f\xd3\xd5\xae\x1c\xa9\xca\x91\xc0Q\x08\ \xd3\xd0*Y\xc9R\xbe\xd8\xbd\xf4\xf4\xf4\xf1\xfc\xf1\xc8\
\x1f&\xd8c\xa8\xe6b\xd4>\x11\x96%\xfb\xf5\x97Q\ \xd2.\xdd\xf8\xdby\xdbD`\xddI\xf6\xce)z\xcb\
:\x89N\x00\x22N:\xc6\xf7U\xf8\x07@\xa2q2\ u\xae\xd4\xab\x88\xcd\x83\xfe\xd5/\x87\xe7\x81\xb4\xd31\
\xccn\xe0\x01\xe0\xe1\x9c\xf2\xdc,__\x8f\xc2\xc9v\ \xdf\xcd%\x9c\xc3+\xcb\xdb\xcae\xc9^\xa1\xd1\x9e\xaf\
W\xe6\xb7\x09\x8b`\xf8+\x19\x85\x9f\x11\xa8\xc0\xf5I\ \xef\xab\xbf\xd2/j\xdbDm\xe2ec\xdeZ\xcd\xc4\
\x9f\xabP\xcdG\xe1 \x12\x01l\x14\x99r\x88\xcb\xed\ !\xd8\xe0\xd7q\xcbk\xb6\xef\xb2\x84\xe2}X\xd7P\
\x0a\x9f\x0f\xbd\xf0\x03\xecP\xe5N\x11\xeeK\xf9<\x8d\ \x98\xd0\x94[\xc16\x8b\xb6+l\xc97\x8e\xe7\x88o\
j.B_\xe3\xe2u\xca\xf1\xe4\xb8\x04\xe1b\xa0+\ \xb0.\xfb2\x02\xfb2\x02\x90\x0aZ\x1e\x00\x00\x01\x00\
BW\xab\xf7\xf8|)\x8a\xb7\x84\xd0\x05\xb0Kdj\ tRNS\x00!s\x17\x05\xcf\x0b\x07\x02\x01\x13n\
\xd6e\x0dpz\xa8\x05\x07\xe4TXK\x9e\xdb\xba\x07\ !\xf3\x1b\xd6\xcc\x8bH6S-^\xb8\x10\xab\xb9\xa1\
x\xb0n6[D\xdaw\xc68;',\x158\x9f\ \x98\xf4h/@{\x0c\x81K\xb3R\x5c\xf0\xc0\x0e\xfa\
h\xe6\x11\x0f\xb5\xfb,\x99\xa1\xba?\xccBC\x15@\ \xf0\xb1Dd\x1f\x15\x83\xfb(\xfa\xc0vD4\xfa\xed\
z\x9a\xcc\xd0,\x0f\x02'\x84Vh\x80/\xc2my\ \x8a\xe0\xc7\xd8\xf6\x8d\xe3X\xa3\xaa\xe9@\xeab\xdd\xe6\
X\xd9\xdd\xaf\xdbB.;T\xbc\x84\xcc\x11X\xae\xca\ \x93\xfc\x92(\xc6L\xcd\xc9vR\xd2\xe8\xaf\xa1\xea\xab\
\xa5@<\xe4\xe27H;\x9fM\xee\xd3]a\x15\x18\ \xe5\xf3\xa5\x96\x90\xdd:\xec\xe5\xc4\xf5\xf9\xc2\xe8\x90d\
\x9a\x00\xbc\x84\xccAy\x98p\xf7\xe7}\x84[\xb3\xc2\ \xfe\xdc\xda\xf1\xd7\xb8\xf7\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\x0f\x1b-\xf0\xa2w\xaa\xcc\xccg\xb9\x5c\x85\xaf\x12\xee\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
b\xd7&\x843\xc3Z=\x0cE\x00\x85\xbf\xfc\xa7\x08\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\xb3\xf1\x95{p\xb8\xbc\x96\xcb\xa4a\xd0;Uf\xe6\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
r\xac\x04.!\xbc\xc9\xf0&i\xe7\xe40z\x82\x8a\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\x05\xe0\x89$\xc4\xe5\x11\x85OVZ\x99\x02o\x88\xf2\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\x8d\xa4\xaf\x0f\x86T^]\xe0\xc5\xe5d\x82\x15\xd0\x8f\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\x84T\xe4\x0b\xed>\x8b*\x9d\x13T\xb4\x12\xb8Qd\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\x0a.\xf7\x86\xd4\xf8\xaa\xca\xaa>\x9fc'[\xe3\x03\ \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
\xa42\xba>\xe5\xb3\x00\xe1\x9b\x04\x0bQ\x95rB\xd6\ \xff\xffss\xaa\x1e4\xff\x00\x00\x0a\x1bIDAT\
\xe5\x9e\x8d\x22\x15M8\xed{\x00\x11'\xed\xb2:\xa4\ x\xda\xbd\x9byX\x14G\x1a\xc6\x9bc`\x86C\x06\
W\xbd^Q\xbe4\x19\x1b~<\xbcN9\x9e<\xbf\ \x04\xb9QNAP\xc1\x03T\xf0\xbeWQ\xe3\xb9f\
\x00>TiY\x02w%}\xbe`\xbbN`\xdd\x03\ \xe3\x15s'\xee\xf5<q\xdc\x85]\xc55\x82x\x22\
\xa4c|?\xa4\xc6\xdf\xee\xb4s\x5c\xb34>@\xaa\ F\xa2\x04c\xd4\xc4N2\x98\x80\x19f2\xc3\xc00\
O\x7f=\xe0s\x02\xca\x9aJ\xcbR\xb8\xd0s\xf9\xae\ r\x88 ^A\xbc\xa2bL4\x9a\xf5\xcc\xbd\xbb\xcf\
\xad\xbd\x95\x00\xd2\xae|\xb6\xb0\xc2\x17\x06\xb3\xf2Y\xfe\ \xb3\xcd\x0c\xd3\xf4Q=\xf5\xf5\xd0\xf0\xfb\x8b\xae\x99\xfa\
.\xa4\xb2\x1a\x86\xb9\xaa{S\x03,A\xb8\x02\xa8t\ \xde\xb7\xab\xaa\xab\xbf\xaa\x1a\x08\xc2Q\xfa\x07\x8f\x9f\x91\
\x95\xef\xca\xb4+\x8bm\x0c\x8d\x87\x80\x1d\x09\x99\xe5(\ \xf8\x0d\x85\xf7\xac\xf1!\x11r\xa2\xb7\xf1K<f\xa3\
/\x02\x1f\xb4qX\x0cUVu\xfb\x1a\x96\xa8\x1a\x0a\ \xc3E\x88k/\xeb\xbb\xcf8\xc6\xc6\xb9\xb7\x1b\xe0\x0b\
/!\x7f\x8a\xb2\x9a\xe0\xc0\x8a-\xbf\x03\x16\xa42\xfa\ 6\xdf\xc8z\xd9\xc0\x9c#\x5c\xfa\xf4\xae\x815<\x03\
\x96\x89\x91Y\x0f \xd2\xee\x04\xbbz\xa16~P4\ \x11\xbd\xaa?\x82\xa7\x7fdp\x8f\x89\xc9\x83\xf8#\xdc\
\xcb{\x5cY\x19v\xb9\x8d@\xaa_\xd7\x08|\x06x\ 9\x9f\x87\xb7{\xcf\xc8\xbb\x0e\xf3\xce\xf7\x9e\xc3\x0d\x9e\
\xaf\x82b\x0eQ\xb8\x0b\x91\x0e\x13##\x01\xa4\x83\xb1\ \xc07\x90\xef\xc7\xf9N\xdf`Y\xc2\xc0n\xeb{Y\
f\xa1Q\xb5\x0c\x10a\xb9\x17\x97\xefEU~=\x93\ \xa4\x8e\xc9\xbcX\xa5\x83\x12\x11\x06\xe6\xb0\xbe\xa2p\xb6\
\xcc\xe8c8\x9c\x06X\xbf\xdb\x0b\xfc\x89\xe7\xf2m#\ |'8\xa8{\xfa\x03\xbd\xad\xc1\x8fy\xfb3\x8b\xfd\
\x9br\x87\x80B$\xcf\xe3D\xbf\xb3\x07\xb0\x22\x95\xd1\ \x11\xfa\xf9\xbfcY\x0c\xb6\x95\xf6\xed\x86\xfc\xa0\xd0\xfc\
\xab\xab\xe0\xa7\xee(\xbc!<\x86\xfd\xeaa\x1e\xe1S\ #\x9dq\x8e\xe4\x0fc\x8c\x84P\x94\x81\xfc\x11\x8cy\
\xe5F\x16\x95\xd7\x03\x88\xb4\x8b\xf0#\xaa\xd3\xf8\x00W\ z<]\x9a\xe8\xacpT\xbf\xef\x9a\x8f\x18|\x91@\
5\xedp\xd0\xa7\xbf\x168\x0f\xb0=b\xe6\xa0\xdcR\ ?\xe9\xf2\x19\x1f\xa1pf4\x1c\xb3<\xc4\xc5\xb1\xc1\
\xeePP\x96\x00z\xe2\x5c\xae\xca\x1fZV\xc8\x8af\ \xef\x9cx\x98\xcd\xac\x016g\x87\x91$\xd8\xa6\xe99\
\x9e\x13\x14\x86\x83\x0b\x01\xdb-\xeec\xbd8_/\xe7\ B\x15EM\xf6!Y<l\xad9,\x0bI\xe2 \
\xc1\x92C@!\x9ck#0\xd5\xb22\x95\xd2\xb4\xc3\ \xeb\xb8\x95e\x1d\xe6~4L\xfc\xdb\xd2\x19\xa1p8\
A:!W\xa8\xb2\xca\xd2|_N8zV\xbf\xee\ \x7f\xb0eP\xcb\xd0\x06\xb2,\x03\xd5o\x06\xea\xa3a\
\x98\xe8\xa1\x92=\x80\xe4\xf9\x17\xec\x1b\x7f\xbb\xa5\xddH\ \xa2\x0d\xbc\x82\xd6X\xd3\x9f \xfa\x08\xe8g\x85R\x1d\
\xaej\xde\x89!7\xa2\xfc\xca\xd2|\x9a\xa3\xdcX\xea\ '\xd0:\xa2\xa7)\xc5<t\xa0\xc3\xde\xce\xfdC\x84\
\xa1\x09\x05\xd0\xe3\xca\x19\x08K,+\x90v\xda9\x1e\ \x0c$\x0e\xec\x9b \xf0Q\xe3\x08\xb1-\x10\xd6\xfe\x0f\
Xai?\x92\xe6\x14\x81\xaa\x0e\xba,\x03\xb6\xda\x98\ )\xc9\x14\xfb$(\x9e7I\xa9\xbf\xf6E\xf1O\xe1\
\x0b\x5c\x90\x8e\xcb\xa2\x89\x9e\x99P\x00\x22\xd6K\x8c*\ \xa4\xb5\x12\x1a\xd0{\x8a\x7f\x0e'6\xfeS:^v\
\xca\xa53\xf7io\xa1\xfb\xfe\x8ee9#i\xca\x89\ `.\x0a\xc8\x94N\xbf=\xd6\x91\xa9\xd0g\xadT\xfa\
\xe1\x9c\xbd\xbaG\x1c.\xc0r\x03I\xe1\x9a\x89~^\ Y\xfa\xc9\x8e\x18Hk\x5c+\x15\xcb\xdc\x1c1\xd0'\
T\x00=\xae\x9c\x0b|\xdc\xca\xa9rC\xd2\xd7\x07\x86\ S*\xfd\xf6)\x8e\xbd\x0d_\xdc,\x91\x01S\x8cc\
\xbeOe\xf4\x1aB\xe8\x09\x9au\x9d \xd9\xa7\x1b\xc0\ \x06\x86\xea\xa5\xd1\xcfJwp\xd9\xe4\xf1\xb24\x06\x1a\
\xec\xfd~\x04\xa7\xf6$\xa4h8~Q\x01\x88L\xac\ ':\x9a\x91\xc4\xb6\xbf)\x05\xa6\xe7\x1d50Y/\
\x9c\x09\xd8\xdc?\xc0?\x8d\xfd\xb0\xd0\x13\xb4\x86\x03K\ \x89\x81I\x0e\xe7dnIR\xe8\xaf\x8dt<+\x1d\
\xf6\xf8\xac\x02\xac\xce\x09\x88Rt\x12=\xae\x00v\xc6\ ,I\x0b\x80\xf3b\x85\xd7\x08\xaf>A\x83\x18Y\xf1\
\xe54\xe0xKg\x7f_,MJk8\xb0\xa7\x10\ X\xa9[\xc0=(\xc8\xcbK\xe8\xbd0`\xbc\xadJ\
\x11\xfc\x15\x82|\x03\xa6,\xf6:e\xdc8\xcdq\x05\ |\xfc\xac\xb1\xe3e\xb2\x84\xf87\xa5A6N&\x1b\
\x90\x87+,\x9c\x80rO\xa9m\xdd\xc2pP\xb1\x08\ ;6>~d\xe7u\x88\x172\x09\x1f\xf9wq\xb4\
\x9aq\x9d \x95\xd1'\x81;\xad\x8cs\x5c9\xde\xc7\ 7\x9a25\x06\x8d&\xd3\xd4\xb8Yd\xd5\xb1\xa8\xf5\
\xef[\x07\xe8u\xe5C9a3\xe6\xab~>\xc2Q\ R\xb08u\x93\x81d\xa0\xd1\x8b\xf3\x80\xd8Oq\x8b\
\xe5\xc6\xf0\x15\xba\xf1\xab\x0c}\x8cGS\xad\x13\xec\x9a\ \x17Q\xbfBC\x89\x86\xa7\xfazN\x8e\x89\xe9\x97\x16\
*\xc9l\x8e\xcd\x98\xbf\x9a\xe7\xdb\xe0\x88\xae\x8c\xbe9\ \xa6\x1cC\x92\x0d\xa6v\x11\x01\xc6\xf1\x0d\xf8\xff\x0b\xcc\
\xf2\xc3\xf7\xf5\x009\xe1Rl\x96|\x85[M\x028\ :\x13I\xa6\x87\x8dr'\x14\x1dX\xdc\xc7M\x1dC\
[\xc3\x81\x1d3\xf6kZ\x95[,L\x9d\x1c,\x1b\ Y\xd8\x0c\x8f\xc1\xdfO\x09\x05\xd7ml 'y\xb2\
\xfb\xe1\xe8\x1e@\xa4\xddsy\x1bH\x19\x16\xee\xe7\x84\ \xdf\xaf\x94\x0f\x17\xdfp\xd2P\x01\x0e\xc2\xdbOq\x8f\
\xf9\xa5V\x9d\xc6\xc3\x8b\xcbu\xc0\xb7L\xed\xc6\xd2L\ \xff\x1b\x10=9&\x1a\xf9zw\x99B64B\xa3\
\xf1\x04\x85H\xe3-\x98\x9f;H\xa7|\xe6\xa0:8\ \x84p\xa7\x00?\xb8\xbe\xcfh\xa1\x89$.\x9d\xac\x07\
\xf4\xc1\xa8\x1e \x1d\xe3,\xcc\x1b\x1f\x84[m\x1a\x1f\ \x86\x19\xc9}\x0eB\xc1\xfa\xb1\x83\x84\xa72'\x1f\x12\
Zs\x02\x1b\xba\xf6\xebN\x15~ja\x9a\xf4b\x9c\ \xda\x0639\x1b1\xc0\x1e\xa8'\xa7\xd8{\xb7+<\
6\xf2\x83Q\x02P\xe1\x02\x8bBs\x8a\xf5z5\xd0\ \x94\x0d\x15\xb0H\x9c=\xad\x01\x9bAT4\xc4b\x96\
Z'\xb0A\x94\xeb\x81\xc1\x92\x0f\x8e\xb5\x93\xd1q\x9c\ \xb9\x1e>\x86u\xa0P#Y\xe3H\xb1\x12f@\xa3\
\xc3\x02x]$\x86p\x9ei\x81*\xac\x0d\xe3\xb8V\ \xc4\xae1\x9d\xd2M\xb0X\xfe\xec\xd4\xa3b\x1d\x80\xfa\
k\x9d\xc0\x8cB\xe8\x97\xf1>\x81\xc2\xf9#C\xc9\x87\ p'\xfc+-\x86\x04\xc5\xda\x1e\xc6\xaa\xe5k\x80\xd4\
\x05\xd0\x19c\x91U\x06\xce<\xb7\x19\xdb\x14\xa151\ 2\x80\x16XS4\x10\x03\x1a_v\xc3\x85\x93\xa6\xed\
4C\x95;,\xcc\x0e>\xc8\xe5\x94\xa1o\x0e\x0c\x01\ \xd8\x06P\x82\xf69<\xc2\xf17Sa 9\x0f\xf3\
\x0eg[\x14\xe6u\x0f\x10j8wkNP>\x85\ \xa8I$\xb6\x114\xc0\xec:\xda\x84\xd3\xd7\x93\xa4/\
\xdf\xfdNS;\xd1\x03m}@\x00j!\x00eu\ \xcf\xf7T\x92\xd4\xdb\xad\xb5\xc1\x07\x98\xdby,\xc3\xdc\
\x14G\xb4[s\x822Q\xcd\x8ap\xb7\xb1\x9d0\x1c\ \xbe\x86LG\xad\xd5&\xa7\x93\x9a\x8a\xb7\x84\x81/\xb1\
B\xee@\x90r\x1d8\xca\xa2\xa0{\x8dm\xca\xa45\ \xa7\xd8\x0b\xf3\x96\xbe\x81\x8cE'E.\xb1d\x83^\
'(\x13\xe1\xe7\x16V\xc7\xec\x9a*I(\x08\xc0\xb1\ \xb8\x22|\x93#\xc6N\x14\xea\xf6\xc3\x87\x0a5\xa5b\
\x8b\xf4}'\xe5\xf3\x8c\x85]\xd9\xb4\xe6\x04\xa5I\xf6\ h\xb8\x9dF\x98\x07\xdf\xe0\xce\x14\xd4\xafo S\xed\
\xf3\x02\x16\xc3@6\x1f$\xecr\x00\x04N4-@\ =\xcbN\xa9dC\xfdv4\x22\xd67\xf3\x04Bd\
\xe1\xbej\xa4ei\xcd\x09J\xa0\xaa\x0aO\x18\xdb\x8d\ S\xb7\x9ff\xffQ\x92\xa7\x8d\xa1\x1aa\x03\x0a\x11k\
\x14\x00j\x9e\xd1C\xa0jyn[s\x82\x89\x11\xe5\ \xfc\x89\xc8\x00\x1b\xa8\xdbW\x06\xe0wF\x94\xa4\x06U\
Qs\xa3 /\xb3\x83\x88`qf\xbd\x0d\xfe\xd7\xd8\ ;\xbb?\xdc\xc0L\xa4~\x05\x95I@\x96\xa9\xae\xd1\
i\x05\xb4\x86\x83\xe2\xe4\x83\xe4\x9bf\x08\xc7!\x22\xb2\ \x06\xa4\x01\xf86\x8f\xdc\x0f\xdd\x00\xc2\x99\x04\x87Hd\
\xd3e^\x0eL/4\xd8\x9d\xf2\xe9\xaa\xc5\x05J\xad\ }\x11\xbb\x1c\x03\x90\x01\x92<\xa0\xf5\xfd\x91\xf5\xdd\xbb\
]\xc4\xf1\xf1\xe2\xb2\x1d8\xd4\xd0l\xae\xa3\xca1\x16\ k`\x15t\x91\xda\x7f\x15\xb2\xbe\x88\xc3\x17t\x17l\
\xfe\x9e\xaf\xd5\xedY\xad\xb7\x83\xa2\xbcdl\xa1\x1c\xed\ x\x0dt\x0f\xf2\xc8\x09\xe8\xea\x22v{\x07\xa2#l\
\xe4\xe0p\x0bg/Z\xd8\x84FK\x04\xe3 l2\ \x90\x01\x06\xb2\xd7\xb8=\x02\xf8\xc1\x0dD\x0a\xc5\x98\x10\
7a\x9eS\xb8W\xcf\x085\x1f2B\xa75'\x18\ \x89y\xa1*\x22&\x08\xd5\xdd#\xe2x:X0\xc8\
\x8d\xe4\xcd\x05\x90\x87\xb9\x0e0\xc7\xd4\xd0\xb1\x8cS\x0f\ \x9eq^vw\xe6B\xb2\x85\x11q4\xfa\x9c\x9d0\
\x9b\xd6\xdb\xc1\x01\xc4\xa2\x07@8\xcc)\xdc\xa8i\xca\ \x13\x22\x84\xc7\xe2\xc0$;\x15\xb3\x93\xa0{]\x8a\x11\
\x16\x0b\x9bHh\x0d\x07\x01Y\xe1\xb7\xa66\x023\x1c\ \xf6\xc2d\x7f\x10\x22p\xce\xed\x12\x9cm\x1f\xf0 \x88\
\xcc\xb3x\xe7\x92~(G\xbeB\xa3%\x02\xc8d\xcc\ \xb4\x1f''\x09y\xa0\xea\xbf\x04\xa3\x9f\x1d\x0c\x9d\x08\
\xf3\x0a\x880\xddF\x00}u\x93\xa3w\x04\xcd.\x82\ W\xe1\x22\xe5\x04\xbb\xf0\xcf(\xd7c\x19\xee\x05\x9c\xc7\
B(\xbe\xd1\xe9!U\xa6;\x08\x09C_\xfd\x86\xcf\ \xa8\xef\xe6\x9c\xb9\xf4\x15\xc5\xa5\xbb9\xe8XK8'\
W\x8d\xd6\xc4\x10\xd3\xa4\x91\x09\x075\xcel\xddg\xf8\ y\xee\xb2\xf5\x00Ba\x0d {\xd0v\xa3\xec\xb3N\
|Ui\xf2\xbd\x83}\x86\xcf\xc7\x1c\x0cS\x9b\x8b\xd4\ \xcan\xb4\xdd9\x83\x88\x95\xc4n\x83\x08\x88\xfe\xfal\
\xb7\x00\xa0\xa9E`%\x00#\xb4zib*\xa2)\ \xc0~W\xd4\xeb\xf3iq\x9a\x1b\x08\x0f\xac\xa7Z\xbe\
E \xe6\xc7\xc6\x1cL\x8f\x1dk\xe3\xdc\xf3\xd7t\x22\ \x14d`\x9b\x0c7\x97&\xcf~\xa9\x0cM\xdb]v\
P\xe3\xd3B\x03\x0eb|\xee\xbcV\xa9b\xach2\ \xac\x1c\xf6\xc2dj\xce\x07 ^\xb3\x7f\xf7\xb3_\xda\
\x11t\x1a>?\xe0\x88\x1a\x8f\xe9\x0d\xd3\x03\x0c\xd1D\ )HY\xdb\x19f$}\x1a{c\xbc\xe6C\x183\
+\x86\xa6ot}\x8e\x06)FM\x88U\x9a\xa2\xbc\ \xed\xe8?\xf3\xc2N{\x94\xdd|\xc0\x08T\xc7\xce\x0c\
\x16L\xfau\x82 -\x9c\x91\x00D\xf8\x9dCp\xe5\ =\x0c90\x86\xfb\x0b\x1e6\xcf\xde\x89\xa5\xa9\x9e\x0e\
\x9a\x11\xd3\x12\xe1\xa7\x8a\xad\x06\x93Y\x04\xbd.\xb31\ \xe4\xc3\xa9\x9e\x9a\x07u \xd0\x06\xc9\xf3\xf1\xfa;?\
\xcc\xfc\x9aWv;\xa8\xb9\x00:\xf2\xa1\xde\x0bTU\ {x\xd7\x1a%\xaf>\x9a{8Q\x93\x03e%j\
&\xab\x08\x06\xed\xb6\xf5w;\x80\xf1\x9d<\xaa\x16!\ J\x9e\xfb\xc2.\x08e7/Y\x83h\xb8\xc9\xa9\x8b\
\xe4u\xc4d\x5c1t,\x04\xe0([\x1d\xb5\xdb\xd9\ \x01l g\x1c\xef\xad\xae\x98\xfb\xec. V\x07\x1f\
kh\x01\xc0\xa4\x9c\x18\xda\xf4\x00[\x9d6\x9b\xbd}\ \xfa\xf0n!5o\x1b\x98\xe1K9\xaf\x93d\xb0~\
i|\x01\xc0\xa4\x1b\x0e\x16\x98\x1a(lqD\xd8h\ \x87\x03*B=omJx\xd6\xc0\x0dP#!t\
\xe1\xec8\x0b\x9b\xbadR\x88 \x88\xec6O\xe9\xe7\ @Wf\xa1\x88\xfa\xf3;\x22xxf\xdb\xb6\xd3\xfc\
\xb0\xd1\xe9\xf2\xd9\x82\xf9E\x05\xb3{]\xa9\xf8\xc2\xa3\ \xfc\xd8\xc9\xb0M\x1cKB#\xfa\xbaP\xafv\xd7>\
z\xa1\xd1\xe7\x04=1\x8e\x04\x0e12R\xf6\xa62\ \xd3\xff\xf2\x8e(n\xe4\xe5\xa1\x0eQ\x94\xa7\xf36\x89\
ls\x0a\xd1\xbd/\x9b:\xcd\xc9\xe8L\x13\x8dN#\ 'i\xc2\xa6Mwr\xc5\xb1\xab\xcd\xe0\x8b:\xa4+\
\xcf\x09D,\xfe\xfa\x85\x97QU\xa7\xf0\xcd\x06\x0b\xbf\ \xaf\xaa\xb3`0TVVuP^\xf9\x1e\xc4\xc3\xdd\
\x13\xe6\xa0mD\x1a\xb8'8\xcb\xd8By\x0e\x0e\x9c\ \x9b\xb9by\x03\xb5@S\x84\x95\xf3\xa8\x01\xe8\xe74\
\x0d|\xda\xc2\xe9i\x85\xb1gR\xd1p{\x07\x22m\ \x89\xd6\xcf\x9d\x8f\x9eKF\xa7EG{z\x0e\xed\x17\
`q\xb4\xdf\x09\xda\xdc\x01\x18\x14+\x01\xcc\xf4\x12\xe6\ \x17\x173}z@\x80\x93\xd3\xe4\xbf\x02\x0c\x5cz\x1b\
3\xcfF\xa0\x91\x86\x83\xb4\xcbB\xcc\xc3\xfahs\x82\ \xc6\xe3s\xe7m\x7f\xee\xcd}\x06\x92\xd9u\x1cZ\xe5\
\x93\xdd\x0e@\xe1f\xee\xd7\x8c\xbd\xe7\xb8\xc4\xd8\xa6A\ a\xd9x\x03$\x9f\xfb\xe8\x9a\xaa\xfa[\xda\xc1|`\
h\x94\xb7\x03U\xce\xb50{\xa5k\xbf\xee\x84\xd1\x19\ r\xb5\x0ao\xe0\xd2^\x10\xe7\xaf\xa9T\x94\x03\xfaz\
B\xd6\x19\x17#\x5clzMY#Q\xf7s\x82\xe0\ \x08\xec\xb7\x5cx\xfdMMp}\x95\xea\xe4O\x9d\xd7\
.\xa7\x0bM\xcdTY;\xf4\xef\x91\x9b\x07\xe6\x02\x80\ o\xff\x01d`%\xde@}.H\xff\xa4\xca\xca\xc9\
\xaet\x8c3-\xec\x1a\x86z\x9e\x13xq\xceU\xe8\ \xf3\xb6\xa2\x14Hr\xf7\xdcF,\x0fv\x00\xf8\xd6\xa6\
6\xb5S9\xd0\xd6\xc3\x02\xe8\x1b\xe0q\x94\xbd\xa6\x85\ O9xd)\xd9\xbb\xe3u\xc8\x1a\x17\xaf\xbf\xb1I\
\xe5\x85/\x9a\xda4\x1au;'P\xbela\xb5\xe7\ \x9c\xbe\xaa\xfa\xa7\xceBH\x1f,\x05\x18x\xb8\x0f\xcb\
]\x9f\xf5C\xdf\x0c\x0b`\xbe\xea\x80M\xce\x1f\x81\xf3\ \x7f\xab\x19\xfa\xf7~\xb6\x16\x96<\x8b\xdf,pK\xc2\
\xbd\x84\x18\x1f/k4\xeamN\xd0\x1b\x97y\xc0\x19\ \xeb\x9f\xc1\xeb\x9fc\xe8\x9f\xbcw\xb9\xd4V>\x17\xbf\
\xa6v\x02k\x0a\xa9\xe7\x811\xfb\xc7\xa2\xfc\xc2\xa2.\ \xbe\xc0\xebo\xfd\xaa\x84\xc1\xbeG\xbbJx\x9cS1\
S\x04\x96[\xd85\x1c\xf54'\xc8\xc1?bq\xf9\ \xf4\xaf\x5c>A\x7f\x80\x9f\x8b\x06o\xc1s\x87!U\
w^\xb9k\xe4\xf7\xefK\x16\x9dvy\xcbb\x5c\xf1\ v]u\xed<X\xbfd!N?h8^\xbf\xb0\
;\x1c\xe6}\xb0O=\xd3\x0a5\x22\xb5Np]\xb8\ \xed M\xc9\xf5\x0e\x8ds\x07\x99\x94\xfe\x9b\xa1\xff\xe5\
\xcao3\x1034\xf5\x0a\xc9\xa2\x87Ov\x8dV\x90\ \x95S'\x18\x9f\xcd\xc6\x19\x88\x004\xc0\x96\xa6R\x1b\
jV\xe1v\xd3\x0a\x01n6\xdf<W\xc0\xd7zb\ \xfb.\xaa,\x9d}q/]Tz\x90\xab_\xca\x00\
(\xf0M\xcc\x1b\x1f\xe0'c\x8f\xf5\x85ya\xc4\xbe\ \xfb\x18\xbc\x22\xca\x00\xadU}\xfd1\xad\xf1#S\xff\
\xb66\x8e\x18z\xbfl\x06j\x91\xae&\x1d\x97\xb9\x1a\ k\xb6~\xe9\xefq?czO\x80\x9a:s\xd7E\
\xac\xd9\x98\x0a \xef\xc0\xfc\x99\x19\x1d\x15\xff\xf1\xbe1\ \xd3\xa7\x9d0\xfa\xfa\xdaykQ\xc9E\x8e\xfe\xa7,\
\xa4\xcb\xd77Tx`\xec\xe7e0-\x97\xe3z\x0b\ p\x06\x22\x05\xf4\xeb\xd4\xea*\xfa\xa2\xb0\xe9\xa8\x85\x82\
\xbb\x86\xa5\x16s\x02\x15n\xc2\xe6\xaf_\xb8ol\xe3\ \xc7\x0c-U\xf5\xb9\x8e\xb2}L\xfd\xab\x94\xfeQ6\
C\x91I\x84\xa3\xdc`\xec `i\xa9\x8b\x0a'\x1b\ 8\x03\xf3\xb6\xa20W\xa9\x95\xbe\xd3\xcc\xf4u\xa7\x81\
\xd5\x1c\x0e<W\xceB\xcd3\xba\x03(\x8c[v\xd1\ \xa3?_`:\xa0\x06\xc2\xd1\x1d,\xfd\xefy\xfaG\
\xbb\x83\xbd\xb8l\x00\xf3\xfc\x81\xc0+)\x9f\x8f\x8e\xbc\ \x97c\x0c(\xcd\x08\xfd\x9aru\xb4\x9c\x98~\xdav\
\x95\xa2\x19\x88z8\xd8*\xe2\xc6\x5c^\x01\xe6\x99\x16\ \xbd\xf1\xbb\x02+'~xz\x92\xe9\xe0\xfa\x8f\xd79\
\xa8\xf0lwF?9\xde\xcf\x8a\xbeF\xa8r\x9d\xa9\ \xfa\x05\x1c\x0ef\xe0~SZ^\xc3\x95/\xacSO\
\xa3\x02\xc7zq\xcb[\xc7\x1a\x98\xa8\xd7\x09b1V\ \x1b\xd5\x91\xaa\xa4\xd3Eo\xd8\xe25_\xberU%\
`\xd1\xf8\x00\xa2\xc5/\x9d\x9c\xf0\xf6p/.\xcf\x02\ \xc0\xd5_\xb4<\xfd\x82\x02\xdc<\xe04M]W\xc8\
\x9f\xb0\xf09\x98\x17>uh\xbfV5\x99d=\x10\ n\xfeJ\xb5\xd2\xba\xa4\x0d\xb3}\xb0\xe5\x09\x1d\xb9\xf9\
\xc5+\xa2\xe7\xcaY\x08k\xb1;\x98\xfbL*\xa3E\ \xd4/g\xd1\xfag\x91\xfa\x05\xd8\x94\xc4I\xa9\xae2\
o\x0e\x9dP\x00=q9]\xe0a\x0b\xa7\x00[\x07\ \xb3\x9b?\xacs\x7f\x22\x8en\x1cs3\x1d\xb0X{\
c,\x98\xb3W\xf7X\xda7,a\x0e\x07mm\xdc\ \xebi5J\xff\xb6v7B\xbf\x19\x9b\x10\xc8]}\
\x9c\xcb\xf1\x1b`\xa6M\x01\x02\x8b\x92\x19}\xbc\xe8\xcf\ \xd5j\xba\xb7\xb7\x9eV\x87\xf7\xa3_S\x95t\xf1\x7f\
K\xe5{\xf4\xe2r7\xf0\x176\xceQ\xd6\xa4\x06X\ v\xdb8\xb1[{\xf9\xca\x97H}\x14\x7f\x84dD\
R\xab\xa4\x92\xb5$D\x11l\x07f\xd9\x18\x0a\xdc\x95\ \xa3\xa6\xa9+\xcd\x85\x16*\xd5>\x8c\x5c>\xb5\xd0\xc6\
\xcc\xe8E\x13>S\xaamv$d\x96\xa3l\xc4\xf6\ w\xcc\xa0\xcd\xa7\xbe?\x0b\xd4\xdf\xbd\x02v\x04\x19\xab\
P\xa8pE\xaa_K\xdec?\x19\x09Q\x046\xbc\ .\xaf\xa1dj\xaa\xd4S\xddX\x0bI\x9b\x81'\xcd\
\x97u8\xba\x10\xebQ\x94\x92k\xc9\x87\xf6\xebv\x11\ ,\x07\xda[\x17X\xddp\xf6\x96\xf6\x00R\xbf\x19\x96\
\xeb\x09!(\xab\xbc\xb8,\xb5\xb6o`B\x5c'0\ \x95\x12\x0aOj\xde\xa9\xacR\xab\xd9\x87\x96Nut\
G\xb8\xb6T\xe3C\x19=@\xf0\x94\xb4{q^@\ \x13\xfcz\x80I\xb1\xf6\xfe=F7\x5c\xb8\xa5->\
\xf9#\xcb\xea\x0c\xa2\x9c\x93\xf2\xf5\xbf-\xed\x1b\x9a\xb0\ \x80\xe4U\xf0o\x1c\x03|\xd4j\xf54\xce/\x02\x15\
&\x86\x06\xbc\x9c\xf2YP\xce\xabxy\xbbI\xaaY\ >]}\xc0Q\xa8et\xc3\x85\x1f\x84\xf4\x0fd\x10\
\x94\xaf\x03y\xcb\x0au \xfc\x87\xd7)V7\x927\ `\x5cc<\xe3x\xc7E\xbef\x9b\x81\x1am1\x9b\
:a\xc5\x13\x94I^\x85\xaf\x94\xbb\x0eS\xf6vb\ f\xed\xed\xcen\xe8\xd0\x17@\x9b,\xe6g\xe6\xa8\xe1\
*\xa3\xebQn\xb2\xaf\x17\xd3\xc8\xb3\xb6\x89E\x10\xd6\ y\x9an\x82\xdf\x8a8\xc1\xdf\xb7vC\xf5\x85\xfb\x82\
:A)Vt\xf7k\xd9A\xbe\xe5\x0d\x01\x05^\x17\ \xfaE\xcb\x89n\xe2\xba\x8c6p\xb7\xe5c\x0e\xfbk\
\x89u\xc6x\x16\xe1\x8f\xad\xaa\x16\xd0\x87\xb2\xa45\x1c\ O}}\xf6\xea\xd3\xfb\xda\x8f\x85h\x99\xdb]\x03\xc4\
D\xc2\x93)\x9fO\x9b$\xf24\x0a(\x98\xaf:\x80\ Ts\xd7(\xa8\xdd\xcf\xa1\xb8V{\xeb\xf6e^\xb1\
r1\xe6\xe9\xc8F\xd2\x89\xf0_=\x09\xf9|\xe9G\ \x8d\xa2\xe6\x15\xdd\xd6'\xfau=\x07\xe6_\x8b\xf8\x1a\
'\x1f\x11\x0e\x07\xbdY\x87\x8bL\xb3\xb8\x1aG\x94\xa4\ :\x9d\xa0\xfe\xfe\xa2W\xa3\xbao\xc0\x83\x9e\xa2\x0a\xcd\
\x06\xf4U\x11\x96\x81yJ\xb2\x11L\x11e\xb5\x97\x90\ OZ\x8ax\x14\x17\x09\xd32\x84\x90\x00j\x9e.\xaf\
\xcb'\xe3\xe1\x92RD0\x1c\xe4Q\xbeX\xce\xac\x7f\ \xaa\xaa\xb2l\xa6\xfc\xd6R$\x02]\x86\x14\xfa\x0a\xd7\
,\xc6\x02\x00H\xf6\xeb/\x85\x8a\xb7~\x1d\x94\x1b\xbc\ hu\x17\xff\xd3\xbd\x0bF\x17(\xd5?\xc8\xba\x8d\x1e\
\x18\xff\xf9\xf6Arp\x85e5\x1cmm\xdc\x0c\xa1\ \x15\xd7\x8f\x22nz@J \xd8\x81nA\x14!=\
%\xdd^a;\xa4\x1a\xcd\x01F[\x8ax.?\x05\ \xf2\xa8@\xdd' t\x0bR\x88\x1e!*\xb0\xf5\xd0\
\xfe\xd2\xae\x80QlS\xe1\xc2\xee~\x8d\xf4\x1e\xc2z\ \xfbX\x0e\xb5\x04F\x11=\x84<\xc3X\x8b\xd3\xafm\
\xa1\xb0\xc4~;\x90\x0a\xa1\xb8\xd5)\x9f\xa5\xa8Z\xbd\ ]\xd1\x93\xff\x9e\xbchu\xcb\xe7\x87\xec\xd1\xf2\xa7E\
\xa1Y\xf5\x00\x00\xa8\xea\x1e\x9f/\x03\x0fY\x97q\x80\ D\x8f\x92\xb2\xdc\xa8\x13\x96\xd7\x19\x03\x93\x89\x9ef\xc8\
\xd9\xa2<\xee\xc5\xe5\xaa\xc9|\xd0d\xab\x88\xeb\xb9r\ \x02\xa3\xees$:\xe3\xe2!Do0$\xd0\xd8\xaa\
\xa3\x04\xbf\xb30\x1a\x7f]\xca\xe7\xafl\x1b\x1f*\xe9\ ;\xce\x16?\xaek5\x06.R\x10\xbdD\xf2\xc2\x05\
\x01\x0ax\x22\x09\x5c\x1e\xc6\xe2\xf2\xc9\x22lF\xf9\xdb\ Fck\x8b\xae\xf68E\xad\xae\xa5\xd5h\x5c\xbc0\
\x94\xafa\x08\xabn\xd8\x11\x97O;\xf0o\xc0\x87C\ \x99\xe8UR\x16e\x04.^m\xa4X\xbd80c\
*rC\xbb\xcfi3TM3\x84\x8f\xa2b\x01\x00\ Q\x0aa\xe7\xee\xff\x0fO2\xbfP\x8b\xfe4z\x00\
\xa4\xa7\xc9\x0c\xcd\xb2\x9eps\x07\xad\xeepX\xde\xe8\ \x00\x00\x00IEND\xaeB`\x82\
\x91\xc6\xe9\xb8\xccU\xe1&\xdbH\x9e\x22\xbcF\x07'\
\xa7\xdeS\xe3\x0coc\x09E\x00\x00^B\xe6\xa0<\
D\xb8\x22\xd8\xaf\xca-\x1d\xed\xdc0c\xbf\xa6C,\
7rz\x122\x1b\xb8R\x94\xbf\xc6.\x82\xb7\x18\xaf\
\xa9pfw\xbfn\x0b\xa3\xb0\xd0\x04\x00\xc3=\xc1\x03\
\x10\x5cK\x1a\x22\xbe\x0a?\x11ee*\xa3o\x85\x5c\
v\xa8\xec\x8c\xcb\xe1\xf9\xe0\xd0\xc62\x0cS\xf1\x97\xc1\
st\xf0\xb90\xfe\xf2\x87\x08U\x00\x00\xbbD\xa6f\
]\xee\x81H\x0e\x8d\x0e\x02\xbfR\xe5\xe7\xdd\x03\xac\xab\
\x9b\xb8\xc3`\xb3\xec\x1c\x94\xbf!8\xaee?\xb9.\
\xce:\xc7\xe7\xcfg\xaa\x86z_C\xe8\x02\x00\xd8(\
2\xe5`\x97\x9f\x01\x13\x06#T\xc8.\x84;E\xb8\
#\xd9\xcf\x0bU\x0f:\x09NQ-T\xe5\x1c\x84\x8b\
\x08gV_\x8c;R>\xcb\xa2\x10|$\x02\x08J\
\x16\xc7s\xf9.p%\xd1_2\xd1\xab\xf0\xb8(\x8f\
\xe6\xe0\xd1Y\xben\x0e\xdd\x83\x88\xec\x88q\x84#|\
\x82\xa0w\xfb\x0c\xa6\x99\xb9\xcc\xc9\x03+R>\xd7D\
%\xf0\xe8\x04P \xed\xcab\x15n\x87\xaa&\x98\xde\
\x01\xbc\x84\xb0I\xf2l\x12aSV\xf8m&\xc3\xae\
\xc2\xedZ\xc5\x11\xe9\xe8u\x99=\x08\x87\x0b\xcc\x95 \
\x12\xf7\xa3\x04\xc1\xb1Q7\xf8Hv\xa1,\x8dz\xd3\
,r\x01\xc0pX\xd9j\xe0\xe4\xc8\x9d\x95f\x90\xe0\
v\xad\xbd\x08\xfb\x09\xd6\xd1\xa7\x11\x5c\x84\xe1\x02\x7fP\
\xcb\xca\x15x\x22\xebp\xb1\xcd\xda\xbe)U\x11@\xe0\
I\xdaz\x5c\xbe%\xc1Vh\x14\x93\xa4\xc9\x80\x22\xfc\
(\x95ay\xb5&\xb8\xd5\x13@\x01/!\x0b\x81\x1f\
\xa3\x93'\xddl\x18\x88\xf0\x7fy\xf8\x9aI0G(\
~k\x12\xb1-\xd2\xee\xc5\xb8\x8c \xd8\xb4\x1e\xba\xdc\
Z\xd2\xa7\xb0j\xaf\xcf\x8a\x91\x99;\xaaEm\x04P\
`w\xa7\xa4\x06\xf3\xfc\x10h\xca\xa8a\x15\xee'\xf8\
\xab\x0feU\xcf\x86\x9a\x0a`\x88t\x5cNU\xb8\x16\
8\xb5\xd6u\xa9\x12\xcf\x08\x5c=\xd1\x89\x9djQ\x17\
\x02\x18\xa2'!'\x89r5\xb0\xb8\xd6u\x89\x02\x85\
gE\xf9v\xcaW\x9b\x94|\x91PW\x02\x18\xc2\xeb\
\x94\x05\xe4\xb8\x1a\xe1|\x1a\xff\x8d!/\xc2\xfd\x0a+\
S\xfd\xfaT\xad+3\x96\xba\x14\xc0\x10;\x122K\
\xe0\x0b\xa2|\x158\xac\xd6\xf51\xc4\x13\xb8\xdd\x81[\
\xbb2\xfaf\xad+S\x8c\xba\x16\xc00\xc1[\xc3\xe9\
\x22\x5c\xa0p>P\xaf1\x84{\x04\xd6\xa8rwj\
\x80GP\xcd\xd5\xbaB\xa5h\x0c\x01\x8c`\xa3\xc8\x94\
\x83\x5cN\x11\xe5l\x84\xc5\xc015\xae\xd2\xcb\xaa\xac\
Sa\xdd\xbb>\xebk\xf1*W\x09\x0d'\x80\xb1\xec\
\x9a*\xc9l\x9e\x13\xc9\xb3\x10\xe1c\x08\xc7\xa1| \
\x12g\xc2\xbb(/\xa1l\xc0a}\x9b\xc33\x8d\x9e\
\x15\xad\xe1\x050\x1e^\x5c\x0eC9Z`^\x1e\xe6\
:\xc2\x1c\x85.\x11\xa6\xab2\x9d\xe0\x8a\xd5v\x0e\x1c\
y\xdf\x07d\x81~\x11\xdeQ\xe5\x1d\x81\x9dye\x9b\
\x03[\x14\xb6 \xbcZ\xef\xc1(6\xfc?\x14\xfa?\
\xdce\xbf\xf3&\x00\x00\x00\x00IEND\xaeB`\
\x82\
\x00\x00\x05~\ \x00\x00\x05~\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -616,10 +566,11 @@ qt_resource_name = b"\
\x06\x0b\xf4\xa7\ \x06\x0b\xf4\xa7\
\x00b\ \x00b\
\x00l\x00a\x00n\x00k\x00_\x001\x002\x008\x00.\x00p\x00n\x00g\ \x00l\x00a\x00n\x00k\x00_\x001\x002\x008\x00.\x00p\x00n\x00g\
\x00\x0c\ \x00\x12\
\x0a\xf7\xc1\x07\ \x0d,\x1a\xc7\
\x00n\ \x00d\
\x00o\x00n\x00e\x00_\x001\x002\x008\x00.\x00p\x00n\x00g\ \x00a\x00i\x00l\x00y\x00c\x00h\x00e\x00c\x00k\x00_\x001\x002\x008\x00.\x00p\x00n\
\x00g\
\x00\x08\ \x00\x08\
\x09n\x91\xb3\ \x09n\x91\xb3\
\x00b\ \x00b\
@@ -645,15 +596,15 @@ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x8db\xb8\xc9\xc8\ \x00\x00\x01\x8db\xb8\xc9\xc8\
\x00\x00\x00P\x00\x02\x00\x00\x00\x03\x00\x00\x00\x05\ \x00\x00\x00\x5c\x00\x02\x00\x00\x00\x03\x00\x00\x00\x05\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x002\x00\x00\x00\x00\x00\x01\x00\x00\x01\xbc\ \x00\x00\x002\x00\x00\x00\x00\x00\x01\x00\x00\x01\xbc\
\x00\x00\x01\x8da\x99\x15\xcb\ \x00\x00\x01\x8df\xb35\xbe\
\x00\x00\x00\x84\x00\x00\x00\x00\x00\x01\x00\x00\x18\xc4\ \x00\x00\x00\x90\x00\x00\x00\x00\x00\x01\x00\x00\x15\xae\
\x00\x00\x01\x8dd\xf15i\ \x00\x00\x01\x8dd\xf15i\
\x00\x00\x00\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x1eE\ \x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x1b/\
\x00\x00\x01\x8dd\xf1K\xbc\ \x00\x00\x01\x8dd\xf1K\xbc\
\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00\x13B\ \x00\x00\x00r\x00\x00\x00\x00\x00\x01\x00\x00\x10,\
\x00\x00\x01\x8dd\xf1\x5c\xbe\ \x00\x00\x01\x8dd\xf1\x5c\xbe\
" "

BIN
dailycheck.icns Normal file

Binary file not shown.

BIN
dailycheck.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

View File

@@ -1,4 +1,5 @@
# coding: utf8 # coding: utf8
import requests
from PySide6 import QtWidgets from PySide6 import QtWidgets
SoftwareStatusRole = 0x0101 SoftwareStatusRole = 0x0101
@@ -32,3 +33,10 @@ def get_with_chained_keys(dic: dict, keys: list, default=None):
if len(keys) == 1: if len(keys) == 1:
return dic[k] return dic[k]
return get_with_chained_keys(dic[k], keys[1:], default) return get_with_chained_keys(dic[k], keys[1:], default)
def request_content(url: str) -> bytes:
req = requests.get(url)
if req.status_code == 200:
return req.content
return b""

BIN
images/dailycheck_128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

34
main.py
View File

@@ -1,44 +1,16 @@
# coding: utf8 # coding: utf8
import sys import sys
from PySide6 import QtWidgets from PySide6 import QtWidgets
from wg_basic import WgBasic from mw_dailycheck import MwDailyCheck
from wg_software import WgSoftware
from wg_extensions import WgExtensions
import daily_check_rc import daily_check_rc
version = (0, 1, 0)
class UiMainWindow(object):
def __init__(self, window: QtWidgets.QWidget):
window.resize(800, 600)
window.setWindowTitle("日常检查工具")
self.vly_m = QtWidgets.QVBoxLayout()
window.setLayout(self.vly_m)
self.tw_m = QtWidgets.QTabWidget(window)
self.vly_m.addWidget(self.tw_m)
self.wg_basic = WgBasic(window)
self.wg_software = WgSoftware(window)
self.wg_extensions = WgExtensions(window)
self.tw_m.addTab(self.wg_basic, "基本信息")
self.tw_m.addTab(self.wg_software, "已安装软件")
self.tw_m.addTab(self.wg_extensions, "已安装插件")
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = UiMainWindow(self)
def main(): def main():
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
win = MainWindow() win = MwDailyCheck(version)
win.show() win.show()
return app.exec() return app.exec()

101
mw_dailycheck.py Normal file
View File

@@ -0,0 +1,101 @@
# coding: utf8
import json
from pathlib import Path
from PySide6 import QtWidgets, QtGui, QtCore
from wg_basic import WgBasic
from wg_software import WgSoftware
from wg_extensions import WgExtensions
from global_vars import (
request_content,
accept_warning,
)
class UiMwDailyCheck(object):
def __init__(self, window: QtWidgets.QMainWindow):
self.cw = QtWidgets.QWidget(window)
window.setCentralWidget(self.cw)
self.vly_m = QtWidgets.QVBoxLayout()
self.cw.setLayout(self.vly_m)
self.tw_m = QtWidgets.QTabWidget(self.cw)
self.vly_m.addWidget(self.tw_m)
self.wg_basic = WgBasic(self.cw)
self.wg_software = WgSoftware(self.cw)
self.wg_extensions = WgExtensions(self.cw)
self.tw_m.addTab(self.wg_basic, "基本信息")
self.tw_m.addTab(self.wg_software, "已安装软件")
self.tw_m.addTab(self.wg_extensions, "已安装插件")
self.menu_bar = window.menuBar()
self.menu_help = self.menu_bar.addMenu("帮助")
self.menu_about = self.menu_bar.addMenu("关于")
self.act_update_safe = QtGui.QAction("更新安全标注", window)
self.act_export_unknown = QtGui.QAction("导出未知", window)
self.act_about = QtGui.QAction("关于", window)
self.act_about_qt = QtGui.QAction("关于 Qt", window)
self.menu_help.addActions([self.act_update_safe, self.act_export_unknown])
self.menu_about.addActions([self.act_about, self.act_about_qt])
class MwDailyCheck(QtWidgets.QMainWindow):
def __init__(self, version: tuple, parent=None):
super().__init__(parent)
self.version = version
self.setWindowTitle("日常检查工具")
self.setWindowIcon(QtGui.QIcon(":/images/dailycheck_128.png"))
self.ui = UiMwDailyCheck(self)
self.ui.act_update_safe.triggered.connect(self.on_act_update_safe_triggered)
self.ui.act_export_unknown.triggered.connect(self.on_act_export_unknown_triggered)
self.ui.act_about.triggered.connect(self.on_act_about_triggered)
self.ui.act_about_qt.triggered.connect(self.on_act_about_qt_triggered)
def sizeHint(self):
return QtCore.QSize(800, 600)
def on_act_about_triggered(self):
msg = f"日常检查工具\n\n适用于 Windows 平台。版本:{self.version[0]}.{self.version[1]}.{self.version[-1]}"
QtWidgets.QMessageBox.about(self, "关于", msg)
def on_act_about_qt_triggered(self):
QtWidgets.QMessageBox.aboutQt(self, "关于 Qt")
def on_act_update_safe_triggered(self):
root_url = "https://julianfreeman.github.io/dailycheckutils"
marks_all = request_content(f"{root_url}/marks_all.json").decode("utf8")
if len(marks_all) == 0:
return
marks_all_d = json.loads(marks_all)
self.ui.wg_software.update_safe(marks_all_d["software_win"])
self.ui.wg_extensions.update_safe(marks_all_d["extensions"])
self.ui.wg_basic.update_safe(marks_all_d["isp"], marks_all_d["manufacturer"])
def on_act_export_unknown_triggered(self):
dirname = QtWidgets.QFileDialog.getExistingDirectory(self, "导出未知")
if len(dirname) == 0:
return
ex_file = Path(dirname, f"未知信息.json")
if accept_warning(self, ex_file.exists(), "警告", "文件已存在,确认覆盖吗?"):
return
unknown_software = self.ui.wg_software.export_unknown()
unknown_ext = self.ui.wg_extensions.export_unknown()
unknown_isp_manu = self.ui.wg_basic.export_unknown()
unknown_all = {
"software_win": unknown_software,
"extensions": unknown_ext,
}
unknown_all.update(unknown_isp_manu)
with open(ex_file, "w", encoding="utf8") as f:
json.dump(unknown_all, f, indent=4, ensure_ascii=False)
QtWidgets.QMessageBox.information(self, "提示", f"已导出到 {ex_file}")

View File

@@ -3,22 +3,19 @@ import os
import wmi import wmi
import json import json
import win32gui import win32gui
import requests
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from PySide6.QtCore import QDir, QSettings from PySide6.QtCore import QDir, QSettings
from PySide6.QtGui import QImage, QIcon, QPixmap from PySide6.QtGui import QImage, QIcon, QPixmap
from global_vars import request_content
def get_isp_name() -> str: def get_isp_name() -> str:
req = requests.get("https://ipinfo.io/")
if req.status_code == 200:
try: try:
data = json.loads(req.content) data = json.loads(request_content("https://ipinfo.io/"))
return data.get("org", "[Not found]") return data.get("org", "[Not found]")
except json.JSONDecodeError: except json.JSONDecodeError:
return "[Decode Error]" return "[Decode Error]"
return "[Return Error]"
def get_win_manufacturer() -> list[str]: def get_win_manufacturer() -> list[str]:
@@ -83,4 +80,3 @@ def extract_win_icon_from_file(icon: str, default: QIcon) -> QIcon:
return QIcon(QPixmap(image)) return QIcon(QPixmap(image))
else: else:
return default return default

View File

@@ -1,5 +1,5 @@
# coding: utf8 # coding: utf8
from PySide6 import QtWidgets from PySide6 import QtWidgets, QtCore, QtGui
from util_func import ( from util_func import (
get_isp_name, get_isp_name,
get_win_manufacturer, get_win_manufacturer,
@@ -45,8 +45,6 @@ class UiWgBasic(object):
self.vly_gbx_license.addWidget(self.pte_license) self.vly_gbx_license.addWidget(self.pte_license)
self.pte_license.setReadOnly(True) self.pte_license.setReadOnly(True)
# self.vly_m.addStretch(1)
class WgBasic(QtWidgets.QWidget): class WgBasic(QtWidgets.QWidget):
@@ -61,3 +59,34 @@ class WgBasic(QtWidgets.QWidget):
self.ui.pte_license.setPlainText(get_win_license()) self.ui.pte_license.setPlainText(get_win_license())
def update_safe(self, isp_safe_info: dict, manu_safe_info: dict):
def set_palette(is_safe: bool | None, lne_w: QtWidgets.QLineEdit):
pal = lne_w.palette()
if is_safe is True:
pal.setColor(QtGui.QPalette.ColorRole.Text, QtCore.Qt.GlobalColor.blue)
elif is_safe is False:
pal.setColor(QtGui.QPalette.ColorRole.Text, QtCore.Qt.GlobalColor.red)
else:
pal.setColor(QtGui.QPalette.ColorRole.Text, QtCore.Qt.GlobalColor.black)
lne_w.setPalette(pal)
isp_text = self.ui.lne_isp.text()
if isp_text in isp_safe_info:
is_isp_safe = isp_safe_info[isp_text]["safe"]
set_palette(is_isp_safe, self.ui.lne_isp)
manu_text = self.ui.lne_manufacturer.text()
if manu_text in manu_safe_info:
is_manu_safe = manu_safe_info[manu_text]["safe"]
set_palette(is_manu_safe, self.ui.lne_manufacturer)
def export_unknown(self) -> dict:
unknown = {}
text_role = QtGui.QPalette.ColorRole.Text
black = QtCore.Qt.GlobalColor.black
if self.ui.lne_isp.palette().color(text_role) == black:
unknown["isp"] = self.ui.lne_isp.text()
if self.ui.lne_manufacturer.palette().color(text_role) == black:
unknown["manufacturer"] = self.ui.lne_manufacturer.text()
return unknown

View File

@@ -1,12 +1,9 @@
# coding: utf8 # coding: utf8
import json
from pathlib import Path
from PySide6 import QtWidgets, QtCore, QtGui from PySide6 import QtWidgets, QtCore, QtGui
from util_ext import scan_extensions, ExtensionsData from util_ext import scan_extensions, ExtensionsData
from global_vars import ( from global_vars import (
ExtensionStatusRole, ExtensionStatusRole,
ExtensionIdRole, ExtensionIdRole,
accept_warning,
) )
@@ -26,35 +23,37 @@ class UiWgExtensions(object):
self.cbx_unsafe.setChecked(True) self.cbx_unsafe.setChecked(True)
self.cbx_unknown.setChecked(True) self.cbx_unknown.setChecked(True)
self.cbx_chrome_compat = QtWidgets.QCheckBox("谷歌兼容模式", window) self.cbx_chrome_compat = QtWidgets.QCheckBox("谷歌兼容模式", window)
self.pbn_export_unknown = QtWidgets.QPushButton("导出未知", window) self.pbn_update = QtWidgets.QPushButton("更新", window)
self.hly_top.addWidget(self.cmbx_browsers) self.hly_top.addWidget(self.cmbx_browsers)
self.hly_top.addWidget(self.cbx_safe) self.hly_top.addWidget(self.cbx_safe)
self.hly_top.addWidget(self.cbx_unsafe) self.hly_top.addWidget(self.cbx_unsafe)
self.hly_top.addWidget(self.cbx_unknown) self.hly_top.addWidget(self.cbx_unknown)
self.hly_top.addStretch(1) self.hly_top.addStretch(1)
self.hly_top.addWidget(self.cbx_chrome_compat) self.hly_top.addWidget(self.cbx_chrome_compat)
self.hly_top.addWidget(self.pbn_export_unknown) self.hly_top.addWidget(self.pbn_update)
self.lv_extensions = QtWidgets.QListView(window) self.lv_extensions = QtWidgets.QListView(window)
self.vly_m.addWidget(self.lv_extensions) self.vly_m.addWidget(self.lv_extensions)
class ExtensionsListModel(QtCore.QAbstractListModel): class BaseExtensionsListModel(QtCore.QAbstractListModel):
def __init__(self, browser: str, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.all_extensions = {} # type: ExtensionsData self.all_extensions = {} # type: ExtensionsData
self.names = [] # type: list[tuple[str, str]] self.names = [] # type: list[tuple[str, str]]
self.icons = {} # type: dict[str, QtGui.QIcon] self.icons = {} # type: dict[str, QtGui.QIcon]
self.safe_info = {} # type: dict[str, dict] self.safe_info = {} # type: dict[str, dict]
self.blank_icon = QtGui.QIcon(":/images/blank_128.png") self.blank_icon = QtGui.QIcon(":/images/blank_128.png")
self.update(browser)
def update(self, browser: str, is_chrome_compat=False): def update(self, **kwargs):
raise NotImplementedError
def update_ext(self, browser: str, is_chrome_compat=False):
"""内部用"""
self.all_extensions.clear() self.all_extensions.clear()
self.names.clear() self.names.clear()
self.icons.clear() self.icons.clear()
self.safe_info.clear()
self.all_extensions = scan_extensions(browser, is_chrome_compat) self.all_extensions = scan_extensions(browser, is_chrome_compat)
for ext_id in self.all_extensions: for ext_id in self.all_extensions:
@@ -67,8 +66,11 @@ class ExtensionsListModel(QtCore.QAbstractListModel):
self.icons[ext_id] = QtGui.QIcon(icon) self.icons[ext_id] = QtGui.QIcon(icon)
self.names.sort(key=lambda x: x[1].lower()) self.names.sort(key=lambda x: x[1].lower())
with open("plg_db_v2.0.json", "r", encoding="utf-8") as f: # print("updated " + browser)
self.safe_info = json.load(f)
def update_safe_info(self, safe_info: dict):
self.safe_info.clear()
self.safe_info = safe_info
def rowCount(self, parent: QtCore.QModelIndex = ...): def rowCount(self, parent: QtCore.QModelIndex = ...):
return len(self.names) return len(self.names)
@@ -83,7 +85,6 @@ class ExtensionsListModel(QtCore.QAbstractListModel):
return self.icons[ext_id] return self.icons[ext_id]
if role == QtCore.Qt.ItemDataRole.BackgroundRole: if role == QtCore.Qt.ItemDataRole.BackgroundRole:
is_safe = self.data(index, ExtensionStatusRole) is_safe = self.data(index, ExtensionStatusRole)
if is_safe is True: if is_safe is True:
return QtGui.QBrush(QtGui.QColor("lightgreen")) return QtGui.QBrush(QtGui.QColor("lightgreen"))
elif is_safe is False: elif is_safe is False:
@@ -99,6 +100,24 @@ class ExtensionsListModel(QtCore.QAbstractListModel):
return ext_id return ext_id
class ChromeExtensionsListModel(BaseExtensionsListModel):
def update(self, is_chrome_compat=False):
super().update_ext("Chrome", is_chrome_compat)
class EdgeExtensionsListModel(BaseExtensionsListModel):
def update(self, **kwargs):
super().update_ext("Edge")
class BraveExtensionsListModel(BaseExtensionsListModel):
def update(self, **kwargs):
super().update_ext("Brave")
class BrowsersListModel(QtCore.QAbstractListModel): class BrowsersListModel(QtCore.QAbstractListModel):
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -131,16 +150,24 @@ class WgExtensions(QtWidgets.QWidget):
self.browsers_list_model = BrowsersListModel(self) self.browsers_list_model = BrowsersListModel(self)
self.ui.cmbx_browsers.setModel(self.browsers_list_model) self.ui.cmbx_browsers.setModel(self.browsers_list_model)
browser = self.get_current_browser() self.ext_list_models: dict[str, BaseExtensionsListModel] = {
self.extensions_list_model = ExtensionsListModel(browser, self) "Chrome": ChromeExtensionsListModel(self),
self.ui.lv_extensions.setModel(self.extensions_list_model) "Edge": EdgeExtensionsListModel(self),
"Brave": BraveExtensionsListModel(self),
}
self.model_is_initial = {
"Chrome": True,
"Edge": True,
"Brave": True,
}
self.switch_model(self.get_current_browser())
self.ui.cbx_chrome_compat.clicked.connect(self.on_cbx_chrome_compat_clicked) self.ui.cbx_chrome_compat.clicked.connect(self.on_cbx_chrome_compat_clicked)
self.ui.cmbx_browsers.currentTextChanged.connect(self.on_cmbx_browsers_current_text_changed) self.ui.cmbx_browsers.currentTextChanged.connect(self.on_cmbx_browsers_current_text_changed)
self.ui.cbx_safe.clicked.connect(self.on_cbx_safe_clicked) self.ui.cbx_safe.clicked.connect(self.on_cbx_safe_clicked)
self.ui.cbx_unsafe.clicked.connect(self.on_cbx_unsafe_clicked) self.ui.cbx_unsafe.clicked.connect(self.on_cbx_unsafe_clicked)
self.ui.cbx_unknown.clicked.connect(self.on_cbx_unknown_clicked) self.ui.cbx_unknown.clicked.connect(self.on_cbx_unknown_clicked)
self.ui.pbn_export_unknown.clicked.connect(self.on_pbn_export_unknown_clicked) self.ui.pbn_update.clicked.connect(self.on_pbn_update_clicked)
def get_current_browser(self) -> str: def get_current_browser(self) -> str:
return self.ui.cmbx_browsers.currentData(QtCore.Qt.ItemDataRole.DisplayRole) return self.ui.cmbx_browsers.currentData(QtCore.Qt.ItemDataRole.DisplayRole)
@@ -158,21 +185,31 @@ class WgExtensions(QtWidgets.QWidget):
self.filters_clicked(None, self.ui.cbx_unknown.isChecked()) self.filters_clicked(None, self.ui.cbx_unknown.isChecked())
def update_model(self, browser: str): def update_model(self, browser: str):
# 切换浏览器时 model = self.ext_list_models[browser]
self.show_all_rows() self.show_all_rows()
self.extensions_list_model.update(browser, self.ui.cbx_chrome_compat.isChecked()) model.update(is_chrome_compat=self.ui.cbx_chrome_compat.isChecked())
self.apply_rows_hidden() self.apply_rows_hidden()
def switch_model(self, browser: str):
if self.model_is_initial[browser] is True:
self.update_model(browser)
self.model_is_initial[browser] = False
self.ui.lv_extensions.setModel(self.ext_list_models[browser])
def on_cbx_chrome_compat_clicked(self): def on_cbx_chrome_compat_clicked(self):
self.update_model(self.get_current_browser()) if self.get_current_browser() == "Chrome":
self.update_model("Chrome")
def on_cmbx_browsers_current_text_changed(self, text: str): def on_cmbx_browsers_current_text_changed(self, text: str):
self.update_model(text) self.switch_model(text)
self.ui.cbx_chrome_compat.setVisible(self.ui.cmbx_browsers.currentText() == "Chrome")
def filters_clicked(self, safe_mark: bool | None, checked: bool): def filters_clicked(self, safe_mark: bool | None, checked: bool):
for row in range(self.extensions_list_model.rowCount()): model = self.ext_list_models[self.get_current_browser()]
idx = self.extensions_list_model.index(row) for row in range(model.rowCount()):
is_safe = self.extensions_list_model.data(idx, ExtensionStatusRole) idx = model.index(row)
is_safe = model.data(idx, ExtensionStatusRole)
if is_safe is safe_mark: if is_safe is safe_mark:
self.ui.lv_extensions.setRowHidden(row, not checked) self.ui.lv_extensions.setRowHidden(row, not checked)
@@ -185,26 +222,22 @@ class WgExtensions(QtWidgets.QWidget):
def on_cbx_unknown_clicked(self, checked: bool): def on_cbx_unknown_clicked(self, checked: bool):
self.filters_clicked(None, checked) self.filters_clicked(None, checked)
def on_pbn_export_unknown_clicked(self): def on_pbn_update_clicked(self):
dirname = QtWidgets.QFileDialog.getExistingDirectory(self, "导出未知") self.update_model(self.get_current_browser())
if len(dirname) == 0:
return
browser = self.get_current_browser() def update_safe(self, safe_info: dict):
ex_file = Path(dirname, f"未知插件_{browser}.json") for browser in self.ext_list_models:
if accept_warning(self, ex_file.exists(), "警告", "文件已存在,确认覆盖吗?"): self.ext_list_models[browser].update_safe_info(safe_info)
return
def export_unknown(self) -> dict[str, dict]:
unknown_ext = {} unknown_ext = {}
for row in range(self.extensions_list_model.rowCount()): for browser in self.ext_list_models:
idx = self.extensions_list_model.index(row) model = self.ext_list_models[browser]
is_safe = self.extensions_list_model.data(idx, ExtensionStatusRole) for row in range(model.rowCount()):
idx = model.index(row)
is_safe = model.data(idx, ExtensionStatusRole)
if is_safe is None: if is_safe is None:
ext_id = self.extensions_list_model.data(idx, ExtensionIdRole) ext_id = model.data(idx, ExtensionIdRole)
name = self.extensions_list_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole) name = model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole)
unknown_ext[ext_id] = {"name": name} unknown_ext[ext_id] = {"name": name}
return unknown_ext
with open(ex_file, "w", encoding="utf8") as f:
json.dump(unknown_ext, f, indent=4, ensure_ascii=False)
QtWidgets.QMessageBox.information(self, "提示", f"已导出到 {ex_file}")

View File

@@ -4,7 +4,9 @@ from util_func import (
get_win_installed_software, get_win_installed_software,
extract_win_icon_from_file, extract_win_icon_from_file,
) )
from global_vars import SoftwareStatusRole from global_vars import (
SoftwareStatusRole,
)
class UiWgSoftware(object): class UiWgSoftware(object):
@@ -21,14 +23,10 @@ class UiWgSoftware(object):
self.cbx_safe.setChecked(True) self.cbx_safe.setChecked(True)
self.cbx_unsafe.setChecked(True) self.cbx_unsafe.setChecked(True)
self.cbx_unknown.setChecked(True) self.cbx_unknown.setChecked(True)
self.pbn_import_filter = QtWidgets.QPushButton("导入过滤文件", window)
self.pbn_export_unknown = QtWidgets.QPushButton("导出未知", window)
self.hly_top.addWidget(self.cbx_safe) self.hly_top.addWidget(self.cbx_safe)
self.hly_top.addWidget(self.cbx_unsafe) self.hly_top.addWidget(self.cbx_unsafe)
self.hly_top.addWidget(self.cbx_unknown) self.hly_top.addWidget(self.cbx_unknown)
self.hly_top.addStretch(1) self.hly_top.addStretch(1)
self.hly_top.addWidget(self.pbn_import_filter)
self.hly_top.addWidget(self.pbn_export_unknown)
self.lv_software = QtWidgets.QListView(window) self.lv_software = QtWidgets.QListView(window)
self.vly_m.addWidget(self.lv_software) self.vly_m.addWidget(self.lv_software)
@@ -38,16 +36,30 @@ class SoftwareListModel(QtCore.QAbstractListModel):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.all_software = {} # type: dict[str, str]
self.names = [] # type: list[str]
self.icons = {} # type: dict[str, QtGui.QIcon]
self.safe_info = {}
self.blank_icon = QtGui.QIcon(":/images/blank_128.png")
self.update()
def update(self):
self.all_software.clear()
self.names.clear()
self.icons.clear()
self.all_software = get_win_installed_software() self.all_software = get_win_installed_software()
self.all_software_wz_qic = {}
blank_icon = QtGui.QIcon(":/images/blank_128.png")
for s in self.all_software: for s in self.all_software:
self.all_software_wz_qic[s] = extract_win_icon_from_file( self.names.append(s)
self.icons[s] = extract_win_icon_from_file(
self.all_software[s], self.all_software[s],
blank_icon self.blank_icon
) )
self.names = sorted(self.all_software.keys(), key=lambda x: x.lower()) self.names.sort(key=lambda x: x.lower())
self.filter_dict = {}
def update_safe_info(self, safe_info: dict):
self.safe_info.clear()
self.safe_info = safe_info
def rowCount(self, parent: QtCore.QModelIndex = ...): def rowCount(self, parent: QtCore.QModelIndex = ...):
return len(self.names) return len(self.names)
@@ -59,12 +71,20 @@ class SoftwareListModel(QtCore.QAbstractListModel):
if role == QtCore.Qt.ItemDataRole.DisplayRole: if role == QtCore.Qt.ItemDataRole.DisplayRole:
return name return name
if role == QtCore.Qt.ItemDataRole.DecorationRole: if role == QtCore.Qt.ItemDataRole.DecorationRole:
return self.all_software_wz_qic[name] return self.icons[name]
if role == SoftwareStatusRole: if role == QtCore.Qt.ItemDataRole.BackgroundRole:
if name not in self.filter_dict: is_safe = self.data(index, SoftwareStatusRole)
return -1 if is_safe is True:
return QtGui.QBrush(QtGui.QColor("lightgreen"))
elif is_safe is False:
return QtGui.QBrush(QtGui.QColor("lightpink"))
else: else:
return self.filter_dict[name]["safe"] return QtGui.QBrush(QtCore.Qt.BrushStyle.NoBrush)
if role == SoftwareStatusRole:
if name not in self.safe_info:
return None
else:
return self.safe_info[name]["safe"]
class WgSoftware(QtWidgets.QWidget): class WgSoftware(QtWidgets.QWidget):
@@ -76,18 +96,36 @@ class WgSoftware(QtWidgets.QWidget):
self.software_list_model = SoftwareListModel(self) self.software_list_model = SoftwareListModel(self)
self.ui.lv_software.setModel(self.software_list_model) self.ui.lv_software.setModel(self.software_list_model)
# self.ui.pbn_export_unknown.clicked.connect(self.on_pbn_export_unknown_clicked) self.ui.cbx_safe.clicked.connect(self.on_cbx_safe_clicked)
self.ui.cbx_unsafe.clicked.connect(self.on_cbx_unsafe_clicked)
self.ui.cbx_unknown.clicked.connect(self.on_cbx_unknown_clicked)
def on_pbn_export_unknown_clicked(self): def filters_clicked(self, safe_mark: bool | None, checked: bool):
unknown_software = {} for row in range(self.software_list_model.rowCount()):
sl_model = self.ui.lv_software.model() # type: SoftwareListModel idx = self.software_list_model.index(row)
for r in range(sl_model.rowCount()): is_safe = self.software_list_model.data(idx, SoftwareStatusRole)
idx = sl_model.index(r) if is_safe is safe_mark:
name = sl_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole) self.ui.lv_software.setRowHidden(row, not checked)
safe = sl_model.data(idx, SoftwareStatusRole)
if safe == -1:
unknown_software[name] = {"safe": -1}
def on_cbx_safe_clicked(self, checked: bool):
self.filters_clicked(True, checked)
def on_cbx_unsafe_clicked(self, checked: bool):
self.filters_clicked(False, checked)
def on_cbx_unknown_clicked(self, checked: bool):
self.filters_clicked(None, checked)
def update_safe(self, safe_info: dict):
self.software_list_model.update_safe_info(safe_info)
def export_unknown(self) -> list[str]:
unknown_software = []
for row in range(self.software_list_model.rowCount()):
idx = self.software_list_model.index(row)
is_safe = self.software_list_model.data(idx, SoftwareStatusRole)
if is_safe is None:
name = self.software_list_model.data(idx, QtCore.Qt.ItemDataRole.DisplayRole)
unknown_software.append(name)
return unknown_software