├── .gitattributes ├── .gitignore ├── Export1.png ├── WeChatMomentExport.sln ├── WeChatMomentExport ├── App.config ├── Core │ ├── Extractors │ │ ├── BasicInfoExtractor.cs │ │ ├── CommentExtractor.cs │ │ ├── IExtractor.cs │ │ ├── ImgExtractor.cs │ │ ├── LikeExtractor.cs │ │ ├── PosterInfoExtractor.cs │ │ ├── SharedExtractor.cs │ │ ├── ShortVideoExtractor.cs │ │ ├── TextExtractor.cs │ │ └── WeishiSharedExtractor.cs │ ├── MomentExporterFacade.cs │ └── MomentSerializer.cs ├── Enum │ └── MomentType.cs ├── Model │ ├── CommentInfo.cs │ ├── FriendInfo.cs │ ├── LikedInfo.cs │ ├── MomentInfo.cs │ ├── MonmentBasicInfo.cs │ ├── SharedItem.cs │ ├── TableInfo.cs │ └── UserInfo.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Utils │ ├── LogUtil.cs │ ├── MomentDBUtil.cs │ ├── PlistUtil.cs │ ├── SQLiteUtil.cs │ ├── StringUtil.cs │ ├── TimeUtil.cs │ └── WCUtil.cs ├── View │ ├── Index.html │ └── static │ │ ├── css │ │ ├── timeline.css │ │ ├── weui.css │ │ └── weui.min.css │ │ └── script │ │ ├── vue-min.js │ │ └── vue.js ├── WeChatMomentExport.csproj └── packages.config ├── readme.md └── update1.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 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true 235 | **/wwwroot/lib/ 236 | 237 | # RIA/Silverlight projects 238 | Generated_Code/ 239 | 240 | # Backup & report files from converting an old project file 241 | # to a newer Visual Studio version. Backup files are not needed, 242 | # because we have git ;-) 243 | _UpgradeReport_Files/ 244 | Backup*/ 245 | UpgradeLog*.XML 246 | UpgradeLog*.htm 247 | ServiceFabricBackup/ 248 | *.rptproj.bak 249 | 250 | # SQL Server files 251 | *.mdf 252 | *.ldf 253 | *.ndf 254 | 255 | # Business Intelligence projects 256 | *.rdl.data 257 | *.bim.layout 258 | *.bim_*.settings 259 | *.rptproj.rsuser 260 | *- Backup*.rdl 261 | 262 | # Microsoft Fakes 263 | FakesAssemblies/ 264 | 265 | # GhostDoc plugin setting file 266 | *.GhostDoc.xml 267 | 268 | # Node.js Tools for Visual Studio 269 | .ntvs_analysis.dat 270 | node_modules/ 271 | 272 | # Visual Studio 6 build log 273 | *.plg 274 | 275 | # Visual Studio 6 workspace options file 276 | *.opt 277 | 278 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 279 | *.vbw 280 | 281 | # Visual Studio LightSwitch build output 282 | **/*.HTMLClient/GeneratedArtifacts 283 | **/*.DesktopClient/GeneratedArtifacts 284 | **/*.DesktopClient/ModelManifest.xml 285 | **/*.Server/GeneratedArtifacts 286 | **/*.Server/ModelManifest.xml 287 | _Pvt_Extensions 288 | 289 | # Paket dependency manager 290 | .paket/paket.exe 291 | paket-files/ 292 | 293 | # FAKE - F# Make 294 | .fake/ 295 | 296 | # JetBrains Rider 297 | .idea/ 298 | *.sln.iml 299 | 300 | # CodeRush personal settings 301 | .cr/personal 302 | 303 | # Python Tools for Visual Studio (PTVS) 304 | __pycache__/ 305 | *.pyc 306 | 307 | # Cake - Uncomment if you are using it 308 | # tools/** 309 | # !tools/packages.config 310 | 311 | # Tabs Studio 312 | *.tss 313 | 314 | # Telerik's JustMock configuration file 315 | *.jmconfig 316 | 317 | # BizTalk build output 318 | *.btp.cs 319 | *.btm.cs 320 | *.odx.cs 321 | *.xsd.cs 322 | 323 | # OpenCover UI analysis results 324 | OpenCover/ 325 | 326 | # Azure Stream Analytics local run output 327 | ASALocalRun/ 328 | 329 | # MSBuild Binary and Structured Log 330 | *.binlog 331 | 332 | # NVidia Nsight GPU debugger configuration file 333 | *.nvuser 334 | 335 | # MFractors (Xamarin productivity tool) working folder 336 | .mfractor/ 337 | 338 | # Local History for Visual Studio 339 | .localhistory/ 340 | 341 | # BeatPulse healthcheck temp database 342 | healthchecksdb 343 | 344 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 345 | MigrationBackup/ -------------------------------------------------------------------------------- /Export1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mr0x01/WeChatMomentExport-iOS/eb8c97676abe6fab50f360c3a68ab032dee4c16a/Export1.png -------------------------------------------------------------------------------- /WeChatMomentExport.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeChatMomentExport", "WeChatMomentExport\WeChatMomentExport.csproj", "{5FD416B6-038F-4209-B97B-FF3181A9FB40}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5FD416B6-038F-4209-B97B-FF3181A9FB40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5FD416B6-038F-4209-B97B-FF3181A9FB40}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5FD416B6-038F-4209-B97B-FF3181A9FB40}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5FD416B6-038F-4209-B97B-FF3181A9FB40}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {86FEBF6F-1118-4E3C-B2AE-119219FF54D1} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /WeChatMomentExport/App.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 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/BasicInfoExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Enum; 8 | using WeChatMomentExport.Model; 9 | using WeChatMomentExport.Utils; 10 | 11 | namespace WeChatMomentExport.Core.Extractors 12 | { 13 | public class BasicInfoExtractor : IExtractor 14 | { 15 | public BasicInfoExtractor(NSArray _object, int i) : base(_object, i) 16 | { 17 | } 18 | public BasicInfoExtractor(NSArray _object) : base(_object, 0) 19 | { 20 | } 21 | 22 | public override MomentInfo Extract() 23 | { 24 | MomentInfo tempInfo = new MomentInfo(); 25 | NSDictionary item1 = (NSDictionary)_object[1]; 26 | tempInfo.momentTime = TimeUtil.TimeStamp2Datetime(item1.ObjectForKey("createtime").ToString()); 27 | tempInfo.momentType = (MomentType)int.Parse(item1.ObjectForKey("contentDescScene").ToString()); 28 | tempInfo.likeCount = int.Parse(item1.ObjectForKey("realLikeCount").ToString()); 29 | tempInfo.commentCount = int.Parse(item1.ObjectForKey("realCommentCount").ToString()); 30 | tempInfo.momentId = _object[2].ToString(); 31 | 32 | return tempInfo; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/CommentExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Model; 8 | 9 | namespace WeChatMomentExport.Core.Extractors 10 | { 11 | public class CommentExtractor : IExtractor 12 | { 13 | private DateTime time { get; set; } 14 | public CommentExtractor(NSArray _object, int i, DateTime time) : base(_object, i) 15 | { 16 | this.time = time; 17 | } 18 | 19 | public override CommentInfo Extract() 20 | { 21 | CommentInfo commentInfo = new CommentInfo(); 22 | commentInfo.commentTime = time; 23 | commentInfo.commentUser = new UserInfo(); 24 | commentInfo.commentUser.userId = _object[i + 1].ToString(); 25 | try 26 | { 27 | commentInfo.commentUser.userName = _object[i + 2].ToString(); 28 | commentInfo.commentContent = _object[i + 3].ToString(); 29 | commentInfo.commentOrder = int.Parse(_object[i + 4].ToString()); 30 | } 31 | catch (Exception) 32 | { 33 | try 34 | { 35 | commentInfo.commentUser.userName = _object[i + 1].ToString(); 36 | commentInfo.commentContent = _object[i + 2].ToString(); 37 | commentInfo.commentOrder = int.Parse(_object[i + 3].ToString()); 38 | } 39 | catch (Exception) 40 | { 41 | return null; 42 | } 43 | } 44 | 45 | if (_object[i + 5].GetType() == typeof(NSString)) 46 | { 47 | commentInfo.commentReplyto = _object[i + 5].ToString(); 48 | } 49 | return commentInfo; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/IExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace WeChatMomentExport.Core.Extractors 9 | { 10 | public abstract class IExtractor where T : class 11 | { 12 | protected NSArray _object { get; set; } 13 | protected int i { get; set; } 14 | public IExtractor(NSArray _object, int i) 15 | { 16 | this._object = _object; 17 | this.i = i; 18 | } 19 | 20 | public abstract T Extract(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/ImgExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace WeChatMomentExport.Core.Extractors 9 | { 10 | public class ImgExtractor : IExtractor 11 | { 12 | public ImgExtractor(NSArray _object, int i) : base(_object, i) 13 | { 14 | } 15 | 16 | public override Uri Extract() 17 | { 18 | Uri url = new Uri(_object[i + 1].ToString()); 19 | if (url.OriginalString.Contains("/150")) 20 | { 21 | return null; 22 | } 23 | else if (url.OriginalString.Contains("/0")) 24 | { 25 | return url; 26 | } 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/LikeExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Model; 8 | 9 | namespace WeChatMomentExport.Core.Extractors 10 | { 11 | public class LikeExtractor : IExtractor 12 | { 13 | private DateTime time { get; set; } 14 | public LikeExtractor(NSArray _object, int i, DateTime time) : base(_object, i) 15 | { 16 | this.time = time; 17 | } 18 | 19 | public override LikedInfo Extract() 20 | { 21 | LikedInfo likedInfo = new LikedInfo(); 22 | likedInfo.likedTime = time; 23 | likedInfo.likedUser = new UserInfo(); 24 | likedInfo.likedUser.userId = _object[++i].ToString(); 25 | likedInfo.likedUser.userName = _object[++i].ToString(); 26 | return likedInfo; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/PosterInfoExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Model; 8 | 9 | namespace WeChatMomentExport.Core.Extractors 10 | { 11 | public class PosterInfoExtractor : IExtractor 12 | { 13 | public PosterInfoExtractor(NSArray _object, int i) : base(_object, i) 14 | { 15 | } 16 | 17 | public PosterInfoExtractor(NSArray _object) : base(_object, 0) 18 | { 19 | } 20 | 21 | public override UserInfo Extract() 22 | { 23 | UserInfo userInfo = new UserInfo(); 24 | userInfo.userId = _object[3].ToString(); 25 | userInfo.userName = _object[4].ToString(); 26 | return userInfo; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/SharedExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Model; 8 | 9 | namespace WeChatMomentExport.Core.Extractors 10 | { 11 | public class SharedExtractor : IExtractor 12 | { 13 | public SharedExtractor(NSArray _object, int i) : base(_object, i) 14 | { 15 | } 16 | 17 | public override SharedItem Extract() 18 | { 19 | SharedItem sharedItem = new SharedItem(); 20 | sharedItem.sharedTitle = _object[i + 4].ToString(); 21 | sharedItem.sharedDigest = _object[i + 5].ToString(); 22 | sharedItem.sharedUrl = new Uri(_object[i + 6].ToString()); 23 | return sharedItem; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/ShortVideoExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace WeChatMomentExport.Core.Extractors 9 | { 10 | public class ShortVideoExtractor : IExtractor 11 | { 12 | public ShortVideoExtractor(NSArray _object, int i) : base(_object, i) 13 | { 14 | } 15 | 16 | public override Uri Extract() 17 | { 18 | Uri url = new Uri(_object[i + 2].ToString()); 19 | return url; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/TextExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Model; 8 | 9 | namespace WeChatMomentExport.Core.Extractors 10 | { 11 | public class TextExtractor : IExtractor 12 | { 13 | private UserInfo posterInfo { get; set; } 14 | public TextExtractor(NSArray _object, int i) : base(_object, i) 15 | { 16 | } 17 | public TextExtractor(NSArray _object, int i, UserInfo posterInfo) : base(_object, i) 18 | { 19 | this.posterInfo = posterInfo; 20 | } 21 | 22 | public override string Extract() 23 | { 24 | for (int j = 1; j < 5; j++) 25 | { 26 | Type type = _object[i + j].GetType(); 27 | string content = _object[i + j].ToString(); 28 | if (type == typeof(NSString) && !content.StartsWith("gh_") && 29 | content != posterInfo.userId && 30 | content != posterInfo.userName) 31 | { 32 | return _object[i + j].ToString(); 33 | } 34 | } 35 | return ""; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/Extractors/WeishiSharedExtractor.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Model; 8 | 9 | namespace WeChatMomentExport.Core.Extractors 10 | { 11 | public class WeishiSharedExtractor : IExtractor 12 | { 13 | public WeishiSharedExtractor(NSArray _object, int i) : base(_object, i) 14 | { 15 | } 16 | 17 | public override SharedItem Extract() 18 | { 19 | SharedItem sharedItem = new SharedItem(); 20 | sharedItem.sharedFrom = "微视"; 21 | sharedItem.sharedUrl = new Uri(_object[i + 14].ToString()); 22 | return sharedItem; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/MomentExporterFacade.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | using WeChatMomentExport.Model; 12 | using WeChatMomentExport.Utils; 13 | 14 | namespace WeChatMomentExport.Core 15 | { 16 | public class MomentExporterFacade 17 | { 18 | private bool skipSharedItem { get; set; } = false; 19 | private string userHash { get; set; } 20 | private WebClient webClient { get; set; } 21 | private List bigOne { get; set; } = new List(); 22 | 23 | public MomentExporterFacade(string userHash, bool skipSharedItem = false) 24 | { 25 | Init(); 26 | this.userHash = userHash; 27 | this.skipSharedItem = skipSharedItem; 28 | } 29 | 30 | public void Start() 31 | { 32 | MomentDBUtil.LoadMomentSQLite(userHash); 33 | DirectoryInfo directoryInfo = new DirectoryInfo("Plist"); 34 | foreach (FileInfo momentPlistFile in directoryInfo.GetFiles()) 35 | { 36 | NSObject plist = PropertyListParser.Parse(momentPlistFile); 37 | MomentSerializer serializer = new MomentSerializer((NSDictionary)plist); 38 | MomentInfo momentInfo = serializer.SerializMoment(); 39 | 40 | if (momentInfo != null && 41 | ( 42 | !(skipSharedItem && momentInfo.momentType == Enum.MomentType.Shared) || (momentInfo.sharedItem != null && momentInfo.sharedItem.sharedFrom == "微视")) 43 | ) 44 | { 45 | LogUtil.Log($"解析{momentInfo.momentId}"); 46 | DownloadFile(momentInfo); 47 | bigOne.Add(momentInfo); 48 | string momentJson = JsonConvert.SerializeObject(momentInfo); 49 | File.WriteAllText($"Json\\{momentInfo.momentId}.json", momentJson, Encoding.UTF8); 50 | } 51 | } 52 | string bigOneJson = JsonConvert.SerializeObject(bigOne); 53 | 54 | bigOneJson = "var moment_data = " + bigOneJson; 55 | File.WriteAllText($"View\\static\\script\\data.js", bigOneJson, Encoding.UTF8); 56 | } 57 | 58 | private void DownloadFile(MomentInfo momentInfo) 59 | { 60 | string guid; 61 | string localFileName; 62 | if (momentInfo.momentImgs != null) 63 | { 64 | momentInfo.momentImgsLocal = new List(); 65 | foreach (Uri img in momentInfo.momentImgs) 66 | { 67 | guid = Guid.NewGuid().ToString(); 68 | localFileName = $"LocalFile\\{guid}.jpg"; 69 | LogUtil.Log($"下载{img}"); 70 | webClient.DownloadFile(img, localFileName); 71 | momentInfo.momentImgsLocal.Add(localFileName); 72 | } 73 | } 74 | if (momentInfo.shortVideoUrl != null) 75 | { 76 | guid = Guid.NewGuid().ToString(); 77 | localFileName = $"LocalFile\\{guid}.mp4"; 78 | LogUtil.Log($"下载{momentInfo.shortVideoUrl}"); 79 | webClient.DownloadFile(momentInfo.shortVideoUrl, localFileName); 80 | momentInfo.shortVideoUrlLocal = localFileName; 81 | } 82 | if (momentInfo.sharedItem != null) 83 | { 84 | guid = Guid.NewGuid().ToString(); 85 | localFileName = $"LocalFile\\{guid}.mp4"; 86 | LogUtil.Log($"下载{momentInfo.sharedItem.sharedUrl}"); 87 | webClient.DownloadFile(momentInfo.sharedItem.sharedUrl, localFileName); 88 | momentInfo.sharedItem.sharedFileLocal = localFileName; 89 | } 90 | } 91 | 92 | public void Init() 93 | { 94 | webClient = new WebClient(); 95 | webClient.Encoding = Encoding.UTF8; 96 | Directory.CreateDirectory("Plist"); 97 | Directory.CreateDirectory("Json"); 98 | Directory.CreateDirectory("View\\LocalFile"); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /WeChatMomentExport/Core/MomentSerializer.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using WeChatMomentExport.Core.Extractors; 8 | using WeChatMomentExport.Enum; 9 | using WeChatMomentExport.Model; 10 | 11 | namespace WeChatMomentExport.Utils 12 | { 13 | public class MomentSerializer 14 | { 15 | private NSDictionary momentPlist { get; set; } 16 | private MomentInfo momentInfo { get; set; } 17 | private NSArray _object { get; set; } 18 | 19 | private List imgs { get; set; } = new List(); 20 | private List likes { get; set; } = new List(); 21 | private List comments { get; set; } = new List(); 22 | 23 | public MomentSerializer(NSDictionary plist) 24 | { 25 | this.momentPlist = plist; 26 | momentInfo = new MomentInfo(); 27 | } 28 | 29 | public MomentInfo SerializMoment() 30 | { 31 | _object = (NSArray)momentPlist.ObjectForKey("$objects"); 32 | IExtractor basicMomentInfo = new BasicInfoExtractor(_object); 33 | momentInfo = basicMomentInfo.Extract(); 34 | 35 | IExtractor poterInfoExtractor = new PosterInfoExtractor(_object); 36 | momentInfo.posterInfo = poterInfoExtractor.Extract(); 37 | 38 | ExtractItems(); 39 | momentInfo.momentImgs = imgs; 40 | momentInfo.like = likes; 41 | momentInfo.comment = comments; 42 | 43 | return momentInfo; 44 | } 45 | 46 | private void ExtractItems() 47 | { 48 | bool extractText = false; 49 | bool extractImgs = false; 50 | bool extractVideo = false; 51 | bool extractSharedItem = false; 52 | 53 | switch (momentInfo.momentType) 54 | { 55 | case MomentType.TextOnly: 56 | extractText = true; 57 | break; 58 | case MomentType.WithImg: 59 | extractText = true; 60 | extractImgs = true; 61 | break; 62 | case MomentType.WithShortVideo: 63 | extractText = true; 64 | extractVideo = true; 65 | break; 66 | case MomentType.Shared: 67 | extractText = true; 68 | extractSharedItem = true; 69 | break; 70 | } 71 | 72 | 73 | 74 | for (int i = 0; i < _object.Count; i++) 75 | { 76 | Type currentNodeType = _object[i].GetType(); 77 | if (currentNodeType == typeof(NSDictionary)) 78 | { 79 | NSDictionary currentItem = (NSDictionary)_object[i]; 80 | if (currentItem.ContainsKey("bDeleted"))//评论或点赞 81 | { 82 | DateTime time = TimeUtil.TimeStamp2Datetime(currentItem["createTime"].ToString()); 83 | string type = currentItem["type"].ToString(); 84 | if (type.ToString() == "1")//点赞 85 | { 86 | IExtractor likeExtractor = new LikeExtractor(_object, i, time); 87 | likes.Add(likeExtractor.Extract()); 88 | } 89 | else if (type.ToString() == "2")//评论 90 | { 91 | IExtractor commentExtractor = new CommentExtractor(_object, i, time); 92 | CommentInfo comment = commentExtractor.Extract(); 93 | if (comment != null) 94 | { 95 | comments.Add(comment); 96 | } 97 | } 98 | } 99 | else if (extractText && currentItem.ContainsValue("WCAppInfo"))//文字 100 | { 101 | IExtractor textExtractor = new TextExtractor(_object, i, momentInfo.posterInfo); 102 | momentInfo.momentText = textExtractor.Extract(); 103 | } 104 | else if (extractImgs && currentItem.ContainsKey("encIdx"))//附图 105 | { 106 | IExtractor imgExtractor = new ImgExtractor(_object, i); 107 | Uri imgUrl = imgExtractor.Extract(); 108 | if (imgUrl != null) 109 | { 110 | imgs.Add(imgUrl); 111 | } 112 | } 113 | else if (extractVideo && currentItem.ContainsValue("WCUrl"))//视频 114 | { 115 | IExtractor shortVideoExtractor = new ShortVideoExtractor(_object, i); 116 | momentInfo.shortVideoUrl = shortVideoExtractor.Extract(); 117 | } 118 | //else if (extractSharedItem)//分享 119 | //{ 120 | 121 | // IExtractor sharedExtractor = new SharedExtractor(_object, i); 122 | // momentInfo.sharedItem = sharedExtractor.Extract(); 123 | //} 124 | } 125 | else if (extractSharedItem && currentNodeType == typeof(NSString)) 126 | { 127 | NSString currentItem = (NSString)_object[i]; 128 | if (currentItem.Content == "WeChat Sight")//微视分享 129 | { 130 | IExtractor weishiSharedExtractor = new WeishiSharedExtractor(_object, i); 131 | momentInfo.sharedItem = weishiSharedExtractor.Extract(); 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /WeChatMomentExport/Enum/MomentType.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 WeChatMomentExport.Enum 8 | { 9 | public enum MomentType 10 | { 11 | /// 12 | /// 有视频(不一定有文字) 13 | /// 14 | WithShortVideo = 0, 15 | /// 16 | /// 只有文字 17 | /// 18 | TextOnly = 2, 19 | /// 20 | /// 有图片(不一定有文字) 21 | /// 22 | WithImg = 3, 23 | /// 24 | /// 分享(不一定有文字) 25 | /// 26 | Shared = 4 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/CommentInfo.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 WeChatMomentExport.Model 8 | { 9 | public class CommentInfo 10 | { 11 | /// 12 | /// 评论内容 13 | /// 14 | public string commentContent { get; set; } 15 | /// 16 | /// 评论时间 17 | /// 18 | public DateTime commentTime { get; set; } 19 | /// 20 | /// 评论的用户 21 | /// 22 | public UserInfo commentUser { get; set; } 23 | /// 24 | /// 回复给谁(可能为空) 25 | /// 26 | public string commentReplyto { get; set; } 27 | /// 28 | /// 评论的排列顺序(越大越靠后) 29 | /// 30 | public int commentOrder { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/FriendInfo.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 WeChatMomentExport.Model 8 | { 9 | public class FriendInfo 10 | { 11 | public List nick_name = new List();//好友昵称 12 | public int like_count { get; set; }//好友点赞数 13 | 14 | public FriendInfo(string nick_name) 15 | { 16 | this.nick_name.Add(nick_name); 17 | like_count = 1; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/LikedInfo.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 WeChatMomentExport.Model 8 | { 9 | public class LikedInfo 10 | { 11 | /// 12 | /// 点赞时间 13 | /// 14 | public DateTime likedTime { get; set; } 15 | /// 16 | /// 点赞的用户 17 | /// 18 | public UserInfo likedUser { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/MomentInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using WeChatMomentExport.Enum; 7 | 8 | namespace WeChatMomentExport.Model 9 | { 10 | public class MomentInfo 11 | { 12 | /// 13 | /// 朋友圈ID 14 | /// 15 | public string momentId { get; set; } 16 | /// 17 | /// 发送人信息 18 | /// 19 | public UserInfo posterInfo { get; set; } = new UserInfo(); 20 | /// 21 | /// 朋友圈的文字内容 22 | /// 23 | public string momentText { get; set; } 24 | /// 25 | /// 朋友圈的配图 26 | /// 27 | public List momentImgs { get; set; } 28 | /// 29 | /// 朋友圈的配图(本地文件) 30 | /// 31 | public List momentImgsLocal { get; set; } 32 | /// 33 | /// 朋友圈url(仅在分享的朋友圈中存在) 34 | /// 35 | public SharedItem sharedItem { get; set; } 36 | /// 37 | /// 朋友圈小视频 38 | /// 39 | public Uri shortVideoUrl { get; set; } 40 | /// 41 | /// 朋友圈小视频 42 | /// 43 | public string shortVideoUrlLocal { get; set; } 44 | /// 45 | /// 朋友圈发布的时间 46 | /// 47 | public DateTime momentTime { get; set; } 48 | /// 49 | /// 朋友圈类型 50 | /// 51 | public MomentType momentType { get; set; } 52 | /// 53 | /// 点赞数 54 | /// 55 | public int likeCount { get; set; } 56 | /// 57 | /// 喜欢列表 58 | /// 59 | public List like { get; set; } 60 | /// 61 | /// 评论数 62 | /// 63 | public int commentCount { get; set; } 64 | /// 65 | /// 评论列表 66 | /// 67 | public List comment { get; set; } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/MonmentBasicInfo.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 WeChatMomentExport.Model 8 | { 9 | public class MonmentBasicInfo 10 | { 11 | public int like_count { get; set; }//本条朋友圈的喜欢数 12 | public DateTime create_time { get; set; }//朋友圈发送时间 13 | public string content { get; set; }//朋友圈文字内容 14 | public Dictionary liked_friends = new Dictionary();//喜欢的朋友 15 | public List comment_list = new List();//评论列表 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/SharedItem.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 WeChatMomentExport.Model 8 | { 9 | public class SharedItem 10 | { 11 | public string sharedTitle { get; set; } 12 | public string sharedDigest { get; set; } 13 | public Uri sharedUrl { get; set; } 14 | public string sharedFileLocal { get; set; } 15 | public string sharedFrom { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/TableInfo.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 WeChatMomentExport.Model 8 | { 9 | public class TableInfo 10 | { 11 | public string type { get; set; } 12 | public string name { get; set; } 13 | public string tbl_name { get; set; } 14 | public int rootpage { get; set; } 15 | public string sql { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WeChatMomentExport/Model/UserInfo.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 WeChatMomentExport.Model 8 | { 9 | public class UserInfo 10 | { 11 | /// 12 | /// 用户微信id 13 | /// 14 | public string userId { get; set; } 15 | /// 16 | /// 用户微信昵称 17 | /// 18 | public string userName { get; set; } 19 | /// 20 | /// 用户微信头像 21 | /// 22 | public Uri userAvatar { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /WeChatMomentExport/Program.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 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 WeChatMomentExport.Core; 9 | using WeChatMomentExport.Utils; 10 | 11 | namespace WeChatMomentExport 12 | { 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | MomentExporterFacade exporterFacade = new MomentExporterFacade("49baa62c85d746efdb46eb2ff92a446b", true); 18 | exporterFacade.Start(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WeChatMomentExport/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("WeChatMomentExport")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WeChatMomentExport")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("5fd416b6-038f-4209-b97b-ff3181a9fb40")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/LogUtil.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 WeChatMomentExport.Utils 8 | { 9 | public class LogUtil 10 | { 11 | public static void Log(string l) 12 | { 13 | string log = string.Format("[{0}]:{1}", DateTime.Now.ToString("HH:mm:ss"), l); 14 | Console.WriteLine(log); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/MomentDBUtil.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 WeChatMomentExport.Utils 9 | { 10 | public class MomentDBUtil 11 | { 12 | public static void LoadMomentSQLite(string profile_hash = "") 13 | { 14 | string dbname = "wc005_008.db"; 15 | var db = new SQLiteUtil(dbname); 16 | var tables = db.SelectTablesName(); 17 | HashSet ids = new HashSet(); 18 | foreach (var table in tables) 19 | { 20 | if (table.type == "table" && table.tbl_name.Equals("MyWC01_" + profile_hash)) 21 | { 22 | LogUtil.Log($"找到{profile_hash},开始导出plist..."); 23 | var datas = db.SelectList($"select Buffer,Id from {table.tbl_name}"); 24 | for (int i = 0; i < datas.Count; i++) 25 | { 26 | var localid = datas[i][1].ToString(); 27 | if (ids.Contains(localid)) 28 | { 29 | continue; 30 | } 31 | else 32 | { 33 | ids.Add(localid); 34 | var bolb_data = (byte[])datas[i][0]; 35 | var path = $"Plist\\{localid}.plist"; 36 | File.WriteAllBytes(path, bolb_data); 37 | } 38 | } 39 | LogUtil.Log($"共找到朋友圈数据{datas.Count}条"); 40 | return; 41 | } 42 | } 43 | LogUtil.Log($"未找到{profile_hash},再次确认输入,或重建朋友圈缓存。"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/PlistUtil.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 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 | 9 | namespace WeChatMomentExport.Utils 10 | { 11 | public class PlistUtil 12 | { 13 | /// 14 | /// 将plist文件转换为xml文件 15 | /// 16 | /// plist文件位置 17 | /// 是否存储转换的xml文件 18 | /// 19 | public string Plist2XML(string path, bool saveToFile) 20 | { 21 | FileInfo plistFileInfo = new FileInfo(path); 22 | NSObject plist = PropertyListParser.Parse(plistFileInfo); 23 | string xml = plist.ToXmlPropertyList(); 24 | if (saveToFile) 25 | { 26 | File.WriteAllText(plistFileInfo.DirectoryName + "\\" + plistFileInfo.Name + ".xml", xml); 27 | } 28 | return xml; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/SQLiteUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.SQLite; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using WeChatMomentExport.Model; 9 | 10 | namespace WeChatMomentExport.Utils 11 | { 12 | public class SQLiteUtil 13 | { 14 | private SQLiteConnection db; 15 | public SQLiteUtil(string db_name) 16 | { 17 | if (File.Exists(db_name)) 18 | { 19 | db = new SQLiteConnection($"Data Source={db_name};Version=3;"); 20 | db.Open(); 21 | } 22 | } 23 | 24 | public SQLiteUtil(string db_name, int db_version) 25 | { 26 | if (File.Exists(db_name)) 27 | { 28 | db = new SQLiteConnection($"Data Source={db_name};Version={db_version};"); 29 | db.Open(); 30 | } 31 | } 32 | 33 | public List SelectTablesName() 34 | { 35 | string sql = "select * from sqlite_master"; 36 | List table = new List(); 37 | var list = SelectList(sql); 38 | foreach (var row in list) 39 | { 40 | TableInfo info = new TableInfo(); 41 | info.type = row[0].ToString(); 42 | info.name = row[1].ToString(); 43 | info.tbl_name = row[2].ToString(); 44 | info.rootpage = int.Parse(row[3].ToString()); 45 | info.sql = row[4].ToString(); 46 | table.Add(info); 47 | } 48 | return table; 49 | } 50 | 51 | public List> SelectList(string sql) 52 | { 53 | SQLiteCommand command = new SQLiteCommand(sql, db); 54 | SQLiteDataReader reader = command.ExecuteReader(); 55 | List> list = new List>(); 56 | if (reader.HasRows) 57 | { 58 | while (reader.Read()) 59 | { 60 | List temp = new List(); 61 | for (int i = 0; i < reader.VisibleFieldCount; i++) 62 | { 63 | temp.Add(reader[i]); 64 | } 65 | list.Add(temp); 66 | } 67 | } 68 | return list; 69 | } 70 | 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/StringUtil.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 | 8 | namespace WeChatMomentExport.Utils 9 | { 10 | public class StringUtil 11 | { 12 | /// 13 | /// 移除表情符号 14 | /// 15 | /// 16 | /// 17 | public string ReplaceHexadecimalSymbols(string txt) 18 | { 19 | string r = "[\x00-\x08\x0B\x0C\x0E-\x1F\x26]"; 20 | return Regex.Replace(txt, r, "", RegexOptions.Compiled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/TimeUtil.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 WeChatMomentExport.Utils 8 | { 9 | public class TimeUtil 10 | { 11 | /// 12 | /// 时间戳转Datetime 13 | /// 14 | /// 15 | /// 16 | public static DateTime TimeStamp2Datetime(string ts) 17 | { 18 | DateTime t1 = new DateTime(1970, 1, 1, 0, 0, 0, 0); 19 | DateTime time = t1.AddSeconds(long.Parse(ts)); 20 | return time.ToLocalTime(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /WeChatMomentExport/Utils/WCUtil.cs: -------------------------------------------------------------------------------- 1 | using Claunia.PropertyList; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | using System.Xml; 10 | using WeChatMomentExport.Model; 11 | 12 | namespace WeChatMomentExport.Utils 13 | { 14 | public class WCUtil 15 | { 16 | public Dictionary friends_info = new Dictionary(); 17 | public Dictionary moments_info = new Dictionary(); 18 | 19 | public void LoadMomentSQLite(string profile_hash = "") 20 | { 21 | string dbname = "wc005_008.db"; 22 | var db = new SQLiteUtil(dbname); 23 | var tables = db.SelectTablesName(); 24 | HashSet ids = new HashSet(); 25 | foreach (var table in tables) 26 | { 27 | if (table.type == "table" && table.tbl_name.Equals("MyWC01_" + profile_hash)) 28 | { 29 | Console.WriteLine($"找到{profile_hash},开始导出..."); 30 | var datas = db.SelectList($"select Buffer,Id from {table.tbl_name}"); 31 | if (!Directory.Exists("Export")) 32 | { 33 | Directory.CreateDirectory("Export"); 34 | } 35 | foreach (var row in datas) 36 | { 37 | var localid = row[1].ToString(); 38 | if (ids.Contains(localid)) 39 | { 40 | continue; 41 | } 42 | else 43 | { 44 | ids.Add(localid); 45 | var bolb_data = (byte[])row[0]; 46 | var path = $"Export\\{localid}.plist"; 47 | File.WriteAllBytes(path, bolb_data); 48 | string xml = FormatPlist(path); 49 | MonmentBasicInfo basic_info = GetBasicInfo(xml); 50 | moments_info.Add(localid, basic_info); 51 | Console.WriteLine(basic_info.content); 52 | } 53 | } 54 | return; 55 | } 56 | } 57 | Console.WriteLine($"未找到{profile_hash},再次确认输入,或重建朋友圈缓存。"); 58 | } 59 | 60 | /// 61 | /// 导出简单的分析文本 62 | /// 63 | /// 点赞最多的前N个好友,默认10个 64 | /// 被赞最多的前N个朋友圈,默认5条 65 | /// 前N个最爱改名的朋友,及他们的昵称,默认999 66 | public void ExportAnalysis(int top_friend = 10, int top_moment = 5, int top_nickname_changed = 999) 67 | { 68 | var top_friends = friends_info.OrderByDescending(a => a.Value.like_count).Take(top_friend).ToList(); 69 | var top_moments = moments_info.OrderByDescending(a => a.Value.like_count).Take(top_moment)/*.OrderByDescending(a=>a.Value.create_time)*/.ToList(); 70 | var friends = friends_info.OrderByDescending(a => a.Value.nick_name.Count).Take(top_nickname_changed); 71 | 72 | string top = ""; 73 | int total_like = 0, moments_amount = 0; 74 | foreach (var moment in moments_info.OrderByDescending(a => a.Value.create_time)) 75 | { 76 | top += $"编号:{moment.Key}\r\n内容:{moment.Value.content}\r\n日期:{moment.Value.create_time}\r\n点赞:{moment.Value.like_count}\r\n\r\n"; 77 | total_like += moment.Value.like_count; 78 | moments_amount++; 79 | } 80 | File.WriteAllText("AllMoments.txt", top); 81 | 82 | top = ""; 83 | top += $"共发朋友圈:{moments_amount}\r\n共收到赞:{total_like}\r\n"; 84 | top += $"点赞Top{top_moment}\r\n"; 85 | foreach (var friend in top_friends) 86 | { 87 | top += $"WXID:{""}\t\t\t\t昵称:{friend.Value.nick_name[0]}\t\t\t\t点赞:{friend.Value.like_count}\r\n"; 88 | } 89 | top += $"被赞Top{top_moment}\r\n\r\n"; 90 | foreach (var moment in top_moments) 91 | { 92 | top += $"内容:{moment.Value.content}\r\n日期:{moment.Value.create_time}\r\n点赞:{moment.Value.like_count}\r\n\r\n"; 93 | } 94 | top += $"改名Top{top_nickname_changed}\r\n\r\n"; 95 | foreach (var friend in friends) 96 | { 97 | top += $"WXID:[{friend.Key}]\r\n"; 98 | for (int i = 0; i < friend.Value.nick_name.Count; i++) 99 | { 100 | top += $"[{friend.Value.nick_name[i]}]"; 101 | if (i > 0 && i % 3 == 0) 102 | { 103 | top += "\r\n"; 104 | } 105 | } 106 | top += "\r\n\r\n"; 107 | } 108 | File.WriteAllText("Report.txt", top); 109 | } 110 | 111 | /// 112 | /// 格式化plist为XML 113 | /// 114 | /// 115 | /// 116 | private static string FormatPlist(string path) 117 | { 118 | var old_one = new FileInfo(path); 119 | var plist = PropertyListParser.Parse(old_one); 120 | string xml = plist.ToXmlPropertyList(); 121 | File.WriteAllText(old_one.DirectoryName + "\\" + old_one.Name + ".xml", xml); 122 | return xml; 123 | } 124 | 125 | /// 126 | /// 解析xml的内容,取出朋友圈的基础信息 127 | /// 128 | /// 129 | /// 130 | private MonmentBasicInfo GetBasicInfo(string xml) 131 | { 132 | MonmentBasicInfo info = new MonmentBasicInfo(); 133 | XmlDocument xmlDocument = new XmlDocument(); 134 | 135 | try 136 | { 137 | xmlDocument.LoadXml(xml); 138 | } 139 | catch (Exception) 140 | { 141 | xmlDocument.LoadXml(ReplaceHexadecimalSymbols(xml)); 142 | } 143 | 144 | var node_list = xmlDocument.SelectSingleNode("/plist/dict/array/dict[1]").ChildNodes;//取点赞数和发布时间 145 | var node = xmlDocument.SelectSingleNode("/plist"); 146 | for (int i = 0; i < node_list.Count; i += 2) 147 | { 148 | var temp_key = node_list[i]; 149 | var temp_val = node_list[i + 1]; 150 | var key = temp_key.InnerText; 151 | var val = temp_val.InnerText; 152 | if (key == "createtime") 153 | { 154 | info.create_time = TStoLocalTime(long.Parse(val)); 155 | continue; 156 | } 157 | else if (key == "realLikeCount") 158 | { 159 | info.like_count = int.Parse(val); 160 | break; 161 | } 162 | } 163 | 164 | try 165 | { 166 | node = xmlDocument.SelectSingleNode("/plist/dict/array//key[. = 'isForceUpdate']").ParentNode.NextSibling;//取自己发表的内容 167 | if (node.Name.Equals("dict")) 168 | { 169 | while (true) 170 | { 171 | if (node.NextSibling.Name.Equals("dict")) 172 | { 173 | info.content = node.InnerText; 174 | if (info.content.Equals("$classnameWCAppInfo$classesWCAppInfoNSObject")) 175 | { 176 | node = xmlDocument.SelectSingleNode("/plist/dict/array//key[. = 'appMsgShareInfo']").ParentNode.NextSibling; 177 | info.content = "[文章标题]" + node.InnerText; 178 | } 179 | break; 180 | } 181 | else 182 | { 183 | node = node.NextSibling; 184 | } 185 | } 186 | } 187 | else if (node.Name.Equals("string")) 188 | { 189 | while (true) 190 | { 191 | node = node.NextSibling; 192 | if (node.Name.Equals("dict")) 193 | { 194 | info.content = node.NextSibling.InnerText; 195 | break; 196 | } 197 | } 198 | } 199 | } 200 | catch (NullReferenceException) 201 | { 202 | node = xmlDocument.SelectSingleNode("/plist/dict/array//key[. = 'bUseXorEncrypt']").ParentNode;//取自己发表的内容 203 | if (node.Name.Equals("dict")) 204 | { 205 | node = node.NextSibling.NextSibling; 206 | info.content = node.InnerText; 207 | } 208 | } 209 | 210 | node_list = xmlDocument.SelectNodes("/plist/dict/array/string"); 211 | bool empty = false; 212 | if (info.like_count != 0) 213 | { 214 | for (int i = 4; i < info.like_count * 2 + 5; i++) 215 | { 216 | var val = node_list[i].InnerText; 217 | if (val == "") 218 | { 219 | empty = true; //处理空行 220 | continue; 221 | } 222 | if (empty == false) 223 | { 224 | if (i % 2 == 0) 225 | { 226 | info.liked_friends.Add(val, node_list[i + 1].InnerText); 227 | if (friends_info.ContainsKey(val)) 228 | { 229 | friends_info[val].like_count++; 230 | if (!friends_info[val].nick_name.Contains(node_list[i + 1].InnerText)) 231 | { 232 | friends_info[val].nick_name.Add(node_list[i + 1].InnerText); 233 | } 234 | } 235 | else 236 | { 237 | friends_info.Add(val, new FriendInfo(node_list[i + 1].InnerText)); 238 | } 239 | } 240 | } 241 | else 242 | { 243 | if (i % 2 != 0) 244 | { 245 | info.liked_friends.Add(val, node_list[i + 1].InnerText); 246 | if (friends_info.ContainsKey(val)) 247 | { 248 | friends_info[val].like_count++; 249 | if (!friends_info[val].nick_name.Contains(node_list[i + 1].InnerText)) 250 | { 251 | friends_info[val].nick_name.Add(node_list[i + 1].InnerText); 252 | } 253 | } 254 | else 255 | { 256 | friends_info.Add(val, new FriendInfo(node_list[i + 1].InnerText)); 257 | } 258 | } 259 | } 260 | } 261 | } 262 | 263 | node = xmlDocument.SelectSingleNode("/plist/dict/array//string[. = 'NSMutableArray']").ParentNode.NextSibling; 264 | int comment_friends_index = node.SelectNodes("array/string").Count; 265 | //if (comment_friends_index > 0) 266 | //{ 267 | // for (int i = 0; i < comment_friends_index; i++) 268 | // { 269 | // CommentInfo comment_info = new CommentInfo(); 270 | // node = node.NextSibling;//评论的属性节点 271 | 272 | // long comment_ts = long.Parse(node.SelectSingleNode("key[. = 'createTime']").NextSibling.InnerText); 273 | // comment_info.comment_time = TStoLocalTime(comment_ts); 274 | // node = node.NextSibling;//评论人微信号 275 | 276 | // string wxid = node.InnerText; 277 | // comment_info.comment_user_id = wxid; 278 | // node = node.NextSibling;//评论人昵称 279 | 280 | 281 | // string nick_name = node.InnerText; 282 | // comment_info.comment_user_name = nick_name; 283 | // node = node.NextSibling;//评论内容 284 | 285 | // string comment = node.InnerText; 286 | // comment_info.comment_cotent = comment; 287 | // node = node.NextSibling;//某种奇怪的ID,会以32递增 288 | 289 | // if (node.NextSibling.Name.Equals("string")) 290 | // { 291 | // node = node.NextSibling;//回复对象 292 | // string reply = node.InnerText; 293 | // comment_info.comment_reply = reply; 294 | // comment_info.type = 0; 295 | // } 296 | // else 297 | // { 298 | // comment_info.type = 1; 299 | // } 300 | // info.comment_list.Add(comment_info); 301 | 302 | // } 303 | //} 304 | return info; 305 | } 306 | 307 | /// 308 | /// 替换xml中的无效字符 309 | /// 310 | /// 311 | /// 312 | private static string ReplaceHexadecimalSymbols(string txt) 313 | { 314 | //12655080931128840365 315 | return txt; 316 | string r = "[\x00-\x08\x0B\x0C\x0E-\x1F\x26]"; 317 | return Regex.Replace(txt, r, "", RegexOptions.Compiled); 318 | } 319 | 320 | /// 321 | /// 时间戳转本地时间 322 | /// 323 | /// 324 | /// 325 | private static DateTime TStoLocalTime(long TS) 326 | { 327 | DateTime converted = new DateTime(1970, 1, 1, 0, 0, 0, 0); 328 | DateTime newDateTime = converted.AddSeconds(TS); 329 | return newDateTime.ToLocalTime(); 330 | } 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /WeChatMomentExport/View/Index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 我的朋友圈 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{moment.momentId}} 18 | {{moment.momentText}} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{moment.momentTime}} 31 | 32 | ♥ 33 | 34 | 35 | 36 | {{liked.likedUser.userName}} 37 | 38 | 39 | 40 | 41 | 42 | 💬 43 | 44 | 45 | 46 | 47 | 48 | {{commentItem.commentUser.userName}}: 49 | 50 | 51 | @{{commentItem.commentReplyto}} 52 | 53 | {{commentItem.commentContent}} 54 | 55 | 56 | 57 | 58 | 59 | 60 | {{flipSide()}} 61 | 62 | 63 | 64 | 65 | 87 | 88 | -------------------------------------------------------------------------------- /WeChatMomentExport/View/static/css/timeline.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | background-color: #474e5d; 7 | font-family: Helvetica, sans-serif; 8 | } 9 | 10 | /* The actual timeline (the vertical ruler) */ 11 | .timeline { 12 | position: relative; 13 | max-width: 1200px; 14 | margin: 0 auto; 15 | } 16 | 17 | /* The actual timeline (the vertical ruler) */ 18 | .timeline::after { 19 | content: ''; 20 | position: absolute; 21 | width: 6px; 22 | background-color: white; 23 | top: 0; 24 | bottom: 0; 25 | left: 50%; 26 | margin-left: -3px; 27 | } 28 | 29 | /* Container around content */ 30 | .container { 31 | padding: 10px 40px; 32 | position: relative; 33 | background-color: inherit; 34 | width: 50%; 35 | } 36 | 37 | /* The circles on the timeline */ 38 | .container::after { 39 | content: ''; 40 | position: absolute; 41 | width: 25px; 42 | height: 25px; 43 | right: -17px; 44 | background-color: white; 45 | border: 4px solid #FF9F55; 46 | top: 15px; 47 | border-radius: 50%; 48 | z-index: 1; 49 | } 50 | 51 | /* Place the container to the left */ 52 | .left { 53 | left: 0; 54 | } 55 | 56 | /* Place the container to the right */ 57 | .right { 58 | left: 50%; 59 | } 60 | 61 | /* Add arrows to the left container (pointing right) */ 62 | .left::before { 63 | content: " "; 64 | height: 0; 65 | position: absolute; 66 | top: 22px; 67 | width: 0; 68 | z-index: 1; 69 | right: 30px; 70 | border: medium solid white; 71 | border-width: 10px 0 10px 10px; 72 | border-color: transparent transparent transparent white; 73 | } 74 | 75 | /* Add arrows to the right container (pointing left) */ 76 | .right::before { 77 | content: " "; 78 | height: 0; 79 | position: absolute; 80 | top: 22px; 81 | width: 0; 82 | z-index: 1; 83 | left: 30px; 84 | border: medium solid white; 85 | border-width: 10px 10px 10px 0; 86 | border-color: transparent white transparent transparent; 87 | } 88 | 89 | /* Fix the circle for containers on the right side */ 90 | .right::after { 91 | left: -16px; 92 | } 93 | 94 | /* The actual content */ 95 | .content { 96 | padding: 20px 30px; 97 | background-color: white; 98 | position: relative; 99 | border-radius: 6px; 100 | } 101 | 102 | /* Media queries - Responsive timeline on screens less than 600px wide */ 103 | @media screen and (max-width: 600px) { 104 | /* Place the timelime to the left */ 105 | .timeline::after { 106 | left: 31px; 107 | } 108 | 109 | /* Full-width containers */ 110 | .container { 111 | width: 100%; 112 | padding-left: 70px; 113 | padding-right: 25px; 114 | } 115 | 116 | /* Make sure that all arrows are pointing leftwards */ 117 | .container::before { 118 | left: 60px; 119 | border: medium solid white; 120 | border-width: 10px 10px 10px 0; 121 | border-color: transparent white transparent transparent; 122 | } 123 | 124 | /* Make sure all circles are at the same spot */ 125 | .left::after, .right::after { 126 | left: 15px; 127 | } 128 | 129 | /* Make all right containers behave like the left ones */ 130 | .right { 131 | left: 0%; 132 | } 133 | } 134 | 135 | .moment_attachment { 136 | max-width: 100%; 137 | max-height: 100%; 138 | } 139 | -------------------------------------------------------------------------------- /WeChatMomentExport/View/static/script/vue-min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.6.11 3 | * (c) 2014-2019 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Vue=t()}(this,function(){"use strict";var e=Object.freeze({});function t(e){return null==e}function n(e){return null!=e}function r(e){return!0===e}function i(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function o(e){return null!==e&&"object"==typeof e}var a=Object.prototype.toString;function s(e){return"[object Object]"===a.call(e)}function c(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function u(e){return n(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function l(e){return null==e?"":Array.isArray(e)||s(e)&&e.toString===a?JSON.stringify(e,null,2):String(e)}function f(e){var t=parseFloat(e);return isNaN(t)?e:t}function p(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}var m=Object.prototype.hasOwnProperty;function y(e,t){return m.call(e,t)}function g(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var _=/-(\w)/g,b=g(function(e){return e.replace(_,function(e,t){return t?t.toUpperCase():""})}),$=g(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),w=/\B([A-Z])/g,C=g(function(e){return e.replace(w,"-$1").toLowerCase()});var x=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function k(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function A(e,t){for(var n in t)e[n]=t[n];return e}function O(e){for(var t={},n=0;n0,Z=J&&J.indexOf("edge/")>0,G=(J&&J.indexOf("android"),J&&/iphone|ipad|ipod|ios/.test(J)||"ios"===K),X=(J&&/chrome\/\d+/.test(J),J&&/phantomjs/.test(J),J&&J.match(/firefox\/(\d+)/)),Y={}.watch,Q=!1;if(z)try{var ee={};Object.defineProperty(ee,"passive",{get:function(){Q=!0}}),window.addEventListener("test-passive",null,ee)}catch(e){}var te=function(){return void 0===B&&(B=!z&&!V&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),B},ne=z&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function re(e){return"function"==typeof e&&/native code/.test(e.toString())}var ie,oe="undefined"!=typeof Symbol&&re(Symbol)&&"undefined"!=typeof Reflect&&re(Reflect.ownKeys);ie="undefined"!=typeof Set&&re(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ae=S,se=0,ce=function(){this.id=se++,this.subs=[]};ce.prototype.addSub=function(e){this.subs.push(e)},ce.prototype.removeSub=function(e){h(this.subs,e)},ce.prototype.depend=function(){ce.target&&ce.target.addDep(this)},ce.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t-1)if(o&&!y(i,"default"))a=!1;else if(""===a||a===C(e)){var c=Pe(String,i.type);(c<0||s0&&(st((u=e(u,(a||"")+"_"+c))[0])&&st(f)&&(s[l]=he(f.text+u[0].text),u.shift()),s.push.apply(s,u)):i(u)?st(f)?s[l]=he(f.text+u):""!==u&&s.push(he(u)):st(u)&&st(f)?s[l]=he(f.text+u.text):(r(o._isVList)&&n(u.tag)&&t(u.key)&&n(a)&&(u.key="__vlist"+a+"_"+c+"__"),s.push(u)));return s}(e):void 0}function st(e){return n(e)&&n(e.text)&&!1===e.isComment}function ct(e,t){if(e){for(var n=Object.create(null),r=oe?Reflect.ownKeys(e):Object.keys(e),i=0;i0,a=t?!!t.$stable:!o,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&r&&r!==e&&s===r.$key&&!o&&!r.$hasNormal)return r;for(var c in i={},t)t[c]&&"$"!==c[0]&&(i[c]=pt(n,c,t[c]))}else i={};for(var u in n)u in i||(i[u]=dt(n,u));return t&&Object.isExtensible(t)&&(t._normalized=i),R(i,"$stable",a),R(i,"$key",s),R(i,"$hasNormal",o),i}function pt(e,t,n){var r=function(){var e=arguments.length?n.apply(null,arguments):n({});return(e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:at(e))&&(0===e.length||1===e.length&&e[0].isComment)?void 0:e};return n.proxy&&Object.defineProperty(e,t,{get:r,enumerable:!0,configurable:!0}),r}function dt(e,t){return function(){return e[t]}}function vt(e,t){var r,i,a,s,c;if(Array.isArray(e)||"string"==typeof e)for(r=new Array(e.length),i=0,a=e.length;idocument.createEvent("Event").timeStamp&&(sn=function(){return cn.now()})}function un(){var e,t;for(an=sn(),rn=!0,Qt.sort(function(e,t){return e.id-t.id}),on=0;onon&&Qt[n].id>e.id;)n--;Qt.splice(n+1,0,e)}else Qt.push(e);nn||(nn=!0,Ye(un))}}(this)},fn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||o(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Re(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},fn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},fn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},fn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||h(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var pn={enumerable:!0,configurable:!0,get:S,set:S};function dn(e,t,n){pn.get=function(){return this[t][n]},pn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,pn)}function vn(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[];e.$parent&&$e(!1);var o=function(o){i.push(o);var a=Me(o,t,n,e);xe(r,o,a),o in e||dn(e,"_props",o)};for(var a in t)o(a);$e(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]="function"!=typeof t[n]?S:x(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;s(t=e._data="function"==typeof t?function(e,t){le();try{return e.call(t,t)}catch(e){return Re(e,t,"data()"),{}}finally{fe()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&y(r,o)||(a=void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&dn(e,"_data",o))}var a;Ce(t,!0)}(e):Ce(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=te();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new fn(e,a||S,S,hn)),i in e||mn(e,i,o)}}(e,t.computed),t.watch&&t.watch!==Y&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i-1:"string"==typeof e?e.split(",").indexOf(t)>-1:(n=e,"[object RegExp]"===a.call(n)&&e.test(t));var n}function An(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=xn(a.componentOptions);s&&!t(s)&&On(n,o,r,i)}}}function On(e,t,n,r){var i=e[t];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),e[t]=null,h(n,t)}!function(t){t.prototype._init=function(t){var n=this;n._uid=bn++,n._isVue=!0,t&&t._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),r=t._parentVnode;n.parent=t.parent,n._parentVnode=r;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(n,t):n.$options=De($n(n.constructor),t||{},n),n._renderProxy=n,n._self=n,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(n),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&qt(e,t)}(n),function(t){t._vnode=null,t._staticTrees=null;var n=t.$options,r=t.$vnode=n._parentVnode,i=r&&r.context;t.$slots=ut(n._renderChildren,i),t.$scopedSlots=e,t._c=function(e,n,r,i){return Pt(t,e,n,r,i,!1)},t.$createElement=function(e,n,r,i){return Pt(t,e,n,r,i,!0)};var o=r&&r.data;xe(t,"$attrs",o&&o.attrs||e,null,!0),xe(t,"$listeners",n._parentListeners||e,null,!0)}(n),Yt(n,"beforeCreate"),function(e){var t=ct(e.$options.inject,e);t&&($e(!1),Object.keys(t).forEach(function(n){xe(e,n,t[n])}),$e(!0))}(n),vn(n),function(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}(n),Yt(n,"created"),n.$options.el&&n.$mount(n.$options.el)}}(wn),function(e){var t={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=ke,e.prototype.$delete=Ae,e.prototype.$watch=function(e,t,n){if(s(t))return _n(this,e,t,n);(n=n||{}).user=!0;var r=new fn(this,e,t,n);if(n.immediate)try{t.call(this,r.value)}catch(e){Re(e,this,'callback for immediate watcher "'+r.expression+'"')}return function(){r.teardown()}}}(wn),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this;if(Array.isArray(e))for(var i=0,o=e.length;i1?k(t):t;for(var n=k(arguments,1),r='event handler for "'+e+'"',i=0,o=t.length;iparseInt(this.max)&&On(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return F}};Object.defineProperty(e,"config",t),e.util={warn:ae,extend:A,mergeOptions:De,defineReactive:xe},e.set=ke,e.delete=Ae,e.nextTick=Ye,e.observable=function(e){return Ce(e),e},e.options=Object.create(null),M.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,A(e.options.components,Tn),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=k(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=De(this.options,e),this}}(e),Cn(e),function(e){M.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&s(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}(e)}(wn),Object.defineProperty(wn.prototype,"$isServer",{get:te}),Object.defineProperty(wn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(wn,"FunctionalRenderContext",{value:Tt}),wn.version="2.6.11";var En=p("style,class"),Nn=p("input,textarea,option,select,progress"),jn=function(e,t,n){return"value"===n&&Nn(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Dn=p("contenteditable,draggable,spellcheck"),Ln=p("events,caret,typing,plaintext-only"),Mn=function(e,t){return Hn(t)||"false"===t?"false":"contenteditable"===e&&Ln(t)?t:"true"},In=p("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Fn="http://www.w3.org/1999/xlink",Pn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Rn=function(e){return Pn(e)?e.slice(6,e.length):""},Hn=function(e){return null==e||!1===e};function Bn(e){for(var t=e.data,r=e,i=e;n(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(t=Un(i.data,t));for(;n(r=r.parent);)r&&r.data&&(t=Un(t,r.data));return function(e,t){if(n(e)||n(t))return zn(e,Vn(t));return""}(t.staticClass,t.class)}function Un(e,t){return{staticClass:zn(e.staticClass,t.staticClass),class:n(e.class)?[e.class,t.class]:t.class}}function zn(e,t){return e?t?e+" "+t:e:t||""}function Vn(e){return Array.isArray(e)?function(e){for(var t,r="",i=0,o=e.length;i-1?hr(e,t,n):In(t)?Hn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Dn(t)?e.setAttribute(t,Mn(t,n)):Pn(t)?Hn(n)?e.removeAttributeNS(Fn,Rn(t)):e.setAttributeNS(Fn,t,n):hr(e,t,n)}function hr(e,t,n){if(Hn(n))e.removeAttribute(t);else{if(q&&!W&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var mr={create:dr,update:dr};function yr(e,r){var i=r.elm,o=r.data,a=e.data;if(!(t(o.staticClass)&&t(o.class)&&(t(a)||t(a.staticClass)&&t(a.class)))){var s=Bn(r),c=i._transitionClasses;n(c)&&(s=zn(s,Vn(c))),s!==i._prevClass&&(i.setAttribute("class",s),i._prevClass=s)}}var gr,_r,br,$r,wr,Cr,xr={create:yr,update:yr},kr=/[\w).+\-_$\]]/;function Ar(e){var t,n,r,i,o,a=!1,s=!1,c=!1,u=!1,l=0,f=0,p=0,d=0;for(r=0;r=0&&" "===(h=e.charAt(v));v--);h&&kr.test(h)||(u=!0)}}else void 0===i?(d=r+1,i=e.slice(0,r).trim()):m();function m(){(o||(o=[])).push(e.slice(d,r).trim()),d=r+1}if(void 0===i?i=e.slice(0,r).trim():0!==d&&m(),o)for(r=0;r-1?{exp:e.slice(0,$r),key:'"'+e.slice($r+1)+'"'}:{exp:e,key:null};_r=e,$r=wr=Cr=0;for(;!zr();)Vr(br=Ur())?Jr(br):91===br&&Kr(br);return{exp:e.slice(0,wr),key:e.slice(wr+1,Cr)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Ur(){return _r.charCodeAt(++$r)}function zr(){return $r>=gr}function Vr(e){return 34===e||39===e}function Kr(e){var t=1;for(wr=$r;!zr();)if(Vr(e=Ur()))Jr(e);else if(91===e&&t++,93===e&&t--,0===t){Cr=$r;break}}function Jr(e){for(var t=e;!zr()&&(e=Ur())!==t;);}var qr,Wr="__r",Zr="__c";function Gr(e,t,n){var r=qr;return function i(){null!==t.apply(null,arguments)&&Qr(e,i,n,r)}}var Xr=Ve&&!(X&&Number(X[1])<=53);function Yr(e,t,n,r){if(Xr){var i=an,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=i||e.timeStamp<=0||e.target.ownerDocument!==document)return o.apply(this,arguments)}}qr.addEventListener(e,t,Q?{capture:n,passive:r}:n)}function Qr(e,t,n,r){(r||qr).removeEventListener(e,t._wrapper||t,n)}function ei(e,r){if(!t(e.data.on)||!t(r.data.on)){var i=r.data.on||{},o=e.data.on||{};qr=r.elm,function(e){if(n(e[Wr])){var t=q?"change":"input";e[t]=[].concat(e[Wr],e[t]||[]),delete e[Wr]}n(e[Zr])&&(e.change=[].concat(e[Zr],e.change||[]),delete e[Zr])}(i),rt(i,o,Yr,Qr,Gr,r.context),qr=void 0}}var ti,ni={create:ei,update:ei};function ri(e,r){if(!t(e.data.domProps)||!t(r.data.domProps)){var i,o,a=r.elm,s=e.data.domProps||{},c=r.data.domProps||{};for(i in n(c.__ob__)&&(c=r.data.domProps=A({},c)),s)i in c||(a[i]="");for(i in c){if(o=c[i],"textContent"===i||"innerHTML"===i){if(r.children&&(r.children.length=0),o===s[i])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===i&&"PROGRESS"!==a.tagName){a._value=o;var u=t(o)?"":String(o);ii(a,u)&&(a.value=u)}else if("innerHTML"===i&&qn(a.tagName)&&t(a.innerHTML)){(ti=ti||document.createElement("div")).innerHTML=""+o+"";for(var l=ti.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;l.firstChild;)a.appendChild(l.firstChild)}else if(o!==s[i])try{a[i]=o}catch(e){}}}}function ii(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var r=e.value,i=e._vModifiers;if(n(i)){if(i.number)return f(r)!==f(t);if(i.trim)return r.trim()!==t.trim()}return r!==t}(e,t))}var oi={create:ri,update:ri},ai=g(function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach(function(e){if(e){var r=e.split(n);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t});function si(e){var t=ci(e.style);return e.staticStyle?A(e.staticStyle,t):t}function ci(e){return Array.isArray(e)?O(e):"string"==typeof e?ai(e):e}var ui,li=/^--/,fi=/\s*!important$/,pi=function(e,t,n){if(li.test(t))e.style.setProperty(t,n);else if(fi.test(n))e.style.setProperty(C(t),n.replace(fi,""),"important");else{var r=vi(t);if(Array.isArray(n))for(var i=0,o=n.length;i-1?t.split(yi).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function _i(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(yi).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function bi(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&A(t,$i(e.name||"v")),A(t,e),t}return"string"==typeof e?$i(e):void 0}}var $i=g(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),wi=z&&!W,Ci="transition",xi="animation",ki="transition",Ai="transitionend",Oi="animation",Si="animationend";wi&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(ki="WebkitTransition",Ai="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Oi="WebkitAnimation",Si="webkitAnimationEnd"));var Ti=z?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function Ei(e){Ti(function(){Ti(e)})}function Ni(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),gi(e,t))}function ji(e,t){e._transitionClasses&&h(e._transitionClasses,t),_i(e,t)}function Di(e,t,n){var r=Mi(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Ci?Ai:Si,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=Ci,l=a,f=o.length):t===xi?u>0&&(n=xi,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Ci:xi:null)?n===Ci?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Ci&&Li.test(r[ki+"Property"])}}function Ii(e,t){for(;e.length1}function Ui(e,t){!0!==t.data.show&&Pi(t)}var zi=function(e){var o,a,s={},c=e.modules,u=e.nodeOps;for(o=0;ov?_(e,t(i[y+1])?null:i[y+1].elm,i,d,y,o):d>y&&$(r,p,v)}(p,h,y,o,l):n(y)?(n(e.text)&&u.setTextContent(p,""),_(p,null,y,0,y.length-1,o)):n(h)?$(h,0,h.length-1):n(e.text)&&u.setTextContent(p,""):e.text!==i.text&&u.setTextContent(p,i.text),n(v)&&n(d=v.hook)&&n(d=d.postpatch)&&d(e,i)}}}function k(e,t,i){if(r(i)&&n(e.parent))e.parent.data.pendingInsert=t;else for(var o=0;o-1,a.selected!==o&&(a.selected=o);else if(N(Wi(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function qi(e,t){return t.every(function(t){return!N(t,e)})}function Wi(e){return"_value"in e?e._value:e.value}function Zi(e){e.target.composing=!0}function Gi(e){e.target.composing&&(e.target.composing=!1,Xi(e.target,"input"))}function Xi(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function Yi(e){return!e.componentInstance||e.data&&e.data.transition?e:Yi(e.componentInstance._vnode)}var Qi={model:Vi,show:{bind:function(e,t,n){var r=t.value,i=(n=Yi(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Pi(n,function(){e.style.display=o})):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;!r!=!t.oldValue&&((n=Yi(n)).data&&n.data.transition?(n.data.show=!0,r?Pi(n,function(){e.style.display=e.__vOriginalDisplay}):Ri(n,function(){e.style.display="none"})):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},eo={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function to(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?to(zt(t.children)):e}function no(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[b(o)]=i[o];return t}function ro(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var io=function(e){return e.tag||Ut(e)},oo=function(e){return"show"===e.name},ao={name:"transition",props:eo,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(io)).length){var r=this.mode,o=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return o;var a=to(o);if(!a)return o;if(this._leaving)return ro(e,o);var s="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=no(this),u=this._vnode,l=to(u);if(a.data.directives&&a.data.directives.some(oo)&&(a.data.show=!0),l&&l.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(a,l)&&!Ut(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=A({},c);if("out-in"===r)return this._leaving=!0,it(f,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),ro(e,o);if("in-out"===r){if(Ut(a))return u;var p,d=function(){p()};it(c,"afterEnter",d),it(c,"enterCancelled",d),it(f,"delayLeave",function(e){p=e})}}return o}}},so=A({tag:String,moveClass:String},eo);function co(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function uo(e){e.data.newPos=e.elm.getBoundingClientRect()}function lo(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete so.mode;var fo={Transition:ao,TransitionGroup:{props:so,beforeMount:function(){var e=this,t=this._update;this._update=function(n,r){var i=Zt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,i(),t.call(e,n,r)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=no(this),s=0;s-1?Gn[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:Gn[e]=/HTMLUnknownElement/.test(t.toString())},A(wn.options.directives,Qi),A(wn.options.components,fo),wn.prototype.__patch__=z?zi:S,wn.prototype.$mount=function(e,t){return function(e,t,n){var r;return e.$el=t,e.$options.render||(e.$options.render=ve),Yt(e,"beforeMount"),r=function(){e._update(e._render(),n)},new fn(e,r,S,{before:function(){e._isMounted&&!e._isDestroyed&&Yt(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,Yt(e,"mounted")),e}(this,e=e&&z?Yn(e):void 0,t)},z&&setTimeout(function(){F.devtools&&ne&&ne.emit("init",wn)},0);var po=/\{\{((?:.|\r?\n)+?)\}\}/g,vo=/[-.*+?^${}()|[\]\/\\]/g,ho=g(function(e){var t=e[0].replace(vo,"\\$&"),n=e[1].replace(vo,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")});var mo={staticKeys:["staticClass"],transformNode:function(e,t){t.warn;var n=Fr(e,"class");n&&(e.staticClass=JSON.stringify(n));var r=Ir(e,"class",!1);r&&(e.classBinding=r)},genData:function(e){var t="";return e.staticClass&&(t+="staticClass:"+e.staticClass+","),e.classBinding&&(t+="class:"+e.classBinding+","),t}};var yo,go={staticKeys:["staticStyle"],transformNode:function(e,t){t.warn;var n=Fr(e,"style");n&&(e.staticStyle=JSON.stringify(ai(n)));var r=Ir(e,"style",!1);r&&(e.styleBinding=r)},genData:function(e){var t="";return e.staticStyle&&(t+="staticStyle:"+e.staticStyle+","),e.styleBinding&&(t+="style:("+e.styleBinding+"),"),t}},_o=function(e){return(yo=yo||document.createElement("div")).innerHTML=e,yo.textContent},bo=p("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),$o=p("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),wo=p("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),Co=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,xo=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,ko="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+P.source+"]*",Ao="((?:"+ko+"\\:)?"+ko+")",Oo=new RegExp("^<"+Ao),So=/^\s*(\/?)>/,To=new RegExp("^<\\/"+Ao+"[^>]*>"),Eo=/^]+>/i,No=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Io=/&(?:lt|gt|quot|amp|#39);/g,Fo=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Po=p("pre,textarea",!0),Ro=function(e,t){return e&&Po(e)&&"\n"===t[0]};function Ho(e,t){var n=t?Fo:Io;return e.replace(n,function(e){return Mo[e]})}var Bo,Uo,zo,Vo,Ko,Jo,qo,Wo,Zo=/^@|^v-on:/,Go=/^v-|^@|^:|^#/,Xo=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,Yo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Qo=/^\(|\)$/g,ea=/^\[.*\]$/,ta=/:(.*)$/,na=/^:|^\.|^v-bind:/,ra=/\.[^.\]]+(?=[^\]]*$)/g,ia=/^v-slot(:|$)|^#/,oa=/[\r\n]/,aa=/\s+/g,sa=g(_o),ca="_empty_";function ua(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:ma(t),rawAttrsMap:{},parent:n,children:[]}}function la(e,t){Bo=t.warn||Sr,Jo=t.isPreTag||T,qo=t.mustUseProp||T,Wo=t.getTagNamespace||T;t.isReservedTag;zo=Tr(t.modules,"transformNode"),Vo=Tr(t.modules,"preTransformNode"),Ko=Tr(t.modules,"postTransformNode"),Uo=t.delimiters;var n,r,i=[],o=!1!==t.preserveWhitespace,a=t.whitespace,s=!1,c=!1;function u(e){if(l(e),s||e.processed||(e=fa(e,t)),i.length||e===n||n.if&&(e.elseif||e.else)&&da(n,{exp:e.elseif,block:e}),r&&!e.forbidden)if(e.elseif||e.else)a=e,(u=function(e){var t=e.length;for(;t--;){if(1===e[t].type)return e[t];e.pop()}}(r.children))&&u.if&&da(u,{exp:a.elseif,block:a});else{if(e.slotScope){var o=e.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[o]=e}r.children.push(e),e.parent=r}var a,u;e.children=e.children.filter(function(e){return!e.slotScope}),l(e),e.pre&&(s=!1),Jo(e.tag)&&(c=!1);for(var f=0;f]*>)","i")),p=e.replace(f,function(e,n,r){return u=r.length,Do(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),Ro(l,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});c+=e.length-p.length,e=p,A(l,c-u,c)}else{var d=e.indexOf("<");if(0===d){if(No.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),C(v+3);continue}}if(jo.test(e)){var h=e.indexOf("]>");if(h>=0){C(h+2);continue}}var m=e.match(Eo);if(m){C(m[0].length);continue}var y=e.match(To);if(y){var g=c;C(y[0].length),A(y[1],g,c);continue}var _=x();if(_){k(_),Ro(_.tagName,e)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(d>=0){for($=e.slice(d);!(To.test($)||Oo.test($)||No.test($)||jo.test($)||(w=$.indexOf("<",1))<0);)d+=w,$=e.slice(d);b=e.substring(0,d)}d<0&&(b=e),b&&C(b.length),t.chars&&b&&t.chars(b,c-b.length,c)}if(e===n){t.chars&&t.chars(e);break}}function C(t){c+=t,e=e.substring(t)}function x(){var t=e.match(Oo);if(t){var n,r,i={tagName:t[1],attrs:[],start:c};for(C(t[0].length);!(n=e.match(So))&&(r=e.match(xo)||e.match(Co));)r.start=c,C(r[0].length),r.end=c,i.attrs.push(r);if(n)return i.unarySlash=n[1],C(n[0].length),i.end=c,i}}function k(e){var n=e.tagName,c=e.unarySlash;o&&("p"===r&&wo(n)&&A(r),s(n)&&r===n&&A(n));for(var u=a(n)||!!c,l=e.attrs.length,f=new Array(l),p=0;p=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,n,o);i.length=a,r=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}A()}(e,{warn:Bo,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,o,a,l,f){var p=r&&r.ns||Wo(e);q&&"svg"===p&&(o=function(e){for(var t=[],n=0;nc&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=Ar(r[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+r[0].length}return c-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Mr(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Br(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Br(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Br(t,"$$c")+"}",null,!0)}(e,r,i);else if("input"===o&&"radio"===a)!function(e,t,n){var r=n&&n.number,i=Ir(e,"value")||"null";Er(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),Mr(e,"change",Br(t,i),null,!0)}(e,r,i);else if("input"===o||"textarea"===o)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Wr:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=Br(t,l);c&&(f="if($event.target.composing)return;"+f),Er(e,"value","("+t+")"),Mr(e,u,f,null,!0),(s||a)&&Mr(e,"blur","$forceUpdate()")}(e,r,i);else if(!F.isReservedTag(o))return Hr(e,r,i),!1;return!0},text:function(e,t){t.value&&Er(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Er(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:bo,mustUseProp:jn,canBeLeftOpenTag:$o,isReservedTag:Wn,getTagNamespace:Zn,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(ba)},xa=g(function(e){return p("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))});function ka(e,t){e&&($a=xa(t.staticKeys||""),wa=t.isReservedTag||T,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||d(e.tag)||!wa(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every($a)))}(t);if(1===t.type){if(!wa(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function(?:\s+[\w$]+)?\s*\(/,Oa=/\([^)]*?\);*$/,Sa=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Ta={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Ea={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Na=function(e){return"if("+e+")return null;"},ja={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Na("$event.target !== $event.currentTarget"),ctrl:Na("!$event.ctrlKey"),shift:Na("!$event.shiftKey"),alt:Na("!$event.altKey"),meta:Na("!$event.metaKey"),left:Na("'button' in $event && $event.button !== 0"),middle:Na("'button' in $event && $event.button !== 1"),right:Na("'button' in $event && $event.button !== 2")};function Da(e,t){var n=t?"nativeOn:":"on:",r="",i="";for(var o in e){var a=La(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":r+='"'+o+'":'+a+","}return r="{"+r.slice(0,-1)+"}",i?n+"_d("+r+",["+i.slice(0,-1)+"])":n+r}function La(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return La(e)}).join(",")+"]";var t=Sa.test(e.value),n=Aa.test(e.value),r=Sa.test(e.value.replace(Oa,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(ja[s])o+=ja[s],Ta[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=Na(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(Ma).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+"($event)":n?"return ("+e.value+")($event)":r?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(r?"return "+e.value:e.value)+"}"}function Ma(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=Ta[e],r=Ea[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var Ia={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:S},Fa=function(e){this.options=e,this.warn=e.warn||Sr,this.transforms=Tr(e.modules,"transformCode"),this.dataGenFns=Tr(e.modules,"genData"),this.directives=A(A({},Ia),e.directives);var t=e.isReservedTag||T;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Pa(e,t){var n=new Fa(t);return{render:"with(this){return "+(e?Ra(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function Ra(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return Ha(e,t);if(e.once&&!e.onceProcessed)return Ba(e,t);if(e.for&&!e.forProcessed)return za(e,t);if(e.if&&!e.ifProcessed)return Ua(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=qa(e,t),i="_t("+n+(r?","+r:""),o=e.attrs||e.dynamicAttrs?Ga((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:b(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)n=function(e,t,n){var r=t.inlineTemplate?null:qa(t,n,!0);return"_c("+e+","+Va(t,n)+(r?","+r:"")+")"}(e.component,e,t);else{var r;(!e.plain||e.pre&&t.maybeComponent(e))&&(r=Va(e,t));var i=e.inlineTemplate?null:qa(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];if(n&&1===n.type){var r=Pa(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+Ga(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ka(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ka))}function Ja(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Ua(e,t,Ja,"null");if(e.for&&!e.forProcessed)return za(e,t,Ja);var r=e.slotScope===ca?"":String(e.slotScope),i="function("+r+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(qa(e,t)||"undefined")+":undefined":qa(e,t)||"undefined":Ra(e,t))+"}",o=r?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function qa(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(r||Ra)(a,t)+s}var c=n?function(e,t){for(var n=0,r=0;r':'',ts.innerHTML.indexOf(" ")>0}var os=!!z&&is(!1),as=!!z&&is(!0),ss=g(function(e){var t=Yn(e);return t&&t.innerHTML}),cs=wn.prototype.$mount;return wn.prototype.$mount=function(e,t){if((e=e&&Yn(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=ss(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){var i=rs(r,{outputSourceRange:!1,shouldDecodeNewlines:os,shouldDecodeNewlinesForHref:as,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return cs.call(this,e,t)},wn.compile=rs,wn}); -------------------------------------------------------------------------------- /WeChatMomentExport/WeChatMomentExport.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5FD416B6-038F-4209-B97B-FF3181A9FB40} 8 | Exe 9 | WeChatMomentExport 10 | WeChatMomentExport 11 | v4.5 12 | 512 13 | true 14 | 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll 39 | 40 | 41 | ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll 42 | 43 | 44 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 45 | 46 | 47 | ..\packages\plist-cil.1.16.0\lib\net45\plist-cil.dll 48 | 49 | 50 | 51 | 52 | 53 | ..\packages\System.Data.SQLite.Core.1.0.109.2\lib\net45\System.Data.SQLite.dll 54 | 55 | 56 | ..\packages\System.Data.SQLite.EF6.1.0.109.0\lib\net45\System.Data.SQLite.EF6.dll 57 | 58 | 59 | ..\packages\System.Data.SQLite.Linq.1.0.109.0\lib\net45\System.Data.SQLite.Linq.dll 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Always 105 | 106 | 107 | Always 108 | 109 | 110 | Always 111 | 112 | 113 | Always 114 | 115 | 116 | Always 117 | 118 | 119 | Always 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /WeChatMomentExport/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 完全重构的iOS微信朋友圈导出 2 | WeChatMomentExport-iOS是用C#编写的朋友圈导出工具 3 | 4 | **重要:仅适用于iOS的朋友圈数据库,兼容iOS微信7.0.12** 5 | 6 | **重要:由于微信6.3.27之后对他人朋友圈的图片进行了加密(wxpc格式),所以暂时不支持导出他人朋友圈。** 7 | # 效果 8 | 9 | 10 | 11 | # 使用说明 12 | ## 从手机导出微信数据库 13 | 14 | **注意:使用前,打开微信,清空一下缓存(此步非必须,但是可以减少备份和拷贝所需的等待时间),然后直接打开自己的朋友圈,往下翻到最早的一条,将自己所有的朋友圈缓存到本地。如果不放心,可以翻页完成之后断网确认是否还能看到,能看到意味着已经缓存成功。** 15 | 16 | **从iOS8.3之后,苹果关闭了沙盒访问,所以无法直接访问微信的Document文件夹了。但是可以曲线救国,用iTunes或iMazing(推荐)备份手机数据,然后从备份数据中提取微信的Document内容。** 17 | 18 | 19 | 20 | 在微信的Document中,存在着至少一个以Hash字符串命名的文件夹(如果在这个手机上登陆过多个微信,则可能存在多个)。像这样的↓ 21 | > eb8a6093b56e2f1c27fbf471ee97c7f9 22 | 23 | 这样的文件夹中就存放着微信用户的个人数据。 24 | 25 | 拷贝wc文件夹下的wc005_008.db至本项目的Debug文件夹中(找不到的话,生成项目)即可。 26 | ## 导出朋友圈数据 27 | 修改Main函数中的初始化部分 28 | ```CS 29 | MomentExporterFacade exporterFacade = new MomentExporterFacade("这里改成自己的Hash字符串", true); 30 | ``` 31 | 32 | F5运行。 33 | 34 | 运行后,会有以下文件(夹)输出: 35 | 36 | 文件(夹) | 说明 37 | --|-- 38 | Plist\ | 存放自己发布过的所有朋友圈(wc005_008.db里导出的原始文件) 39 | Json\ | 存放所有解析好的朋友圈JSON文件 40 | View\LocalFile\ | 存放下载到本地的朋友圈中的文件(图片,视频之类的) 41 | View\static\script\data.js | 用于展示的朋友圈数据 42 | ## 导出完成 43 | 导出完成后,打开Debug\View\Index.html就能看到自己的朋友圈时间线。 44 | 45 | 由于本人前端技术非常的凑合,所以并没有弄出特别华丽的展示效果,有兴趣的朋友可以PullRequest。 46 | 47 | # 已知问题 48 | 1. 除了微视分享外,其他的分享内容没有做兼容,不能正确导出分享的内容。 49 | 2. 有的评论没有评论人昵称,但这个不是解析的问题,而是plist里确实没有。此时用评论人id进行了替代。 50 | 3. 有的评论没有评论人昵称和评论人id,同样的,plist里确实没有。此时此条评论丢弃,因为没法确定哪个是评论本体。 51 | 4. 有的朋友圈明明是发了一张图片,但是plist里的类型却是小视频,所以解析出来的内容也会有误。 52 | 5. 不能导出别人的朋友圈,只能导出自己的。 53 | 54 | # 其他 55 | 一些已知作用的文件(夹)。 56 | 57 | 文件(夹)名 | 作用 58 | ------|----- 59 | Audio | 语音消息的缓存 60 | DB\MM.sqlite|聊天记录数据库 61 | DB\WCDB_Contact.sqlite|通讯录数据库 62 | Img|聊天图片缓存 63 | Video|聊天小视频缓存 64 | wc\wc005_008.db|朋友圈缓存 65 | -------------------------------------------------------------------------------- /update1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mr0x01/WeChatMomentExport-iOS/eb8c97676abe6fab50f360c3a68ab032dee4c16a/update1.png --------------------------------------------------------------------------------
{{moment.momentId}}
{{moment.momentText}}