├── .gitattributes ├── .gitignore ├── README.md ├── ZuAnBot.sln ├── ZuAnBotUpdate ├── Assets │ └── log4net.config ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── LogHelper.cs ├── ManifestHelper.cs ├── Program.cs ├── Properties │ └── launchSettings.json └── ZuAnBotUpdate.csproj ├── ZuAnBot_Wpf ├── Api │ ├── Apis.cs │ └── Dtos │ │ └── VersionDto.cs ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── Images │ │ ├── QQ.png │ │ ├── github.png │ │ ├── lol.ico │ │ ├── 删除.png │ │ ├── 复制.png │ │ ├── 添加.png │ │ ├── 祖安.ico │ │ ├── 祖安助手使用交流群群聊二维码.png │ │ ├── 粘贴.png │ │ └── 编辑.png │ ├── Url.json │ ├── log4net.config │ └── wordsLibrary.json ├── Constants │ ├── DialogTokens.cs │ ├── Events │ │ └── Events.cs │ ├── Params.cs │ └── Titles.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── Helper │ ├── EnvConfigs.cs │ ├── ExceptionExtension.cs │ ├── GlobalKeyboardHook.cs │ ├── IPCHelper.cs │ ├── JsonHelper.cs │ ├── LocalConfigHelper.cs │ ├── LogHelper.cs │ ├── ManifestHelper.cs │ ├── MessageHelper.cs │ ├── ProgressFileStream.cs │ ├── VersionHelper.cs │ ├── Win32Helper.cs │ └── WordsHelper.cs ├── Properties │ ├── AssemblyInfo.cs │ └── app.manifest ├── ViewModels │ ├── MainWindowViewModel.cs │ ├── WordEditViewModel.cs │ ├── WordLibrary.cs │ └── WordsLibrarySetViewModel.cs ├── Views │ ├── Conveters │ │ └── Bool2Brush.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── WordEdit.xaml │ ├── WordEdit.xaml.cs │ ├── WordsLibrarySet.xaml │ └── WordsLibrarySet.xaml.cs ├── ZuAnBot_Wpf.csproj └── build.bat ├── 截图_客户端.png ├── 截图_游戏.jpg ├── 截图_自定义词库.png └── 截图_软件.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | /ZuAnBot_WinForm 342 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZuAnBot(祖安助手) 2 | 一键喷人(随机输入骂人语录),可在英雄联盟游戏和客户端中使用。只为反击喷子!欢迎来到祖安。 3 | 4 | 受[祖安宝典](https://github.com/cndiandian/zuanbot.com)启发,想到自己玩LOL经常受喷子的气,还没能力喷回去,于是开发了这个小工具 :smirk: 5 | 6 | # 使用 7 | 从[Latest Release](https://github.com/liuke-wuhan/ZuAnBot/releases/latest)中下载软件:ZuAnBot.exe 8 | 9 | 软件免安装,下载后直接打开即可 10 | 11 | ## 注意 12 | * 注意节制,软件的初心是对付喷子的,不要成为了喷子 :smirk: 13 | 14 | * 不要高频使用,用太多被举报的话可能还是有禁言或封号的风险,我自己用的不是很多,只是被禁言一次,兄弟们被封号可以去[QQ群](https://github.com/liuke-wuhan/ZuAnBot#%E7%A4%BE%E7%BE%A4)或者[Issues](https://github.com/liuke-wuhan/ZuAnBot/issues)上反映一下具体封号的使用场景 15 | 16 | # 社群 17 | * QQ群: 18 | 19 | ![/QQ群](./ZuAnBot_Wpf/Assets/Images/祖安助手使用交流群群聊二维码.png) 20 | 21 | 22 | # 功能 23 | * 一键喷人 24 | * 自定义词库 25 | * 逐字发送(更安全,绕过屏蔽) 26 | 27 | 28 | 29 | # 截图 30 | ![/截图](./截图_软件.png) 31 | 32 | ![/截图](./截图_自定义词库.png) 33 | 34 | ![/截图](./截图_游戏.jpg) 35 | 36 | ![/截图](./截图_客户端.png) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ZuAnBot.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32407.343 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZuAnBot_Wpf", "ZuAnBot_Wpf\ZuAnBot_Wpf.csproj", "{E72F3F57-8D6C-489F-AA7C-60BCA6C76A13}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{14FAFE8E-5E70-440F-9A9A-F178E12B5F94}" 9 | ProjectSection(SolutionItems) = preProject 10 | README.md = README.md 11 | EndProjectSection 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZuAnBotUpdate", "ZuAnBotUpdate\ZuAnBotUpdate.csproj", "{C4D7A4E9-F74D-4CA9-997D-07E200176C05}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {E72F3F57-8D6C-489F-AA7C-60BCA6C76A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {E72F3F57-8D6C-489F-AA7C-60BCA6C76A13}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {E72F3F57-8D6C-489F-AA7C-60BCA6C76A13}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {E72F3F57-8D6C-489F-AA7C-60BCA6C76A13}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {C4D7A4E9-F74D-4CA9-997D-07E200176C05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {C4D7A4E9-F74D-4CA9-997D-07E200176C05}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {C4D7A4E9-F74D-4CA9-997D-07E200176C05}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {C4D7A4E9-F74D-4CA9-997D-07E200176C05}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {4F9A81BE-7DC0-4758-8B6E-7607BFE45F16} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /ZuAnBotUpdate/Assets/log4net.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ZuAnBotUpdate/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /ZuAnBotUpdate/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 23 | 24 | 25 | 26 | 27 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 28 | 29 | 30 | 31 | 32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 38 | 39 | 40 | 41 | 42 | The order of preloaded assemblies, delimited with line breaks. 43 | 44 | 45 | 46 | 47 | 48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 49 | 50 | 51 | 52 | 53 | Controls if .pdbs for reference assemblies are also embedded. 54 | 55 | 56 | 57 | 58 | Controls if runtime assemblies are also embedded. 59 | 60 | 61 | 62 | 63 | Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. 64 | 65 | 66 | 67 | 68 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 69 | 70 | 71 | 72 | 73 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 74 | 75 | 76 | 77 | 78 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 79 | 80 | 81 | 82 | 83 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 84 | 85 | 86 | 87 | 88 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 89 | 90 | 91 | 92 | 93 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 94 | 95 | 96 | 97 | 98 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 99 | 100 | 101 | 102 | 103 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. 104 | 105 | 106 | 107 | 108 | A list of unmanaged 32 bit assembly names to include, delimited with |. 109 | 110 | 111 | 112 | 113 | A list of unmanaged 64 bit assembly names to include, delimited with |. 114 | 115 | 116 | 117 | 118 | The order of preloaded assemblies, delimited with |. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 127 | 128 | 129 | 130 | 131 | A comma-separated list of error codes that can be safely ignored in assembly verification. 132 | 133 | 134 | 135 | 136 | 'false' to turn off automatic generation of the XML Schema file. 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /ZuAnBotUpdate/LogHelper.cs: -------------------------------------------------------------------------------- 1 | using log4net; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | 10 | 11 | namespace ZuAnBotUpdate 12 | { 13 | public class LogHelper 14 | { 15 | private ILog _logger = null; 16 | public static string logPath; 17 | 18 | public LogHelper(string logDirName = null, string loggerName = "ZuanBot") 19 | { 20 | if (logDirName == null) 21 | InitLogger("ZuanBot", loggerName); 22 | else 23 | { 24 | InitLogger(logDirName, loggerName); 25 | } 26 | } 27 | 28 | public ILog Logger4 29 | { 30 | get { return _logger; } 31 | } 32 | 33 | /// 34 | /// 日志将放入%temp%/CECommonLog文件夹 35 | /// 36 | /// 37 | private void InitLogger(string logDirName, string loggerName) 38 | { 39 | logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TEMP", logDirName); 40 | if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath); 41 | log4net.GlobalContext.Properties["fname"] = Path.Combine(logPath, "log");//日志存放位置 42 | 43 | var stream = ManifestHelper.GetManifestStream("log4net.config"); 44 | log4net.Config.XmlConfigurator.Configure(stream); 45 | stream.Close(); 46 | //创建一个实例,名字可随便取 47 | _logger = LogManager.GetLogger(loggerName); 48 | _logger.Info("==================初始化Logger===================="); 49 | } 50 | 51 | } 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /ZuAnBotUpdate/ManifestHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ZuAnBotUpdate 10 | { 11 | /// 12 | /// 清单资源工具类 13 | /// 14 | public static class ManifestHelper 15 | { 16 | public static Stream GetManifestStream(string jsonName) 17 | { 18 | return Assembly.GetExecutingAssembly().GetManifestResourceStream($"ZuAnBotUpdate.Assets.{jsonName}"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ZuAnBotUpdate/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace ZuAnBotUpdate 7 | { 8 | public class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | var logger = new LogHelper().Logger4; 13 | try 14 | { 15 | logger.Info("进入更新程序args:" + args.Aggregate((a, b) => a + " " + b)); 16 | if (args.Length != 2) return; 17 | 18 | var path1 = args[0]; 19 | var path2 = args[1]; 20 | 21 | if (string.IsNullOrWhiteSpace(path1) || string.IsNullOrWhiteSpace(path2)) return; 22 | 23 | logger.Info($"源:{path1} 目标:{path2}"); 24 | 25 | var process = Process.GetProcesses().FirstOrDefault(x=>x.MainWindowTitle=="祖安助手"); 26 | 27 | if (process == null || process.MainModule?.FileName != path2) 28 | { 29 | logger.Info($"未找到祖安助手进程"); 30 | return; 31 | } 32 | 33 | process.Kill(); 34 | process.WaitForExit(1000); 35 | File.Copy(path1, path2, true); 36 | 37 | Process.Start(path2); 38 | 39 | logger.Info("更新程序执行成功"); 40 | } 41 | catch (Exception e) 42 | { 43 | logger.Error("更新程序执行出错", e); 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /ZuAnBotUpdate/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ZuAnBotUpdate": { 4 | "commandName": "Project", 5 | "commandLineArgs": "C:\\Users\\Admin\\AppData\\Local\\Temp\\祖安助手.exe F:\\work\\LOL\\ZuAnBot\\ZuAnBot_Wpf\\bin\\Release\\net472\\祖安助手.exe" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /ZuAnBotUpdate/ZuAnBotUpdate.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net472 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Api/Apis.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | using RestSharp; 10 | using RestSharp.Serializers.NewtonsoftJson; 11 | using ZuAnBot_Wpf.Api.Dtos; 12 | using ZuAnBot_Wpf.Helper; 13 | 14 | namespace ZuAnBot_Wpf.Api 15 | { 16 | public class Apis 17 | { 18 | readonly RestClient _client; 19 | 20 | #region Construct 21 | /// 22 | /// 获取单例 23 | /// 24 | /// 25 | public static Apis GetInstance() => instance; 26 | private static readonly Apis instance = new Apis(); 27 | private Apis() 28 | { 29 | try 30 | { 31 | _client = ConstructClient(EnvConfigHelper.Env.UseApi); 32 | } 33 | catch (Exception ex) 34 | { 35 | ex.Show(); 36 | } 37 | } 38 | private RestClient ConstructClient(string baseUrl) 39 | { 40 | var client = new RestClient(baseUrl); 41 | client.Options.MaxTimeout = 120000; 42 | client.Options.ThrowOnAnyError = false; 43 | 44 | client.UseNewtonsoftJson(new JsonSerializerSettings 45 | { 46 | NullValueHandling = NullValueHandling.Ignore, 47 | DateFormatString = "yyyy-MM-dd HH:mm:ss" 48 | }); 49 | client.AddDefaultHeader("Accept", "application/json"); 50 | client.AddDefaultHeader("Content-Type", "application/json"); 51 | return client; 52 | } 53 | #endregion Construct 54 | 55 | #region private 56 | /// 57 | /// api响应包装 58 | /// code 编号 200 对应 返回 result code 编号 0 对应 返回 data 59 | /// 60 | /// 61 | class ResultWrapper 62 | { 63 | public int? Code { get; set; } 64 | public string Msg { get; set; } 65 | public T Data { get; set; } 66 | } 67 | 68 | private T HandleResponseOrError(RestResponse> response, RestClient client) 69 | { 70 | var url = (client.Options.BaseUrl + response.Request.Resource).Replace("//", "/"); 71 | 72 | if (response.IsSuccessful) 73 | { 74 | if (response.Data.Code == 200) 75 | { 76 | //LkLogHelper.Log.Info($"〔{url}〕请求成功,返回值:〔{JsonConvert.SerializeObject(response.Data.Result)}〕"); 77 | return response.Data.Data; 78 | } 79 | else 80 | { 81 | throw new ApiException($"code:{response.Data.Code}\nmessage:{response.Data.Msg}", new Exception($"url:{url}")); 82 | } 83 | 84 | } 85 | else 86 | { 87 | if (response.ContentType == "text/plain") 88 | throw new ApiException($"({(int)response.StatusCode} {response.StatusDescription}){response.Content}", new Exception($"url:{url}")); 89 | else 90 | { 91 | if (response.StatusCode == 0 && response.ErrorMessage == "第 3 行,位置 6 上的开始标记“meta”与结束标记“head”不匹配。 第 107 行,位置 5。") 92 | { 93 | throw new ApiException($"后台服务不可用", new Exception($"url:{url}")); 94 | } 95 | else if (response.StatusCode == 0 && response.ErrorMessage == "The request timed-out.") 96 | { 97 | throw new ApiException($"请求超时", new Exception($"url:{url}")); 98 | } 99 | else 100 | { 101 | throw new ApiException($"({(int)response.StatusCode} {response.StatusDescription}){response.ErrorMessage}", new Exception($"url:{url}")); 102 | } 103 | } 104 | 105 | } 106 | } 107 | #endregion 108 | 109 | /// 110 | /// 文件备份 111 | /// 112 | /// 113 | /// 114 | public async Task Use() 115 | { 116 | var request = new RestRequest("/api/auth/use", Method.Post); 117 | 118 | var response = await _client.ExecuteAsync>(request); 119 | 120 | HandleResponseOrError(response, _client); 121 | } 122 | 123 | /// 124 | /// 获取最新版本 125 | /// 126 | /// 127 | public async Task GetLatestVersion() 128 | { 129 | var request = new RestRequest("/api/versions/latest", Method.Get); 130 | 131 | var response = await _client.ExecuteAsync>(request); 132 | 133 | return HandleResponseOrError(response, _client); 134 | } 135 | 136 | /// 137 | /// 下载文件 138 | /// 139 | /// 目录 140 | /// 文件名 141 | /// url 142 | /// 143 | /// 输入字符串为空 144 | /// 文件下载失败 145 | public string DownloadFile(string dir, string fileName, string fileUrl) 146 | { 147 | if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName)); 148 | if (string.IsNullOrWhiteSpace(dir)) throw new ArgumentNullException(nameof(dir)); 149 | fileName = Legalize(fileName); 150 | if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName)); 151 | 152 | var filePath = Path.Combine(dir, fileName); 153 | 154 | try 155 | { 156 | using (var writer = new ProgressFileStream(filePath, false)) 157 | { 158 | var request = new RestRequest(fileUrl); 159 | _client.DownloadStream(request).CopyTo(writer); 160 | } 161 | } 162 | catch (Exception ex) 163 | { 164 | File.Delete(filePath); 165 | throw new Exception($"文件下载失败!请重试\nUrl为:{fileUrl}", ex); 166 | } 167 | 168 | return filePath; 169 | } 170 | 171 | /// 172 | /// 使路径合法化,符合windows文件/文件夹命名规则 173 | /// 174 | /// 175 | /// 176 | private string Legalize(string path) 177 | { 178 | path = path.Replace("\\", "") 179 | .Replace("/", "") 180 | .Replace(":", "") 181 | .Replace("*", "") 182 | .Replace("?", "") 183 | .Replace("\"", "") 184 | .Replace("<", "") 185 | .Replace(">", "") 186 | .Replace("|", ""); 187 | return path; 188 | } 189 | } 190 | 191 | /// 192 | /// 后端报错引起的异常 193 | /// 194 | [Serializable] 195 | public class ApiException : Exception 196 | { 197 | public ApiException() { } 198 | public ApiException(string message) : base(message) { } 199 | public ApiException(string message, Exception inner) : base(message, inner) { } 200 | protected ApiException( 201 | System.Runtime.Serialization.SerializationInfo info, 202 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Api/Dtos/VersionDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ZuAnBot_Wpf.Api.Dtos 8 | { 9 | public class VersionDto 10 | { 11 | public string Url { get; set; } = ""; 12 | public string FileName { get; set; } = ""; 13 | public string VersionName { get; set; } = ""; 14 | public string Description { get; set; } = ""; 15 | public DateTime Date { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | M827.904 541.184a278.592 278.592 0 0 0 0-58.368l73.728-58.368c15.872-13.056 20.224-35.584 9.728-53.504l-78.592-137.728c-9.984-17.408-31.232-25.856-51.712-18.176l-86.528 35.072c-15.872-11.52-32.512-21.248-49.664-29.184l-12.8-92.928c-3.072-20.224-20.736-35.584-41.216-35.584h-157.44c-20.736 0-38.144 14.848-41.216 35.84L378.88 221.44c-17.152 7.936-33.792 17.92-49.664 29.184L242.688 215.296c-18.688-7.168-41.216 0.768-51.2 18.176L112.64 371.2c-10.24 17.92-6.144 40.448 10.24 53.76l73.472 58.112c-1.024 9.472-1.536 19.2-1.536 29.184 0 9.728 0.512 19.456 1.536 29.184l-73.984 58.368c-15.872 13.056-19.968 35.584-9.728 53.248l78.848 137.728c9.984 17.408 32 25.856 51.456 17.92l86.528-35.072c15.872 11.52 32.512 21.248 49.664 29.184l13.056 93.696c3.328 20.48 20.48 35.328 41.216 35.328h157.44c20.48 0 38.144-15.36 41.216-35.84l13.056-93.184c17.152-7.936 33.792-17.664 49.664-29.184l86.784 35.328c4.864 1.792 9.984 2.816 15.104 2.816 14.848 0 28.672-7.936 36.096-20.992l78.592-137.728c10.24-17.664 6.4-40.192-9.984-53.76l-73.472-58.112z m-25.344 231.168l-99.584-40.448c-7.424-3.072-15.872-1.792-22.272 3.072-19.712 15.36-40.704 27.648-62.464 36.608-7.424 3.072-12.544 9.728-13.568 17.664l-14.592 104.96-155.392 2.304-15.104-107.264c-1.024-7.936-6.4-14.592-13.568-17.664-21.76-8.96-42.752-21.504-62.464-36.608-4.096-3.072-8.96-4.608-13.824-4.608-2.816 0-5.888 0.512-8.448 1.536l-96.256 41.472-80.896-137.216 84.48-66.816c6.144-4.864 9.472-12.8 8.448-20.48-1.536-12.032-2.56-24.32-2.56-36.608 0-12.288 1.024-24.576 2.56-36.608 1.024-7.936-2.048-15.616-8.448-20.48l-83.712-64 76.544-139.264 99.584 40.448c7.424 3.072 15.872 1.792 22.272-3.072 19.712-15.36 40.96-27.648 62.72-36.864 7.424-3.072 12.544-9.728 13.568-17.664l12.544-104.704 157.696-2.304 15.104 107.264c1.024 7.936 6.4 14.592 13.568 17.664 21.76 8.96 42.752 21.504 62.464 36.864 6.4 4.864 14.848 6.144 22.272 3.072l96.256-41.472 80.896 137.216-84.48 66.816c-6.144 4.864-9.472 12.544-8.448 20.48 1.536 12.032 2.56 24.32 2.56 36.608 0 12.288-1.024 24.576-2.56 36.608-1.024 7.936 2.304 15.616 8.448 20.48l83.712 63.744-77.056 139.264zM512 345.6c-91.136 0-165.12 74.752-165.12 166.4s73.984 166.4 165.12 166.4 165.12-74.752 165.12-166.4-73.984-166.4-165.12-166.4z m0 297.216c-71.168 0-129.28-58.624-129.28-130.816 0-72.192 58.112-130.816 129.28-130.816s129.28 58.624 129.28 130.816c0 72.192-57.856 130.816-129.28 130.816z 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using ImTools; 10 | using Prism.DryIoc; 11 | using Prism.Ioc; 12 | using ZuAnBot_Wpf.Api; 13 | using ZuAnBot_Wpf.Constants; 14 | using ZuAnBot_Wpf.Helper; 15 | using ZuAnBot_Wpf.ViewModels; 16 | using ZuAnBot_Wpf.Views; 17 | 18 | namespace ZuAnBot_Wpf 19 | { 20 | /// 21 | /// App.xaml 的交互逻辑 22 | /// todo:多行词库 23 | /// 24 | public partial class App : PrismApplication 25 | { 26 | public App() 27 | { 28 | try 29 | { 30 | this.Startup += App_Startup; 31 | 32 | //wpf 程序异常捕获,而不崩溃退出 33 | DispatcherUnhandledException += App_DispatcherUnhandledException; 34 | TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 35 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 36 | } 37 | catch (Exception e) 38 | { 39 | e.Show(); 40 | } 41 | } 42 | 43 | #region 只运行一个程序 44 | [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:删除未读的私有成员", Justification = "<挂起>")] 45 | private System.Threading.Mutex mutex; 46 | private async void App_Startup(object sender, StartupEventArgs e) 47 | { 48 | try 49 | { 50 | mutex = new System.Threading.Mutex(true, "ZuAnBot_Wpf", out bool ret); 51 | 52 | if (!ret) 53 | { 54 | var handle = Win32Helper.FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, Titles.Main); 55 | IPCHelper.SendMessage("显示", handle); 56 | Environment.Exit(0); 57 | return; 58 | } 59 | 60 | await Apis.GetInstance().Use(); 61 | } 62 | catch (ApiException ex) 63 | { 64 | #if DEBUG 65 | ex.Show(); 66 | #endif 67 | } 68 | catch (Exception ex) 69 | { 70 | ex.Show(); 71 | } 72 | } 73 | #endregion 74 | 75 | protected override Window CreateShell() 76 | { 77 | return Container.Resolve(); 78 | } 79 | 80 | protected override void RegisterTypes(IContainerRegistry containerRegistry) 81 | { 82 | containerRegistry.RegisterDialog(); 83 | containerRegistry.RegisterDialog(); 84 | } 85 | 86 | #region 异常处理 87 | private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 88 | { 89 | e.SetObserved(); 90 | ExceptionHandler(e.Exception.InnerException); 91 | } 92 | 93 | private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) 94 | { 95 | e.Handled = true; 96 | ExceptionHandler(e.Exception); 97 | } 98 | 99 | private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 100 | { 101 | ExceptionHandler((Exception)e.ExceptionObject); 102 | } 103 | 104 | private void ExceptionHandler(Exception ex) 105 | { 106 | Dispatcher.Invoke(() => 107 | { 108 | ex.Show(); 109 | }); 110 | } 111 | #endregion 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/QQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/QQ.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/github.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/lol.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/lol.ico -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/删除.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/删除.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/复制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/复制.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/添加.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/添加.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/祖安.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/祖安.ico -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/祖安助手使用交流群群聊二维码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/祖安助手使用交流群群聊二维码.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/粘贴.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/粘贴.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Images/编辑.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuke-wuhan/ZuAnBot/31e695a5c80efa53f354624c2ed530a26c36b96f/ZuAnBot_Wpf/Assets/Images/编辑.png -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/Url.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "prod", 3 | "envs": [ 4 | { 5 | "name": "sit", 6 | "useApi": "http://localhost:7286" 7 | }, 8 | { 9 | "name": "prod", 10 | "useApi": "http://www.dongdongtuotuo.top:8080" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/log4net.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Assets/wordsLibrary.json: -------------------------------------------------------------------------------- 1 | { 2 | "Categories": [ 3 | { 4 | "CategoryName": "默认词库", 5 | "Words": [ 6 | { 7 | "Content": "如果思考是生存的证明,我真难判断你是不是一具尸体" 8 | }, 9 | { 10 | "Content": "家里哪个亲人去西方极乐净土了?" 11 | }, 12 | { 13 | "Content": "你家住海边吗?管这么宽" 14 | }, 15 | { 16 | "Content": "掀起你的头盖骨,让我来看看里面的水" 17 | }, 18 | { 19 | "Content": "你好像青青草原那个小灰灰,三千多集还是个儿子" 20 | }, 21 | { 22 | "Content": "你是不是性無能所以在網上自我高潮找存在感 " 23 | }, 24 | { 25 | "Content": "本想和你爸散散步,没想到他先走一步" 26 | }, 27 | { 28 | "Content": "你是不是打多了王者荣耀,脑子里也有一个王者峡谷,深不见底?" 29 | }, 30 | { 31 | "Content": "你是个孤儿也不能拿这个当借口嗷 懂8?" 32 | }, 33 | { 34 | "Content": "你是那青青草原懒羊羊,三千多集顶着翔" 35 | }, 36 | { 37 | "Content": "以前你叫斧码,现在你叫斤石" 38 | }, 39 | { 40 | "Content": "这么不要脸,这么没心没肺,你的体重应该会很轻吧?" 41 | }, 42 | { 43 | "Content": "你要是有病别来找我, 我又不是兽医。" 44 | }, 45 | { 46 | "Content": "就是嫉妒我户口本可以翻页(你是孤儿)" 47 | }, 48 | { 49 | "Content": "你怕不是吃shi吃多了,一个劲儿的叫我爸爸。" 50 | }, 51 | { 52 | "Content": "上帝把智慧洒满人间,就你打了个伞。" 53 | }, 54 | { 55 | "Content": "你上辈子一定是个塑料袋 那么会装" 56 | }, 57 | { 58 | "Content": "中国那么多兵器你不学,偏学剑!" 59 | }, 60 | { 61 | "Content": "十宗罪绝对真实改编。你家谱上的人全是活生生的例子啊!" 62 | }, 63 | { 64 | "Content": "我给你妈一扁担打得他不知天上宫阙今夕是何年" 65 | }, 66 | { 67 | "Content": "真是让人震惊,犬类排泄物居然也回说话" 68 | }, 69 | { 70 | "Content": "能认识自己是个什么东西,都是好样的。" 71 | }, 72 | { 73 | "Content": "你脑子不错,可以给老八加口热汤" 74 | }, 75 | { 76 | "Content": "你为什么比美团外卖还能送?" 77 | }, 78 | { 79 | "Content": "遇到你之前,我的世界是黑白的,遇到你之后,哇靠,全黑了。" 80 | }, 81 | { 82 | "Content": "对不起啊,我没有资格骂你神经病,毕竟我不是兽医。" 83 | }, 84 | { 85 | "Content": "你这种人永远买不到自己合适的鞋 因为你码没了" 86 | }, 87 | { 88 | "Content": "你脑子里的水倒出来是不是当初冲了龙王庙又漫了金山?" 89 | }, 90 | { 91 | "Content": "你什么牌的塑料袋 这么能装呢?" 92 | }, 93 | { 94 | "Content": "你脖子上顶的是肿瘤吗?" 95 | }, 96 | { 97 | "Content": "好家伙,你自己拍全家福挺孤单吧" 98 | }, 99 | { 100 | "Content": "平时你爸用你洗衣服吗 我看你像个棒槌" 101 | }, 102 | { 103 | "Content": "我今天想骂人 不骂你" 104 | }, 105 | { 106 | "Content": "你应该没被蚊子叮过吧,皮真厚" 107 | }, 108 | { 109 | "Content": "你和你妈打电话一定要你先挂电话,不然你妈就会挂掉" 110 | }, 111 | { 112 | "Content": "网络上你重拳出击,现实中你蹬着三轮车卖鸡" 113 | }, 114 | { 115 | "Content": "[转账到账通知]您尾号为1145的储蓄卡收到一笔汇款,金额20,000,000,000元。【天地银行】" 116 | }, 117 | { 118 | "Content": "快看天上尼玛在散步" 119 | }, 120 | { 121 | "Content": "有病你治病,你别找我啊,我又不是兽医。" 122 | }, 123 | { 124 | "Content": "鼠牛虎兔龙蛇羊猴鸡狗猪你自己看看你少了什么" 125 | }, 126 | { 127 | "Content": "你不贱不骚不婊,毕竟用来形容人的词,用在你身上不合适" 128 | }, 129 | { 130 | "Content": "嫉妒是一种不好的情感,我理解你嫉妒我有家人,可你这么说有点过分了" 131 | }, 132 | { 133 | "Content": "你吗在那边缺纸钱了自己去烧别给我叽歪" 134 | }, 135 | { 136 | "Content": "这么不要脸,这么没心没肺,你的体重应该会很轻吧?" 137 | }, 138 | { 139 | "Content": "希望你打游戏时可以把脑子从粪坑里捞出来带上" 140 | }, 141 | { 142 | "Content": "您可真秀,自拍就是族谱" 143 | }, 144 | { 145 | "Content": "我是运气多好出门竟然踩了个你" 146 | }, 147 | { 148 | "Content": "子不教父之过,你骂人我的错。" 149 | }, 150 | { 151 | "Content": " 你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了你急了" 152 | }, 153 | { 154 | "Content": "敦煌来的?壁画那么多!" 155 | }, 156 | { 157 | "Content": "我希望你能做条好狗不要总像得了狂犬病一样到处咬人" 158 | }, 159 | { 160 | "Content": "你没事儿跟我比比啥呢,我又不是吕洞宾" 161 | }, 162 | { 163 | "Content": "你知道百度为什么搜不到你吗?你要去搜狗。" 164 | }, 165 | { 166 | "Content": "你要么闭嘴,要么藏好你的妈" 167 | }, 168 | { 169 | "Content": "十二生肖 鼠牛虎兔龙蛇羊猴鸡狗猪 你看你少了什么?" 170 | }, 171 | { 172 | "Content": "看您这逻辑,直肠通大脑吧" 173 | }, 174 | { 175 | "Content": "你是老子见过最他妈大的笔盒,装这么多笔不累吗?" 176 | }, 177 | { 178 | "Content": "天气转凉,给你妈多穿点,别让她凉了" 179 | }, 180 | { 181 | "Content": "回去把宁母亲灰泡水喝,可能心情会好一点😊" 182 | }, 183 | { 184 | "Content": "想给你妈献朵花,可是我没花,你也没有妈" 185 | }, 186 | { 187 | "Content": "他们怎么就能因为你长的像就说你是猪呢?" 188 | }, 189 | { 190 | "Content": "多脏的字骂你我都嫌干净。" 191 | }, 192 | { 193 | "Content": "但凡你爸妈当年多唠嗑一分钟 今天也就少了个要分类的垃圾" 194 | }, 195 | { 196 | "Content": "长的像人真的难为你了。" 197 | }, 198 | { 199 | "Content": "朋友,你要不把你自己的自拍发出来看看,让大伙看看你的全家福有多么幸福?" 200 | }, 201 | { 202 | "Content": "你脖子上顶着的是个肿瘤吗" 203 | }, 204 | { 205 | "Content": "蛆虫会说话?真是生物学奇迹!" 206 | }, 207 | { 208 | "Content": "你棺材板是不是没钉牢 你怎么破土而出了?" 209 | }, 210 | { 211 | "Content": "去看猪圈都不用跑趟农村,去你家里就行了。" 212 | }, 213 | { 214 | "Content": "别人站着赚钱, 你码躺着。" 215 | }, 216 | { 217 | "Content": "你到底是个什么杂交品种,脑仁儿竟然还没瓜子大" 218 | }, 219 | { 220 | "Content": "你不去当厨子可惜了,甩锅甩的那么厉害。" 221 | }, 222 | { 223 | "Content": "你怎么不在了啊是专门装哑巴上街卖艺了吗可惜你无才无艺二胡捧你手机就断线建议没异能还是不要哑巴恰烂钱" 224 | }, 225 | { 226 | "Content": "你在野区刷微信步数?" 227 | }, 228 | { 229 | "Content": "仔细看看我这巴掌真适合你那大脸" 230 | }, 231 | { 232 | "Content": "您一个阴间的东西,就别指望着能理解咱们阳间的话" 233 | }, 234 | { 235 | "Content": "以后不要再说你一无所有了,你不是还有病吗" 236 | }, 237 | { 238 | "Content": "开始是网友,后来是你的埋尸人" 239 | }, 240 | { 241 | "Content": "我是算命的。请问您算什么东西?" 242 | }, 243 | { 244 | "Content": "东边不亮西边亮,憨批啥样你啥样" 245 | }, 246 | { 247 | "Content": "每次来拍全家福的时候就只有你一个人" 248 | }, 249 | { 250 | "Content": "我想给你妈送朵花,我开玩笑的,毕竟我哪有花,你哪有妈。" 251 | }, 252 | { 253 | "Content": "你好像那喜羊羊 三千多集找不着妈" 254 | }, 255 | { 256 | "Content": "阁下中午吃奥利给噎着了是怎么的?用为父给你来的稀的溜溜儿缝嘛" 257 | }, 258 | { 259 | "Content": "从小妈妈就告诉我,对人要怀有善意,后来我遇到了很多人,明白了不是每一个人都有妈妈。 ​​​" 260 | }, 261 | { 262 | "Content": "我不由感叹这真是个好时代,因为就连你这种人话都不会说的生物也有权力上网发言。" 263 | }, 264 | { 265 | "Content": "你刚才是不是丢东西了, 你脑子不见了。" 266 | }, 267 | { 268 | "Content": "wdnmd" 269 | }, 270 | { 271 | "Content": "不就是个2,在这儿装什么13" 272 | }, 273 | { 274 | "Content": "你看天上你妈在飞,开玩笑的你哪来的妈" 275 | }, 276 | { 277 | "Content": "没事多看看天空,你妈可能在看你" 278 | }, 279 | { 280 | "Content": "你适合玩庄周,活在梦里" 281 | }, 282 | { 283 | "Content": "你傻了吧唧好像我去年路过怡红院的那个老鸨的狗咬死的一只鸡" 284 | }, 285 | { 286 | "Content": "豁 什么意思 现在狗也能打字聊天了吗?" 287 | }, 288 | { 289 | "Content": "我想啐你一口 又怕你脏了我的唾沫" 290 | }, 291 | { 292 | "Content": "你的户口本就是一动物百科" 293 | }, 294 | { 295 | "Content": "你的主人是二郎神吧" 296 | }, 297 | { 298 | "Content": "我打了狂犬疫苗 你以为我会怕你?" 299 | }, 300 | { 301 | "Content": "嘴巴如果闲得慌 不如去公共厕所挨个把马桶舔干净 做个人人称赞的好东西" 302 | }, 303 | { 304 | "Content": "天工造物不测,怎么造出你这么个东西" 305 | }, 306 | { 307 | "Content": "你的头和蒲公英一样,风一吹,它就秃了" 308 | }, 309 | { 310 | "Content": "我刚才给你妈打电话啊,你妈怎么挂了" 311 | }, 312 | { 313 | "Content": "你爸妈真会生 好的东西都自己留着" 314 | }, 315 | { 316 | "Content": "看到这坨💩🐎,这是你最后的晚餐。" 317 | }, 318 | { 319 | "Content": "你是ETC吗 自动抬杠" 320 | }, 321 | { 322 | "Content": "我骂人从不扯到你父母 因为他们生了你这么个东西已经够倒霉的了" 323 | }, 324 | { 325 | "Content": "赶紧滚吧,在爷面前爬来爬去的晦气玩意" 326 | }, 327 | { 328 | "Content": "您这是刚出宫还是刚出土" 329 | }, 330 | { 331 | "Content": "这么咄咄逼人 一直没人疼爱缺关注?" 332 | }, 333 | { 334 | "Content": "如果梦到你,那真是见鬼。" 335 | }, 336 | { 337 | "Content": "别人唱歌要钱,你唱歌要命。" 338 | }, 339 | { 340 | "Content": "你做鸭都不配" 341 | }, 342 | { 343 | "Content": "我们要向前看,不错过些歪瓜劣枣怎么知道什么是好的。" 344 | }, 345 | { 346 | "Content": "真羡慕你一个人就能吃顿团圆饭" 347 | }, 348 | { 349 | "Content": "放心吧,不会有富婆看上你的,不过别伤心,也许富婆的狗会看上你,毕竟你长的很符合它的口味。看见你我就想起了我的狗,可惜我不是富婆。" 350 | }, 351 | { 352 | "Content": "隔着屏幕就感觉到你那俩眼珠子都离鸡了,眼睛给你算是糟践了,抠出来给我当炮儿踩还能听个响儿" 353 | }, 354 | { 355 | "Content": "虽然大脑和大肠长的挺像,但也不要什么都往里面装。对不起,忘了你没大脑。" 356 | }, 357 | { 358 | "Content": "我不想和狗吵架。" 359 | }, 360 | { 361 | "Content": "你简直浪费你爸妈那仅有两分钟的一晚" 362 | }, 363 | { 364 | "Content": "看着你那可悲可笑的滑稽样。我心底产生了一种悲痛感。" 365 | }, 366 | { 367 | "Content": "你在你家厕所没吃饱吗" 368 | }, 369 | { 370 | "Content": "裤裆里拉二胡,扯卵弹" 371 | }, 372 | { 373 | "Content": "今晚我又在你坟头撒了一把白磷,看你的尸体变成烟花于浩瀚星空中绚烂绽放,而我在你坟头载歌载舞疯狂蹦迪。你被洒水车浇灌过的骨灰洋洋洒洒,改善了方圆几里的酸性土壤。" 374 | }, 375 | { 376 | "Content": "你好像那懒羊羊 三千多集头顶着坨翔" 377 | }, 378 | { 379 | "Content": "不自知的东西,照了镜子也没有用。——钱钟书" 380 | }, 381 | { 382 | "Content": "你爸和你妈打电话,你猜谁先挂" 383 | }, 384 | { 385 | "Content": "刚好,我QQ农场缺条🐶,我看你不错今天下午就来上班吧" 386 | }, 387 | { 388 | "Content": "在?我今晚就把你🐴墓碑掰弯" 389 | }, 390 | { 391 | "Content": "最近猪肉涨价了,你是不是也以为自己跟着水涨船高了?" 392 | }, 393 | { 394 | "Content": "瞧我这记性,又把你当人看了" 395 | }, 396 | { 397 | "Content": "你是不是把敌敌畏当可乐喝了,把你八毛钱十二斤的脑子喝傻了。" 398 | }, 399 | { 400 | "Content": "提前给你上柱香,全家走好才像样" 401 | }, 402 | { 403 | "Content": "啥比光辉事迹竟然令啥比发出啧啧赞叹正所谓物以类聚人以群分狗以品相遇" 404 | }, 405 | { 406 | "Content": "无聊的时候看看天,因为你妈也在天上看你" 407 | }, 408 | { 409 | "Content": "你出生的时候是不是被扔起来三次,被接住了两次?" 410 | }, 411 | { 412 | "Content": "有空一起上厕所吧,我看你那狗嘴里挺会喷粪的。" 413 | }, 414 | { 415 | "Content": "W D N M D" 416 | }, 417 | { 418 | "Content": "你脑袋上长个头不是拿来拍皮球的" 419 | }, 420 | { 421 | "Content": "你出生的时候是不是把脑子扔了,把胎盘塞脑壳里养大了" 422 | }, 423 | { 424 | "Content": "请宁那宛如犬类排泄物的思维器官给老八吃" 425 | }, 426 | { 427 | "Content": "为什么不理我 是想让我变成狗不理吗" 428 | }, 429 | { 430 | "Content": "赶紧闭嘴吧,你嘴巴直通脑子,一说话我都能看见汪洋大海。" 431 | }, 432 | { 433 | "Content": "我把你妈藏起来让你找不到妈" 434 | }, 435 | { 436 | "Content": "满手老茧的老撸货凭什么对人家指指点点" 437 | }, 438 | { 439 | "Content": "你就是个草履虫,杠吃杠吐的玩意" 440 | }, 441 | { 442 | "Content": "你在野区採灵芝呢?" 443 | }, 444 | { 445 | "Content": "看见你我就想起了我的狗。" 446 | }, 447 | { 448 | "Content": "如果愚蠢有质量,许多人早已变成黑洞" 449 | }, 450 | { 451 | "Content": "深圳禁止食狗肉了,原来是在保护你和你全家啊😃 " 452 | }, 453 | { 454 | "Content": "本大爹将你这弱智小儿一手击在墙上抠都抠不下来" 455 | }, 456 | { 457 | "Content": "你是野草成了精,兜兜转转找不着母亲?" 458 | }, 459 | { 460 | "Content": "您还没出殡呢?" 461 | }, 462 | { 463 | "Content": "你的嘴简直能给农田施肥" 464 | }, 465 | { 466 | "Content": "现在脑瘫复健医院也联网了吗,怎么康复期的病人都出来恶心人了" 467 | }, 468 | { 469 | "Content": "别对着我叫,我小时候被狗吓过。" 470 | }, 471 | { 472 | "Content": "你是莎士比亚没有士" 473 | }, 474 | { 475 | "Content": "你妈为了阻止你谈恋爱竟然给了你这张脸。" 476 | }, 477 | { 478 | "Content": "👴 专业配钥匙,这是官方的许可,恁配吗?哦,恁不配哦。啥?恁配?那恁配几把?" 479 | }, 480 | { 481 | "Content": "你长脑袋只是为了让自己看起来高一点吧。对不起,我忘了你没有脑袋,又戳到你的伤心出了吧!你脖子上顶的不是肿瘤,而是水泡,看起来是个肉瘤,其实一戳水直流。" 482 | }, 483 | { 484 | "Content": "你怕不是一条蚯蚓,脸和屁股长的一样。" 485 | }, 486 | { 487 | "Content": "你怎么跟美羊羊一样3000多集了还找不到妈🙂" 488 | }, 489 | { 490 | "Content": "不错嘛,年纪轻轻就知道用脸吓唬人" 491 | }, 492 | { 493 | "Content": "你在这逼逼赖赖这么久,看中哪块坟墓是你的?" 494 | }, 495 | { 496 | "Content": "年少不知🐴珍贵,老来对坟空流泪" 497 | }, 498 | { 499 | "Content": "每个人都有妈妈,而你就不一样了,你不是人" 500 | }, 501 | { 502 | "Content": "你是我见过的容量最大的铅笔盒了,装那么多笔你不累吗?" 503 | }, 504 | { 505 | "Content": "你真对不起你爸妈辛苦的那一晚" 506 | }, 507 | { 508 | "Content": "今天想骂人 所以不骂你" 509 | }, 510 | { 511 | "Content": "刚从厕所出来,好歹先把嘴擦擦干净" 512 | }, 513 | { 514 | "Content": "别光喝酒吃点头孢" 515 | }, 516 | { 517 | "Content": "你户口本单页的呢嘚瑟个啥" 518 | }, 519 | { 520 | "Content": "苍蝇走在你脸上都崴脚" 521 | }, 522 | { 523 | "Content": "你再这样说你🐴 的追悼会我就不去了" 524 | }, 525 | { 526 | "Content": "你很擅长魔术吗?变畜生变得挺厉害的" 527 | }, 528 | { 529 | "Content": "你打字这么慢是在查新华字典吗🐶 " 530 | }, 531 | { 532 | "Content": "脑瘫只能是脑瘫,毕竟医学都救不了你" 533 | }, 534 | { 535 | "Content": "长城要是用你脸皮做的 孟姜女能哭倒才怪" 536 | }, 537 | { 538 | "Content": "xswl穷酸废物真把自己当条贵宾犬了搁着叫唤你🐎呢" 539 | }, 540 | { 541 | "Content": "你长脑袋只是为了让自己看起来高一点吧" 542 | }, 543 | { 544 | "Content": "我留你狗命是因为我想保护动物毕竟你做只狗不容易" 545 | }, 546 | { 547 | "Content": "你的户口簿上只有一位吗?" 548 | }, 549 | { 550 | "Content": "您好可怜,有人生,有人养,没人教的野孩子" 551 | }, 552 | { 553 | "Content": "很心疼你,大家都是人,就你一个畜牲,越努力想当个人越可怜" 554 | }, 555 | { 556 | "Content": "咋滴你管那么多呢?收粪车从你家门前路过你都要拿勺子尝尝咸淡?" 557 | }, 558 | { 559 | "Content": "不必自卑,虽然你智商方面和我的差距就像是我和爱因斯坦,但你的户口本所占用的空间比我少一大坨" 560 | }, 561 | { 562 | "Content": "你就像那美羊羊,三千集找不到娘" 563 | }, 564 | { 565 | "Content": "认了二郎神做主人就开始乱叫?" 566 | }, 567 | { 568 | "Content": "跟混畜生道的人讲人道是行不通的。" 569 | }, 570 | { 571 | "Content": "以后打电话记得让别人先挂 不然你就挂了" 572 | }, 573 | { 574 | "Content": "你说话咋这么臭呢?嘴里长痔疮了?" 575 | }, 576 | { 577 | "Content": "我见过脑袋有洞的,就没见过比你洞还多的,呀呀呀,可别动,脑壳里的水晃出来真TM像个花洒" 578 | }, 579 | { 580 | "Content": "我这聊着聊着还有点饿了呢 emmm想拿你骨灰熬点稀饭" 581 | }, 582 | { 583 | "Content": "你是不是掉过粪坑里,脖子以上全截肢了?" 584 | }, 585 | { 586 | "Content": "垃圾都有家,你却没有。" 587 | }, 588 | { 589 | "Content": "人不犯我我不犯人 人若犯我 我挖你祖坟" 590 | }, 591 | { 592 | "Content": "你永远都不能用尿滋醒一个张开嘴接的人" 593 | }, 594 | { 595 | "Content": "我没病,你没礼貌,让我们原谅彼此的不完美好吗?" 596 | }, 597 | { 598 | "Content": "完了,你不理我,我成狗不理了!" 599 | }, 600 | { 601 | "Content": "你在无中生有 你在暗度陈仓 你在凭空想象 你在凭空捏造 你在无言无语 你在无可救药 你是逝者安息 你是一路走好 你是傻子巴拉 你是永无止境 你是没钱买药 你是头脑有病 你是眼里有泡 你是嘴里刘能 你是污言秽语 你是咎由自取 你是殃及无辜 你是祸害众生 你是仓皇失措 你是暗度陈仓 你是无可救药" 602 | }, 603 | { 604 | "Content": "有空一起上厕所 我看你嘴很会喷shi" 605 | }, 606 | { 607 | "Content": "看你这么凶,我的qq农场缺只狗,明天来上班" 608 | }, 609 | { 610 | "Content": "知道为什么一会说你有🐴 一会说你没🐴 吗?因为你🐴 是薛定谔的🐴,👴 我一观测,你🐴就坍缩没了 " 611 | }, 612 | { 613 | "Content": "想必阁下就是东莞站街劳模吧" 614 | }, 615 | { 616 | "Content": "这是第一次当你的老爸,我们的心情都有点复杂" 617 | }, 618 | { 619 | "Content": "你是数字界和字母界的二把手" 620 | }, 621 | { 622 | "Content": "别半夜翻箱想不开了。今天你爹我就说你到这了" 623 | }, 624 | { 625 | "Content": "你看上去好眼熟 像我昨天扔出去的那袋垃圾" 626 | }, 627 | { 628 | "Content": "扔块肉在键盘上,狗都比你会连招。" 629 | }, 630 | { 631 | "Content": "请问芋泥玛奇朵还要加热吗?因为泥玛凉了" 632 | }, 633 | { 634 | "Content": "你慢点说,我狗语不是很好" 635 | }, 636 | { 637 | "Content": "不用您付钱不用您费力,刚好我人缘好,马上就给您雇团队来个至尊级别的黑人抬棺,音乐呢就放今天是个好日子,棺材咱们也不封严实了,让你听听大家的乐呵声" 638 | }, 639 | { 640 | "Content": "虽然你没对象,但你一个人就是狗粮。" 641 | }, 642 | { 643 | "Content": "你爸妈可真会生,好的东西都自个留着" 644 | }, 645 | { 646 | "Content": "大年初一你全家在奈何桥上看烟花" 647 | }, 648 | { 649 | "Content": "以前我还可以纵容你,但是在辣鸡分类后你只能呆这了。" 650 | }, 651 | { 652 | "Content": "以前你叫李闯 后来你改名叫李门" 653 | }, 654 | { 655 | "Content": "真羡慕他这种身份证拿出来就是全家福的人" 656 | }, 657 | { 658 | "Content": "真好啊 你出生就是户主" 659 | }, 660 | { 661 | "Content": "你就是那野草成了精,兜兜转转找不到母亲" 662 | }, 663 | { 664 | "Content": "你嘴在化粪池都泡开花了" 665 | }, 666 | { 667 | "Content": "几个马呢说话这么嚣张" 668 | }, 669 | { 670 | "Content": "杨戬喊你呢!" 671 | }, 672 | { 673 | "Content": "咋了?怂了?怂了就把宁那宛如犬类排泄物的思维器官给老八吃" 674 | }, 675 | { 676 | "Content": "你是敦煌来的吧?逼话这么多。" 677 | }, 678 | { 679 | "Content": "小嘴抹了开塞露似的,总往外喷" 680 | }, 681 | { 682 | "Content": "你的人生,总结起来就八个字儿——生的荒唐,死的窝囊🙊" 683 | }, 684 | { 685 | "Content": "说你蠢,你还真把自己当猪" 686 | }, 687 | { 688 | "Content": "我看你嘴挺能说的 能帮我暖暖脚么" 689 | }, 690 | { 691 | "Content": "网络上你重拳出击,现实中你蹬着三轮卖鸡" 692 | }, 693 | { 694 | "Content": "天上星星亮晶晶 好像你妈眨眼睛" 695 | }, 696 | { 697 | "Content": "天凉了,你妈也凉了" 698 | }, 699 | { 700 | "Content": "粪车打你家门口过你都得尝尝咸淡呢" 701 | } 702 | ] 703 | }, 704 | { 705 | "CategoryName": "自定义词库", 706 | "Words": [] 707 | } 708 | ] 709 | } -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Constants/DialogTokens.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ZuAnBot_Wpf.Constants 8 | { 9 | public static class DialogTokens 10 | { 11 | public const string MainWindow = nameof(MainWindow); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Constants/Events/Events.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Prism.Events; 7 | 8 | namespace ZuAnBot_Wpf.Constants.Events 9 | { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Constants/Params.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ZuAnBot_Wpf.Constants 8 | { 9 | public static class Params 10 | { 11 | public static readonly string Library = nameof(Library); 12 | public static readonly string WordContent = nameof(WordContent); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Constants/Titles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ZuAnBot_Wpf.Constants 8 | { 9 | public static class Titles 10 | { 11 | public static readonly string Main = "祖安助手"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 23 | 24 | 25 | 26 | 27 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 28 | 29 | 30 | 31 | 32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 38 | 39 | 40 | 41 | 42 | The order of preloaded assemblies, delimited with line breaks. 43 | 44 | 45 | 46 | 47 | 48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 49 | 50 | 51 | 52 | 53 | Controls if .pdbs for reference assemblies are also embedded. 54 | 55 | 56 | 57 | 58 | Controls if runtime assemblies are also embedded. 59 | 60 | 61 | 62 | 63 | Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. 64 | 65 | 66 | 67 | 68 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 69 | 70 | 71 | 72 | 73 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 74 | 75 | 76 | 77 | 78 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 79 | 80 | 81 | 82 | 83 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 84 | 85 | 86 | 87 | 88 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 89 | 90 | 91 | 92 | 93 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 94 | 95 | 96 | 97 | 98 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 99 | 100 | 101 | 102 | 103 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. 104 | 105 | 106 | 107 | 108 | A list of unmanaged 32 bit assembly names to include, delimited with |. 109 | 110 | 111 | 112 | 113 | A list of unmanaged 64 bit assembly names to include, delimited with |. 114 | 115 | 116 | 117 | 118 | The order of preloaded assemblies, delimited with |. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 127 | 128 | 129 | 130 | 131 | A comma-separated list of error codes that can be safely ignored in assembly verification. 132 | 133 | 134 | 135 | 136 | 'false' to turn off automatic generation of the XML Schema file. 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/EnvConfigs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ZuAnBot_Wpf.Helper 8 | { 9 | public class Env 10 | { 11 | /// 12 | /// 环境名 13 | /// 14 | public string Name { get; set; } 15 | 16 | /// 17 | /// 18 | /// 19 | public string UseApi { get; set; } 20 | } 21 | 22 | public class EnvConfigs 23 | { 24 | /// 25 | /// 当前环境 26 | /// 27 | public string Env { get; set; } 28 | 29 | /// 30 | /// 环境配置列表 31 | /// 32 | public List Envs { get; set; } 33 | } 34 | 35 | public static class EnvConfigHelper 36 | { 37 | private static readonly string JsonFile = "Url.json"; 38 | 39 | public static Env Env 40 | { 41 | get 42 | { 43 | var configs = JsonHelper.DeserializeManifestJson(JsonFile); 44 | 45 | return configs.Envs.FirstOrDefault(x => x.Name == configs.Env) ?? throw new Exception($"不存在配置环境:{configs.Env}"); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/ExceptionExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using HandyControl.Controls; 9 | using ZuAnBot_Wpf.Api; 10 | using MessageBox = HandyControl.Controls.MessageBox; 11 | 12 | namespace ZuAnBot_Wpf.Helper 13 | { 14 | public static class ExceptionExtension 15 | { 16 | /// 17 | /// 获取错误描述 18 | /// 19 | /// 20 | /// 21 | public static string GetDescription(this Exception ex) 22 | { 23 | return GetDescriptionInternal(ex); 24 | } 25 | 26 | private static string GetDescriptionInternal(Exception ex, int n = 1) 27 | { 28 | string innerError = ""; 29 | if (ex.InnerException != null && n < 5) 30 | { 31 | var innerDesc = GetDescriptionInternal(ex.InnerException, ++n); 32 | innerError = $"\n--- 内部错误 ---\n:{innerDesc}"; 33 | } 34 | 35 | return $"{ex.GetSourceDesc()}{ex.Message}\n[{ex.GetType().FullName}]调用堆栈:\n{ex.StackTrace}{innerError}"; 36 | } 37 | 38 | /// 39 | /// 描述异常来源,是后端还是客户端 40 | /// 41 | /// 42 | /// 43 | public static string GetSourceDesc(this Exception ex) 44 | { 45 | if (ex is ApiException) 46 | { 47 | return "[服务器异常]"; 48 | } 49 | else if(ex is HttpRequestException) 50 | { 51 | return "[服务器异常]"; 52 | } 53 | else 54 | { 55 | return "[客户端异常]"; 56 | } 57 | } 58 | 59 | /// 60 | /// 通过messagebox弹出错误 61 | /// 62 | /// 63 | public static void Show(this Exception e, string msg = "", bool showDetail = true) 64 | { 65 | if (!string.IsNullOrWhiteSpace(msg)) 66 | msg += "\n"; 67 | 68 | if (showDetail) 69 | msg += e.GetDescription(); 70 | else 71 | msg += e.Message; 72 | 73 | LogHelper.Log.Error(msg); 74 | 75 | MessageHelper.Error(msg); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/GlobalKeyboardHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | using System.Threading; 9 | using System.Windows.Forms; 10 | 11 | namespace ZuAnBot_Wpf.Helper 12 | { 13 | public class GlobalKeyboardHook 14 | { 15 | private const int WH_KEYBOARD_LL = 13; 16 | 17 | private const int WM_KEYDOWN = 256; 18 | 19 | private const int WM_KEYUP = 257; 20 | 21 | private const int WM_SYSKEYDOWN = 260; 22 | 23 | private const int WM_SYSKEYUP = 261; 24 | 25 | private GlobalKeyboardHook.LLKeyboardHook llkh; 26 | 27 | public List HookedKeys = new List(); 28 | 29 | private IntPtr Hook = IntPtr.Zero; 30 | 31 | public GlobalKeyboardHook() 32 | { 33 | this.llkh = new GlobalKeyboardHook.LLKeyboardHook(this.HookProc); 34 | } 35 | 36 | [DllImport("user32.dll", CharSet = CharSet.None, ExactSpelling = false)] 37 | private static extern int CallNextHookEx(IntPtr hhk, int code, int wParam, ref GlobalKeyboardHook.keyBoardHookStruct lParam); 38 | 39 | ~GlobalKeyboardHook() 40 | { 41 | this.unhook(); 42 | } 43 | 44 | public void hook() 45 | { 46 | IntPtr hInstance = GlobalKeyboardHook.LoadLibrary("User32"); 47 | this.Hook = GlobalKeyboardHook.SetWindowsHookEx(13, this.llkh, hInstance, 0); 48 | } 49 | 50 | public int HookProc(int Code, int wParam, ref GlobalKeyboardHook.keyBoardHookStruct lParam) 51 | { 52 | if (Code >= 0) 53 | { 54 | Keys key = (Keys)lParam.vkCode; 55 | if (this.HookedKeys.Contains(key)) 56 | { 57 | KeyEventArgs kArg = new KeyEventArgs(key); 58 | if ((wParam == 256 || wParam == 260) && this.KeyDown != null) 59 | { 60 | this.KeyDown(this, kArg); 61 | } 62 | else if ((wParam == 257 || wParam == 261) && this.KeyUp != null) 63 | { 64 | this.KeyUp(this, kArg); 65 | } 66 | if (kArg.Handled) 67 | { 68 | return 1; 69 | } 70 | } 71 | } 72 | return GlobalKeyboardHook.CallNextHookEx(this.Hook, Code, wParam, ref lParam); 73 | } 74 | 75 | [DllImport("kernel32.dll", CharSet = CharSet.None, ExactSpelling = false)] 76 | private static extern IntPtr LoadLibrary(string lpFileName); 77 | 78 | [DllImport("user32.dll", CharSet = CharSet.None, ExactSpelling = false)] 79 | private static extern IntPtr SetWindowsHookEx(int idHook, GlobalKeyboardHook.LLKeyboardHook callback, IntPtr hInstance, uint theardID); 80 | 81 | public void unhook() 82 | { 83 | GlobalKeyboardHook.UnhookWindowsHookEx(this.Hook); 84 | } 85 | 86 | [DllImport("user32.dll", CharSet = CharSet.None, ExactSpelling = false)] 87 | private static extern bool UnhookWindowsHookEx(IntPtr hInstance); 88 | 89 | public event KeyEventHandler KeyDown; 90 | 91 | public event KeyEventHandler KeyUp; 92 | 93 | public struct keyBoardHookStruct 94 | { 95 | public int vkCode; 96 | 97 | public int scanCode; 98 | 99 | public int flags; 100 | 101 | public int time; 102 | 103 | public int dwExtraInfo; 104 | } 105 | 106 | public delegate int LLKeyboardHook(int Code, int wParam, ref GlobalKeyboardHook.keyBoardHookStruct lParam); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/IPCHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Forms; 9 | using System.Windows.Interop; 10 | 11 | namespace ZuAnBot_Wpf.Helper 12 | { 13 | public class IPCHelper 14 | { 15 | /// 16 | /// 通过IPC发送消息到其他线程 17 | /// 18 | /// 字符串消息 19 | /// 目标窗口句柄 20 | public static void SendMessage(string s, IntPtr hWnd) 21 | { 22 | if (hWnd == null) return; 23 | 24 | byte[] sarr = Encoding.Default.GetBytes(s); 25 | int len = sarr.Length; 26 | CopyData cds2; 27 | cds2.dwData = (IntPtr)0; 28 | cds2.cbData = len + 1; 29 | cds2.lpData = s; 30 | 31 | // 发送消息 32 | SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ref cds2); 33 | } 34 | 35 | private static Action _handler; 36 | public static void ObserveMessage(Window window, Action handler) 37 | { 38 | _handler = handler; 39 | 40 | HwndSource hwndSource = PresentationSource.FromVisual(window) as HwndSource; 41 | if (hwndSource != null) 42 | { 43 | IntPtr handle = hwndSource.Handle; 44 | hwndSource.AddHook(new HwndSourceHook(WndProc)); 45 | } 46 | } 47 | private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 48 | { 49 | if (msg == WM_COPYDATA) 50 | { 51 | //int value = wParam.ToInt32(); 52 | //var enumValue = (string)(object)value; 53 | 54 | CopyData cds = (CopyData)Marshal.PtrToStructure(lParam, typeof(CopyData)); 55 | 56 | // 自定义行为 57 | var message = cds.lpData; 58 | _handler.Invoke(message); 59 | } 60 | return hwnd; 61 | } 62 | 63 | [DllImport("User32.dll", EntryPoint = "SendMessage")] 64 | private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyData lParam); 65 | public const int WM_COPYDATA = 0x004A; // 固定数值,不可更改 66 | 67 | } 68 | 69 | struct CopyData 70 | { 71 | public IntPtr dwData; // 任意值 72 | public int cbData; // 指定lpData内存区域的字节数 73 | [MarshalAs(UnmanagedType.LPStr)] 74 | public string lpData; // 发送给目标窗口所在进程的数据 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/JsonHelper.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.IO; 3 | using System.Linq; 4 | using ZuAnBot_Wpf.ViewModels; 5 | 6 | namespace ZuAnBot_Wpf.Helper 7 | { 8 | /// 9 | /// json持久化工具类 10 | /// 11 | public static class JsonHelper 12 | { 13 | public static T DeserializeManifestJson(string jsonName) 14 | { 15 | var stream = ManifestHelper.GetManifestStream(jsonName); 16 | 17 | return DeserializeStream(stream); 18 | } 19 | 20 | /// 21 | /// 反序列化词库 22 | /// 23 | /// 24 | public static WordsLibrary DeserializeWordsLibrary() 25 | { 26 | using (var stream = new FileStream(LocalConfigHelper.WordsLibraryPath, FileMode.Open)) 27 | { 28 | #region 给word的category导航属性赋值 29 | var libray = DeserializeStream(stream); 30 | foreach (var category in libray.Categories) 31 | { 32 | category.TargetCategories = libray.Categories.Where(x => x != category).ToList(); 33 | 34 | category.Library = libray; 35 | 36 | foreach (var word in category.Words) 37 | { 38 | word.Category = category; 39 | } 40 | } 41 | 42 | #endregion 给word的category导航属性赋值 43 | 44 | return libray; 45 | } 46 | } 47 | 48 | /// 49 | /// 序列化词库 50 | /// 51 | /// 52 | public static void SerializeWordsLibrary(WordsLibrary library) 53 | { 54 | if (library == null) return; 55 | 56 | var tempfile = Path.GetTempFileName(); 57 | using (var streamWritter = File.CreateText(tempfile)) 58 | { 59 | JsonSerializer serializer = new JsonSerializer(); 60 | serializer.Serialize(streamWritter, library); 61 | } 62 | 63 | File.Copy(tempfile, LocalConfigHelper.WordsLibraryPath, true); 64 | } 65 | 66 | private static T DeserializeStream(Stream stream) 67 | { 68 | StreamReader reader = new StreamReader(stream); 69 | JsonSerializer serializer = new JsonSerializer(); 70 | return (T)serializer.Deserialize(reader, typeof(T)); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/LocalConfigHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ZuAnBot_Wpf.Helper 9 | { 10 | /// 11 | /// 本地配置文件 12 | /// 13 | public class LocalConfigHelper 14 | { 15 | private static readonly string wordsLibraryFile = "wordsLibrary.json"; 16 | 17 | /// 18 | /// 配置文件目录 19 | /// 20 | public static string Dir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ZuAnBot"); 21 | 22 | /// 23 | /// 词库文件路径 24 | /// 25 | public static string WordsLibraryPath => GetPath(wordsLibraryFile); 26 | 27 | /// 28 | /// 获取配置文件路径 29 | /// 30 | /// 31 | /// 32 | public static string GetPath(string configFileName) 33 | { 34 | return Path.Combine(Dir, configFileName); 35 | } 36 | 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/LogHelper.cs: -------------------------------------------------------------------------------- 1 | using log4net; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | 10 | 11 | namespace ZuAnBot_Wpf.Helper 12 | { 13 | public static class LogHelper 14 | { 15 | private static ILog _logger = null; 16 | public static string logPath; 17 | 18 | static LogHelper() 19 | { 20 | InitLogger("ZuanBot", "ZuanBot"); 21 | } 22 | 23 | /// 24 | /// 若没初始化,日志将放入%temp%\ZuanBot文件夹 25 | /// 26 | public static ILog Log 27 | { 28 | get { return _logger; } 29 | } 30 | 31 | /// 32 | /// 初始化日志存放位置,若不初始化则存放在%temp%\ZuanBot 33 | /// 34 | /// 35 | private static void InitLogger(string logDirName, string loggerName) 36 | { 37 | logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TEMP", logDirName); 38 | if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath); 39 | log4net.GlobalContext.Properties["fname"] = Path.Combine(logPath, "log");//日志存放位置 40 | 41 | var stream = ManifestHelper.GetManifestStream("log4net.config"); 42 | log4net.Config.XmlConfigurator.Configure(stream); 43 | stream.Close(); 44 | //创建一个实例,名字可随便取 45 | _logger = LogManager.GetLogger(loggerName); 46 | _logger.Info("==================初始化Logger===================="); 47 | } 48 | 49 | } 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/ManifestHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ZuAnBot_Wpf.Helper 10 | { 11 | /// 12 | /// 清单资源工具类 13 | /// 14 | public static class ManifestHelper 15 | { 16 | public static Stream GetManifestStream(string jsonName) 17 | { 18 | return Assembly.GetExecutingAssembly().GetManifestResourceStream($"ZuAnBot_Wpf.Assets.{jsonName}"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/MessageHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace ZuAnBot_Wpf.Helper 9 | { 10 | public static class MessageHelper 11 | { 12 | public static void Error(string msg) 13 | { 14 | MessageBox.Show(msg, "错误", MessageBoxButton.OK, MessageBoxImage.Error); 15 | } 16 | 17 | public static void Info(string msg) 18 | { 19 | MessageBox.Show(msg, "通知", MessageBoxButton.OK, MessageBoxImage.Information); 20 | } 21 | 22 | /// 23 | /// 警告并询问用户是否继续 24 | /// 25 | /// 26 | /// 是否继续 27 | public static bool Warning(string msg) 28 | { 29 | return MessageBox.Show(msg, "警告", MessageBoxButton.OKCancel, MessageBoxImage.Warning) == MessageBoxResult.OK; 30 | } 31 | 32 | /// 33 | /// 询问用户是否继续 34 | /// 35 | /// 36 | /// 是否继续 37 | public static bool Question(string msg) 38 | { 39 | return MessageBox.Show(msg, "", MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.OK; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/ProgressFileStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ZuAnBot_Wpf.Helper 9 | { 10 | /// 11 | /// 可以记录传输进度的文件流 12 | /// 13 | public class ProgressFileStream : FileStream 14 | { 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// true为上传流,false为下载流 20 | public ProgressFileStream(string path, bool isRead) : base(path, isRead ? FileMode.Open : FileMode.Create, isRead ? FileAccess.Read : FileAccess.Write) 21 | { 22 | } 23 | 24 | public long CurrentSize { get; private set; } 25 | 26 | 27 | /// 28 | /// 流的传输监控器 29 | /// 30 | public event EventHandler Progress; 31 | 32 | public override void Write(byte[] array, int offset, int count) 33 | { 34 | base.Write(array, offset, count); 35 | 36 | CurrentSize += count; 37 | if (Progress != null) 38 | Progress(this, new FileEventArgs(CurrentSize, Length)); 39 | } 40 | 41 | public override int Read(byte[] array, int offset, int count) 42 | { 43 | int res = base.Read(array, offset, count); 44 | 45 | CurrentSize += count; 46 | if (Progress != null) 47 | Progress(this, new FileEventArgs(CurrentSize, Length));//上传进度 48 | 49 | return res; 50 | } 51 | } 52 | 53 | public class FileEventArgs : EventArgs 54 | { 55 | public FileEventArgs(long currentSize, long totalSize) 56 | { 57 | CurrentSize = currentSize; 58 | TotalSize = totalSize; 59 | } 60 | 61 | public long CurrentSize { get; } 62 | public long TotalSize { get; } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/VersionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | using ZuAnBot_Wpf.Api; 8 | using ZuAnBot_Wpf.Api.Dtos; 9 | 10 | namespace ZuAnBot_Wpf.Helper 11 | { 12 | public static class VersionHelper 13 | { 14 | private static readonly Apis _apis = Apis.GetInstance(); 15 | private readonly static string _pattern = @"^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}$"; 16 | 17 | /// 18 | /// 获取最新版本 19 | /// 20 | /// 21 | public static async Task GetLatestVersion() 22 | { 23 | try 24 | { 25 | return await _apis.GetLatestVersion(); 26 | } 27 | catch (ApiException) 28 | { 29 | return null; 30 | } 31 | } 32 | 33 | /// 34 | /// 获取当前版本名称 35 | /// 36 | /// 37 | public static string GetCurrentVersionName() 38 | { 39 | return System.Windows.Application.ResourceAssembly.GetName().Version.ToString(3); 40 | } 41 | 42 | /// 43 | /// 输入后端最新版本信息,判断当前版本是否是最新版本 44 | /// 45 | /// 46 | /// 47 | public static bool IsNewestVersion(VersionDto latestVersion) 48 | { 49 | if (latestVersion == null) return true; 50 | 51 | var currentVersionName = GetCurrentVersionName(); 52 | 53 | return CompareVersion(currentVersionName, latestVersion.VersionName) >= 0; 54 | } 55 | 56 | /// 57 | /// 比较两个版本号 58 | /// 59 | /// 60 | /// 61 | /// v1小于v2则返回-1,v1等于v2返回0,v1大于v2返回1 62 | /// 63 | public static int CompareVersion(string v1, string v2) 64 | { 65 | if (!Regex.IsMatch(v1, _pattern) || !Regex.IsMatch(v2, _pattern)) 66 | { 67 | throw new ArgumentOutOfRangeException("指定版本号格式无效"); 68 | } 69 | 70 | var version1 = v1.Split('.').Select(int.Parse).ToArray(); 71 | var version2 = v2.Split('.').Select(int.Parse).ToArray(); 72 | 73 | for (int i = 0; i < 3; i++) 74 | { 75 | if (version1[i] > version2[i]) 76 | return 1; 77 | else if (version1[i] < version2[i]) 78 | return -1; 79 | } 80 | 81 | return 0; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/Win32Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using ZuAnBot_Wpf.Constants; 10 | 11 | namespace ZuAnBot_Wpf.Helper 12 | { 13 | /// 14 | /// win32帮助类 15 | /// 16 | public static class Win32Helper 17 | { 18 | public static bool SetWindowForeground(string winTitle) 19 | { 20 | var hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, winTitle); 21 | 22 | if (hWnd != IntPtr.Zero) 23 | { 24 | SetForegroundWindow(hWnd); //Activate it 25 | return true; 26 | } 27 | 28 | return false; 29 | } 30 | 31 | public static bool SetMainWindowForeground() 32 | { 33 | return SetWindowForeground(Titles.Main); 34 | } 35 | 36 | public static bool CloseWindow(string winTitle) 37 | { 38 | var hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, winTitle); 39 | 40 | if (hWnd != IntPtr.Zero) 41 | { 42 | SendMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); 43 | return true; 44 | } 45 | 46 | return false; 47 | } 48 | 49 | 50 | [DllImport("user32.dll")] 51 | public static extern IntPtr FindWindowEx(IntPtr handleParent, IntPtr handleChild, string className, string WindowName); 52 | 53 | /// 54 | /// 获取窗体的句柄函数 55 | /// 56 | /// 窗口类名 57 | /// 窗口标题名 58 | /// 返回句柄 59 | [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)] 60 | private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 61 | 62 | [DllImport("User32.dll")] 63 | private static extern IntPtr SetForegroundWindow(IntPtr hWnd); 64 | 65 | const int WM_SYSCOMMAND = 0x0112; 66 | const int SC_CLOSE = 0xF060; 67 | [DllImport("user32.dll")] 68 | private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Helper/WordsHelper.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using RestSharp; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Web.UI.WebControls; 10 | using ZuAnBot_Wpf.ViewModels; 11 | 12 | namespace ZuAnBot_Wpf.Helper 13 | { 14 | public class WordsHelper 15 | { 16 | 17 | public static string GetZAWord() 18 | { 19 | var client = new RestClient("https://api.shadiao.app/"); 20 | var request = new RestRequest("nmsl", Method.Get); 21 | request.AddParameter("level", "min"); 22 | var response = client.Execute(request); 23 | if (response.IsSuccessful) 24 | { 25 | var res = JObject.Parse(response.Content); 26 | 27 | return res["data"]["text"].ToString(); 28 | } 29 | else 30 | return "连接服务器失败"; 31 | } 32 | 33 | 34 | public static void EnsureValidContent(string value) 35 | { 36 | if( !IsVaileContent(value,out string msg)) 37 | { 38 | throw new ArgumentOutOfRangeException(msg, innerException: null); 39 | } 40 | } 41 | 42 | private static bool IsVaileContent(string value,out string msg) 43 | { 44 | if (string.IsNullOrWhiteSpace(value)) 45 | { 46 | msg = "词条内容不为空"; 47 | return false; 48 | } 49 | if (value.Length > 200) 50 | { 51 | msg = "词条内容不能超过200"; 52 | return false; 53 | } 54 | 55 | msg = ""; 56 | return true; 57 | } 58 | 59 | public static bool IsVaileContent(string value) 60 | { 61 | return IsVaileContent(value, out _); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的一般信息由以下 8 | // 控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("祖安助手")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("ZuAnBot_Wpf")] 15 | [assembly: AssemblyCopyright("Copyright © 2022")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 会使此程序集中的类型 20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 21 | //请将此类型的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(false)] 23 | 24 | //若要开始生成可本地化的应用程序,请设置 25 | //.csproj 文件中的 CultureYouAreCodingWith 26 | //例如,如果您在源文件中使用的是美国英语, 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(未在页面中找到资源时使用, 37 | //或应用程序资源字典中找到时使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(未在页面中找到资源时使用, 40 | //、应用程序或任何主题专用资源字典中找到时使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下列四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 52 | //通过使用 "*",如下所示: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("3.0.2.0")] 55 | [assembly: AssemblyFileVersion("3.0.2.0")] 56 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Properties/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 62 | 63 | 64 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.CompilerServices; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows; 11 | using System.Windows.Forms; 12 | using Prism.Commands; 13 | using Prism.Mvvm; 14 | using Prism.Services.Dialogs; 15 | using WindowsInput; 16 | using ZuAnBot_Wpf.Api; 17 | using ZuAnBot_Wpf.Constants; 18 | using ZuAnBot_Wpf.Helper; 19 | using ZuAnBot_Wpf.Views; 20 | 21 | namespace ZuAnBot_Wpf.ViewModels 22 | { 23 | public class MainWindowViewModel : BindableBase 24 | { 25 | private readonly IDialogService _dialogService; 26 | private readonly Apis _apis = Apis.GetInstance(); 27 | GlobalKeyboardHook hook; 28 | 29 | public WordsLibrary Library { get; set; } 30 | 31 | #region 绑定属性 32 | private bool _IsPerWord = false; 33 | /// 34 | /// 是否逐字发送 35 | /// 36 | public bool IsPerWord 37 | { 38 | get { return _IsPerWord; } 39 | set { SetProperty(ref _IsPerWord, value); } 40 | } 41 | 42 | private bool _IsAll = false; 43 | /// 44 | /// 是否发送所有人消息 45 | /// 46 | public bool IsAll 47 | { 48 | get { return _IsAll; } 49 | set { SetProperty(ref _IsAll, value); } 50 | } 51 | 52 | 53 | private bool _IsNotifyIconBlink; 54 | /// 55 | /// 托盘图标是否闪烁 56 | /// 57 | public bool IsNotifyIconBlink 58 | { 59 | get { return _IsNotifyIconBlink; } 60 | set { SetProperty(ref _IsNotifyIconBlink, value); } 61 | } 62 | 63 | private bool _IsNotifyIconShow = true; 64 | /// 65 | /// 托盘图标是否显示 66 | /// 67 | public bool IsNotifyIconShow 68 | { 69 | get { return _IsNotifyIconShow; } 70 | set { SetProperty(ref _IsNotifyIconShow, value); } 71 | } 72 | 73 | private string _Version; 74 | /// 75 | /// 程序版本号 76 | /// 77 | public string Version 78 | { 79 | get { return _Version; } 80 | set { SetProperty(ref _Version, value); } 81 | } 82 | 83 | private bool _NeedUpdate; 84 | /// 85 | /// 是否需要更新 86 | /// 87 | public bool NeedUpdate 88 | { 89 | get { return _NeedUpdate; } 90 | set { SetProperty(ref _NeedUpdate, value); } 91 | } 92 | 93 | #endregion 绑定属性 94 | 95 | public MainWindowViewModel(IDialogService dialogService) 96 | { 97 | _dialogService = dialogService; 98 | } 99 | 100 | #region 命令 101 | #region LoadedCommand 102 | private DelegateCommand _LoadedCommand; 103 | public DelegateCommand LoadedCommand => _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand)); 104 | async void ExecuteLoadedCommand() 105 | { 106 | try 107 | { 108 | LoadWordsLibrary(); 109 | 110 | HookKeys(); 111 | 112 | Version = "v" + System.Windows.Application.ResourceAssembly.GetName().Version.ToString(3); 113 | 114 | await InitNeedUpdate(); 115 | } 116 | catch (Exception e) 117 | { 118 | e.Show(); 119 | } 120 | } 121 | 122 | private async Task InitNeedUpdate() 123 | { 124 | var latestVersion = await VersionHelper.GetLatestVersion(); 125 | NeedUpdate = !VersionHelper.IsNewestVersion(latestVersion); 126 | } 127 | 128 | private void LoadWordsLibrary() 129 | { 130 | try 131 | { 132 | //第一试用本软件没有本地词库,用资源清单的词库 133 | if (!File.Exists(LocalConfigHelper.WordsLibraryPath)) 134 | { 135 | var dir = Path.GetDirectoryName(LocalConfigHelper.WordsLibraryPath); 136 | if (!Directory.Exists(dir)) 137 | { 138 | Directory.CreateDirectory(dir); 139 | } 140 | 141 | var manifestStream = ManifestHelper.GetManifestStream("wordsLibrary.json"); 142 | using (var stream = File.Create(LocalConfigHelper.WordsLibraryPath)) 143 | { 144 | manifestStream.CopyTo(stream); 145 | } 146 | } 147 | 148 | Library = JsonHelper.DeserializeWordsLibrary();//反序列化本地词库 149 | } 150 | catch (Exception e) 151 | { 152 | e.Show("读取词库失败!"); 153 | File.Delete(LocalConfigHelper.WordsLibraryPath); 154 | App.Current.Shutdown(); 155 | } 156 | } 157 | 158 | #endregion 159 | 160 | #region 访问GitHub 161 | private DelegateCommand _VisitGitHubCommand; 162 | public DelegateCommand VisitGitHubCommand => _VisitGitHubCommand ?? (_VisitGitHubCommand = new DelegateCommand(ExecuteVisitGitHubCommand)); 163 | void ExecuteVisitGitHubCommand() 164 | { 165 | try 166 | { 167 | System.Diagnostics.Process.Start("https://github.com/liuke-wuhan/ZuAnBot"); 168 | } 169 | catch (Exception e) 170 | { 171 | e.Show(); 172 | } 173 | } 174 | #endregion 175 | 176 | #region SetCommand 177 | private DelegateCommand _SetCommand; 178 | 179 | public DelegateCommand SetCommand => _SetCommand ?? (_SetCommand = new DelegateCommand(ExecuteSetCommand)); 180 | void ExecuteSetCommand() 181 | { 182 | try 183 | { 184 | var parameters = new DialogParameters(); 185 | parameters.Add(Params.Library, Library); 186 | IDialogResult r = null; 187 | _dialogService.ShowDialog(nameof(WordsLibrarySet), parameters, result => r = result); 188 | 189 | if (r.Result == ButtonResult.OK) 190 | { 191 | JsonHelper.SerializeWordsLibrary(Library); 192 | } 193 | else 194 | { 195 | LoadWordsLibrary(); 196 | } 197 | } 198 | catch (Exception e) 199 | { 200 | e.Show(); 201 | } 202 | } 203 | #endregion 204 | 205 | #region UpdateCommand 206 | private bool _UpdateEnabled = true; 207 | public bool UpdateEnabled 208 | { 209 | get { return _UpdateEnabled; } 210 | set { SetProperty(ref _UpdateEnabled, value); } 211 | } 212 | private DelegateCommand _UpdateCommand; 213 | public DelegateCommand UpdateCommand => _UpdateCommand ?? (_UpdateCommand = new DelegateCommand(ExecuteUpdateCommand).ObservesCanExecute(() => UpdateEnabled)); 214 | async void ExecuteUpdateCommand() 215 | { 216 | try 217 | { 218 | UpdateEnabled = false; 219 | 220 | var latestVersion = await VersionHelper.GetLatestVersion(); 221 | if (VersionHelper.IsNewestVersion(latestVersion)) 222 | { 223 | MessageHelper.Info($"当前版本已经是最新版本"); 224 | } 225 | else 226 | { 227 | var result = MessageHelper.Question($"当前版本为{VersionHelper.GetCurrentVersionName()},最新版本为{latestVersion.VersionName},是否更新?"); 228 | if (result) 229 | { 230 | var tempDir = Path.GetTempPath();//临时目录 231 | var file = _apis.DownloadFile(tempDir, latestVersion.FileName, latestVersion.Url);//下载到临时目录 232 | 233 | //使用更新 234 | var assembly = Assembly.GetExecutingAssembly(); 235 | var stream = assembly.GetManifestResourceStream("costura.zuanbotupdate.exe"); 236 | var tempPath = Path.Combine(Path.GetTempPath(), "zuanbotupdate.exe"); 237 | using (var tempStream = File.Create(tempPath)) 238 | { 239 | stream.CopyTo(tempStream); 240 | } 241 | 242 | var startInfo = new ProcessStartInfo(tempPath, $"\"{file}\" \"{assembly.Location}\""); 243 | //设置不在新窗口中启动新的进程 244 | startInfo.CreateNoWindow = true; 245 | //不使用操作系统使用的shell启动进程 246 | startInfo.UseShellExecute = false; 247 | //将输出信息重定向 248 | startInfo.RedirectStandardOutput = true; 249 | Process.Start(startInfo); 250 | } 251 | } 252 | } 253 | catch (Exception e) 254 | { 255 | e.Show(); 256 | } 257 | finally 258 | { 259 | UpdateEnabled = true; 260 | } 261 | } 262 | #endregion 263 | 264 | 265 | #endregion 266 | 267 | /// 268 | /// 按键勾子 269 | /// 270 | private void HookKeys() 271 | { 272 | hook = new GlobalKeyboardHook(); 273 | hook.KeyUp += Hook_KeyUp; 274 | hook.HookedKeys.Add(Keys.F2); 275 | hook.HookedKeys.Add(Keys.F3); 276 | hook.HookedKeys.Add(Keys.F11); 277 | hook.HookedKeys.Add(Keys.F12); 278 | hook.hook(); 279 | } 280 | 281 | /// 282 | /// 勾子事件处理 283 | /// 284 | /// 285 | /// 286 | private void Hook_KeyUp(object sender, KeyEventArgs e) 287 | { 288 | try 289 | { 290 | string word = ""; 291 | if (e.KeyCode == Keys.F2) 292 | word += Library.GetLoacalWord("默认词库"); 293 | else if (e.KeyCode == Keys.F3) 294 | word += Library.GetLoacalWord("自定义词库"); 295 | else if (e.KeyCode == Keys.F11) 296 | { 297 | IsAll = !IsAll; 298 | return; 299 | } 300 | else if (e.KeyCode == Keys.F12) 301 | { 302 | IsPerWord = !IsPerWord; 303 | return; 304 | } 305 | else 306 | { 307 | return; 308 | } 309 | 310 | string allPre = IsAll ? "/all " : ""; 311 | 312 | var builder = Simulate.Events(); 313 | if (IsPerWord) 314 | { 315 | foreach (var item in word) 316 | { 317 | builder = builder. 318 | Click(WindowsInput.Events.KeyCode.Enter).Wait(100). 319 | Click(allPre + item).Wait(100). 320 | Click(WindowsInput.Events.KeyCode.Enter).Wait(100); 321 | 322 | } 323 | } 324 | else 325 | { 326 | builder = builder. 327 | Click(WindowsInput.Events.KeyCode.Enter).Wait(100). 328 | Click(allPre + word).Wait(100). 329 | Click(WindowsInput.Events.KeyCode.Enter).Wait(100); 330 | } 331 | builder.Invoke(); 332 | } 333 | catch (ArgumentOutOfRangeException) 334 | { 335 | MessageHelper.Error($"词库为空"); 336 | } 337 | catch (Exception ex) 338 | { 339 | ex.Show(); 340 | } 341 | } 342 | 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/ViewModels/WordEditViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DryIoc; 7 | using Prism.Commands; 8 | using Prism.Mvvm; 9 | using Prism.Services.Dialogs; 10 | using ZuAnBot_Wpf.Constants; 11 | using ZuAnBot_Wpf.Helper; 12 | 13 | namespace ZuAnBot_Wpf.ViewModels 14 | { 15 | public class WordEditViewModel : BindableBase, IDialogAware 16 | { 17 | private string _Content; 18 | /// 19 | /// 内容 20 | /// 21 | public string Content 22 | { 23 | get { return _Content; } 24 | set 25 | { 26 | SetProperty(ref _Content, value); 27 | } 28 | } 29 | 30 | #region Dialog 31 | public string Title => "词条"; 32 | 33 | public event Action RequestClose; 34 | 35 | public bool CanCloseDialog() { return true; } 36 | 37 | public void OnDialogClosed() { } 38 | 39 | public void OnDialogOpened(IDialogParameters parameters) 40 | { 41 | Content = parameters.GetValue(Params.WordContent); 42 | } 43 | #endregion Dialog 44 | 45 | #region OkCommand 46 | private DelegateCommand _OkCommand; 47 | public DelegateCommand OkCommand => _OkCommand ?? (_OkCommand = new DelegateCommand(ExecuteOkCommand)); 48 | void ExecuteOkCommand() 49 | { 50 | try 51 | { 52 | WordsHelper.EnsureValidContent(Content); 53 | 54 | var parameters = new DialogParameters(); 55 | parameters.Add(Params.WordContent, Content); 56 | RequestClose?.Invoke(new DialogResult(ButtonResult.OK, parameters)); 57 | } 58 | catch (ArgumentOutOfRangeException e) 59 | { 60 | e.Show(showDetail: false); 61 | } 62 | catch (Exception e) 63 | { 64 | e.Show(); 65 | } 66 | } 67 | #endregion 68 | 69 | #region CancelCommand 70 | private DelegateCommand _CancelCommand; 71 | public DelegateCommand CancelCommand => _CancelCommand ?? (_CancelCommand = new DelegateCommand(ExecuteCancelCommand)); 72 | void ExecuteCancelCommand() 73 | { 74 | try 75 | { 76 | RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel)); 77 | } 78 | catch (Exception e) 79 | { 80 | e.Show(); 81 | } 82 | } 83 | #endregion 84 | 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/ViewModels/WordLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Windows.Controls; 6 | using Prism.Mvvm; 7 | using Prism.Ioc; 8 | using Prism.Events; 9 | using Prism.Commands; 10 | using ZuAnBot_Wpf.Helper; 11 | using Newtonsoft.Json; 12 | using Prism.Services.Dialogs; 13 | using ZuAnBot_Wpf.Views; 14 | using ZuAnBot_Wpf.Constants; 15 | using ZuAnBot_Wpf.Constants.Events; 16 | using System.Windows; 17 | 18 | namespace ZuAnBot_Wpf.ViewModels 19 | { 20 | /// 21 | /// 词库,包括多类词条 22 | /// 23 | public class WordsLibrary : BindableBase 24 | { 25 | private ObservableCollection _Categories; 26 | public ObservableCollection Categories 27 | { 28 | get { return _Categories; } 29 | set { SetProperty(ref _Categories, value); } 30 | } 31 | } 32 | 33 | /// 34 | /// 词条类 35 | /// 36 | public class WordsCategory : BindableBase 37 | { 38 | [JsonIgnore] 39 | public WordsLibrary Library { get; set; } 40 | 41 | private ObservableCollection _Words; 42 | public ObservableCollection Words 43 | { 44 | get { return _Words; } 45 | set { SetProperty(ref _Words, value); } 46 | } 47 | 48 | private List _TargetCategories; 49 | [JsonIgnore] 50 | /// 51 | /// 其他类别 52 | /// 53 | public List TargetCategories 54 | { 55 | get { return _TargetCategories; } 56 | set { SetProperty(ref _TargetCategories, value); } 57 | } 58 | 59 | public string CategoryName { get; set; } 60 | 61 | private Word _SelectedWord; 62 | [JsonIgnore] 63 | /// 64 | /// 当前选中的word 65 | /// 66 | public Word SelectedWord 67 | { 68 | get { return _SelectedWord; } 69 | set 70 | { 71 | SetProperty(ref _SelectedWord, value); 72 | 73 | DeleteEnabled = value != null; 74 | EditEnabled = value != null; 75 | CopyEnabled = value != null; 76 | } 77 | } 78 | 79 | public WordsCategory() 80 | { 81 | } 82 | 83 | #region 命令 84 | 85 | public void RefreshSelectedWord() 86 | { 87 | SelectedWord = Words.FirstOrDefault(x => x.IsSelected); 88 | } 89 | 90 | #region AddCommand 91 | private bool _AddEnabled = true; 92 | [JsonIgnore] 93 | public bool AddEnabled 94 | { 95 | get { return _AddEnabled; } 96 | set { SetProperty(ref _AddEnabled, value); } 97 | } 98 | private DelegateCommand _AddCommand; 99 | [JsonIgnore] 100 | public DelegateCommand AddCommand => _AddCommand ?? (_AddCommand = new DelegateCommand(ExecuteAddCommand).ObservesCanExecute(() => AddEnabled)); 101 | void ExecuteAddCommand() 102 | { 103 | try 104 | { 105 | AddEnabled = false; 106 | 107 | //弹出词条对话框 108 | var dialogService = ContainerLocator.Container.Resolve(); 109 | 110 | dialogService.ShowDialog(nameof(WordEdit), 111 | new DialogParameters($"{Params.WordContent}={""}"), 112 | r => 113 | { 114 | if (r.Result == ButtonResult.OK) 115 | { 116 | var content = r.Parameters.GetValue(Params.WordContent); 117 | var word = new Word { Content = content, Category = this }; 118 | 119 | int index; 120 | if (SelectedWord == null) 121 | { 122 | index = 0; 123 | } 124 | else 125 | { 126 | index = Words.IndexOf(SelectedWord) + 1; 127 | } 128 | Words.Insert(index, word); 129 | } 130 | }); 131 | 132 | } 133 | catch (ArgumentOutOfRangeException e) 134 | { 135 | e.Show(showDetail: false); 136 | } 137 | catch (Exception e) 138 | { 139 | e.Show(); 140 | } 141 | AddEnabled = true; 142 | } 143 | #endregion 144 | 145 | #region DeleteCommand 146 | private bool _DeleteEnabled = false; 147 | [JsonIgnore] 148 | public bool DeleteEnabled 149 | { 150 | get { return _DeleteEnabled; } 151 | set { SetProperty(ref _DeleteEnabled, value); } 152 | } 153 | private DelegateCommand _DeleteCommand; 154 | [JsonIgnore] 155 | public DelegateCommand DeleteCommand => _DeleteCommand ?? (_DeleteCommand = new DelegateCommand(ExecuteDeleteCommand).ObservesCanExecute(() => DeleteEnabled)); 156 | void ExecuteDeleteCommand() 157 | { 158 | try 159 | { 160 | DeleteEnabled = false; 161 | 162 | int index = Words.IndexOf(SelectedWord); 163 | 164 | Words.Remove(SelectedWord); 165 | 166 | if (index < Words.Count) 167 | Words[index].IsSelected = true; 168 | } 169 | catch (Exception e) 170 | { 171 | e.Show(); 172 | } 173 | DeleteEnabled = true; 174 | } 175 | #endregion 176 | 177 | #region EditCommand 178 | private bool _EditEnabled = false; 179 | [JsonIgnore] 180 | public bool EditEnabled 181 | { 182 | get { return _EditEnabled; } 183 | set { SetProperty(ref _EditEnabled, value); } 184 | } 185 | private DelegateCommand _EditCommand; 186 | [JsonIgnore] 187 | public DelegateCommand EditCommand => _EditCommand ?? (_EditCommand = new DelegateCommand(ExecuteEditCommand).ObservesCanExecute(() => EditEnabled)); 188 | void ExecuteEditCommand() 189 | { 190 | try 191 | { 192 | EditEnabled = false; 193 | 194 | //弹出词条对话框 195 | var dialogService = ContainerLocator.Container.Resolve(); 196 | 197 | IDialogResult result = null; 198 | dialogService.ShowDialog(nameof(WordEdit), 199 | new DialogParameters($"{Params.WordContent}={SelectedWord.Content}"), r => result = r); 200 | 201 | if (result.Result == ButtonResult.OK) 202 | { 203 | var content = result.Parameters.GetValue(Params.WordContent); 204 | 205 | SelectedWord.Content = content; 206 | } 207 | } 208 | catch (ArgumentOutOfRangeException e) 209 | { 210 | e.Show(showDetail: false); 211 | } 212 | catch (Exception e) 213 | { 214 | e.Show(); 215 | } 216 | EditEnabled = true; 217 | } 218 | #endregion 219 | 220 | #region CopyCommand 221 | private bool _CopyEnabled = false; 222 | [JsonIgnore] 223 | public bool CopyEnabled 224 | { 225 | get { return _CopyEnabled; } 226 | set { SetProperty(ref _CopyEnabled, value); } 227 | } 228 | private DelegateCommand _CopyCommand; 229 | [JsonIgnore] 230 | public DelegateCommand CopyCommand => _CopyCommand ?? (_CopyCommand = new DelegateCommand(ExecuteCopyCommand).ObservesCanExecute(() => CopyEnabled)); 231 | void ExecuteCopyCommand() 232 | { 233 | try 234 | { 235 | CopyEnabled = false; 236 | 237 | Clipboard.SetText(SelectedWord.Content, TextDataFormat.Text); 238 | } 239 | catch (Exception e) 240 | { 241 | e.Show(); 242 | } 243 | CopyEnabled = true; 244 | } 245 | #endregion 246 | 247 | #region CopyToCommand 248 | private DelegateCommand _CopyToCommand; 249 | public DelegateCommand CopyToCommand => _CopyToCommand ?? (_CopyToCommand = new DelegateCommand(ExecuteCopyToCommand)); 250 | void ExecuteCopyToCommand(string parameter) 251 | { 252 | try 253 | { 254 | var sourceWord = Library.Categories.SelectMany(x => x.Words).FirstOrDefault(x => x.IsSelected); 255 | 256 | if (sourceWord == null) 257 | { 258 | MessageHelper.Error("未选中任何词条"); 259 | return; 260 | } 261 | 262 | var word = new Word() { Content = sourceWord.Content, Category = this }; 263 | Words.Insert(0, word); 264 | } 265 | catch (Exception e) 266 | { 267 | e.Show(); 268 | } 269 | } 270 | #endregion 271 | 272 | #region PasteCommand 273 | private bool _PasteEnabled = true; 274 | [JsonIgnore] 275 | public bool PasteEnabled 276 | { 277 | get { return _PasteEnabled; } 278 | set { SetProperty(ref _PasteEnabled, value); } 279 | } 280 | private DelegateCommand _PasteCommand; 281 | 282 | [JsonIgnore] 283 | public DelegateCommand PasteCommand => _PasteCommand ?? (_PasteCommand = new DelegateCommand(ExecutePasteCommand).ObservesCanExecute(() => PasteEnabled)); 284 | void ExecutePasteCommand() 285 | { 286 | try 287 | { 288 | PasteEnabled = false; 289 | 290 | var clipboardText = Clipboard.GetText(TextDataFormat.Text); 291 | 292 | int offset = 1; 293 | foreach (var text in clipboardText.Split('\n')) 294 | { 295 | if (!WordsHelper.IsVaileContent(text)) continue; 296 | 297 | var word = new Word { Content = text, Category = this }; 298 | 299 | int index; 300 | if (SelectedWord == null) 301 | { 302 | index = 0; 303 | } 304 | else 305 | { 306 | index = Words.IndexOf(SelectedWord) + offset++; 307 | } 308 | Words.Insert(index, word); 309 | } 310 | } 311 | catch (ArgumentOutOfRangeException e) 312 | { 313 | e.Show(showDetail: false); 314 | } 315 | catch (Exception e) 316 | { 317 | e.Show(); 318 | } 319 | PasteEnabled = true; 320 | } 321 | #endregion 322 | 323 | #endregion 命令 324 | 325 | } 326 | 327 | /// 328 | /// 词条 329 | /// 330 | public class Word : BindableBase 331 | { 332 | [JsonIgnore] 333 | public WordsCategory Category { get; set; } 334 | 335 | private string _Content; 336 | /// 337 | /// 内容 338 | /// 339 | public string Content 340 | { 341 | get { return _Content; } 342 | set 343 | { 344 | value = value.Replace('\r', char.MinValue); 345 | 346 | WordsHelper.EnsureValidContent(value); 347 | 348 | SetProperty(ref _Content, value); 349 | } 350 | } 351 | 352 | private bool _IsSelected; 353 | [JsonIgnore] 354 | /// 355 | /// 是否选中 356 | /// 357 | public bool IsSelected 358 | { 359 | get { return _IsSelected; } 360 | set 361 | { 362 | SetProperty(ref _IsSelected, value); 363 | 364 | Category.RefreshSelectedWord(); 365 | } 366 | } 367 | } 368 | 369 | public static class WordsLibraryExtension 370 | { 371 | public static string GetLoacalWord(this WordsLibrary library, string categoryName) 372 | { 373 | var words = library.Categories.First(x => x.CategoryName == categoryName); 374 | 375 | Random random = new Random((int)DateTime.Now.Ticks); 376 | var word = words.Words[random.Next(0, words.Words.Count)]; 377 | 378 | return word.Content; 379 | } 380 | 381 | //static int i = 0; 382 | //public static string GetTestWord(out int index) 383 | //{ 384 | // index = i; 385 | // if (i >= wordsLibrary.Categories.Count) return "测试完毕!"; 386 | 387 | // WordsLibrary words = wordsLibrary; 388 | 389 | // var word = words.Categories[i++]; 390 | 391 | // return word; 392 | //} 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/ViewModels/WordsLibrarySetViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Prism.Commands; 7 | using Prism.Mvvm; 8 | using Prism.Services.Dialogs; 9 | using ZuAnBot_Wpf.Constants; 10 | using ZuAnBot_Wpf.Helper; 11 | 12 | namespace ZuAnBot_Wpf.ViewModels 13 | { 14 | /// 15 | /// 词库 16 | /// 17 | /// 后期可考虑分为视图模型和模型 18 | public class WordsLibrarySetViewModel : BindableBase, IDialogAware 19 | { 20 | private WordsLibrary _Library; 21 | /// 22 | /// 词库 23 | /// 24 | public WordsLibrary Library 25 | { 26 | get { return _Library; } 27 | set { SetProperty(ref _Library, value); } 28 | } 29 | 30 | #region IDialogAware 31 | public string Title => "词库定义"; 32 | 33 | public event Action RequestClose; 34 | 35 | public bool CanCloseDialog() { return true; } 36 | 37 | public void OnDialogClosed() { } 38 | 39 | public void OnDialogOpened(IDialogParameters parameters) 40 | { 41 | try 42 | { 43 | Library = parameters.GetValue(Params.Library); 44 | } 45 | catch (Exception e) 46 | { 47 | e.Show(); 48 | } 49 | } 50 | #endregion IDialogAware 51 | 52 | #region SaveCommand 53 | private DelegateCommand _SaveCommand; 54 | public DelegateCommand SaveCommand => _SaveCommand ?? (_SaveCommand = new DelegateCommand(ExecuteSaveCommand)); 55 | void ExecuteSaveCommand() 56 | { 57 | try 58 | { 59 | RequestClose?.Invoke(new DialogResult(ButtonResult.OK)); 60 | } 61 | catch (Exception e) 62 | { 63 | e.Show(); 64 | } 65 | } 66 | #endregion 67 | 68 | #region CancelCommand 69 | private DelegateCommand _CancelCommand; 70 | public DelegateCommand CancelCommand => _CancelCommand ?? (_CancelCommand = new DelegateCommand(ExecuteCancelCommand)); 71 | void ExecuteCancelCommand() 72 | { 73 | try 74 | { 75 | RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel)); 76 | } 77 | catch (Exception e) 78 | { 79 | e.Show(); 80 | } 81 | } 82 | #endregion 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Views/Conveters/Bool2Brush.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows.Media; 9 | 10 | namespace ZuAnBot_Wpf.Views.Conveters 11 | { 12 | public class Bool2Brush : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | var b = (bool)value; 17 | 18 | if (b) 19 | return new SolidColorBrush(Colors.Red); 20 | else 21 | return new SolidColorBrush(Colors.Black); 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /ZuAnBot_Wpf/Views/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 39 | 49 | 50 | 61 | 64 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 86 | 87 | 88 | 89 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |