From 966791755938e0676617f620a89e5f14747681bc Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Wed, 9 Nov 2022 12:08:59 +0800 Subject: [PATCH] file nesting --- src/Snap.Hutao/Snap.Hutao/.filenesting.json | 31 ++ src/Snap.Hutao/Snap.Hutao/Assets/Logo.ico | Bin 0 -> 67646 bytes .../Context/Database/AppDbContext.cs | 8 +- .../Snap.Hutao/Core/CoreEnvironment.cs | 6 +- .../Snap.Hutao/Core/TaskSchedulerHelper.cs | 38 +++ .../Windowing/BackdropType.cs} | 23 +- .../Core/Windowing/ExtendedWindow.cs | 30 +- .../Core/Windowing/SystemBackdrop.cs | 60 +++- .../Core/Windowing/WindowSubclassManager.cs | 9 +- .../EnumerableExtension.Collection.cs | 34 +++ .../EnumerableExtension.Dictionary.cs | 80 +++++ .../Extension/EnumerableExtension.List.cs | 65 ++++ .../Extension/EnumerableExtension.cs | 123 -------- .../Extension/StringBuilderExtensions.cs | 24 ++ .../Snap.Hutao/LaunchGameWindow.xaml.cs | 1 + .../Message/BackdropTypeChangedMessage.cs | 26 ++ .../Snap.Hutao/Message/UserChangedMessage.cs | 2 +- .../20221108081525_DailyNoteEntry.Designer.cs | 282 ++++++++++++++++++ .../20221108081525_DailyNoteEntry.cs | 55 ++++ .../Migrations/AppDbContextModelSnapshot.cs | 67 +++++ .../Model/Binding/LaunchGame/LaunchScheme.cs | 9 +- .../Model/Binding/{ => User}/User.cs | 4 +- .../Model/Binding/User/UserAndRole.cs | 34 +++ .../DailyNoteEntryConfiguration.cs | 21 ++ .../Snap.Hutao/Model/Entity/DailyNoteEntry.cs | 98 ++++++ .../Snap.Hutao/Model/Entity/GachaItem.cs | 1 + .../Snap.Hutao/Model/Entity/SettingEntry.cs | 5 + .../Model/Metadata/Achievement/Achievement.cs | 5 - .../Achievement/AchievementTrigger.cs | 30 -- .../Service/DailyNote/DailyNoteService.cs | 44 +++ .../UrlProvider/GachaLogUrlStokenProvider.cs | 2 +- .../Snap.Hutao/Service/Game/GameService.cs | 22 +- .../Service/Game/Locator/ManualGameLocator.cs | 2 +- .../Snap.Hutao/Service/InfoBarService.cs | 1 - .../Snap.Hutao/Service/User/IUserService.cs | 9 +- .../Snap.Hutao/Service/User/UserHelper.cs | 2 +- .../Snap.Hutao/Service/User/UserService.cs | 72 +++-- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 22 +- .../Snap.Hutao/View/Page/AchievementPage.xaml | 2 +- .../View/Page/AnnouncementPage.xaml | 259 ++++++++-------- .../Snap.Hutao/View/Page/DailyNotePage.xaml | 72 ++++- .../Snap.Hutao/View/Page/GachaLogPage.xaml | 2 +- .../Snap.Hutao/View/Page/LaunchGamePage.xaml | 51 ++-- .../Snap.Hutao/View/Page/SettingPage.xaml | 16 + src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 3 +- .../ViewModel/AvatarPropertyViewModel.cs | 5 +- .../ViewModel/DailyNoteViewModel.cs | 67 ++++- .../ExperimentalFeaturesViewModel.cs | 2 +- .../ViewModel/LaunchGameViewModel.cs | 8 +- .../Snap.Hutao/ViewModel/SettingViewModel.cs | 53 +++- .../Snap.Hutao/ViewModel/UserViewModel.cs | 2 +- .../Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs | 16 +- .../Web/Hoyolab/Bbs/User/UserClient.cs | 2 +- .../Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs | 24 ++ .../Snap.Hutao/Web/Hoyolab/Cookie.cs | 72 ++--- .../Web/Hoyolab/HttpClientExtensions.cs | 16 +- .../Snap.Hutao/Web/Hoyolab/PlayerUid.cs | 5 + .../SdkStatic/Hk4e/Launcher/ResourceClient.cs | 3 +- .../Hoyolab/Takumi/Binding/BindingClient.cs | 2 +- .../Hoyolab/Takumi/Binding/BindingClient2.cs | 2 +- .../Hoyolab/Takumi/Binding/UserGameRole.cs | 2 +- .../Takumi/GameRecord/DailyNote/DailyNote.cs | 171 +++++++++++ .../Takumi/GameRecord/DailyNote/Expedition.cs | 47 +++ .../GameRecord/DailyNote/ExpeditionStatus.cs | 20 ++ .../GameRecord/DailyNote/RecoveryTime.cs | 77 +++++ .../GameRecord/DailyNote/Transformer.cs | 40 +++ .../Takumi/GameRecord/GameRecordClient.cs | 40 ++- .../Snap.Hutao/Web/Hutao/HomaClient.cs | 3 +- .../Web/Response/KnownReturnCode.cs | 2 +- .../Snap.Hutao/Web/Response/Response.cs | 10 - 70 files changed, 1953 insertions(+), 490 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/.filenesting.json create mode 100644 src/Snap.Hutao/Snap.Hutao/Assets/Logo.ico create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs rename src/Snap.Hutao/Snap.Hutao/{Model/Metadata/Achievement/AchievementTriggerType.cs => Core/Windowing/BackdropType.cs} (50%) create mode 100644 src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Message/BackdropTypeChangedMessage.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.Designer.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.cs rename src/Snap.Hutao/Snap.Hutao/Model/Binding/{ => User}/User.cs (99%) create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Binding/User/UserAndRole.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/DailyNoteEntryConfiguration.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/DailyNote.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/ExpeditionStatus.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/RecoveryTime.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Transformer.cs diff --git a/src/Snap.Hutao/Snap.Hutao/.filenesting.json b/src/Snap.Hutao/Snap.Hutao/.filenesting.json new file mode 100644 index 00000000..4018919b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/.filenesting.json @@ -0,0 +1,31 @@ +{ + "help": "https://go.microsoft.com/fwlink/?linkid=866610", + "dependentFileProviders": { + "add": { + "extensionToExtension": { + "add": { + ".json": [ ".txt" ] + } + }, + "pathSegment": { + "add": { + ".*": [ ".cs" ] + } + }, + "fileSuffixToExtension": { + "add": { + "DesignTimeFactory.cs": [".cs"] + } + }, + "fileToFile": { + "add": { + "app.manifest": [ "App.xaml.cs" ], + "Package.appxmanifest": [ "App.xaml.cs" ], + "GlobalUsing.cs": [ "Program.cs" ], + ".filenesting.json": [ "Program.cs" ], + ".editorconfig": [ "Program.cs" ] + } + } + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Assets/Logo.ico b/src/Snap.Hutao/Snap.Hutao/Assets/Logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c93b948be1224ca622b16baff7d35292651d762 GIT binary patch literal 67646 zcmd3P2Yl33_C28pgoF|xJs}C{z4zXGuY^EILLl_sJ4o-S2zF@-DjgIHAQlvR@7*o- z-RQc4dj99W@62Q-2}Rc5?tl05oXKQT$hq&{bI-l+zAjzN`2QtKy70d)UA%jlb+PW! zrHeNgUy>Kq>+)26pgs9R|2_TbrCw(~y%c=+ENjTBj`E z)Y`LebgNn4{MN3P+LGs1c2TVx_8w_H``N`#e;$9g*6oMRwD#;5sr%Zz)-E@A@OgpZ z`*hWRPrlZrhihv>PF3sZSu0x2%+u9-# ze)exP&RBx(mPsI9hGk}%he7sH*me3vzNhow;nDZMgS&q^%q->W^3df5cMwy<_t1Yo zvp%sf>lK2*jv?^yk2iUbnMEdg_j7{v5O2^1)UtE)#}Myucm%|Pb^ziJCyx-=yZRy^ zbUKJV;!-xM6%~IYdiQq+@#hLeFDrjo3`l{s(e{amef!&>pr{5TnBjQsxAt7xG5;Jawi+eG84)8%Q;@`Vp0BoJcz}0IGyaE=&(|=QWWst_JH#S7`<5%(Ku+D`$_I!Ic*Sf%FY6Ikv;ni;!3YhHLt*hS z!%9lYMILV5PR-57%g+B#vN{ZK<3 z(8mPv2@XSP2d+5?jh;t4DMCoxbX1HvgVLr);o@C{8~VDz-lZ0Pq010O%o8(jMr!^Y zNH4kvNqIXMtE!Pucn5v|^PR>5@&hE7AJ@f!2jLj77~QR^FKYu`EmGmexKvb9YrsFh zuo{susi>pKG^| z%~R1ldo?8H8~;8}efb-_gVTv=5&gTbiT^k29cJS1&CFw9recNZ_tx8iS)UY)U$_eA zzq*9To8T4N2%8}p^br+s8d?QM z_ae9kO@()OGwSEQj>d)WW901DP}cke0%NDp1|HI4K|4MuJ|MC5nBoEYIM8FzFxo&x z2RrE6zZ~7oecC=IX6@`fnGBaRGftFnac}$6zN9T|5YY9XVnM@sG<{jqKXHkzRQV(n@beM#UY- zuRDaC+C%V+Sd7q=8<9Wa2!awM5^ zd-!7p9}u1Q00z2E?i7E?5xVyBhMki)eEh-?pO~evmlzNcos7ifT*z~sUpPzOV8{y? z_r>r3Q;eGT7K%rm zL`>>hcm%bebj&jyY@oFHX~X^(AK)Fk4L8sSbnV~X2UIa$hQZdsi#di9>>P(8AUK+K zkgZ~X%TQm$C1fEcE(1v^d6x={Ya0qnYK8Afj(?B-Z}|Vy<3krdfaRd*PVpB_ASt&_ zX$Jb=`RTv>ioBY!^z+#!@n3O0`g3FZxibFwuoxJHJ!f9U`rSv+r3Z5f;x9|=z^tb` z`VMl0Jvrb&TMvXLZ9-Pf5yt-0C>Zr9l8W|HAEQ6%T=KtR|u+Q%Ytf#*&10PzQDRSz3{KvdoVScwnlfPW?JJ_&vL*`lA7 z4f+kVMgKu|7--|D9_3@dfJlTzB-0);P+r~m;@F9E`kwyuqBH+~f7kyF|IEiHKmS$5 z=%xhLVV557WtW(oIwqyc}u9hmj^eQ~cD&=J8m*b1%;5G(m0u^ZnfaUARXDN6~Z4 zdIfa&`YYm3&Y4zNgWHauL9f2l33Pkk_)+-*>PP);hLCqN9ym{fz1u9>!%RM(O1_v# z&2kc4JZB@4d4E>fZj?5hB56H zTFx7CL5Tt3S$kn&-_(HzbnRDy8_ca0-h#P)_0>HW8}JE?K<%h;m_Bzorp{Tug&5y) ztvKu7CmO8m&Hd-!r+++5F1{3Daj9_i^hLh`1JSi>H*_((0cPEL=~iz(_R%eK^;(y% zJz>$$3K5AJn6hjGcAtI)r$70T_Rz-Z<@uZqw-dk4Ie=N843vzVLVu!h0KvbYc0BRV z(&@kCfr|Hc%=665qlv3%MIG(R%shp*yA^TFo4WK4?-YN{7NXIumm4)+JMH?O+$O%l9C&_->@+-im~D{v7JZq9e1G z0KcGV2#jBdstFfNY#?vs6NVTd+OUJ)!VWorXn{R?4L0E3hChEloefwH7>wMaYT~~F zlV&b`l{xlDLoNFup8OjO zR{rRQUi~%fyIY`Z4@;9}*1ZpHr3Wnf_NTU&g87?o$0P52W8ignKJpCra!BVmS>z!+ zsfzFYS=r4~f4NBfClW8>&s^V3bO3p@?C3wt%<0oF!!1Qa8n<8-IUqS?CwssBYu$S} zk`L6N-{2N0CM-U{FX0yQz}j{`pql*DnOL{st;gN?(Q2>*QnO1?P}%tXnNKc$ zdNthT^QS-g7gpYWKXNNapr&~WT4t}nn3+pa*)S26qqyoPqO7h3nY68ts06tC1i~*Q z8sTy22&D$);u#3b{&whgLtmKD4)jaeLwDMQyw%;bZ!UUc}wJZz_F34bt*QV)bnYF~}~B`r#BfxK88WVhMG?wTRW# ziu5%j<45rU@*eNNx$p^Zp${BXX*CcN~?*@VC_id zd(?ON`O9j2y#o2Z)QRKSwjqnDR>CB-gDZ; z0BQie$xG?uCFVQ1c`^4-q8?O^w9G=pC1t|d&6oRW2ziYcbLbFNPdDEGr$6}xcRhLm zV;CPI(+cQMM`Jv>!m_P*VgC9Zn6h*uR@}S`t9R_fs$2J9$>uvSVg71lmDFI$@(r|? zH_7Y1z`++j#yyX{%zeCzI!>L64PO3Xh@cIKEyxnT;N>5|{MrpY&8-YRLD@k!at3k) zci%wRI&-fQ7d`%>U3ePMl3Nve7}BqL~cNjfi)&J(6vt_2HDsnJ~<1~acQha z`cMyWge5sj?|v?VIe*_s<=g1rI^ci!l~3X3AC9n$GIGvFWK^@BIC4COjh_R**i4i+ zPQio)Yp`I$POQ6qKUVMDhbH=k8*jhQz{8B~Nx0&ZUtFYK{{<#4SxeiFp$|wf*nr*^ z!l+aG1Vz*LI-?sog4jS;#s#zP=G>2bWDFNQNBQiYuIOPAfgXL688d9L_~yOjqrc+e zcfMvE7QQChzG)9Z2b^{B!T5E_6}t3rR{L4uOaCvqUzc7H#Fze{XAtYG{ zdp!h0z2gxYnT@pU3XGe!4$Icvj76(9VdS_OSbx`H6b+j|9#8`-+wrg)GF8=qBjVOv z)(2=YU>*M!OPE&{AfG1`4E>V%vgP!;(rIuzs>p|@t4{zW50njNDV>16yGn< zy{E0dDE_5OcL(MT{tDy%gM;WlV-cTHj>_RJn6Ypr*4}hC?zsOH4n6(?o_g&gyz=q4 zc<=8Q@$q-R*Ffvj*L*PpD?gm*p`X@fXY_ zw>!YxZsC?YP}w+E|s8(`ij>jr1^v}9k6RVsS1r=}+#eCCSZuiEc!s<0_*7qwB8}(IH2%n&w%65*^J}y^ub3BJ|L_1Q3D5%JkZ{6fr%E} zRdFKLI&?n%${eP1{Pnmg-~IV7IQr_BSart%6xB9z&n8o^EK$6F!oqckV2{Dp1E(&l z;po4Y@gw^NRerbX&i(X<;T?Q{-VVYUzXn>{t2ofdDhhr0H|#Sog*K2vA58v69HmDf zHoX*!w%viC=q&O9H~P9z*6$P1tzR~E(gLm`bS2kUc0jx6+BcW@d(&4WAtJUI8*jY_ zFMaSA;(U?#{(jB)Ufm9U$LR}iplQl{j2J(k+CU*~V4Q&uN?(ZH4~XB_uSoWa_yjFx zeQ7#%g<5!qO+jh%Gl~lc{`&YYkM0rcO!1e0o9LY;e*SWK%$4ys#!TX?=y4Cf{WbA^ zjB(*c6b)}?+}}^^FJ95_8-E}DbBPra57zBE2>;NSPHiBN{VW#Lv-=KCKtG#wSPo8u z#USQbgXsHv(lt#{k8vk9 z9e3P!kXZklxL@0TzB=9=zmI(H+xI?*>C3m0BadS4RjG6#YsX2-4@h1pd{6Q|u?67< zf#J(kzA%{DfpEux_Swj&c?5YQPb(eJ82^y8yG=Nt^a6Id!SzqaUyqx5RGf}FzUaPu zezmx7^0mgj1W5(e03qQ7mrvH=UL7;2nu=+T?` zW4{RW>Yv0V@qj*|Un=oW(`>+ky(8p_UHgO)ciMvZ0rp5(42;H-)!Xpamp^E}{Hu?tRnf12jL-t#1bWg0pbq$>&u(7hEof*`NKo{H$l+_>fx3 zJZ!l47%@(!4UAAWU}Zaw{Y4X*A5LMtXsXH+MK6$iL9my2(bGH_Zh>Ry_dDV*wlL6b zN*n(D%dUvO%4fts2p_V{{h#75IeRH}!+@xKJoe!a7}heKno&Hg?S@cGk5jet!A|V` z*4cm+{lEabOyvjqTBp$l$Ojk?%*hGF4sNgrU@Ty6XcetRI*eOXOt50TKxzgW?t*;_^M=v%V?$wO zpK=-ekEmLJ^#5qRz*oSZ{R`al$*+7Oa9?%A82Nr{ph`S5A_hHXHeSy3d zmNE*%#;stjWg<#zM&pgozWuN5cRl9qA7A?5Zx}XuGPS`+lpVNu#L@;DOz<|wUY=VF zC|7w)4|7jMyU*iAi&+PLrXRS0D zTOT;9c!cPNo`Fg1n=4~2wUqg9F>G}CtvvtC#Y{8d-4rD`{rMn8~$745ZCy< zC6C;8$8Jnpz73mqpTMde2Y4nxV!`ms*uXgSwxsrLE;+3y{9~7QioYlO0Sxhb_a4%O-`Hz3#$;^(1-O5{qIK!yA@uM|^n`*4?y|_+0#tu$O#r+g%SM zBWD4%^hNNb=I%`$*FR_$@=8`<^KHlR`e#3=9O7zmMLz%0x4)pSc``QKa}=9)9mks6 z$qAE7X#Edch5h{i?=5z1zvFhUY@s`a$(P&^P{Og9a>i&>nwv7K*;#poi&e z-v1-~&wTnX_UVnJwpEMBlsxYJU$Fh~Y4YJ%p0TKflV^218z_OTD{a7~0M^co2lNG& zR>7=e_^}5_%MBbn@~KO0N6Uu2NURuxT?dczZ~fc#`Fsd`H zrAa>V{HuQ+$0weGsc5{XU-*FWWGBv^dj*FMy^gA?dzB4@g|g?{dmgGruEC4!85520 zN_Oz!x4&Y+$_-et{XT{NCgucNST7vQnz}vv#w`bqG}u8m(JzU=tX>vA)cy{3z&|*h z8i1}Z+!%jV!%#85W1Z`Nh5u9J{MBRTz}0UQb+8Ebdi)(TSx`@%?7-x zdCp4u=fwuf6epBDU}+Vg%MEg2?UKtg6-zOH(`^_vdA8E=I%>3ko~HNS-+spST@Pc+ zE&K85XJ6s);WtoPdKZF&*V8AgMhtzysq^n%l^0xi_jA;bpQ&&cA0SKo!HQeUi_&grO~w30RHb=2+G;caR< z#{BIg#{P4!eS(LdeVhCDI%7dQZl}fY-|@^FpR>+)7fwBP9^ZcZ6Si-E1~JiFXb0=q z&$*B>;~yr!i}CLz&qWv9dfUC2xBhm`2k2}->_GB^tdbFm^O^LYv6m$&+`vs;U1{7fq+J3A1;0)84K#*>{CnLm&TYF z%6dqkTD|)YL0D2g9z1y(pZ()E)?&ZGwmqkKj%G1Z%jaPIEeG)O>+j>c@Be}Ce)uPz zICmaR)0X1I)2}IRa6P!c|JCn|>us?_Y(Q*IV*aWv_u;^UkKyaDTd{EAag3h4Lve;H z{T%t)-3L!%>XMBnK42O9hr0KYxZj4omIH*6BQ+wf_^62w5DmEhknuYFCI6?sM~%nK zlJ&6md*1lt|1$nEqrpG21in!-;S*NPn%$i$&+`nOKpPmxzIob!_<_LDa1$HwAH{z! zZGfD>+|rq~6Ce7H66OgBJa3N&v333G^HKu7%9XXr>i)IJB4I`AO( zsXF|1`d=r#O&x6Ezl{Hmqfc=^m-DRTH00OL;hET4o{3~nOxQ&7fzEBfjW%Fq8_C{@ zXxagN0rNxg0lSW#R{bYlF=g;bUJAeDF}RUEY2W|zCx!dJex|Sg=o3Wdu#U z*mLy&KK}G`-1p!K?7W9(D-)++NaSLak6w#6-u_tOFaP<7@7Z4~ z)Woad9$p2P$k}j=*aqK}S(v}^R=oB0d-&y-i&(by7VLZQ7@mFW3#AWUP9J{$oxkDe zldmw&UmKR~vAvd$rDsfPL(8}C!=laX>FdwhY`fU6;y?HQAP1=fb;RGnf02eiXIhw< z$6h(^D-OUjb^R;;YxtjfkFh_Z1pYB|c|Lv|`(nf37c~pMoSPv2z}N;P9ykomC(ln- zoIv=%Q1StZ1)>}D8x+i#zY{N$bKiO7QS^0$UqyPGOu1hh{+;iC2mgh{vyxxO-dFaqcHHme`v&e;$et+9ld{bCui!7|10Ucy zO&6bH1jNoGmgIEarD+|I3`PuKla%`?bEtHmr?tm*=wY74|3`KK4(=U-J8NUtYxHAN_*f?jRW!^eG_wHZFpZ|%bnHyjeM13&090R>GVdI|+gza(+Vjd`OR2a(*p)wtx7K z@t1Q#5A(d@FXX44Kd8xU?FbN)PnoB7@;w1G)lUO=AUq5OcB9|{MSUgT%q{u`cn=_B^UPexGY z7=^!W5bFd%rLYMoL}+RyCeGnGmJDi0oYm(Tb|W0aS7X?iWoT{v5vQIJEuby`6D}?v z(*Pg4_)broS?SH1dT$_$4d4qhf_x}EGoO$sLG_*`*{u;^t zrV;6K&?hdu$sSSnPGi63|2ccE6aF7la8T<3V!p4 zLk8mj@sFKL{B<_KM|;<7^7|^y7ceh0u>sMD#0IDnIj}BVQa=e7KKPcJ-bMb6f1%#` z9f}(nTOtZE&@+WOUpZ{)4;+JwF*H0Eo{^&vlr$6GG4lN^tVrbIR7_eXoD^4O~3fo8C$8r{OR8e^F?H`zc&@c^e!P?)S zvk?4JOc!4pY#T`5zmVtKsr3dnC>|jA>z8}rDE7alC|{tD1zK*X_XE@jbT;73I(WhG zv3TyiZ%ktMvv2Wi3H^I@%OZHi@$9XC0S0-ebEVNAsiz7WlJ02*%SKf{?@U|-ah#HXQdtZg>X*U<@`Tuxh{SU#Gm>veZFP`hd2*l4|?0w zv!|V!p@HLxoeB1Q&@S@uF_@?7&8bxuoqFRvGW>C+i9)SETWX!r-D67zO=x5M9; zA6q-nKg3H)M!=epAuOJebh((eWH&VI)Aye9o(dmcK%nMSk$;o#hp z+Tyug>a`f6VEsBe4@!b zOC9#==TxyjI-2J;9#MUEdVhbt_|KqL>E;#=2M3>P!r#*?iF>w)m`ltz#opDo2F{)( zy3a8l==gxxiTDY>C{r8orVU8_a17(|6FjSGtg#8_`tX}yS)2U`lV>gD8HyD8`BGIs zbo68mnSR58wMMP4tcW&|MxP&~YKgrqoG@UJJG}hjc}_lp^9w?enp29$o_~iN_r0-I}l&5`F&%X5KbtoEiW*(0pSF8u34DKbLcl8cogsQ{H6SzFgySH(;OXAjb^)XPIRZf%0K3 zJRfv|-}B$(eE&KA@;ko#;osovAB?oTN^HHIvA{Zp``>uBH}iMq2kLBMhy5STUO;z* zq;8|;!FggCw;??9PGt*&TsZT9oS`k}*YZ5g?^6qNi^C)Dn$AiTA8b?hm;$b{|D^RFVAJ^nIlv-4gO){vc2x!*a%m3<@P2gEnnvz|E6f%%Dj zFlVAvVg2?!?Ct$h#rZ$e_Y3}#SFYK*gELS(P*T%`)f>0+d_t^=4alBVeH-mLAaerg zc?(Y2%C&`da1%AmTM(SOo#(q*D>TeUkUUT81sD9gTP9)2&G(x4{r|-O%hw)ZA9P3r zxjK2hvvUAr0kvF{J#Wefot#6+-N!1N{iyj3pYS+hGFK=rD0ZO51wP7i<_NMRR|tre z&uMm`;sI?ye1SFN`Mk|{EBu%5+^_n{>|Nq$V@=EtHLcLCLqAa+_kIzab$&qNf#lMT zthG#>xsV#&CrX!*oc%-gX-n_uhu{37IQfP5{?0z4k68oyf}Hak`h=hOdC`q7skrbF zea|QC4}6IR^X3;uk!_BM@kX_R_!yM1oS$1tdsw7wMErs5|GtAfP*6Vw^SA6`AMtSVx@=7=k{$x35$N?| zsTs8Ol)13CbO?J(v8_g5Apdwh2veM4Sb4wA3B8<8@Dr0o6K1~1)NV3 z!+gPmJ()xKbKR&)=aD_c4zD z`sbnpxH2C09$d+OrV#cCtc6(*d-Sm4{9c0%R5J!p2VyMHa{xK7EPb)Q>_PX7S)$GY z_{Xi}@5T3UF20uMm6~u$i@|Zs^`BIGU-X*OoTqftgOBpOehaxz9Q)-*To?YL=g;4C z2f`wGrjGqPS{(4WEdF*5!3sAG|GDHMClMUejHt}@3geKZHJp*V5n*XtsoCG8tu)3t z&c_N#p`9nK)@&hevC@Rx{9;htFr7U_VZ^girxQ(N4#6G*)|Na?Y=C`*0h$fCvX4ai zOKqKFv1-!}e8^s%ljojiPwzVV{z^Cxjbm*wf<5Ksk3qYU~Q_7|%qGaO<#OUaWn z+-vYO`$+!!&);zISKx_f-y|=a%X-ri_{1*8iASH|xq$t6`pwUk2B^ng9>vz86Y^j_ zxD>qyvX9Y$wR-mHb+ZWPJg;h=<*m}}pl>>Dpl$AGw_ZarG-y1$B4%^eIOjt}E#SXp zl*apI@3Zc&YC5`{LNwme`U%wIFG2R}t<-#CGpac^L(YJdP!FhPoT$1k{3Qk)eDM>; z0P1}qSqgU-&cYrtB)AhBNQJFqxFPS8_+QiV2r|m=LSF5D;=2hEnL7~0zSd}-Z;H;_ ztCpN~ipn9!&AyXd=T>DKA<63)+Z&LOH-cyVbNIa8C_j`Q0``_E8yK%_z=L%oLtmlT zfPXz47$;V4*nv5VS8)adv2}0sm5{k zNfTD^%*J`<7;k;@cf9xECwTh#w=pDiIeW8LqO5*3r1z-x$Dgoe$1dEy|54=&-ude< zDps&90QsDs(ra)L`q)%()`$o5J@)ALa6r$2ygOu2744vkcEDO5XBo)MVbO!k`};BX zp9GK4DXIt1Gkm716Kk{WG;LR(Pf8ug$}W;~m_AcFZ$;Boo{wftSm{8_HKj*D=AZTQ zy>9#^h7=EPMrvjye0i2p;=i4p2lIhgQyU;hFvWi!wVP82B`wED&i}V)Kr%HdR$`8c`s1Im1P|O~4o;9)wMBV7|SVEg|4bG%~T!Q&q z@8Mj^JJ{!Q2ja5E;1TMAak-q?6utrFBUiG&=wH%ef?t27=6m9C%v`z|lNM~@IkaEc zs|eJOnZ>=Ihd#EYydx-y``n8dbACY|fAqGlrfpQw4miuNZ=%WtL=TXjx1mAfln)R) zuyx_=621rLw|#j{R4gI~{ndvXJRY6@B(fnF!l*?>+fl)f@&FV0-B4xzrB&OCI4 z5hti)y+L>0L~MZCx@b!d?iI{S6A%@j!~V>N71xu#V!xPNxQ3Om=X^2N-gA&~sfzPA z=CI#jDLkS!z}|laXZzm7_{Va}5EJm21c2OoVNXJ7dQO%vwx+%nHF z*_NP>bw2&StLpdZ+H;7C3+C3;YX;L-lOvejFhreYck-%Y9BWqRz+L^Dc)tW^TBw>% znW^7r{$S@4i($<7&8;{$l{KGkg1rT2qH+F>*Z^@?JVEXilN?Z=5B$-`<8S;0#nmG* zg0rAR`xh=C``;udB>vX+Vcd_C6^9c}ADy|G{+u%~i}#~$^0S;P^)!Z0;(aG&$B1>C z4aBpriFcuK9pbsCqss4jN6tYEHIInQor*IEXAFp4$oL}MP}fVs+M)E6xd)BrzKJH+ zOI3M+J~wFVC6RujHXD%KKx|;3Z8%1?%;0$f>EYLUMCJU?yj8crmRhD)bSV~Wxf{MQ z!`NfQx`kIEeBx(e&h~q7f;}k*-gu95QFdd~E%#!~lp8UCXB>~5e4O)hCg26m1Zw0Q zEsMc9=x0~L#d##0X(c{h{6O!4>5L1NTvaf)u3`Qb&e;dq8ukIw7dwVLM6^Ekc#z*4 z$9$FVOI@LdHG4bQ_fMQfS5iG8vgdW&zz`??L;TP3j)v;y$(XTd74l2!4SwInB}CRF?NuHk~0P;uHyGzY={Rw%n=+tsOJ!S(H&IXKym}sUu>KkNIy~AnIk9W zgad8;ux!J2U4GZ5{l52i&O3R9{f42bjP(Wl{VU-XoCh0so>>f@P5dX|?x!x`sh@tq zlk7p=cH%J%p`W(#tfHowujju~hdz(C@2z4h*#g!9J9> z!a2t#I+ONw)EfSy{Vy?KHG3+mM~=qC*-JQMlC@hM7Z7cDD9be=uRWw*Pvqz0 zi-jLh1JAvO{O}en9xz`NKQP#Vcl7c7Qa|pzw^XkY=zGd|?vQ=OHjaL{W%on)fEu!B z$;P~0&Tij-;yjjayqz<*GKeeZQ+VuS)G#e2{Bkiw^(Q%6|*~ve)&oUiNzR zxO2?yA4O`#5#Eb)n6uB0v0gj^@%cNEUUihO9pUFR+ej=qi1;F&pU&UQ{NYa8*Fs_+ ztNO?!&k$Z{+*fGYW6s<_^%XX;raFS!nj5z5I?R24`7;48fA}?yoqhqSC1coy@hz`$Il|d@gRfztMZlU5Rc@&EgE@m-7avaeIhkDzSy6Bs`6 zDHM)6g{X{m@Q<3ryL3(z+%o#T&m6+y!GyfyeFiV`rx;!7tG2bkJ$4L3)(<`8*+5k z?!^c6v5KT!urGj1vx6#F+LUm%tPA&eG|X+P85f4}aTsg;O5 zC9mhy9y8cMM)h%}ElAH;d=cxR`FjwlxZwuo2g_&|T8?OZ&Oqxc6rG5()$GF9OVWbd z51r|t)lkk=ugPpr$=gM{`Iu)tZ`*gAn$N2UNE!~0m>ReQ=WteXIW?*}<_C+3|18Fc z8E_6>g#8B}QF*{U`wqh+vKG(3`!{&_#PRGpbp^*fu6#AyO6B@It7|!!z08hf7~n`7 zaID~}q#ab#@B6}ndV!_GFj&}e+14sM=xtrev!BD5FAQVOSVJ3NOz6-1i>%t_f{PvW zrFONLbpV;;uE+1X>Obx9m+!Uv%xegbNnjt#R%HW=R&7RhF6VjaxPX^$25VPC700z8 z?+N6cS)r*L8N2T#W(Rc~PmA?}z3g$pJScWL?+n?AnsHB}h`vBpVf`b>ANjDdhn(8u zygzLw{lirHgX83o^aUjkBCg;*#slGoTX^TzN);FUcu$`?dq7Sgen4zskbMjia;tfV z$bPKfbp&VG3x4j6&+ym_?_&SSGg!}ljoC{#p^92?8s8^=JD&H8g~bqW-l5~g`-2Cu zwqffY2S?@w&VGsfS(ONgC)W%g$653f@WtQ0p$_mH3Th_9GkO$GKJ$Un6b7l8MvNhR z9O#(KnPohy%bBG8Y*I1MsT5XDWwe8G+Cc>dI58I(5J}EZKwGFmU)q4BeJ$-^IBh`e zpqBoimT|$<4wOIOU2%49Nx19O(`tUBaJD}i|HnUUMQmCQwd^%mvSzDVlNkr3|4(lN zL*3)mTsG0O9lYwv*VZ61>o&xb%S+rBz9%tXe0w(ccXrK#@CvR)Nz>!%zQnTT)4VT_ zi?N}k@i7!NJjQz#=OUPSS2klqX7v%yCT2_^Ulg7gUnJb{ZgRt$StsG#={TMNGl~b+ zPD%XwVl2L8FD9^_UEWB&KckHEhtiloa|RjDn~MG;I+5gra{gG&B_W^J_Yp`RiJVCv z%rhD0mM-+soV#n~NIZS$6EYE%Rj2MJx$WNls)C%Zt zlvcnzQThk0*z*?{&2s{*TgfaEXYXp}myJp<*3Kbo=TEgWM{OE`b{1W;0kH!s_HkIU zcg@O|SV=zMnG9Qx7`R20!acf%xglp9F$NBe7{%3q{PNMPBk>%U^qg@G*5Tiyw<~Rc z^(DrIK~60Mih<7TX?LOzV6I?q7l~x{u~wu{MP1HpG#4#FOX&)X$el~wMdAYe z0hic;IqgC2gY4ORpt^U%!qS>~m^0^YdvmU}FY9l~Sbpp7KL!62Z~cwBx;sj$hN~E` zWX%?32Mcc8fb^`koKSMXA+FRRSl8%7{UeatO?1v3+^Y|=hguDqxZlWEWQ~p6>g75=9FUZjO0?8eXeL)2CM434y=Lba-^x|AyA8Lux zSElurUR@*5*AMMkOSTxuyTN_4*>}7M^%EB2)YC7cVcHtFh2{iI5(eo zvd-+_m&30aO3br$OYqlhz+yls=ezK8&V_u9i+2hSa@K6XnX^yw5}GltjI%DPHegca zI+HcIay=Rg*ze?C!?-YLG$N0;ce(3?{eWK`X-iK!#=3@!>zCI32pT|3JL)i1_m#q3k0~r&XStsh&+Z)!d zwTMtYV2|SPf`4Ysadn?!al@l(@5`PS{A(sWZCLt`<#Xa6N*NP`8|05Tsd%I03Hltt z*cZ6?^1Lz67zpyE3k9#3G6!bICk=`qzi9q)Y5Fg)};iw@eLT2KOgn^b1^J?I&9RDhlNYr%i2djEAo5dFU#K1 znOeYlELA)}^8>Z@6Ae5-bYkfjcX4HpwYfhvw5g1fJLm)UtK3icd|utdy10K<=>f*r z>!0g=f{F>W0pSM4JNRbJuq|mHk@al=mjQ-at`Gz&r>}4#+Nwt!UtHp z^8ltUy$Q`zmto4hm6)+$HD?g)ixi=$;eIA$0IFngth2$bd zXegME@fE8vp>hpI6wkqo@i$?@s5O{5;bt^eFT&zk?1h=X2Tiq$sSjqb-o{yxDaE{t zxQ3jj6e&67oZ(-Ar0ge1sbkAa~P z%mX&l2Wa>&T)7e9ydy(+Kmc{2VBXOj8O1Xh-VxM3gW(xISH%J0?rD|Bcz^0i*7qM% z{=c*RueSkt)Z2l?35gfN7u$RRd12Lqyoa?6F*!Gp8yusJ>2id^`!s%-uG0?VMLV1i zpU9cyCake4U(iB3(CI{hO{@b)lW%5W=DORkfHlAgoZC^yb4ZzG^}HiEm*;Azb=mVQ z0naD*8yLb~LTWk8yZQ~16~#HePO6{4M({UTxvVdHs{TXe8&qF9`;ut`a(`@n-aLc9 zA6YyXD;DhK+Q)StCN{3c!Wnm}=VM3Q2n!1IZUwp8E^5Aq7z0iy4N&%Ul`j4pW8VI`-UjsX zK)$Y|@p0q~dyx0E)9#CICpS2*@=D1swV1%Xv-ko2OieosOJ|;tBzeM8#stpeW}Hyx z&lnHH2lV5)f!=*>5X9Wi%_o6(GbFOEl+2Z-dIh9^z=^qmEPZ|;y@htVUIOVq?>CTj z5cU&DUSY!+psgG}=J4(?xkFOUBZzHqu9?9G@-aMnwgLb8l6hFMP_Wn5)Ulf}Z|Y9m zxbQx;|C?qk#-6jU@$S-pP@DTr-HT*=r=sjzndL8Wn;Os`#$SB?1JAure^Sgk)->LE zGY`3i>{;p2+tdc=6RJiu@;)5R24*c>sr!}&MHz$;RB>e_QlcR58 zKDbxqfjPq;Rym&R>8tqwWe<9eNKPQ{FBox<`>~8R@BlS~Q{)tSZg9-N4Ma!Q_@U@V zdzmNP&YsGx)C`0hEMkw%EENlcFUov67tSG)dXefWQ)kiJYDdyXVCXGmUf`(uNwoe_ zu>*_#yf=IZ&mu^#F=w1A8&GyY{>T1P%K_4tDmJ0_4f0$&pCWu;IN!vIwU|`Dp7`I_ zhX0sNm^)>M!hb^Z8s_#t8}fU7o`1Q=KdS!-_V@7ao8+tlOqf1TVc#%znmX$zJz%|h zS(w;B@80HUo;cl*6J+GH^@@lO2oKNVUDQR$D;$Qz6gltDej?t7lE8Y8`0CucM;OzY z|BpF)Z8pIBbZaI)gY4?N$#ZHL51vplLHMEYNu?dp4sxg=W)IWsK=ejcV`42Ku5drO z!ERNXklr)d{{xwGNiT^!SLcvrMipa0+kB!n8;}~3oK4ewfWDWYjD2Luj0vIi1)5%< z^_w#<)Y(CQ&cGgE6Qi&f{I#X|hrzUoqNMS}f4#zg;S}-pZTnw*|B^Youx##bEac1~ znX_^^yxYJ2NAQ;%|E^QdAt@sd)8;P0cclQ~`f`NmrF@^6J zJ)ngBmXZeu4>0ionPn(08^QB<>|Gd=z+Srb)baK!8^{~+2z~hzSGNJ~|6!BPBc*6F zeKhY*Y&oO2f%pPFH_+Qbf#i!iJIG?LkRf#?Y7a?_Jqg9s4+{^l&O3>Iyo5RGjpT<5 z$qSe#@-<}xYDQI?4QLtxdkfi1qHMsMHlXV%l0I`e( z0Y1zJSW^^#pcZXF7b67!ocJ*c|4H2cOJ?0`g1>M8v4{E7ZpY1gPaEz+ZjW_mA72ap zdY-@Y*f}Jo=U^6lmSw&`=|7VaN?%^4WozxH4 zGaj(FQ@CL#HlWQV(foj^4amK3b!yKEh{$k7uvDE(3Ut`^!GCOf;Jn_Y(e-y zT66;>$DdfW4$J53x!*o4<({80ZmYt7`h;ybbm8-B-uq$?SHoXn&wamP?@@)l^i0lO zvI^7ZEyL*X(-9U@D2CWvm_SpdH8=TlSf-H!&jfb`_WW zsUiDBbhH5#511S3dr73Pa0t1AJvl>v&MMR9S!-v{^k+`=J*I=%(=YRE+G2v<2ejcI z7dD)_&wAA5&r)$-@BhX3Pii6`;QpUI`&QO={{5%nFW8IEU&R>~iPZLIE?SA%tnbd^ z}L z5bda(+(PmMpYYKLh@Q+E0_Ovf6Nny}z`nD%!iU&@s@Z_@0}>0E8>sxyXkMY}DGShg zj9odSN^GF76}49O6dChE)n_bzKo<`NIV95e%NaEK0)0#{!~w|zSZfNd#^g@$pU%BM zXVP{on0^Ot-gBnYGd`X9ef?`!+W&&R)C-g7^96TVbGRl9 z?NIb%jT36Uhq3en(L8e-p0Pva27b}=RBut6PGsCurs+h|Z)!iZ(9mDnW&`Afy4*l! zSt?GT?>A>HsiO_#AS|eg{(l|n3TNZSMfdCC|9u!!$9s9_?ZKom>v=C*+qrH%zE^v^ z68?8Q{4BBy%ZT|J%wD{jet$JuCe9&FnLPW+9&eugj|@pbYGhWK?QS!O7#0#r0&* z&tqM;Vf0j$v->dK`+CJ7JSYK)#5_JS1?j{;kG1i_%n}r3^S;!gT2vNOXQV9z2SuT8 zKj8zWHqejv`n9(K(F7u>6Q-t@pty7xO7l=PMu>tbF_IuytW8sI)4^$0-TtKvA=`YdaFEwcc{_IhgekI`oq7#W$D6>mt zj=46wR+~}f=)u~p9nZV6mZGR$GmLzON6qXZaswed3QJA1pWG3b8e6+9|$z! z0%aA$iNE-Pk*FXis2J9)?(wp7%|*EMj~9tnbP!p?PN?fo3GMBmjUTktko0z7 zz@Hj{I0$?NPS7PB2_a8)bc~)+(2fRw%LI6 z6X<3Vc$KxRS?3et+zD(1c~<+=94%u*C{-&b-D zm@M%DS>yyo{G7Xc0BZ$3lnwOk*^6@ld{_erqFu!+O+c>;2p339DnKFQK!qNE+CWL= za5RjWg1QFIlJktC@14rHd^`R0{fe_mOpw~KXhsG*AV)Ou3A(&M&jI9f6;e0Q)s93b zmiuzupGK7s%^n^aruL z+p)K~1#8kbz>PD6JiP_e=RzOyHocQRLKmc<^VrbkfG{1`$Km#SPqYD88xq3XPe)<>Y5S712`$q%R%kr&!hD+&)DrfR=S=Co-)qW6^)jpO|(f1LMS4u6UJnXKtZ zA6IF))N9&&vwF^68Ly!{>+cba0IyJ_#H6Eu{=SHOPapFIYdziyf9g5PE(|s?9LY)S zgRtxmTYD#BAHe&xf)NrHM;)2=#rmMwfy|FfU_MY#Tx0M7rSwZhr4<;_(2V&@MT=O& znK_dcXI2K)tkSu*d*(=8O9uKsHOOZ?BzC|0Yyutwpm$6wh1F;^d#)S-%XXR#6axPoz^ zh`9O(MR3+R=N?U%j)*AU#myBK!5IhS8sYgewY9kot1rDB@20$W{BE*mK`Y z|9PT;_sQ9Wwljz^oVO(R?}TvfLUuJZfs{q+teEtYMrEu-e9i{OgSIosGK)Ze=E%@T zpw|fe+IXSNwAXAv?mKGp1A0!7mDB=b-CuN{)YMwunaa5iT$}f1sKsd4ngFl18n1YR&W+I1NNAcA0!m*MoJld0(Hbp_LoV&sBncW>PgaX zB()~#J(B)&jq_=7U(TiK&z=hRmovy(op=rB(oh2mkh?6Vau($Syw6|{`^fjQFKwIB zhO{1Y{W)~$CDCgH7#Mu&IkWc zt*0{^X^+3~zYTkiv$r{c{=dP1zoygZ_W#gu1bT-df?97*Y9TTcbIA89bpD^13*OZ6 zD~g9Q?|^!vlO_iIpCLT#a_guI}Nxyd4(4Xidi&R@EcXA$Zc6GC`b za2j>|+3W*fPg~f=9*`Z%FNi%P7VlHMF_Ar@(kB|vz96ap#TIBw@D|-q<`L-6p39k4 z>05T=+yQIH1onU3&2u9s5u3e^JYXt&(dVM7`51kG=~)wr4Q+nF=#CqM4e0ViYQ{2~ zMC&otKg9D467o|C(X3&j3K{rVYr`rB5_Sh>Lv42^~@hRcOrd@iWL#; z5s%2+&ULF=;haYyGbh3`wyR!2=}QPnS;wBimCWrLIZrFc=&Y&Cr{m&WBC!G4|CP;0 zQPFf5+IG zVa(H;n#QA%YZUi+bMqurme--7aXgC10H5)!u)RxQh*lA9$YY z{68fHO@Cx^Xq0RUQjXmFu9@rejAx#EV01Y1~gvi>{Ux` zu!3BmLe-4y-7DegQ;B)(?^wBbKSq?#;oP-21h9rCv(^7dtzVD1@ndKB%ja)Dc^>Xw zJ{UWBHii+yxcCBP1ET+Ud-4p|kO1=k5QTp_wSU20xPCSFdtKdVj2<%yO=ERSo;OcK zZVB_Ym0R!#`(&Twy-4Sk-~V0P`#SueY5g6Ke{%`vzQ2fv-uwVZcz@Su-tQL1c%XPe zB=2!&ElT{qaDbxHGK?DCjOlac8T`NEgv(Z8(F&eLA^uBNZ^Ha#t68faM;);a!C^71 zy(g(UzJ4BsHh)ZJQ_8$C{R}dB)Gal)vfb=To-ftr)3vh!Id8~wr(y>_N*kEOKFm$z z2fGmzH-~2y*i$6)?3GTyc@ESGoxLj&mN1gDN=w+IS&y<2GvOCjz?l&(oMFk@7|#+^ z6-+^}pY%~{dbw|4JhmS?t#W^<)fwY@t#aT!Rtp8=*9R+5e*X3-7C|9gXI(liRST4UA@gU_NVl%kJ8bWDf!h>lBT-&h`N1l8mL zm1t@iM?Y|*i481XxsKRx)Xt=>+JL2ezKC~3PMJL)6}6ln#C+4g2jf|k zS9^XL8=xQ1X423Gc(;x84oO@Po>(;EK9wU#ABoIw3X0*p{Ll*49;L5L2emKkdjleOB3w_gnQ@-Fgg#k>9cE{DJHz}f3=VIOInnv*2G zKEp<|sGQ7~8bBatq)P2a>VCojrRFC-U}Rl$JNzf3gnH6O&YeB|^+i0*d086Y`(4HT z^FRKE=YIMf&l3AbiT(c1|AjlB=UGD501v+YAzq=Ky!;dJD!JjaU;Z8Q)^0**c)Wod z#3iP)PF&5s-Het=Q!sns654?l2gLs`<=&URBk4n4#varqYc^r=s`dOgEyskZvpC~( zBtjxtdv#7!=YIl}Z*RBHIkVu(c5x*e5d6gt$Q@@gmq>U1SbB?t8o?F75CHU+{GD-p0ufHn{HFsze4{Y>t)?~ z^XK1i^ukLhpbji{AUQ%L&oPx%4W}kJ6O(7mGWY=T|FV`6bFl&GL0+;(deS#5KOk{J za)p-3(@{Z=8kJbh-m`e>@a#z@fA4Ho@#XA5W)0}?F)-l+i~|Ngz*r!8fy}2;voLvA zQCLC~?La$oB=`^U8A;4bVLphnU3k{ptQXH{_-7$7JP-e`z4L&I@=W`_L$UW3dsi%o ziUkA&8&brAiVZvVu2>KhM6fINjs?47@7QZB$)+cpWD`p^v7{&H#&xrMeZT7-1_w-x zy6^j)?>i?s=gu?F%rM~muU=j0C2Hi|fx2#NLgHOE8Bm>Gv#^LU*aH82X*uvb{MEi3 zK(4=83x8^OmrLxdMKWS+j8z8^2dak*Xw$r%bfm99V?U|`x_9X-J-YRi0sYan;R9tY z*q{6QKXUT^uX5qLU*UNx{;KCsLl39F`L~>4wr=W|Ph`u@AEi~lk7%lvs98mut!ydptP+3sdgSv*68V49G zWoz`JpR+x4e_GNX?`53>q_coH<*K%!ccTyf7&C{ck*LMBstb9S4iQq5nkyR(=w>OK9!19svF*qoxMo3kE1a&Q9<7->=GG`9R$z@EwrJHBT`??ld{Xelpd#CMWPu0x3{jl<4oz5zUMF~(J+|2AS_a4l3pkI(#g|)D`8hhdE^rt@w zdGF%FffgD!3pKrz#vUYR;!5oNAai-M>-gv0{}KK=H?lK6U~~T<_M)tnSxKw#!+RJR zPzM{NKJkH;&8Sa?_cf;5xAzeAaX(qWe1x>SkLBXm)Fgoad35_#!f5y(c$38If z%xgY-C|mCQLndxWl@jO^|2`w-;{Bh%9=&fT_swH4H?{!JzmLDL`f!@~5o2hGoobC0 z`DI?!R|pE}EaN7{Wa+_sCtKra0$o$Q(^yGpnlrb9{>{PB_ma}pdcn_q%qhdYK3}MG2s52p znAsNwYiD=nz>J{(QRg~7yW`0=@3j)W#Em@LoA0`j4>K^jU^Mg}~2VH)@&{FQdLoO?&gSE_1qf=UCS^e@-0Wz7p|4S!I8=6W^96Hc8# zXPLHUyBxT~zU2qZ9sQbm0fr%Ah6PVbcpNS{8$L|~+ z8I_5x6=Q5fc;1RX*kcpMD@|Z8PKQ3?IOd+`X6P;;XhZ2^eAG1JS3{+F>tLx`gLyXW zBX(rpT8^Ccm~UdmQ!zEO+%jai?XeBl9Jv~cXNx}cfHSWNUjMC=yKqx__?npSxhD3u zUm&&R)tYAdeu{ThY{5>w$5A8s)F&)mrUo_oZ~l+se-YiRb3Q$p@iceoS{XcYN|wK) zb*oT=e=ss&+`Lt?ogCuPuYLi0`is7PB3HruDm;Hhv4{~y`+)ni$C=^d%i zkRB>}QGLS(N!rb?v+}(?{&w=|Ou?dDAZ1#a-tv-U{uChS)KN-IP`JggGkJ63C67+qOn0GgF!c^%` zY@-wPNsY;Am&Yg0SFk$tz#Jd;wB*R8G|@1F7O>^aK3A^LMU8sQ-ptFM#=K4Jj+GXa zjyzlRW=B{T!~1IUw+e|6w+7b!Nt^%gQqzxE`6y=CGJ_i(q0iT>>CL=}VP?+9^W?tm zwHM)keec!_@3IerIjF?+X3SeoOpv^X`JK(BRZIGlsqKhfxlOiz_M_~_hr7s*w2Scm zIq<$fobNQWkcJI#;@-av?;pSSSZ3}%DwV*#G<;uz*xcxO>*VTpkFEB;4R;&wtIjw0 z6E{fzRnFY{Qv8WEcs36J|Mt=i?0fW%kY4=;OMm{xkPcm`Pi~b-2R2%0Kuu~Rk7m+? zzO+%ugo!cJsdK?zOa$8u>{a%wY*0UOS_0S1I08N-y=#>_DO(U7r|rQfje z(uw+{Mm~Yq49o>1PFK33C;7qp)P+zJU!nnh9(9@9>4xqe!p!fE%=TnQM;_*K<_d%^ zybNvRE9xb+8w8OPx5jyDy0Jrz{bj26ZP@FzdJP}(Z#Mw@tO?IQ&C`3f2n2t8D{%Xx z^FsI&Te|SYKc&|QcBj>CU~Is#6XWUK!XIg*arsU%Wcni6{PB0P^Jco7M&Dm}0QL`_ z$SLf8#ry>FUQZf)pZ3)w=|fL+$(ro4rshMB3pKt=4qbSv_Z4@J&DOJGPmYLK!NvRO zvf<1X@^U?-p-*dR6Vw4;bBv4v1LbdxH|f#G!R@+a#*dW-w8o%DO*g4l!xet=kbt%w zrFXDtn_~&0+^}O=AjsNXrzS@A-e|%!_AG=@$eHmS;9SD%_ zy#~stag${dasF`=V`TiKX)=0zluV9}m#C@m=cq_>*&Q-tMy~h^T61DMm8n6hN^eSF zNT>`S!wj34cw_>0Aij?Jy~^)8$7CFzH(_d`*%dL0+DPqd3=1E|{Pv#kMQ7>~L(TEi zYj9Tyz>f@K&Ua|f;Sxj~x3M>IooYkCd_CBwWSoWIKbPm&`D9suOjsg$^0#14M{CKO zm-$Ww22(3VFBtRlDwEIDehc;Y^;D|Vn7PVzd96A#_Nudws~j_UBFPIr%ChU>c{1Q- z@YlT1U}ie?9X!(1g(u((42@5cp^D5mV=ibO{hKprKxv^jyIhZb{vbofXW$Rs%J)VFWIR6eT8&!L8k=IssELLKbe5UcziW-jAo`sLkBsDur`BLRJ)8X{NAa1= z?~4$hme2q-?`_(L8@_a9|F9b|ocn_%5gZbp%+i0Q-$xQ{ekG;!r$F9O|tS zhgwPCKa%%N;3Zz7x4=qMqH_p(Zf}%HI~uw3y{-~ zUl@C(iNbeUIT|q=ON=|9rGXdv0LO4`u%m)_xJuO@w-k+ALgBhj)@g7 z=)as>GjT4?Y@;F-C3o>E%rq~@oFW$~TDhLF`;`CN4W2A%x4zA^{cQ5y9)IQiV+`cm z_x8uqf*3;!@DJ@04$n>o??joNm?Vo=Y?S%zby&PCSr#o_XRhlu?2skP*GoJ+90g5G zpci%E5O(O{FVv~qkQ%fKeCJtv)wEYBcb+`>dlkjKNmFUtF--bmS7^`M(9x5K8BSux z1-x7=Q7k3rilx{LV#uMyY1>O>mk{={4^3%n!RIE6bV+VMJL~$rKnfEVe&zg15ipqcW_bOCChT(&$O;CyN zF>b|{|MB?WZ^;eKH^wYnBZK+QXMyv<+YjXuy8bHI>vE*O`2gw)dcpg% ztoRoJODC{(OybPvz`u^0Im+Y9z;HT8u~#~<$$s>|<0@jj;N5Vwl%8=>^3&H+sNYd3 z9e+V`cQ`B#rKUnBtEE7p9#Rc^vT_wqY=e%XJ3?KE!MfJ>Wf#}USIz;xEdMJ_=&om< zfcCQXI%e%vaB$4e>|N~k5_2R+kvQfj4`?WN13kvaF_W^IRB`uX4q6N8 zK6;u=TeU;Z-~3tnPnvCXKpE^m<^M*+S@(Z%M=pQ!2;1)|{%Qv*{^##Kg!jLdU4QwM zeIVViL)#hr+jR(&5h{zY)8;Q;YxsZL?jy2!+kSX|M<(y<a+pIjYUKIOFHmN}l>N!T+-4qki4ecpLPk zcEB?1K6o9OU%iI6sg2gXo6WE>_uTm0@O^eUZF;uB==`0>a+2EcV;|gS?$2fEHz`T{ z!p6zCnd{);@68P03tv2W>G?0ZN9^=x=`v^Y0lEI|zccw?akt@r3H(Q}BeY1l+RQ|5 zBd&fSQU@Q;w_|TP``HiF*bc-FtZlXb!QY2k^AqUyi(fx7cpLs#A3${h^l;P;V7d+Y@KT=E=l_1JSvtj)OqyB3Qemu+-VEFK#^ z0UWLJUhO)E0@K8y!3J@tzfK%o@bL;pfj{Cco-6SG%x}1fFvg2;M*Sy_|eOIJj>wZ$EQ5Pxh z)=u)fg-YH!?WGcRB)vz^WCrwXcAbu7e$|?n#~&S_^Me-dJf4Za4SO5@m$3s=9yc;T?fN_k;^47E9DMeOL$kf&&@@#X%3<5NVdwko z6X#a@#KLoq4c3Cc$_eC!!vsDf(t2+h!%X*alBY_TP_TXO}A0Yo&5`>DpJVb`%sI&w zd{8W2(1Q0KDLgn0TknE6HQy%=b)gxHVm_4kPY=mmp|3f)%C?mJt{tVIcVA9FDcF?# z1Rl)wtlwG6!_y)Cq9tj^8QJ&dZL`Pm06zD$<-5fdd~??fmYU6aQX6*J=zHs&ug4O< zdcV~24VPS1T8VR6Z^=bYGk-bXSHfTUe|_4Ua^cPoSunTZuX5l9cHq1n$Iv?o?8)(` z2WO1zz3~Zi{+L&e?VpFev8wxBFHLOc9Xs7`d_eBc-+w6kKKVxCQ}!^+y#;gS zRPKip!yRm5KO@IQ%bbO);Ppdh*1F<U@HZ;#?S?O?h5z|NO(S{rbX7t2t}oU+e*7 zw%Yp+W#D)Gx}qaa%4<{4iId-6e7rPyE&80~Zg)@|pa+ZRF7|UM?QG&_mq^Z%;ZmS+ zcPRkgxhk?x&Bb5Jx_5#W*b(8$JPD-(PJX2YWP0J6%nqy9L5jEpNS>;#C07ON_6HD4 z`|zHT2ez6Zn@w=~^Pid5`98A?Q>E+hnNqjKtL=YOd9&@}N0Ro}FEjC1%rE2rs}HD; zud(Z_YJKJdwC_be__)m6u!r4s1k`7(Q#{ZX*`$p%h4lsIv zdeFNMWygDem(KJgH=@T!`MwkNgb_o>;map*9Z%iYY+1N$z3f2$Tk*dL4P-wT3_U2F zY{W-gvwpiQgbs#{m>@l|0lJ0tm!3TaNjN7Hf2E5I+UVS=54oid%;67XUP})t&Hj$k z<-M^9RhHV@e)@Pdx_**4G}&R~eBnXxeEbDN2aeEz$^l2;y+$TjT(J$E=J7L|#nCyM zxdAOCU)_!d<7T0wFn(P#F zC1>o{+wi;E`!*V|*A>dYm#MvAcgHL7zkZg=p1s%qD%Uc60OkGjU(u8BX zN6a6uDwd*^;dSz)Tnk_iY7DRpdcR%2@$61&$nLZNS$XCyx$%<~|F_Wl>H{kNZ~V;u zywAUv6o!S+8Q0&zw=!rcTOnpne z2F$uLxfZp_tS4{&0S3EF8X#_G!Txh@vQ`Q(TdK&glSU7eNxUc}CY-U#19U>Exbx!N zdcQb$?!tg@sEkE6(QzVq%wOcs!G|qJ`&V#oXp#aUcPZlW^O(ve%*%3 zhHD>#{}ZeJ2m4E3KScLiW&b+-oDqvssK3|x|IP;gp2&XX`Ps;Q)%}~5r_ukbvGvz* zHnVGE@4-{X7POZK`gq4)<}&X-WRBW~8ZVeRcbSBB8z8O7Wvi{%xUs)@(0kabRVV4v zxi2zdptOcIbWY9F987a&uD2AYoHL&CKenGkzG>p36So zpYm_klkq&Z|M@$Qn9IwK!KlSjkQ%nw6?@PHzg-jZ-{61d<}b4D$VG$yRrvjN&H2Iq zC$R6&5aaRf&_{9BQT$UfXCI{ZXCnt#+r{Tk#U`FX~@(0In{vg+g z@tnK+vt0T17rFSrivQ`a(q+@zUr6wf3E~?_pK1Fp#`X*EJH*KT8FQB6`>&8S==9Cl z`ikXN{D0lKV$Qx3#tyLR0;~Mr2VLlUwxKIFZrdlDcO8-qT=jE?jhbxonTYd%){UtD<8?>Nt!mRjLMdn(Z7TV#DuU}>=@;z0Gk3VC0-^h8-JyI(6Jih%Abbcy2 zVYgTs;o~(X319%-QTdA^cpkwrQ^FQ-zUsXAr9M; znP`b}fbTi`99YF4n)<=U(=T_WUiZXREB^5R#k;@Y!#x83pXK0tcaibbGQ#(jnSH(zyF0=A$AOZ`mHR#@F9LnduiLbr^)qey+_xc z{o(&vl1MGWvehZFdfhhcx}CBXI?z2kYWp8NdRF!$@6{hvzSpz=@EO^)|AcJAH{1zr zs2X~%wpfBdY)CZEufo@SkGr#8Qp=jlnN-pU?-ecq*JYhpj}uFdz?crUqKOW_!C ztc5S%cx#58muhss+Jfr$>GKx+J4;StKpDAU3$db=``@w7Z!|HMpTsj{n6dkdxOJ4( zhprj^e^HE7=4t?$&qj z$+T@Jup3)R=N{De7{A|Y|Eb>}KYI!Jy^Y4ESM1fcPe!Jz{NK6nxa`9pJPiJaj-E4T z7nrMD*beWfWax|yTlY!w);-jIQ6oNQ31_jHG2%^KqxK-XGT+i3ciUs74SoGJax)I- z?HrDpufz8@VV-G zt2|$I@TwF9`}|G9rA60B{4Mf(8G1lv`o1^5l*K!*%Gx8)&28joHa&mx(;uZ`^+0BU2SdRfydA*Zosgk3>L|#{#dFnS<9FmCJy_<=zVsQ!nZPsCg@%akW4v~#%Z=2WCKKYSc ze=@s0&>NP8%-)Wdgf*wL#P;>)HBMOFH5xkX0{-FBfBfbQTQW0tbPu{&@-n#FzjyVK zD{}OsyK)(O&&JoMm?051l;2sXB66^@RCMz-_#eflvAsuigzd5Is?X^;f=#gVg9oza z+D)0X{(!i(Y$r{r^9yQAFH8phq4@v3=`o0+4sX_im9li@CUe%})2R&5qqLwlf#Phl z_qJg3EB4ztTkr!?b{vp}OOvVdT1el(Tp2uKqC^ZCXY7C;)G~W9-$-%JYWL}U8s-(k z`%S$9hzSHr-3HC1J~847@LNtG>~4AGiAwvOGftFaL#^J%#Rb7I7sYr%R6^v#idUrem6V5#>9yyJ!Rg+->TJi z6IVBPY3&~j|I-&45Mt&-j*grvY6H$A_mi}Y`Nza}m#^BCiNEH7H7C4AZG(-wjE}Gp zd9VJ0(#5)zRP(bj@$+RU@r5w#{BG*+_ZuPI!M_=GT<)p^ve^Ar{M{S2rpC~Ue>1-z zu^qPt!T1EwfYxu=(*X9p(t$p2;aa{*q~y$v?dPzXSbU-^-wW@9_cP!9?O(*DSuf)Y zyhbc&c+56FgPei+JU&yeoILsHQ*II^`3uHM5p<4Mt64I4<9RuA^B?31|7QFO)!Fu+ zkN%J1z7*$<{qryScnh}K)=TfnS$uxg?~3hlYCt2CQY3cWKG|{OCim9ov*T{NKY>oz z^U(v@^u{fjz|XWF5iRc26gEfx_9E9iS?laLUHc9u|I!E_yb*bsAm%~!qu+Yqm%-vR~;!V+YaL5`6}YG`$P$={XM~ z$LJr}8Qu!Q=1s0b-YQ?z)B+vmJsXLTA+CxjS;D> zpgKMQUq6wfu>+MI%KI8a?%jW+>2dAYrH`~h=J>S?F}T)a2C>R}d;IGl4>TXBexk~M zLjwkT+v6bd@Cu@?kllVIdy&H*Ao&UmliYcSO3QZ3=&k(5@VeSiDxsU ze{Skqa&TXR#}EGK@5ugo+9yBAeDZ)ce_nC6(!uZBdA9d%yYi8YoXwo34&lsO8z6(H z&XXNiJ~p{6{Z7(8`4*dIhXhdL(~%ncLDXGMLPy8p^Cf`kSZp+(=FACf5n$qh!-(T6 zmg*BKt~0CPk*#Ig7yp;wr=^%p!%zcod4KL(KQ{N6Uksh~8UuBiS*}v1@x?jBWIywd0 z!9jVO7?U&kMrUZM$$TjoxKW%-j}@olBP3__7|H1}o!7AUkpYE^CCIT4zkU|~+fN?x zd)k1x!VmsWlm@2$*6)7ab7yZomPM(jvqc#WJPv9P+!K>s!-Fo{={_|7M&>Vy7iGh^jyRu3uZtA3B>Osr_Gcg;zB;m z5$hU0K*mqj`dxbe!8i_oK4!*zdUxpi18>`zPQ8HodTRF#B#$s~SS0nFqcZuwE4{0M z;P2%Vz$`LbtT&qsKo97gGSv?a8c|!$4jX$KPU{>N@iC+)Md_8iQQ8EA6@XpZVe+viB`?`=VrN3xBCE z-!Wp0tU7!d|KyR0V?K|@Y_BPO9K%kSvvH4vgO~Pz4`rXg4E#Tp2U8N}({tS4=mzce z=?_0oiJJqq<4vw8GHM31;^^}N_mSj`Bat6UD}9-DqkOOOKb$;32WA)rgmk6fuQUFi zzto}6)xN*N#{agf*66#|^_5z6nThGyGK(xwxey%MS6X5ZPk{GdWk$2s^j57!j430w z;7~9|3iz*+Vm8Zx~B;$R*OOv+O6%*zEG-`T`>_`6V^%Oq9iO;^5wQ1L7(6o8XRUgJ2 z=E)MhBt=rO33bM>?K58Gdg&`_k%=S6El-iI1BOcvPGsyXiAVR(z?UC@jo{G?yG?sM zLb^!LzC*F?*eMV;03IJM-NUKxp!RDB{@zexzKXw|j$N2BO7F5i`o9G+zy=Me5zfr_ zJj?(3n$EDU!8`=ldcO1lcuHev!0==1Mi ziTO+`0!WVQ}H!cBeHQ{c}Y#MT^Q z`E7!_vumIieIMQF74B({ANe0&_K2SUh}~r-_}DUB5l=-(~-r*4mXS2mZ#7P@j^TGU|_-8DIlCtdXt}Yi++@ZSKgOC@KOXektuB85XX zOHO2dK93|R);$$_V4XOX(7pkEUkbg3i}>z;`5Txv;d6fv@+{Bm`!ehSwF#f=7wmiU zj`SWo6J=X6`XBQmBu3U_N(i3~3H9esn$QyM= z7kRa8Pd{rzsZg2yv-H)}tmTt!4*m0aJ}dsJ1DMI6_}B52T5e6PHURuzo7m^b*m>~Z zug}EhZGY<@U9EQTuaa`=1N_HC@VCZ$EY7i#%Oy^7H(V&rWyWChg-Nbz*oAdxiDMyZ z$_)Rfq*th#u>Sam-;@N;EEvD&4S!!vj>ZQxM)aF;LjCutEvQ#Lyzl?1qcw0^`?QT& zSyPv#FatW0S-s5Pp`NHpt@`*$rKCvFGE%0Di&Ux7(9AiuVyv-$Yy7_!{WhxWZL@IA zH6tIaJ^EFci&&vDbIj{_U;_{v_GV5>18?f{9{kteg;fSzpgxFNQCmzAd(cAMKUeiw zaVj(k`}&8Z*Z1&Ccd6Sry_$=A zdfBor=~b&WNcZsxNpBO_Bi*-U`}8K>{^>PpH%YHlwO)GNdcNtN-fh!8yaUqR8#hl! z|EK%-2B%l6>7Ksk@TGLH`>($r{kxC6_aMD=`LXE^PATaY+i^@ucgz``ZgC8FV6phT z>)~C}Ai2F6`z2D=k_Z72zT#g)t zZfAWx*ZHjXj@j?0XMNrB2lrXu{pa1E-@u>Wz@Oj1pWncr-@yN+Z(w@X-}6WM%5pyI k`yFpT|2|iCeVSe0X4l8MT*>Y~ie&8W=x@%d?+eNQ0b_J#CIA2c literal 0 HcmV?d00001 diff --git a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs index e090853d..43d12c36 100644 --- a/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Context/Database/AppDbContext.cs @@ -61,6 +61,11 @@ public class AppDbContext : DbContext /// public DbSet GameAccounts { get; set; } = default!; + /// + /// 实时便笺 + /// + public DbSet DailyNotes { get; set; } = default!; + /// /// 构造一个临时的应用程序数据库上下文 /// @@ -76,6 +81,7 @@ public class AppDbContext : DbContext { modelBuilder .ApplyConfiguration(new AvatarInfoConfiguration()) - .ApplyConfiguration(new UserConfiguration()); + .ApplyConfiguration(new UserConfiguration()) + .ApplyConfiguration(new DailyNoteEntryConfiguration()); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs index c8bae443..7f6b3524 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs @@ -15,12 +15,12 @@ namespace Snap.Hutao.Core; /// internal static class CoreEnvironment { - // 计算过程:https://gist.github.com/Lightczx/373c5940b36e24b25362728b52dec4fd + // 计算过程:https://github.com/UIGF-org/Hoyolab.Salt /// /// 动态密钥1的盐 /// - public const string DynamicSecret1Salt = "yUZ3s0Sna1IrSNfk29Vo6vRapdOyqyhB"; + public const string DynamicSecret1Salt = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a"; /// /// 动态密钥2的盐 @@ -35,7 +35,7 @@ internal static class CoreEnvironment /// /// 米游社 Rpc 版本 /// - public const string HoyolabXrpcVersion = "2.38.1"; + public const string HoyolabXrpcVersion = "2.40.1"; /// /// 标准UA diff --git a/src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs b/src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs new file mode 100644 index 00000000..cc906c9c --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/TaskSchedulerHelper.cs @@ -0,0 +1,38 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.Win32.TaskScheduler; +using SchedulerTask = Microsoft.Win32.TaskScheduler.Task; + +namespace Snap.Hutao.Core; + +/// +/// 任务计划器服务 +/// +internal class TaskSchedulerHelper +{ + private const string DailyNoteRefreshTaskName = "SnapHutaoDailyNoteRefreshTask"; + + /// + /// 注册实时便笺刷新任务 + /// + /// 间隔(秒) + public void RegisterForDailyNoteRefresh(int interval) + { + TimeSpan intervalTime = TimeSpan.FromSeconds(interval); + if (TaskService.Instance.GetTask(DailyNoteRefreshTaskName) is SchedulerTask targetTask) + { + TimeTrigger? trigger = targetTask.Definition.Triggers[0] as TimeTrigger; + trigger!.Repetition.Interval = intervalTime; + targetTask.RegisterChanges(); + } + else + { + TaskDefinition task = TaskService.Instance.NewTask(); + task.RegistrationInfo.Description = "胡桃实时便笺刷新任务 | 请勿编辑或删除。"; + task.Triggers.Add(new TimeTrigger() { Repetition = new(intervalTime, TimeSpan.Zero), }); + task.Actions.Add("explorer", "hutao://DailyNote/Refresh"); + TaskService.Instance.RootFolder.RegisterTaskDefinition(DailyNoteRefreshTaskName, task); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/BackdropType.cs similarity index 50% rename from src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/BackdropType.cs index ec1d0dcb..b4c51158 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTriggerType.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/BackdropType.cs @@ -1,25 +1,30 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -namespace Snap.Hutao.Model.Metadata.Achievement; +namespace Snap.Hutao.Core.Windowing; /// -/// 成就触发器类型 +/// 背景类型 /// -public enum AchievementTriggerType +public enum BackdropType { /// - /// 任务 + /// 无 /// - Quest = 1, + None = 0, /// - /// 子任务 + /// 亚克力 /// - SubQuest = 2, + Acrylic, /// - /// 日常任务 + /// 云母 /// - DailyTask = 3, + Mica, + + /// + /// 变种云母 + /// + MicaAlt, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs index ab63e572..9a87ddd6 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/ExtendedWindow.cs @@ -1,12 +1,16 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.Mvvm.Messaging; using Microsoft.UI; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Snap.Hutao.Core.Logging; using Snap.Hutao.Extension; +using Snap.Hutao.Message; using Snap.Hutao.Win32; +using System.IO; +using Windows.ApplicationModel; using Windows.Graphics; using Windows.UI; using Windows.Win32.Foundation; @@ -15,11 +19,10 @@ using WinRT.Interop; namespace Snap.Hutao.Core.Windowing; /// -/// 窗口管理器 -/// 主要包含了针对窗体的 P/Inoke 逻辑 +/// 扩展窗口 /// /// 窗体类型 -internal sealed class ExtendedWindow +internal sealed class ExtendedWindow : IRecipient where TWindow : Window, IExtendedWindowSource { private readonly HWND handle; @@ -33,8 +36,10 @@ internal sealed class ExtendedWindow private readonly bool useLegacyDragBar; + private SystemBackdrop? systemBackdrop; + /// - /// 构造一个新的窗口状态管理器 + /// 构造一个新的扩展窗口 /// /// 窗口 /// 充当标题栏的元素 @@ -65,6 +70,17 @@ internal sealed class ExtendedWindow return new(window, window.TitleBar); } + /// + public void Receive(BackdropTypeChangedMessage message) + { + if (systemBackdrop != null) + { + systemBackdrop.BackdropType = message.BackdropType; + bool micaApplied = systemBackdrop.TryApply(); + logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed"); + } + } + private static void UpdateTitleButtonColor(AppWindowTitleBar appTitleBar) { appTitleBar.ButtonBackgroundColor = Colors.Transparent; @@ -102,7 +118,7 @@ internal sealed class ExtendedWindow private void InitializeWindow() { appWindow.Title = "胡桃"; - + appWindow.SetIcon(Path.Combine(Package.Current.InstalledLocation.Path, "Assets/Logos/Logo.ico")); ExtendsContentIntoTitleBar(); Persistence.RecoverOrInit(appWindow, window.PersistSize, window.InitSize); @@ -113,12 +129,14 @@ internal sealed class ExtendedWindow appWindow.Show(true); - bool micaApplied = new SystemBackdrop(window).TryApply(); + systemBackdrop = new(window); + bool micaApplied = systemBackdrop.TryApply(); logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed"); bool subClassApplied = subclassManager.TrySetWindowSubclass(); logger.LogInformation(EventIds.SubClassing, "Apply {name} : {result}", nameof(WindowSubclassManager), subClassApplied ? "succeed" : "failed"); + Ioc.Default.GetRequiredService().Register(this); window.Closed += OnWindowClosed; } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs index 02af6bb5..c21c18ef 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/SystemBackdrop.cs @@ -1,9 +1,13 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Composition; using Microsoft.UI.Composition.SystemBackdrops; using Microsoft.UI.Xaml; +using Snap.Hutao.Context.Database; +using Snap.Hutao.Core.Database; +using Snap.Hutao.Model.Entity; using System.Runtime.InteropServices; using Windows.System; using WinRT; @@ -18,7 +22,7 @@ public class SystemBackdrop private readonly Window window; private DispatcherQueueHelper? dispatcherQueueHelper; - private MicaController? backdropController; + private ISystemBackdropControllerWithTargets? backdropController; private SystemBackdropConfiguration? configuration; /// @@ -28,38 +32,68 @@ public class SystemBackdrop public SystemBackdrop(Window window) { this.window = window; + using (IServiceScope scope = Ioc.Default.CreateScope()) + { + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + SettingEntry entry = appDbContext.Settings.SingleOrAdd(SettingEntry.SystemBackdropType, BackdropType.Mica.ToString()); + BackdropType = Enum.Parse(entry.Value!); + } } + /// + /// 背景类型 + /// + public BackdropType BackdropType { get; set; } + /// /// 尝试设置背景 /// /// 是否设置成功 public bool TryApply() { - if (!MicaController.IsSupported()) + bool isSupport = BackdropType switch + { + BackdropType.Acrylic => DesktopAcrylicController.IsSupported(), + BackdropType.Mica or BackdropType.MicaAlt => MicaController.IsSupported(), + _ => false, + }; + + if (!isSupport) { return false; } else { - dispatcherQueueHelper = new(); - dispatcherQueueHelper.Ensure(); + // Previous one + if (backdropController != null) + { + backdropController.RemoveAllSystemBackdropTargets(); + } + else + { + dispatcherQueueHelper = new(); + dispatcherQueueHelper.Ensure(); + } // Hooking up the policy object - configuration = new(); + configuration = new() + { + IsInputActive = true, // Initial configuration state. + }; + SetConfigurationSourceTheme(configuration); + window.Activated += OnWindowActivated; window.Closed += OnWindowClosed; ((FrameworkElement)window.Content).ActualThemeChanged += OnWindowThemeChanged; - // Initial configuration state. - configuration.IsInputActive = true; - SetConfigurationSourceTheme(configuration); - - backdropController = new() + backdropController = BackdropType switch { - // Mica Alt - Kind = MicaKind.BaseAlt, + BackdropType.Acrylic => new DesktopAcrylicController(), + BackdropType.Mica => new MicaController() { Kind = MicaKind.Base }, + BackdropType.MicaAlt => new MicaController() { Kind = MicaKind.BaseAlt }, + _ => throw Must.NeverHappen(), }; + backdropController.AddSystemBackdropTarget(window.As()); backdropController.SetSystemBackdropConfiguration(configuration); @@ -69,7 +103,7 @@ public class SystemBackdrop private void OnWindowActivated(object sender, WindowActivatedEventArgs args) { - Must.NotNull(configuration!).IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated; + configuration!.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated; } private void OnWindowClosed(object sender, WindowEventArgs args) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs index c4c91c02..a53185d7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclassManager.cs @@ -52,9 +52,10 @@ internal class WindowSubclassManager : IDisposable bool titleBarHooked = true; - // only hook up drag bar proc when use legacy Window.ExtendsContentIntoTitleBar + // only hook up drag bar proc when not use legacy Window.ExtendsContentIntoTitleBar if (isLegacyDragBar) { + titleBarHooked = false; hwndDragBar = FindWindowEx(hwnd, default, "DRAG_BAR_WINDOW_CLASS", string.Empty); if (!hwndDragBar.IsNull) @@ -90,6 +91,12 @@ internal class WindowSubclassManager : IDisposable window.ProcessMinMaxInfo((MINMAXINFO*)lParam.Value, scalingFactor); break; } + + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + { + return new(0); + } } return DefSubclassProc(hwnd, uMsg, wParam, lParam); diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs new file mode 100644 index 00000000..be1d75c0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Collection.cs @@ -0,0 +1,34 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.ObjectModel; + +namespace Snap.Hutao.Extension; + +/// +/// 部分 +/// +public static partial class EnumerableExtension +{ + /// + /// 移除集合中满足条件的项 + /// + /// 集合项类型 + /// 集合 + /// 是否应当移除 + /// 移除的个数 + public static int RemoveWhere(this Collection collection, Func shouldRemovePredicate) + { + int count = 0; + foreach (T item in collection.ToList()) + { + if (shouldRemovePredicate.Invoke(item)) + { + collection.Remove(item); + count++; + } + } + + return count; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs new file mode 100644 index 00000000..528121e3 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs @@ -0,0 +1,80 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Extension; + +/// +/// 部分 +/// +public static partial class EnumerableExtension +{ + /// + /// 获取值或默认值 + /// + /// 键类型 + /// 值类型 + /// 字典 + /// 键 + /// 默认值 + /// 结果值 + public static TValue? GetValueOrDefault2(this IDictionary dictionary, TKey key, TValue? defaultValue = default) + where TKey : notnull + { + if (dictionary.TryGetValue(key, out TValue? value)) + { + return value; + } + + return defaultValue; + } + + /// + /// 增加计数 + /// + /// 键类型 + /// 字典 + /// 键 + public static void Increase(this Dictionary dict, TKey key) + where TKey : notnull + { + ++CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _); + } + + /// + /// 增加计数 + /// + /// 键类型 + /// 字典 + /// 键 + /// 增加的值 + public static void Increase(this Dictionary dict, TKey key, int value) + where TKey : notnull + { + // ref the value, so that we can manipulate it outside the dict. + ref int current = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _); + current += value; + } + + /// + /// 增加计数 + /// + /// 键类型 + /// 字典 + /// 键 + /// 是否存在键值 + public static bool TryIncrease(this Dictionary dict, TKey key) + where TKey : notnull + { + ref int value = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key); + if (!Unsafe.IsNullRef(ref value)) + { + ++value; + return true; + } + + return false; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs new file mode 100644 index 00000000..a8cfb3c0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.List.cs @@ -0,0 +1,65 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Extension; + +/// +/// 部分 +/// +public static partial class EnumerableExtension +{ + /// + public static double AverageNoThrow(this List source) + { + Span span = CollectionsMarshal.AsSpan(source); + + if (span.IsEmpty) + { + return 0; + } + + long sum = 0; + + for (int i = 0; i < span.Length; i++) + { + sum += span[i]; + } + + return (double)sum / span.Length; + } + + /// + /// 如果传入列表不为空则原路返回, + /// 如果传入列表为空返回一个空的列表 + /// + /// 源类型 + /// 源 + /// 源列表或空列表 + public static List EmptyIfNull(this List? source) + { + return source ?? new(); + } + + /// + /// 移除表中首个满足条件的项 + /// + /// 项的类型 + /// 表 + /// 是否应当移除 + /// 是否移除了元素 + public static bool RemoveFirstWhere(this IList list, Func shouldRemovePredicate) + { + for (int i = 0; i < list.Count; i++) + { + if (shouldRemovePredicate.Invoke(list[i])) + { + list.RemoveAt(i); + return true; + } + } + + return false; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs index b869cf5c..149d911b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs @@ -1,9 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace Snap.Hutao.Extension; /// @@ -11,26 +8,6 @@ namespace Snap.Hutao.Extension; /// public static partial class EnumerableExtension { - /// - public static double AverageNoThrow(this List source) - { - Span span = CollectionsMarshal.AsSpan(source); - - if (span.IsEmpty) - { - return 0; - } - - long sum = 0; - - for (int i = 0; i < span.Length; i++) - { - sum += span[i]; - } - - return (double)sum / span.Length; - } - /// /// 计数 /// @@ -63,18 +40,6 @@ public static partial class EnumerableExtension return source ?? Enumerable.Empty(); } - /// - /// 如果传入列表不为空则原路返回, - /// 如果传入列表为空返回一个空的列表 - /// - /// 源类型 - /// 源 - /// 源列表或空列表 - public static List EmptyIfNull(this List? source) - { - return source ?? new(); - } - /// /// 将源转换为仅包含单个元素的枚举 /// @@ -99,94 +64,6 @@ public static partial class EnumerableExtension return source.FirstOrDefault(predicate) ?? source.FirstOrDefault(); } - /// - /// 获取值或默认值 - /// - /// 键类型 - /// 值类型 - /// 字典 - /// 键 - /// 默认值 - /// 结果值 - public static TValue? GetValueOrDefault2(this IDictionary dictionary, TKey key, TValue? defaultValue = default) - where TKey : notnull - { - if (dictionary.TryGetValue(key, out TValue? value)) - { - return value; - } - - return defaultValue; - } - - /// - /// 增加计数 - /// - /// 键类型 - /// 字典 - /// 键 - public static void Increase(this Dictionary dict, TKey key) - where TKey : notnull - { - ++CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _); - } - - /// - /// 增加计数 - /// - /// 键类型 - /// 字典 - /// 键 - /// 增加的值 - public static void Increase(this Dictionary dict, TKey key, int value) - where TKey : notnull - { - // ref the value, so that we can manipulate it outside the dict. - ref int current = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _); - current += value; - } - - /// - /// 增加计数 - /// - /// 键类型 - /// 字典 - /// 键 - /// 是否存在键值 - public static bool TryIncrease(this Dictionary dict, TKey key) - where TKey : notnull - { - ref int value = ref CollectionsMarshal.GetValueRefOrNullRef(dict, key); - if (!Unsafe.IsNullRef(ref value)) - { - ++value; - return true; - } - - return false; - } - - /// - /// 移除表中首个满足条件的项 - /// - /// 项的类型 - /// 表 - /// 是否应当移除 - /// 是否移除了元素 - public static bool RemoveFirstWhere(this IList list, Func shouldRemovePredicate) - { - for (int i = 0; i < list.Count; i++) - { - if (shouldRemovePredicate.Invoke(list[i])) - { - list.RemoveAt(i); - return true; - } - } - - return false; - } - /// public static Dictionary ToDictionaryOverride(this IEnumerable source, Func keySelector) where TKey : notnull diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs new file mode 100644 index 00000000..11a1e954 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Extension/StringBuilderExtensions.cs @@ -0,0 +1,24 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text; + +namespace Snap.Hutao.Extension; + +/// +/// 扩展方法 +/// +public static class StringBuilderExtensions +{ + /// + /// 当条件符合时执行 + /// + /// 字符串建造器 + /// 条件 + /// 附加的字符串 + /// 同一个字符串建造器 + public static StringBuilder AppendIf(this StringBuilder sb, bool condition, string? value) + { + return condition ? sb.Append(value) : sb; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs index 25437c8a..0000ae2a 100644 --- a/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs @@ -35,6 +35,7 @@ public sealed partial class LaunchGameWindow : Window, IDisposable, IExtendedWin scope = scopeFactory.CreateScope(); RootGrid.DataContext = scope.ServiceProvider.GetRequiredService(); + Closed += (s, e) => Dispose(); } /// diff --git a/src/Snap.Hutao/Snap.Hutao/Message/BackdropTypeChangedMessage.cs b/src/Snap.Hutao/Snap.Hutao/Message/BackdropTypeChangedMessage.cs new file mode 100644 index 00000000..2a2085b9 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Message/BackdropTypeChangedMessage.cs @@ -0,0 +1,26 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.Windowing; + +namespace Snap.Hutao.Message; + +/// +/// 背景类型改变消息 +/// +internal class BackdropTypeChangedMessage +{ + /// + /// 构造一个新的背景类型改变消息 + /// + /// 背景类型 + public BackdropTypeChangedMessage(BackdropType backdropType) + { + BackdropType = backdropType; + } + + /// + /// 背景类型 + /// + public BackdropType BackdropType { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Message/UserChangedMessage.cs b/src/Snap.Hutao/Snap.Hutao/Message/UserChangedMessage.cs index 3bf2443a..57d1165f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Message/UserChangedMessage.cs +++ b/src/Snap.Hutao/Snap.Hutao/Message/UserChangedMessage.cs @@ -1,7 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Model.Binding; +using Snap.Hutao.Model.Binding.User; namespace Snap.Hutao.Message; diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.Designer.cs new file mode 100644 index 00000000..43118a2d --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.Designer.cs @@ -0,0 +1,282 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Snap.Hutao.Context.Database; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20221108081525_DailyNoteEntry")] + partial class DailyNoteEntry + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.10"); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("Current") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("achievements"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("achievement_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Info") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("avatar_infos"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ShowInHomeWidget") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("gacha_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("GachaType") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("QueryType") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("gacha_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttachUid") + .HasColumnType("TEXT"); + + b.Property("MihoyoSDK") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("game_accounts"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("settings"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Cookie") + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.cs new file mode 100644 index 00000000..860460b0 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221108081525_DailyNoteEntry.cs @@ -0,0 +1,55 @@ +// +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + public partial class DailyNoteEntry : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "daily_notes", + columns: table => new + { + InnerId = table.Column(type: "TEXT", nullable: false), + UserId = table.Column(type: "TEXT", nullable: false), + Uid = table.Column(type: "TEXT", nullable: false), + DailyNote = table.Column(type: "TEXT", nullable: true), + ResinNotifyThreshold = table.Column(type: "INTEGER", nullable: false), + ResinNotifySuppressed = table.Column(type: "INTEGER", nullable: false), + HomeCoinNotifyThreshold = table.Column(type: "INTEGER", nullable: false), + HomeCoinNotifySuppressed = table.Column(type: "INTEGER", nullable: false), + TransformerNotify = table.Column(type: "INTEGER", nullable: false), + TransformerNotifySuppressed = table.Column(type: "INTEGER", nullable: false), + DailyTaskNotify = table.Column(type: "INTEGER", nullable: false), + DailyTaskNotifySuppressed = table.Column(type: "INTEGER", nullable: false), + ExpeditionNotify = table.Column(type: "INTEGER", nullable: false), + ExpeditionNotifySuppressed = table.Column(type: "INTEGER", nullable: false), + ShowInHomeWidget = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_daily_notes", x => x.InnerId); + table.ForeignKey( + name: "FK_daily_notes_users_UserId", + column: x => x.UserId, + principalTable: "users", + principalColumn: "InnerId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_daily_notes_UserId", + table: "daily_notes", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "daily_notes"); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index a20c886d..1f24f21b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -82,6 +82,62 @@ namespace Snap.Hutao.Migrations b.ToTable("avatar_infos"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ShowInHomeWidget") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => { b.Property("InnerId") @@ -197,6 +253,17 @@ namespace Snap.Hutao.Migrations b.Navigation("Archive"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => { b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs index 4a29e9b9..e1d8adf9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/LaunchGame/LaunchScheme.cs @@ -18,11 +18,13 @@ public class LaunchScheme /// 通道 /// 通道描述字符串 /// 子通道 - public LaunchScheme(string name, string channel, string subChannel) + /// 启动器Id + public LaunchScheme(string name, string channel, string subChannel, string launcherId) { Name = name; Channel = channel; SubChannel = subChannel; + LauncherId = launcherId; } /// @@ -39,4 +41,9 @@ public class LaunchScheme /// 子通道 /// public string SubChannel { get; set; } + + /// + /// 启动器Id + /// + public string LauncherId { get; set; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs similarity index 99% rename from src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs index d9423096..4103abf5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs @@ -8,7 +8,7 @@ using Snap.Hutao.Web.Hoyolab.Bbs.User; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using EntityUser = Snap.Hutao.Model.Entity.User; -namespace Snap.Hutao.Model.Binding; +namespace Snap.Hutao.Model.Binding.User; /// /// 用于视图绑定的用户 @@ -146,4 +146,4 @@ public class User : ObservableObject return UserInfo != null && UserGameRoles.Any(); } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/UserAndRole.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/UserAndRole.cs new file mode 100644 index 00000000..4b77ce39 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/UserAndRole.cs @@ -0,0 +1,34 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; +using EntityUser = Snap.Hutao.Model.Entity.User; + +namespace Snap.Hutao.Model.Binding.User; + +/// +/// 角色与实体用户 +/// +public class UserAndRole +{ + /// + /// 构造一个新的实体用户与角色 + /// + /// 实体用户 + /// 角色 + public UserAndRole(EntityUser user, UserGameRole role) + { + User = user; + Role = role; + } + + /// + /// 实体用户 + /// + public EntityUser User { get; private set; } + + /// + /// 角色 + /// + public UserGameRole Role { get; private set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/DailyNoteEntryConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/DailyNoteEntryConfiguration.cs new file mode 100644 index 00000000..c8054ed4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/DailyNoteEntryConfiguration.cs @@ -0,0 +1,21 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Snap.Hutao.Model.Entity.Configuration; + +/// +/// 实时便笺入口配置 +/// +internal class DailyNoteEntryConfiguration : IEntityTypeConfiguration +{ + /// + public void Configure(EntityTypeBuilder builder) + { + builder.Property(e => e.DailyNote) + .HasColumnType("TEXT") + .HasConversion>(); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs new file mode 100644 index 00000000..6883985f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/DailyNoteEntry.cs @@ -0,0 +1,98 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Snap.Hutao.Model.Entity; + +/// +/// 实时便笺入口 +/// +[Table("daily_notes")] +public class DailyNoteEntry +{ + /// + /// 内部Id + /// + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid InnerId { get; set; } + + /// + /// 用户Id + /// + [ForeignKey(nameof(UserId))] + public Guid UserId { get; set; } + + /// + /// 用户 + /// + public User User { get; set; } = default!; + + /// + /// Uid + /// + public string Uid { get; set; } = default!; + + /// + /// Json!!! 实时便笺 + /// + public DailyNote? DailyNote { get; set; } + + /// + /// 树脂提醒阈值 + /// + public int ResinNotifyThreshold { get; set; } + + /// + /// 用于判断树脂是否继续提醒 + /// + public bool ResinNotifySuppressed { get; set; } + + /// + /// 洞天宝钱提醒阈值 + /// + public int HomeCoinNotifyThreshold { get; set; } + + /// + /// 用于判断洞天宝钱是否继续提醒 + /// + public bool HomeCoinNotifySuppressed { get; set; } + + /// + /// 参量质变仪提醒 + /// + public bool TransformerNotify { get; set; } + + /// + /// 用于判断参量质变仪是否继续提醒 + /// + public bool TransformerNotifySuppressed { get; set; } + + /// + /// 每日委托提醒 + /// + public bool DailyTaskNotify { get; set; } + + /// + /// 用于判断每日委托是否继续提醒 + /// + public bool DailyTaskNotifySuppressed { get; set; } + + /// + /// 探索派遣提醒 + /// + public bool ExpeditionNotify { get; set; } + + /// + /// 用于判断探索派遣是否继续提醒 + /// + public bool ExpeditionNotifySuppressed { get; set; } + + /// + /// 是否在主页显示小组件 + /// + public bool ShowInHomeWidget { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs index a495f57f..695a64fd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/GachaItem.cs @@ -26,6 +26,7 @@ public class GachaItem /// /// 存档 /// + [ForeignKey(nameof(ArchiveId))] public GachaArchive Archive { get; set; } = default!; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs index 8a947f70..30f57d73 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.cs @@ -22,6 +22,11 @@ public class SettingEntry /// public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible"; + /// + /// 窗口背景类型 + /// + public const string SystemBackdropType = "SystemBackdropType"; + /// /// 启动游戏 全屏 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs index 6446faa3..3d8ced19 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/Achievement.cs @@ -43,11 +43,6 @@ public class Achievement /// public int Progress { get; set; } - /// - /// 触发器 - /// - public IEnumerable? Triggers { get; set; } - /// /// 图标 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs deleted file mode 100644 index 5178701f..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Achievement/AchievementTrigger.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Model.Metadata.Achievement; - -/// -/// 成就触发器 -/// -public class AchievementTrigger -{ - /// - /// 触发器类型 - /// - public AchievementTriggerType Type { get; set; } - - /// - /// Id - /// - public string Id { get; set; } = default!; - - /// - /// 标题 - /// - public string Title { get; set; } = default!; - - /// - /// 描述 - /// - public string Description { get; set; } = default!; -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs new file mode 100644 index 00000000..b46f505c --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteService.cs @@ -0,0 +1,44 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Context.Database; +using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; + +namespace Snap.Hutao.Service.DailyNote; + +/// +/// 实时便笺服务 +/// +[Injection(InjectAs.Singleton)] +internal class DailyNoteService +{ + private readonly AppDbContext appDbContext; + + /// + /// 构造一个新的实时便笺服务 + /// + /// 数据库上下文 + public DailyNoteService(AppDbContext appDbContext) + { + this.appDbContext = appDbContext; + } + + public async ValueTask RefreshDailyNotesAndNotifyAsync() + { + GameRecordClient gameRecordClient = Ioc.Default.GetRequiredService(); + + foreach (Model.Entity.DailyNoteEntry entry in appDbContext.DailyNotes.Include(n => n.User)) + { + entry.DailyNote = await gameRecordClient.GetDialyNoteAsync(entry.User, entry.Uid).ConfigureAwait(false); + } + + await appDbContext.SaveChangesAsync().ConfigureAwait(false); + + } + + private async ValueTask NotifyDailyNoteAsync() + { + + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs index eeb68976..8e960ef8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs @@ -35,7 +35,7 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider /// public async Task> GetQueryAsync() { - Model.Binding.User? user = userService.Current; + Model.Binding.User.User? user = userService.Current; if (user != null && user.SelectedUserGameRole != null) { if (user.Cookie!.ContainsSToken()) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs index 5f4b7c5d..41e8f631 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/GameService.cs @@ -168,25 +168,38 @@ internal class GameService : IGameService elements = IniSerializer.Deserialize(readStream).ToList(); } + bool changed = false; + foreach (IniElement element in elements) { if (element is IniParameter parameter) { if (parameter.Key == "channel") { - parameter.Value = scheme.Channel; + if (parameter.Value != scheme.Channel) + { + parameter.Value = scheme.Channel; + changed = true; + } } if (parameter.Key == "sub_channel") { - parameter.Value = scheme.SubChannel; + if (parameter.Value != scheme.SubChannel) + { + parameter.Value = scheme.SubChannel; + changed = true; + } } } } - using (FileStream writeStream = File.Create(configPath)) + if (changed) { - IniSerializer.Serialize(writeStream, elements); + using (FileStream writeStream = File.Create(configPath)) + { + IniSerializer.Serialize(writeStream, elements); + } } } @@ -340,6 +353,7 @@ internal class GameService : IGameService if (isOk) { + await ThreadHelper.SwitchToMainThreadAsync(); gameAccount.UpdateName(name); // sync database diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs index d75aa184..8c616d37 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Locator/ManualGameLocator.cs @@ -44,7 +44,7 @@ internal class ManualGameLocator : IGameLocator { FileOpenPicker picker = pickerFactory.GetFileOpenPicker(); picker.FileTypeFilter.Add(".exe"); - picker.SuggestedStartLocation = PickerLocationId.ComputerFolder; + picker.SuggestedStartLocation = PickerLocationId.Desktop; // System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component. // Not sure what's going on here. diff --git a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs index afed7251..e9bd7318 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using Microsoft.UI.Xaml.Controls; -using Snap.Hutao.Core.Threading; using Snap.Hutao.Service.Abstraction; namespace Snap.Hutao.Service; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index bd189307..33515ca9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -1,10 +1,9 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.Threading; using Snap.Hutao.Web.Hoyolab; using System.Collections.ObjectModel; -using BindingUser = Snap.Hutao.Model.Binding.User; +using BindingUser = Snap.Hutao.Model.Binding.User.User; namespace Snap.Hutao.Service.User; @@ -18,6 +17,12 @@ public interface IUserService /// BindingUser? Current { get; set; } + /// + /// 异步获取角色与用户集合 + /// + /// 角色与用户集合 + Task> GetRoleCollectionAsync(); + /// /// 初始化用户服务及所有用户 /// 异步获取同步的用户信息集合 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs index 18697503..f8bfac3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. using System.Collections.ObjectModel; -using BindingUser = Snap.Hutao.Model.Binding.User; +using BindingUser = Snap.Hutao.Model.Binding.User.User; namespace Snap.Hutao.Service.User; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index ec122468..bf53743a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -5,13 +5,12 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.DependencyInjection; using Snap.Hutao.Context.Database; using Snap.Hutao.Core.Database; -using Snap.Hutao.Core.Threading; +using Snap.Hutao.Extension; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Bbs.User; -using Snap.Hutao.Web.Hoyolab.Takumi.Auth; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using System.Collections.ObjectModel; -using BindingUser = Snap.Hutao.Model.Binding.User; +using BindingUser = Snap.Hutao.Model.Binding.User.User; namespace Snap.Hutao.Service.User; @@ -27,6 +26,7 @@ internal class UserService : IUserService private BindingUser? currentUser; private ObservableCollection? userCollection; + private ObservableCollection? roleCollection; /// /// 构造一个新的用户服务 @@ -86,15 +86,17 @@ internal class UserService : IUserService public async Task RemoveUserAsync(BindingUser user) { await Task.Yield(); - Must.NotNull(userCollection!); // Sync cache - userCollection.Remove(user); + userCollection!.Remove(user); + roleCollection!.RemoveWhere(r => r.User.InnerId == user.Entity.InnerId); // Sync database using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + + // Note: cascade deleted dailynotes appDbContext.Users.RemoveAndSave(user.Entity); } } @@ -137,6 +139,27 @@ internal class UserService : IUserService return userCollection; } + /// + public async Task> GetRoleCollectionAsync() + { + if (roleCollection == null) + { + List userAndRoles = new(); + ObservableCollection observableUsers = await GetUserCollectionAsync().ConfigureAwait(false); + foreach (BindingUser user in observableUsers.ToList()) + { + foreach (UserGameRole role in user.UserGameRoles) + { + userAndRoles.Add(new(user.Entity, role)); + } + } + + roleCollection = new(userAndRoles); + } + + return roleCollection; + } + /// public async Task> ProcessInputCookieAsync(Cookie cookie) { @@ -148,7 +171,7 @@ internal class UserService : IUserService { // 检查 login ticket 是否存在 // 若存在则尝试升级至 stoken - await TryAddMultiTokenAsync(cookie, uid).ConfigureAwait(false); + await cookie.TryAddMultiTokenAsync(uid).ConfigureAwait(false); // 检查 uid 对应用户是否存在 if (UserHelper.TryGetUserByUid(userCollection, uid, out BindingUser? userWithSameUid)) @@ -180,32 +203,14 @@ internal class UserService : IUserService } else if (cookie.ContainsLTokenAndCookieToken()) { - return await TryCreateUserAndAddAsync(userCollection, cookie).ConfigureAwait(false); + return await TryCreateUserAndAddAsync(cookie).ConfigureAwait(false); } } return new(UserOptionResult.Incomplete, null!); } - private async Task TryAddMultiTokenAsync(Cookie cookie, string uid) - { - if (cookie.TryGetLoginTicket(out string? loginTicket)) - { - // get multitoken - Dictionary multiToken = await Ioc.Default - .GetRequiredService() - .GetMultiTokenByLoginTicketAsync(loginTicket, uid, default) - .ConfigureAwait(false); - - if (multiToken.Count >= 2) - { - cookie.InsertMultiToken(uid, multiToken); - cookie.RemoveLoginTicket(); - } - } - } - - private async Task> TryCreateUserAndAddAsync(ObservableCollection users, Cookie cookie) + private async Task> TryCreateUserAndAddAsync(Cookie cookie) { using (IServiceScope scope = scopeFactory.CreateScope()) { @@ -217,8 +222,19 @@ internal class UserService : IUserService if (newUser != null) { // Sync cache - await ThreadHelper.SwitchToMainThreadAsync(); - users.Add(newUser); + if (userCollection != null) + { + await ThreadHelper.SwitchToMainThreadAsync(); + userCollection!.Add(newUser); + + if (roleCollection != null) + { + foreach (UserGameRole role in newUser.UserGameRoles) + { + roleCollection.Add(new(newUser.Entity, role)); + } + } + } // Sync database appDbContext.Users.AddAndSave(newUser.Entity); diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 759205a7..fefee604 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -30,6 +30,7 @@ True true embedded + Assets\Logo.ico @@ -115,28 +116,31 @@ - + - - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml index 0de5595a..b847d54c 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml @@ -148,7 +148,7 @@ BorderThickness="{ThemeResource CardBorderThickness}" BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" Padding="8" - Margin="0,16,0,0" + Margin="0,8,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Center" MinHeight="48"> diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml index 301bd7bf..5051279e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml @@ -23,145 +23,144 @@ - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Margin="16,16,0,-4"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/DailyNotePage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/DailyNotePage.xaml index 424a6502..e3670c5b 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/DailyNotePage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/DailyNotePage.xaml @@ -4,13 +4,26 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:shc="using:Snap.Hutao.Control" - xmlns:shcm="using:Snap.Hutao.Control.Markup" + xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:sc="using:SettingsUI.Controls" + xmlns:shc="using:Snap.Hutao.Control" + xmlns:shcb="using:Snap.Hutao.Control.Behavior" + xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shv="using:Snap.Hutao.ViewModel" mc:Ignorable="d" d:DataContext="{d:DesignInstance shv:DailyNoteViewModel}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + + + + + + + + + @@ -20,11 +33,62 @@ - + + + + + + + + + + + + + + + + + + [Injection(InjectAs.Scoped)] -internal class DailyNoteViewModel : ObservableObject, ISupportCancellation +internal class DailyNoteViewModel : ObservableObject, ISupportCancellation, IDisposable { + private readonly IUserService userService; + private readonly List> refreshTimes = new() { new("4 分钟 | 0.5 树脂", 240), @@ -22,6 +28,23 @@ internal class DailyNoteViewModel : ObservableObject, ISupportCancellation new("60 分钟 | 7.5 树脂", 3600), }; + private bool isReminderNotification; + private NamedValue? selectedRefreshTime; + private ObservableCollection? userAndRoles; + + /// + /// 构造一个新的实时便笺视图模型 + /// + /// 用户服务 + /// 异步命令工厂 + public DailyNoteViewModel(IUserService userService, IAsyncRelayCommandFactory asyncRelayCommandFactory) + { + this.userService = userService; + + OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync); + TrackRoleCommand = asyncRelayCommandFactory.Create(TrackRoleAsync); + } + /// public CancellationToken CancellationToken { get; set; } @@ -29,4 +52,44 @@ internal class DailyNoteViewModel : ObservableObject, ISupportCancellation /// 刷新时间 /// public List> RefreshTimes { get => refreshTimes; } -} + + /// + /// 选中的刷新时间 + /// + public NamedValue? SelectedRefreshTime { get => selectedRefreshTime; set => SetProperty(ref selectedRefreshTime, value); } + + /// + /// 提醒式通知 + /// + public bool IsReminderNotification { get => isReminderNotification; set => SetProperty(ref isReminderNotification, value); } + + /// + /// 用户与角色集合 + /// + public ObservableCollection? UserAndRoles { get => userAndRoles; set => userAndRoles = value; } + + /// + /// 打开界面命令 + /// + public ICommand OpenUICommand { get; } + + /// + /// 跟踪角色命令 + /// + public ICommand TrackRoleCommand { get; } + + public void Dispose() + { + throw new NotImplementedException(); + } + + private async Task OpenUIAsync() + { + UserAndRoles = await userService.GetRoleCollectionAsync().ConfigureAwait(true); + } + + private async Task TrackRoleAsync() + { + + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs index 7a66a3f5..f0022951 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/ExperimentalFeaturesViewModel.cs @@ -68,7 +68,7 @@ internal class ExperimentalFeaturesViewModel : ObservableObject IUserService userService = Ioc.Default.GetRequiredService(); IInfoBarService infoBarService = Ioc.Default.GetRequiredService(); - if (userService.Current is Model.Binding.User user) + if (userService.Current is Model.Binding.User.User user) { if (user.SelectedUserGameRole == null) { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs index 7377299f..879dd11c 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/LaunchGameViewModel.cs @@ -35,8 +35,8 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation private readonly List knownSchemes = new() { - new LaunchScheme(name: "官方服 | 天空岛", channel: "1", subChannel: "1"), - new LaunchScheme(name: "渠道服 | 世界树", channel: "14", subChannel: "0"), + new LaunchScheme(name: "官方服 | 天空岛", channel: "1", subChannel: "1", launcherId: "18"), + new LaunchScheme(name: "渠道服 | 世界树", channel: "14", subChannel: "0", launcherId: "17"), // new LaunchScheme(name: "国际服 | 暂不支持", channel: "1", subChannel: "0"), }; @@ -231,7 +231,7 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation } catch (UnauthorizedAccessException) { - infoBarService.Warning("切换服务器失败,保存配置文件时发生异常\n请以管理员模式启动胡桃。"); + infoBarService.Warning("读取或保存配置文件时发生异常,请以管理员模式启动胡桃。"); } } @@ -239,7 +239,7 @@ internal class LaunchGameViewModel : ObservableObject, ISupportCancellation { if (!gameService.SetGameAccount(SelectedGameAccount)) { - Ioc.Default.GetRequiredService().Warning("切换账号失败"); + infoBarService.Warning("切换账号失败"); } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs index b884f214..09a31086 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs @@ -3,10 +3,12 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using Snap.Hutao.Context.Database; using Snap.Hutao.Core.Database; -using Snap.Hutao.Core.Threading; +using Snap.Hutao.Core.Windowing; using Snap.Hutao.Factory.Abstraction; +using Snap.Hutao.Model; using Snap.Hutao.Model.Entity; using Snap.Hutao.Service.Game; using Snap.Hutao.Service.Game.Locator; @@ -22,9 +24,17 @@ internal class SettingViewModel : ObservableObject private readonly AppDbContext appDbContext; private readonly IGameService gameService; private readonly SettingEntry isEmptyHistoryWishVisibleEntry; + private readonly SettingEntry selectedBackdropTypeEntry; + private readonly List> backdropTypes = new() + { + new("亚克力", BackdropType.Acrylic), + new("云母", BackdropType.Mica), + new("变种云母", BackdropType.MicaAlt), + }; private bool isEmptyHistoryWishVisible; private string gamePath; + private NamedValue selectedBackdropType; /// /// 构造一个新的测试视图模型 @@ -40,10 +50,16 @@ internal class SettingViewModel : ObservableObject Experimental = experimental; - isEmptyHistoryWishVisibleEntry = appDbContext.Settings - .SingleOrAdd(e => e.Key == SettingEntry.IsEmptyHistoryWishVisible, () => new(SettingEntry.IsEmptyHistoryWishVisible, true.ToString()), out _); + isEmptyHistoryWishVisibleEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.IsEmptyHistoryWishVisible, true.ToString()); IsEmptyHistoryWishVisible = bool.Parse(isEmptyHistoryWishVisibleEntry.Value!); + selectedBackdropTypeEntry = appDbContext.Settings.SingleOrAdd(SettingEntry.SystemBackdropType, BackdropType.Mica.ToString()); + BackdropType type = Enum.Parse(selectedBackdropTypeEntry.Value!); + + // prevent unnecessary backdrop setting. + selectedBackdropType = backdropTypes.Single(t => t.Value == type); + OnPropertyChanged(nameof(SelectedBackdropType)); + GamePath = gameService.GetGamePathSkipLocator(); SetGamePathCommand = asyncRelayCommandFactory.Create(SetGamePathAsync); @@ -67,9 +83,11 @@ internal class SettingViewModel : ObservableObject get => isEmptyHistoryWishVisible; set { - SetProperty(ref isEmptyHistoryWishVisible, value); - isEmptyHistoryWishVisibleEntry.Value = value.ToString(); - appDbContext.Settings.UpdateAndSave(isEmptyHistoryWishVisibleEntry); + if (SetProperty(ref isEmptyHistoryWishVisible, value)) + { + isEmptyHistoryWishVisibleEntry.Value = value.ToString(); + appDbContext.Settings.UpdateAndSave(isEmptyHistoryWishVisibleEntry); + } } } @@ -83,6 +101,29 @@ internal class SettingViewModel : ObservableObject set => SetProperty(ref gamePath, value); } + /// + /// 背景类型 + /// + public List> BackdropTypes { get => backdropTypes; } + + /// + /// 选中的背景类型 + /// + public NamedValue SelectedBackdropType + { + get => selectedBackdropType; + [MemberNotNull(nameof(selectedBackdropType))] + set + { + if (SetProperty(ref selectedBackdropType, value)) + { + selectedBackdropTypeEntry.Value = value.Value.ToString(); + appDbContext.Settings.UpdateAndSave(selectedBackdropTypeEntry); + Ioc.Default.GetRequiredService().Send(new Message.BackdropTypeChangedMessage(value.Value)); + } + } + } + /// /// 实验性功能 /// diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs index 7e77f7e4..82ba9748 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs @@ -6,7 +6,7 @@ using CommunityToolkit.Mvvm.Input; using Snap.Hutao.Core.IO.DataTransfer; using Snap.Hutao.Core.Threading; using Snap.Hutao.Factory.Abstraction; -using Snap.Hutao.Model.Binding; +using Snap.Hutao.Model.Binding.User; using Snap.Hutao.Service.Abstraction; using Snap.Hutao.Service.Navigation; using Snap.Hutao.Service.User; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs index 150f320f..ec6bb1cc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs @@ -43,6 +43,17 @@ internal static class ApiEndpoints /// public const string GameRecordCharacter = $"{ApiTakumiRecordApi}/character"; + /// + /// 游戏记录实时便笺 + /// + /// uid + /// 服务器区域 + /// 游戏记录实时便笺字符串 + public static string GameRecordDailyNote(string uid, string server) + { + return $"{ApiTakumiRecordApi}/dailyNote?server={server}&role_id={uid}"; + } + /// /// 游戏记录主页 /// @@ -121,12 +132,13 @@ internal static class ApiEndpoints /// /// 启动器资源 /// + /// 启动器Id /// 通道 /// 子通道 /// 启动器资源字符串 - public static string SdkStaticLauncherResource(string channel, string subChannel) + public static string SdkStaticLauncherResource(string launcherId, string channel, string subChannel) { - return $"{SdkStaticLauncherApi}/resource?key=eYd89JmJ&launcher_id=18&channel_id={channel}&sub_channel_id={subChannel}"; + return $"{SdkStaticLauncherApi}/resource?key=eYd89JmJ&launcher_id={launcherId}&channel_id={channel}&sub_channel_id={subChannel}"; } // https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?filter_adv=true&key=eYd89JmJ&language=zh-cn&launcher_id=18 diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs index 237b893f..06d74223 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs @@ -36,7 +36,7 @@ internal class UserClient /// 用户 /// 取消令牌 /// 详细信息 - public async Task GetUserFullInfoAsync(Model.Binding.User user, CancellationToken token = default) + public async Task GetUserFullInfoAsync(Model.Binding.User.User user, CancellationToken token = default) { Response? resp = await httpClient .SetUser(user) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs new file mode 100644 index 00000000..e75309c5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs @@ -0,0 +1,24 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab; + +/// +/// 键部分 +/// +[SuppressMessage("", "SA1310")] +[SuppressMessage("", "SA1600")] +public partial class Cookie +{ + public const string COOKIE_TOKEN = "cookie_token"; + public const string ACCOUNT_ID = "account_id"; + + public const string LOGIN_TICKET = "login_ticket"; + public const string LOGIN_UID = "login_uid"; + + public const string LTOKEN = "ltoken"; + public const string LTUID = "ltuid"; + + public const string STOKEN = "stoken"; + public const string STUID = "stuid"; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs index b98f3f92..800a1b83 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs @@ -3,6 +3,7 @@ using Microsoft.Web.WebView2.Core; using Snap.Hutao.Extension; +using Snap.Hutao.Web.Hoyolab.Takumi.Auth; namespace Snap.Hutao.Web.Hoyolab; @@ -90,20 +91,6 @@ public partial class Cookie return inner.ContainsKey(STOKEN); } - /// - /// 插入Stoken - /// - /// uid - /// tokens - public void InsertMultiToken(string uid, Dictionary multiToken) - { - inner[STUID] = uid; - inner[STOKEN] = multiToken[STOKEN]; - - inner[LTUID] = uid; - inner[LTOKEN] = multiToken[LTOKEN]; - } - /// /// 插入 Stoken /// @@ -115,15 +102,6 @@ public partial class Cookie inner[STOKEN] = cookie.inner[STOKEN]; } - /// - /// 移除 LoginTicket - /// - public void RemoveLoginTicket() - { - inner.Remove(LOGIN_TICKET); - inner.Remove(LOGIN_UID); - } - /// /// 移除无效的键 /// @@ -186,6 +164,34 @@ public partial class Cookie } } + /// + /// 异步尝试添加MultiToken + /// + /// uid + /// 任务 + public async Task TryAddMultiTokenAsync(string uid) + { + if (TryGetLoginTicket(out string? loginTicket)) + { + // get multitoken + Dictionary multiToken = await Ioc.Default + .GetRequiredService() + .GetMultiTokenByLoginTicketAsync(loginTicket, uid, default) + .ConfigureAwait(false); + + if (multiToken.Count >= 2) + { + inner[STUID] = uid; + inner[STOKEN] = multiToken[STOKEN]; + inner[LTUID] = uid; + inner[LTOKEN] = multiToken[LTOKEN]; + + inner.Remove(LOGIN_TICKET); + inner.Remove(LOGIN_UID); + } + } + } + /// /// 转换为Cookie的字符串表示 /// @@ -194,24 +200,4 @@ public partial class Cookie { return string.Join(';', inner.Select(kvp => $"{kvp.Key}={kvp.Value}")); } -} - -/// -/// 键部分 -/// -[SuppressMessage("", "SA1310")] -[SuppressMessage("", "SA1600")] -public partial class Cookie -{ - public const string COOKIE_TOKEN = "cookie_token"; - public const string ACCOUNT_ID = "account_id"; - - public const string LOGIN_TICKET = "login_ticket"; - public const string LOGIN_UID = "login_uid"; - - public const string LTOKEN = "ltoken"; - public const string LTUID = "ltuid"; - - public const string STOKEN = "stoken"; - public const string STUID = "stuid"; } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs index a06b6775..7c12df53 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Logging; -using Snap.Hutao.Model.Binding; +using Snap.Hutao.Model.Binding.User; using Snap.Hutao.Web.Request; using System.Net.Http; using System.Net.Http.Json; @@ -64,7 +64,7 @@ internal static class HttpClientExtensions /// 设置用户的Cookie /// /// http客户端 - /// 用户 + /// 绑定用户 /// 客户端 internal static HttpClient SetUser(this HttpClient httpClient, User user) { @@ -72,6 +72,18 @@ internal static class HttpClientExtensions return httpClient; } + /// + /// 设置用户的Cookie + /// + /// http客户端 + /// 实体用户 + /// 客户端 + internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user) + { + httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString()); + return httpClient; + } + /// /// 设置Referer /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs index e8159f3a..ba3a52a2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/PlayerUid.cs @@ -41,6 +41,11 @@ public struct PlayerUid get => region ??= EvaluateRegion(Value[0]); } + public static implicit operator PlayerUid(string source) + { + return new(source); + } + /// public override string ToString() { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs index 3c42c98e..5c9c3e4d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/SdkStatic/Hk4e/Launcher/ResourceClient.cs @@ -39,8 +39,9 @@ internal class ResourceClient /// 游戏资源 public async Task GetResourceAsync(LaunchScheme scheme, CancellationToken token = default) { + string url = ApiEndpoints.SdkStaticLauncherResource(scheme.LauncherId, scheme.Channel, scheme.SubChannel); Response? response = await httpClient - .TryCatchGetFromJsonAsync>(ApiEndpoints.SdkStaticLauncherResource(scheme.Channel, scheme.SubChannel), options, logger, token) + .TryCatchGetFromJsonAsync>(url, options, logger, token) .ConfigureAwait(false); return response?.Data; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs index 8c2c8674..5f4aef23 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs @@ -3,7 +3,7 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Extension; -using Snap.Hutao.Model.Binding; +using Snap.Hutao.Model.Binding.User; using Snap.Hutao.Web.Response; using System.Net.Http; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs index e61d2c2f..22d73332 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Model.Binding; +using Snap.Hutao.Model.Binding.User; using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Response; using System.Net.Http; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs index ef5b6e8c..5984d023 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs @@ -64,7 +64,7 @@ public class UserGameRole get => $"{RegionName} | Lv.{Level}"; } - public static explicit operator PlayerUid(UserGameRole userGameRole) + public static implicit operator PlayerUid(UserGameRole userGameRole) { return new PlayerUid(userGameRole.GameUid, userGameRole.Region); } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/DailyNote.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/DailyNote.cs new file mode 100644 index 00000000..6c1fa885 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/DailyNote.cs @@ -0,0 +1,171 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; + +/// +/// 实时便笺 +/// +public class DailyNote +{ + /// + /// 当前树脂 + /// + [JsonPropertyName("current_resin")] + public int CurrentResin { get; set; } + + /// + /// 最大树脂 + /// + [JsonPropertyName("max_resin")] + public int MaxResin { get; set; } + + /// + /// 树脂恢复时间 类型的秒数 + /// + [JsonPropertyName("resin_recovery_time")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] + public int ResinRecoveryTime { get; set; } + + /// + /// 格式化的树脂恢复时间 + /// + public string ResinRecoveryTargetTime + { + get + { + DateTime tt = DateTime.Now.AddSeconds(ResinRecoveryTime); + int totalDays = (tt - DateTime.Today).Days; + string day = totalDays switch + { + 0 => "今天", + 1 => "明天", + 2 => "后天", + _ => $"{totalDays}天", + }; + return $"{day} {tt:HH:mm}"; + } + } + + /// + /// 委托完成数 + /// + [JsonPropertyName("finished_task_num")] + public int FinishedTaskNum { get; set; } + + /// + /// 委托总数 + /// + [JsonPropertyName("total_task_num")] + public int TotalTaskNum { get; set; } + + /// + /// 4次委托额外奖励是否领取 + /// + [JsonPropertyName("is_extra_task_reward_received")] + public bool IsExtraTaskRewardReceived { get; set; } + + /// + /// 每日委托奖励字符串 + /// + public string ExtraTaskRewardDescription + { + get + { + return IsExtraTaskRewardReceived + ? "已领取「每日委托」奖励" + : FinishedTaskNum == TotalTaskNum + ? "「每日委托」奖励待领取" + : "今日完成委托次数不足"; + } + } + + /// + /// 剩余周本折扣次数 + /// + [JsonPropertyName("remain_resin_discount_num")] + public int RemainResinDiscountNum { get; set; } + + /// + /// 周本树脂减免使用次数 + /// + public int ResinDiscountUsedNum + { + get => ResinDiscountNumLimit - RemainResinDiscountNum; + } + + /// + /// 周本折扣总次数 + /// + [JsonPropertyName("resin_discount_num_limit")] + public int ResinDiscountNumLimit { get; set; } + + /// + /// 当前派遣数 + /// + [JsonPropertyName("current_expedition_num")] + public int CurrentExpeditionNum { get; set; } + + /// + /// 最大派遣数 + /// + [JsonPropertyName("max_expedition_num")] + public int MaxExpeditionNum { get; set; } + + /// + /// 派遣 + /// + [JsonPropertyName("expeditions")] + public List Expeditions { get; set; } = default!; + + /// + /// 当前洞天宝钱 + /// + [JsonPropertyName("current_home_coin")] + public int CurrentHomeCoin { get; set; } + + /// + /// 最大洞天宝钱 + /// + [JsonPropertyName("max_home_coin")] + public int MaxHomeCoin { get; set; } + + /// + /// 洞天宝钱恢复时间 类型的秒数 + /// + [JsonPropertyName("home_coin_recovery_time")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] + public int HomeCoinRecoveryTime { get; set; } + + /// + /// 格式化的洞天宝钱恢复时间 + /// + public string HomeCoinRecoveryTargetTimeFormatted + { + get + { + DateTime reach = DateTime.Now.AddSeconds(HomeCoinRecoveryTime); + int totalDays = (reach - DateTime.Today).Days; + string day = totalDays switch + { + 0 => "今天", + 1 => "明天", + 2 => "后天", + _ => $"{totalDays}天", + }; + return $"{day} {reach:HH:mm}"; + } + } + + /// + /// 日历链接 + /// + [JsonPropertyName("calendar_url")] + public string CalendarUrl { get; set; } = default!; + + /// + /// 参量质变仪 + /// + [JsonPropertyName("transformer")] + public Transformer Transformer { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs new file mode 100644 index 00000000..f09cf96c --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Expedition.cs @@ -0,0 +1,47 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; + +/// +/// 探索派遣 +/// +public class Expedition +{ + /// + /// 图标 + /// + [JsonPropertyName("avatar_side_icon")] + public Uri AvatarSideIcon { get; set; } = default!; + + /// + /// 状态 Ongoing:派遣中 Finished:已完成 + /// + [JsonPropertyName("status")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public ExpeditionStatus Status { get; set; } + + /// + /// 剩余时间 + /// + [JsonPropertyName("remained_time")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] + public int RemainedTime { get; set; } + + /// + /// 格式化的剩余时间 + /// + public string RemainedTimeFormatted + { + get + { + if (Status == ExpeditionStatus.Finished) + { + return "已完成"; + } + + TimeSpan ts = new(0, 0, RemainedTime); + return ts.Hours > 0 ? $"{ts.Hours}时" : $"{ts.Minutes}分"; + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/ExpeditionStatus.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/ExpeditionStatus.cs new file mode 100644 index 00000000..2cf50657 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/ExpeditionStatus.cs @@ -0,0 +1,20 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; + +/// +/// 探索派遣状态 +/// +public enum ExpeditionStatus +{ + /// + /// 进行中 + /// + Ongoing, + + /// + /// 完成 + /// + Finished, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/RecoveryTime.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/RecoveryTime.cs new file mode 100644 index 00000000..ad4141ab --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/RecoveryTime.cs @@ -0,0 +1,77 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Extension; +using System.Text; + +namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; + +/// +/// 参量质变仪恢复时间包装 +/// 已准备完成 $后可再次使用 +/// 冷却中 可使用 +/// +public class RecoveryTime +{ + /// + /// 日 + /// + [JsonPropertyName("Day")] + public int Day { get; set; } + + /// + /// 时 + /// + [JsonPropertyName("Hour")] + public int Hour { get; set; } + + /// + /// 分 + /// + [JsonPropertyName("Minute")] + public int Minute { get; set; } + + /// + /// 秒 + /// + [JsonPropertyName("Second")] + public int Second { get; set; } + + /// + /// 是否已经到达 + /// + [JsonPropertyName("reached")] + public bool Reached { get; set; } + + /// + /// 获取格式化的剩余时间 + /// + public string TimeFormatted + { + get + { + if (Reached) + { + return "已准备完成"; + } + else + { + return new StringBuilder() + .AppendIf(Day > 0, $"{Day}天") + .AppendIf(Hour > 0, $"{Hour}时") + .AppendIf(Minute > 0, $"{Minute}分") + .AppendIf(Second > 0, $"{Second}秒") + .Append(" 后可再次使用") + .ToString(); + } + } + } + + /// + /// 获取格式化的状态 + /// + public string ReachedFormatted + { + get => Reached ? "可使用" : "冷却中"; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Transformer.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Transformer.cs new file mode 100644 index 00000000..d924c32f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/DailyNote/Transformer.cs @@ -0,0 +1,40 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote; + +/// +/// 参量质变仪 +/// +public class Transformer +{ + /// + /// 是否拥有该道具 + /// + [JsonPropertyName("obtained")] + public bool Obtained { get; set; } + + /// + /// 恢复时间包装 + /// + [JsonPropertyName("recovery_time")] + public RecoveryTime? RecoveryTime { get; set; } + + /// + /// Wiki链接 + /// + [JsonPropertyName("wiki")] + public Uri Wiki { get; set; } = default!; + + /// + /// 是否提醒 + /// + [JsonPropertyName("noticed")] + public bool Noticed { get; set; } + + /// + /// 上个任务的Id + /// + [JsonPropertyName("latest_job_id")] + public string LastJobId { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index 4b879eb4..90bdcd29 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -3,7 +3,7 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Extension; -using Snap.Hutao.Model.Binding; +using Snap.Hutao.Model.Binding.User; using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; using Snap.Hutao.Web.Response; @@ -34,6 +34,42 @@ internal class GameRecordClient this.logger = logger; } + /// + /// 异步获取实时便笺 + /// + /// 用户 + /// 查询uid + /// 取消令牌 + /// 实时便笺 + public async Task GetDialyNoteAsync(User user, PlayerUid uid, CancellationToken token = default) + { + Response? resp = await httpClient + .SetUser(user) + .UsingDynamicSecret(options, ApiEndpoints.GameRecordDailyNote(uid.Value, uid.Region)) + .GetFromJsonAsync>(token) + .ConfigureAwait(false); + + return resp?.Data; + } + + /// + /// 异步获取实时便笺 + /// + /// 用户 + /// 查询uid + /// 取消令牌 + /// 实时便笺 + public async Task GetDialyNoteAsync(Model.Entity.User user, PlayerUid uid, CancellationToken token = default) + { + Response? resp = await httpClient + .SetUser(user) + .UsingDynamicSecret(options, ApiEndpoints.GameRecordDailyNote(uid.Value, uid.Region)) + .GetFromJsonAsync>(token) + .ConfigureAwait(false); + + return resp?.Data; + } + /// /// 获取玩家基础信息 /// @@ -68,7 +104,7 @@ internal class GameRecordClient /// 获取玩家深渊信息 /// /// 用户 - /// 1:当期,2:上期 + /// 期 /// 取消令牌 /// 深渊信息 public Task GetSpiralAbyssAsync(User user, SpiralAbyssSchedule schedule, CancellationToken token = default) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs index 4699d5e8..b4a9169d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs @@ -3,6 +3,7 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Extension; +using Snap.Hutao.Model.Binding.User; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; @@ -172,7 +173,7 @@ internal class HomaClient /// 用户 /// 取消令牌 /// 玩家记录 - public async Task GetPlayerRecordAsync(Snap.Hutao.Model.Binding.User user, CancellationToken token = default) + public async Task GetPlayerRecordAsync(User user, CancellationToken token = default) { PlayerInfo? playerInfo = await gameRecordClient .GetPlayerInfoAsync(user, token) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs b/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs index 260f78a3..4d1a0b0e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Response/KnownReturnCode.cs @@ -41,7 +41,7 @@ public enum KnownReturnCode : int /// /// 访问过于频繁 /// - VIsitTooFrequently = -110, + VisitTooFrequently = -110, /// /// 应用Id错误 diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs b/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs index 06b1c138..8d44268b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs @@ -89,14 +89,4 @@ public class Response : Response { return ReturnCode == 0; } - - /// - public override int GetHashCode() - { - int j = ReturnCode.GetHashCode(); - int k = Message == null ? 0 : Message.GetHashCode(); - int i = Data == null ? 0 : Data.GetHashCode(); - - return (((j * 31) + k) * 31) + i; - } } \ No newline at end of file