浏览代码

v0.9.4.0 Release!
1.导出HTML新增了文件与表情的支持,需要导出表情,请先使用表情预下载功能,由于略微做了一些频率限制,预下载可能会较久,请耐心等待。
2.调整了软件本身的显示逻辑
3.旧版消息工具已经移动至管理界面,如需使用,工作区,右键,管理即可

Suxue 1 年之前
父节点
当前提交
5ae3e6ef5d
共有 11 个文件被更改,包括 251 次插入127 次删除
  1. 64 34
      Export/HtmlExport.cs
  2. 49 42
      Export/TXTExport.cs
  3. 1 1
      Main2.xaml
  4. 1 0
      Model/WXModel.cs
  5. 5 2
      Pages/Manager.xaml
  6. 16 1
      Pages/Manager.xaml.cs
  7. 4 4
      Pages/Workspace.xaml
  8. 11 13
      Pages/Workspace.xaml.cs
  9. 13 1
      README.md
  10. 84 26
      WXUserReader.cs
  11. 3 3
      WechatBakTool.csproj

+ 64 - 34
Export/HtmlExport.cs

@@ -10,6 +10,7 @@ using WechatBakTool.Model;
 using System.Xml;
 using System.Xml;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
 using WechatBakTool.ViewModel;
 using WechatBakTool.ViewModel;
+using System.Security.Policy;
 
 
 namespace WechatBakTool.Export
 namespace WechatBakTool.Export
 {
 {
@@ -92,53 +93,82 @@ namespace WechatBakTool.Export
                     }
                     }
                     HtmlBody += string.Format("<p class=\"content\"><video controls style=\"max-height:300px;max-width:300px;\"><source src=\"{0}\" type=\"video/mp4\" /></video></p></div>", path);
                     HtmlBody += string.Format("<p class=\"content\"><video controls style=\"max-height:300px;max-width:300px;\"><source src=\"{0}\" type=\"video/mp4\" /></video></p></div>", path);
                 }
                 }
+                else if(msg.Type == 47)
+                {
+                    string? path = reader.GetAttachment(WXMsgType.Emoji, msg);
+                    if (path == null)
+                    {
+#if DEBUG
+                        File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Emoji Error Path=>", path));
+                        File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Emoji Error Msg=>", JsonConvert.SerializeObject(msg)));
+#endif
+                        HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "表情未预下载或加密表情");
+                        continue;
+                    }
+                    HtmlBody += string.Format("<p class=\"content\"><img src=\"{0}\" style=\"max-height:300px;max-width:300px;\"/></p></div>", path);
+                }
                 else if (msg.Type == 49)
                 else if (msg.Type == 49)
                 {
                 {
-                    using (var decoder = LZ4Decoder.Create(true, 64))
+                    if(msg.SubType == 6||msg.SubType == 19||msg.SubType == 40)
                     {
                     {
-                        byte[] target = new byte[10240];
-                        int res = 0;
-                        if (msg.CompressContent != null)
-                            res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
-
-                        byte[] data = target.Skip(0).Take(res).ToArray();
-                        string xml = Encoding.UTF8.GetString(data);
-                        if (!string.IsNullOrEmpty(xml))
+                        string? path = reader.GetAttachment(WXMsgType.File, msg);
+                        if(path == null)
+                        {
+                            HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "文件不存在");
+                            continue;
+                        }
+                        else
                         {
                         {
-                            xml = xml.Replace("\n", "");
-                            XmlDocument xmlObj = new XmlDocument();
-                            xmlObj.LoadXml(xml);
-                            if (xmlObj.DocumentElement != null)
+                            HtmlBody += string.Format("<p class=\"content\">{0}</p><p><a href=\"{1}\">点击访问</a></p></div>", "文件:" + path, path);
+                        }
+                    }
+                    else
+                    {
+                        using (var decoder = LZ4Decoder.Create(true, 64))
+                        {
+                            byte[] target = new byte[10240];
+                            int res = 0;
+                            if (msg.CompressContent != null)
+                                res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
+
+                            byte[] data = target.Skip(0).Take(res).ToArray();
+                            string xml = Encoding.UTF8.GetString(data);
+                            if (!string.IsNullOrEmpty(xml))
                             {
                             {
-                                string title = "";
-                                string appName = "";
-                                string url = "";
-                                XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
-                                if (findNode != null)
+                                xml = xml.Replace("\n", "");
+                                XmlDocument xmlObj = new XmlDocument();
+                                xmlObj.LoadXml(xml);
+                                if (xmlObj.DocumentElement != null)
                                 {
                                 {
-                                    if (findNode.Count > 0)
+                                    string title = "";
+                                    string appName = "";
+                                    string url = "";
+                                    XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
+                                    if (findNode != null)
                                     {
                                     {
-                                        title = findNode[0]!.InnerText;
+                                        if (findNode.Count > 0)
+                                        {
+                                            title = findNode[0]!.InnerText;
+                                        }
                                     }
                                     }
-                                }
-                                findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
-                                if (findNode != null)
-                                {
-                                    if (findNode.Count > 0)
+                                    findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
+                                    if (findNode != null)
                                     {
                                     {
-                                        appName = findNode[0]!.InnerText;
+                                        if (findNode.Count > 0)
+                                        {
+                                            appName = findNode[0]!.InnerText;
+                                        }
                                     }
                                     }
-                                }
-                                findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
-                                if (findNode != null)
-                                {
-                                    if (findNode.Count > 0)
+                                    findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
+                                    if (findNode != null)
                                     {
                                     {
-                                        url = findNode[0]!.InnerText;
+                                        if (findNode.Count > 0)
+                                        {
+                                            url = findNode[0]!.InnerText;
+                                        }
                                     }
                                     }
+                                    HtmlBody += string.Format("<p class=\"content\">{0}|{1}</p><p><a href=\"{2}\">点击访问</a></p></div>", appName, title, url);
                                 }
                                 }
-                                HtmlBody += string.Format("<p class=\"content\">{0}|{1}</p><p><a href=\"{2}\">点击访问</a></p></div>", appName, title, url);
-
                             }
                             }
                         }
                         }
                     }
                     }

+ 49 - 42
Export/TXTExport.cs

@@ -70,67 +70,74 @@ namespace WechatBakTool.Export
                         txtMsg = "[视频]";
                         txtMsg = "[视频]";
                         break;
                         break;
                     case 49:
                     case 49:
-                        try
+                        if (msg.SubType == 6 || msg.SubType == 19 || msg.SubType == 40)
                         {
                         {
-                            using (var decoder = LZ4Decoder.Create(true, 64))
+                            txtMsg = "[文件]";
+                        }
+                        else
+                        {
+                            try
                             {
                             {
-                                byte[] target = new byte[10240];
-                                int res = 0;
-                                if (msg.CompressContent != null)
-                                    res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
-
-                                byte[] data = target.Skip(0).Take(res).ToArray();
-                                string xml = Encoding.UTF8.GetString(data);
-                                if (!string.IsNullOrEmpty(xml))
+                                using (var decoder = LZ4Decoder.Create(true, 64))
                                 {
                                 {
-                                    xml = xml.Replace("\n", "");
-                                    XmlDocument xmlObj = new XmlDocument();
-                                    xmlObj.LoadXml(xml);
-                                    if (xmlObj.DocumentElement != null)
+                                    byte[] target = new byte[10240];
+                                    int res = 0;
+                                    if (msg.CompressContent != null)
+                                        res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
+
+                                    byte[] data = target.Skip(0).Take(res).ToArray();
+                                    string xml = Encoding.UTF8.GetString(data);
+                                    if (!string.IsNullOrEmpty(xml))
                                     {
                                     {
-                                        string title = "";
-                                        string appName = "";
-                                        string url = "";
-                                        XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
-                                        if (findNode != null)
+                                        xml = xml.Replace("\n", "");
+                                        XmlDocument xmlObj = new XmlDocument();
+                                        xmlObj.LoadXml(xml);
+                                        if (xmlObj.DocumentElement != null)
                                         {
                                         {
-                                            if (findNode.Count > 0)
+                                            string title = "";
+                                            string appName = "";
+                                            string url = "";
+                                            XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
+                                            if (findNode != null)
                                             {
                                             {
-                                                title = findNode[0]!.InnerText;
+                                                if (findNode.Count > 0)
+                                                {
+                                                    title = findNode[0]!.InnerText;
+                                                }
                                             }
                                             }
-                                        }
-                                        findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
-                                        if (findNode != null)
-                                        {
-                                            if (findNode.Count > 0)
+                                            findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
+                                            if (findNode != null)
                                             {
                                             {
-                                                appName = findNode[0]!.InnerText;
+                                                if (findNode.Count > 0)
+                                                {
+                                                    appName = findNode[0]!.InnerText;
+                                                }
                                             }
                                             }
-                                        }
-                                        findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
-                                        if (findNode != null)
-                                        {
-                                            if (findNode.Count > 0)
+                                            findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
+                                            if (findNode != null)
                                             {
                                             {
-                                                url = findNode[0]!.InnerText;
+                                                if (findNode.Count > 0)
+                                                {
+                                                    url = findNode[0]!.InnerText;
+                                                }
                                             }
                                             }
+                                            txtMsg = string.Format("{0},标题:{1},链接:{2}", appName, title, url);
+                                        }
+                                        else
+                                        {
+                                            txtMsg = "[分享链接出错了]";
                                         }
                                         }
-                                        txtMsg = string.Format("{0},标题:{1},链接:{2}", appName, title, url);
                                     }
                                     }
                                     else
                                     else
                                     {
                                     {
                                         txtMsg = "[分享链接出错了]";
                                         txtMsg = "[分享链接出错了]";
                                     }
                                     }
                                 }
                                 }
-                                else
-                                {
-                                    txtMsg = "[分享链接出错了]";
-                                }
                             }
                             }
-                        }
-                        catch
-                        {
-                            txtMsg = "[分享链接出错了]";
+                            catch
+                            {
+                                txtMsg = "[分享链接出错了]";
+                            }
                         }
                         }
                         break;
                         break;
                 }
                 }

+ 1 - 1
Main2.xaml

@@ -5,7 +5,7 @@
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:local="clr-namespace:WechatBakTool"
         xmlns:local="clr-namespace:WechatBakTool"
         mc:Ignorable="d" WindowStartupLocation="CenterScreen" WindowStyle="None" WindowState="Normal" Background="Transparent" AllowsTransparency="True" ResizeMode="NoResize"
         mc:Ignorable="d" WindowStartupLocation="CenterScreen" WindowStyle="None" WindowState="Normal" Background="Transparent" AllowsTransparency="True" ResizeMode="NoResize"
-        Title="Main2" Height="550" Width="950"  >
+        Title="WechatBakTool" Height="550" Width="950"  >
     <Window.Resources>
     <Window.Resources>
         <Style TargetType="local:Main2">
         <Style TargetType="local:Main2">
             <!-- 设置窗体的WindowChrome -->
             <!-- 设置窗体的WindowChrome -->

+ 1 - 0
Model/WXModel.cs

@@ -128,6 +128,7 @@ namespace WechatBakTool.Model
         public string StrTalker { get; set; } = "";
         public string StrTalker { get; set; } = "";
         [Column("StrContent")]
         [Column("StrContent")]
         public string StrContent { get; set; } = "";
         public string StrContent { get; set; } = "";
+        public string DisplayContent { get; set; } = "";
         [Column("CompressContent")]
         [Column("CompressContent")]
         public byte[]? CompressContent { get; set; }
         public byte[]? CompressContent { get; set; }
         [Column("BytesExtra")]
         [Column("BytesExtra")]

+ 5 - 2
Pages/Manager.xaml

@@ -15,8 +15,11 @@
         <Button Name="btn_export_all" Margin="35,110" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_export_all_Click"></Button>
         <Button Name="btn_export_all" Margin="35,110" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_export_all_Click"></Button>
         <Label Content="{Binding LabelStatus}" HorizontalAlignment="Left" Margin="110,110,0,0" VerticalAlignment="Top"/>
         <Label Content="{Binding LabelStatus}" HorizontalAlignment="Left" Margin="110,110,0,0" VerticalAlignment="Top"/>
 
 
-        <Label Margin="30,155" Content="表情包预下载" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
-        <Button Name="btn_emoji_download" Margin="35,185" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_emoji_download_Click"></Button>
+        <Label Margin="30,155,0,0" Content="表情包预下载" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
+        <Button Name="btn_emoji_download" Margin="35,185,0,0" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_emoji_download_Click"></Button>
+
+        <Label Margin="30,225,0,0" Content="旧版分析工具" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
+        <Button Name="btn_analyse" Margin="35,255,0,0" Height="26" Width="60" Content="打开" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_analyse_Click" ></Button>
 
 
     </Grid>
     </Grid>
 </Page>
 </Page>

+ 16 - 1
Pages/Manager.xaml.cs

@@ -95,8 +95,23 @@ namespace WechatBakTool.Pages
         {
         {
             if (UserReader != null)
             if (UserReader != null)
             {
             {
-                UserReader.PreDownloadEmoji();
+                Task.Run(() =>
+                {
+                    UserReader.PreDownloadEmoji();
+                    MessageBox.Show("所有表情预下载完毕");
+                });
+            }
+        }
+
+        private void btn_analyse_Click(object sender, RoutedEventArgs e)
+        {
+            if (UserReader == null || Main2.CurrentUserBakConfig == null)
+            {
+                MessageBox.Show("请先读取数据");
+                return;
             }
             }
+            Analyse analyse = new Analyse(Main2.CurrentUserBakConfig, UserReader);
+            analyse.Show();
         }
         }
     }
     }
 }
 }

+ 4 - 4
Pages/Workspace.xaml

@@ -126,7 +126,7 @@
                 </Setter.Value>
                 </Setter.Value>
             </Setter>
             </Setter>
         </Style>
         </Style>
-        
+
         <!-- 这里是listview滚动条的滑动块部分样式-->
         <!-- 这里是listview滚动条的滑动块部分样式-->
         <Style x:Key="ScrollBarThumbVertical" TargetType="{x:Type Thumb}">
         <Style x:Key="ScrollBarThumbVertical" TargetType="{x:Type Thumb}">
             <Setter Property="OverridesDefaultStyle" Value="true"/>
             <Setter Property="OverridesDefaultStyle" Value="true"/>
@@ -158,7 +158,7 @@
             <Grid Margin="0">
             <Grid Margin="0">
                 <Label Margin="60,8,0,0" FontWeight="Bold" VerticalAlignment="Top" Content="{Binding NickName}"/>
                 <Label Margin="60,8,0,0" FontWeight="Bold" VerticalAlignment="Top" Content="{Binding NickName}"/>
                 <Label Margin="60,25,0,8" VerticalAlignment="Top" HorizontalAlignment="Left" Width="380">
                 <Label Margin="60,25,0,8" VerticalAlignment="Top" HorizontalAlignment="Left" Width="380">
-                    <TextBlock Text="{Binding StrContent}" TextWrapping="Wrap"  />
+                    <TextBlock Text="{Binding DisplayContent}" TextWrapping="Wrap"  />
                 </Label>
                 </Label>
             </Grid>
             </Grid>
         </DataTemplate>
         </DataTemplate>
@@ -215,7 +215,7 @@
         </ListView>
         </ListView>
         <Label Content="{Binding WXContact.NickName}" HorizontalAlignment="Left" Margin="258,21,0,0" VerticalAlignment="Top"/>
         <Label Content="{Binding WXContact.NickName}" HorizontalAlignment="Left" Margin="258,21,0,0" VerticalAlignment="Top"/>
         <ListView x:Name="list_msg" Margin="230,60,0,60" Background="Transparent" BorderThickness="0,1,0,1" BorderBrush="#BB2775b6" ItemTemplate="{DynamicResource MsgText}">
         <ListView x:Name="list_msg" Margin="230,60,0,60" Background="Transparent" BorderThickness="0,1,0,1" BorderBrush="#BB2775b6" ItemTemplate="{DynamicResource MsgText}">
-            
+
         </ListView>
         </ListView>
         <ComboBox Name="cb_export" Width="120" Height="30" Style="{StaticResource ComboBoxStyle}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="30,15" ItemsSource="{Binding ExportItems}" SelectedItem="{Binding SelectExportItem}" DisplayMemberPath="Name" SelectedValuePath="Value" IsEnabled="{Binding SelectContact}" Background="#2775b6" />
         <ComboBox Name="cb_export" Width="120" Height="30" Style="{StaticResource ComboBoxStyle}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="30,15" ItemsSource="{Binding ExportItems}" SelectedItem="{Binding SelectExportItem}" DisplayMemberPath="Name" SelectedValuePath="Value" IsEnabled="{Binding SelectContact}" Background="#2775b6" />
         <Button x:Name="btn_open_workspace" Width="80" Height="30" Style="{StaticResource  ButtonStyle}" Content="打开文件夹" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,390,15" Click="btn_open_workspace_Click">
         <Button x:Name="btn_open_workspace" Width="80" Height="30" Style="{StaticResource  ButtonStyle}" Content="打开文件夹" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,390,15" Click="btn_open_workspace_Click">
@@ -225,7 +225,7 @@
                 </Style>
                 </Style>
             </Button.Resources>
             </Button.Resources>
         </Button>
         </Button>
-        <Button x:Name="btn_analyse" Width="80" Height="30" Style="{StaticResource  ButtonStyle}" Content="旧版消息工具" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,292,15" Click="btn_analyse_Click" >
+        <Button x:Name="btn_pre_emoji" Width="80" Height="30" Style="{StaticResource  ButtonStyle}" Content="表情预下载" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,292,15" IsEnabled="{Binding SelectContact}" Click="btn_pre_emoji_Click" >
             <Button.Resources>
             <Button.Resources>
                 <Style TargetType="{x:Type Border}">
                 <Style TargetType="{x:Type Border}">
                     <Setter Property="CornerRadius" Value="3"/>
                     <Setter Property="CornerRadius" Value="3"/>

+ 11 - 13
Pages/Workspace.xaml.cs

@@ -114,23 +114,10 @@ namespace WechatBakTool.Pages
             Process.Start("explorer.exe ", Main2.CurrentUserBakConfig!.UserWorkspacePath);
             Process.Start("explorer.exe ", Main2.CurrentUserBakConfig!.UserWorkspacePath);
         }
         }
 
 
-        private void btn_analyse_Click(object sender, RoutedEventArgs e)
-        {
-            if (UserReader == null || Main2.CurrentUserBakConfig == null)
-            {
-                MessageBox.Show("请先读取数据");
-                return;
-            }
-            Analyse analyse = new Analyse(Main2.CurrentUserBakConfig, UserReader);
-            analyse.Show();
-        }
-
         private void Export_Click(object sender, RoutedEventArgs e)
         private void Export_Click(object sender, RoutedEventArgs e)
         {
         {
             Task.Run(() =>
             Task.Run(() =>
             {
             {
-                
-
                 if (ViewModel.WXContact == null || UserReader == null)
                 if (ViewModel.WXContact == null || UserReader == null)
                 {
                 {
                     MessageBox.Show("请选择联系人", "错误");
                     MessageBox.Show("请选择联系人", "错误");
@@ -170,5 +157,16 @@ namespace WechatBakTool.Pages
             });
             });
             
             
         }
         }
+
+        private void btn_pre_emoji_Click(object sender, RoutedEventArgs e)
+        {
+            if(UserReader != null && ViewModel.WXContact != null)
+            {
+                Task.Run(() => {
+                    UserReader.PreDownloadEmoji(ViewModel.WXContact.UserName);
+                    MessageBox.Show("用户所有表情预下载完毕");
+                });
+            }
+        }
     }
     }
 }
 }

+ 13 - 1
README.md

@@ -24,12 +24,16 @@
 <br/>
 <br/>
 
 
 ### 免责声明
 ### 免责声明
-
 **本项目仅供学习使用,严禁商业使用**<br/>
 **本项目仅供学习使用,严禁商业使用**<br/>
+**本项目完全免费,问你要钱的都是骗子**<br/>
 **使用本项目初衷是作者研究微信数据库的运行使用,您使用本软件导致的后果,包含但不限于数据损坏,记录丢失等问题,作者不承担相关责任。**<br/>
 **使用本项目初衷是作者研究微信数据库的运行使用,您使用本软件导致的后果,包含但不限于数据损坏,记录丢失等问题,作者不承担相关责任。**<br/>
 **因软件特殊性质,请在使用时获得微信账号所有人授权。**
 **因软件特殊性质,请在使用时获得微信账号所有人授权。**
 <br/>
 <br/>
 
 
+### 隐私声明
+**本项目不会上传任何你的数据至任何第三方系统**<br/>
+**如果发生任何回传行为,请检查是否为第三方修改版本**<br/>
+
 ### 近期开发规划
 ### 近期开发规划
 本项目技术栈为:
 本项目技术栈为:
 C# + .NET6.0 + WPF MVVM(目前MVVM不是特别完全!莫喷!) <br/>
 C# + .NET6.0 + WPF MVVM(目前MVVM不是特别完全!莫喷!) <br/>
@@ -40,6 +44,14 @@ C# + .NET6.0 + WPF MVVM(目前MVVM不是特别完全!莫喷!) <br/>
 - [ ] 手动模式(合适离线分析)
 - [ ] 手动模式(合适离线分析)
 <br/>
 <br/>
 
 
+### 部分问题
+Q:支持手机端吗<br/>
+A:<b>在手机端</b>使用迁移功能即可,路径:我->设置->聊天->聊天记录迁移与备份->迁移<br/>
+<br/>
+Q:怎么导出全部的记录<br/>
+A:工作区->右键->管理,就见了。<br/>
+<br/>
+
 ### 使用说明
 ### 使用说明
 **本说明为新版本说明,即将发版**<br/>
 **本说明为新版本说明,即将发版**<br/>
 0.安装.NET Desktop Runtime(如已经安装忽略)<br/>
 0.安装.NET Desktop Runtime(如已经安装忽略)<br/>

+ 84 - 26
WXUserReader.cs

@@ -21,6 +21,7 @@ using System.Windows;
 using System.Net.Http;
 using System.Net.Http;
 using System.Reflection.Metadata;
 using System.Reflection.Metadata;
 using System.Threading;
 using System.Threading;
+using Newtonsoft.Json;
 
 
 namespace WechatBakTool
 namespace WechatBakTool
 {
 {
@@ -37,6 +38,7 @@ namespace WechatBakTool
             UserBakConfig = userBakConfig;
             UserBakConfig = userBakConfig;
             LoadDB(path);
             LoadDB(path);
             InitCache();
             InitCache();
+            EmojiCacheInit();
         }
         }
 
 
         public void LoadDB(string path)
         public void LoadDB(string path)
@@ -107,7 +109,7 @@ namespace WechatBakTool
             }
             }
         }
         }
 
 
-        public void PreDownloadEmoji()
+        public void PreDownloadEmoji(string username = "")
         {
         {
             if (UserBakConfig == null)
             if (UserBakConfig == null)
                 return;
                 return;
@@ -115,11 +117,10 @@ namespace WechatBakTool
             HttpClientHandler handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate };
             HttpClientHandler handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate };
             HttpClient httpClient = new HttpClient(handler);
             HttpClient httpClient = new HttpClient(handler);
 
 
-            List<WXMsg> msgs = GetTypeMsg("47");
+            List<WXMsg> msgs = GetTypeMsg("47", username);
             int i = 0;
             int i = 0;
 
 
-            EmojiCacheInit();
-
+            // 下载前的Emoji Cache不用做了,在Init的时候已经做了
             foreach (var msg in msgs)
             foreach (var msg in msgs)
             {
             {
                 i++;
                 i++;
@@ -150,14 +151,18 @@ namespace WechatBakTool
                             url = item != null ? item.InnerText : "";
                             url = item != null ? item.InnerText : "";
 
 
                             if (EmojiCache.ContainsKey(md5))
                             if (EmojiCache.ContainsKey(md5))
+                            {
+                                i--;
                                 continue;
                                 continue;
-
+                            }
                             if (url == "")
                             if (url == "")
+                            {
+                                i--;
                                 continue;
                                 continue;
-
+                            }
                             else
                             else
                             {
                             {
-                                string path = Path.Combine(UserBakConfig.UserWorkspacePath, "Emoji", md5 + ".jpg");
+                                string path = Path.Combine(UserBakConfig.UserWorkspacePath, "Emoji", md5 + ".gif");
                                 try
                                 try
                                 {
                                 {
                                     HttpResponseMessage res = httpClient.GetAsync(url).Result;
                                     HttpResponseMessage res = httpClient.GetAsync(url).Result;
@@ -182,6 +187,9 @@ namespace WechatBakTool
 
 
                 }
                 }
             }
             }
+
+            // 下载完成后可能变化,检查一下
+            EmojiCacheInit();
         }
         }
 
 
         public byte[]? GetHeadImgCahce(string username)
         public byte[]? GetHeadImgCahce(string username)
@@ -298,7 +306,7 @@ namespace WechatBakTool
             return con.Query<WXChatRoom>(query);
             return con.Query<WXChatRoom>(query);
         }
         }
 
 
-        public List<WXMsg> GetTypeMsg(string type)
+        public List<WXMsg> GetTypeMsg(string type,string username)
         {
         {
             List<WXMsg> tmp = new List<WXMsg>();
             List<WXMsg> tmp = new List<WXMsg>();
             for (int i = 0; i <= 99; i++)
             for (int i = 0; i <= 99; i++)
@@ -307,8 +315,17 @@ namespace WechatBakTool
                 if (con == null)
                 if (con == null)
                     return tmp;
                     return tmp;
 
 
-                string query = "select * from MSG where Type=?";
-                List<WXMsg> wXMsgs = con.Query<WXMsg>(query, type);
+                List<WXMsg> wXMsgs;
+                if (username == "")
+                {
+                    string query = "select * from MSG where Type=?";
+                    wXMsgs = con.Query<WXMsg>(query, type);
+                }
+                else
+                {
+                    string query = "select * from MSG where Type=? and StrTalker = ?";
+                    wXMsgs = con.Query<WXMsg>(query, type, username);
+                }
                 tmp.AddRange(wXMsgs);
                 tmp.AddRange(wXMsgs);
             }
             }
             return tmp;
             return tmp;
@@ -351,9 +368,9 @@ namespace WechatBakTool
                             else
                             else
                                 w.NickName = contact.NickName;
                                 w.NickName = contact.NickName;
                         }
                         }
-
                     }
                     }
 
 
+                    // 群聊处理
                     if (uid.Contains("@chatroom"))
                     if (uid.Contains("@chatroom"))
                     {
                     {
                         string userId = "";
                         string userId = "";
@@ -393,20 +410,37 @@ namespace WechatBakTool
                             }
                             }
                         }
                         }
                     }
                     }
+                    
+                    
+                    // 发送人名字处理
                     if (w.IsSender)
                     if (w.IsSender)
                         w.NickName = "我";
                         w.NickName = "我";
 
 
+                    w.DisplayContent = w.StrContent;
+                    // 额外格式处理
                     if (w.Type != 1)
                     if (w.Type != 1)
                     {
                     {
                         if (w.Type == 10000)
                         if (w.Type == 10000)
                         {
                         {
                             w.Type = 1;
                             w.Type = 1;
                             w.NickName = "系统消息";
                             w.NickName = "系统消息";
-                            w.StrContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
+                            w.DisplayContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
+                        }
+                        else if (w.Type == 49 && (w.SubType == 6 || w.SubType == 19 || w.SubType == 40))
+                        {
+                            WXSessionAttachInfo? attachInfos = GetWXMsgAtc(w);
+                            if (attachInfos == null)
+                            {
+                                w.DisplayContent = "附件不存在";
+                            }
+                            else
+                            {
+                                w.DisplayContent = Path.Combine(UserBakConfig!.UserResPath, attachInfos.attachPath);
+                            }
                         }
                         }
                         else
                         else
                         {
                         {
-                            w.StrContent = "[界面未支持格式]Type=" + w.Type;
+                            w.DisplayContent = "[界面未支持格式]Type=" + w.Type;
                         }
                         }
                     }
                     }
                     tmp.Add(w);
                     tmp.Add(w);
@@ -481,9 +515,10 @@ namespace WechatBakTool
             if (!Directory.Exists(tmpPath))
             if (!Directory.Exists(tmpPath))
                 Directory.CreateDirectory(tmpPath);
                 Directory.CreateDirectory(tmpPath);
 
 
+            // 这部分是查找
             // 如果是图片和视频,从附件库中搜索
             // 如果是图片和视频,从附件库中搜索
             string? path = null;
             string? path = null;
-            if (type == WXMsgType.Image || type == WXMsgType.Video)
+            if (type == WXMsgType.Image || type == WXMsgType.Video || type == WXMsgType.File)
             {
             {
                 WXSessionAttachInfo? atcInfo = GetWXMsgAtc(msg);
                 WXSessionAttachInfo? atcInfo = GetWXMsgAtc(msg);
                 if (atcInfo == null)
                 if (atcInfo == null)
@@ -507,39 +542,61 @@ namespace WechatBakTool
                 }
                 }
                 path = tmp_file_path;
                 path = tmp_file_path;
             }
             }
+            else if (type == WXMsgType.Emoji)
+            {
+                XmlDocument xmlDocument = new XmlDocument();
+                xmlDocument.LoadXml(msg.StrContent);
+                XmlNode? node = xmlDocument.SelectSingleNode("/msg/emoji");
+                if (node != null)
+                {
+                    if (node.Attributes != null)
+                    {
+                        XmlNode? item = node.Attributes.GetNamedItem("md5");
+                        string md5 = item != null ? item.InnerText : "";
+                        if (EmojiCache.ContainsKey(md5))
+                        {
+                            path = string.Format("Emoji\\{0}.gif", md5);
+                        }
+                    }
+                }
+            }
 
 
             if (path == null)
             if (path == null)
                 return null;
                 return null;
 
 
+            // 这部分是解密
             // 获取到原路径后,开始进行解密转移,只有图片和语音需要解密,解密后是直接归档目录
             // 获取到原路径后,开始进行解密转移,只有图片和语音需要解密,解密后是直接归档目录
-            if(type == WXMsgType.Image || type== WXMsgType.Audio)
+            if (type == WXMsgType.Image || type == WXMsgType.Audio)
             {
             {
                 path = DecryptAttachment(type, path);
                 path = DecryptAttachment(type, path);
             }
             }
-            else if (type == WXMsgType.Video)
+            else if (type == WXMsgType.Video || type == WXMsgType.File)
             {
             {
-                string video_dir = Path.Combine(UserBakConfig.UserWorkspacePath, "Video");
-                if(!Directory.Exists(video_dir))
-                    Directory.CreateDirectory(video_dir);
+                string to_dir;
+                if (type == WXMsgType.Video)
+                    to_dir = Path.Combine(UserBakConfig.UserWorkspacePath, "Video");
+                else
+                    to_dir = Path.Combine(UserBakConfig.UserWorkspacePath, "File");
+                if (!Directory.Exists(to_dir))
+                    Directory.CreateDirectory(to_dir);
                 FileInfo fileInfo = new FileInfo(path);
                 FileInfo fileInfo = new FileInfo(path);
                 // 目标视频路径
                 // 目标视频路径
-                string video_file_path = Path.Combine(video_dir, fileInfo.Name);
+                string to_file_path = Path.Combine(to_dir, fileInfo.Name);
                 // 视频的路径是相对路径,需要加上资源目录
                 // 视频的路径是相对路径,需要加上资源目录
                 path = Path.Combine(UserBakConfig.UserResPath, path);
                 path = Path.Combine(UserBakConfig.UserResPath, path);
                 // 原文件存在,目标不存在
                 // 原文件存在,目标不存在
-                if (!File.Exists(video_file_path) && File.Exists(path))
+                if (!File.Exists(to_file_path) && File.Exists(path))
                 {
                 {
                     // 复制
                     // 复制
-                    File.Copy(path, video_file_path);
-                    path = video_file_path;
+                    File.Copy(path, to_file_path);
+                    path = to_file_path;
                 }
                 }
-                else if (File.Exists(video_file_path))
+                else if (File.Exists(to_file_path))
                 {
                 {
-                    path = video_file_path;
+                    path = to_file_path;
                 }
                 }
                 else
                 else
                     return null;
                     return null;
-                    
             }
             }
 
 
             if (path == null)
             if (path == null)
@@ -611,5 +668,6 @@ namespace WechatBakTool
         Video = 1,
         Video = 1,
         Audio = 2,
         Audio = 2,
         File = 3,
         File = 3,
+        Emoji = 4,
     }
     }
 }
 }

+ 3 - 3
WechatBakTool.csproj

@@ -6,9 +6,9 @@
     <Nullable>enable</Nullable>
     <Nullable>enable</Nullable>
     <UseWPF>true</UseWPF>
     <UseWPF>true</UseWPF>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
-    <AssemblyVersion>0.9.3.0</AssemblyVersion>
-    <FileVersion>0.9.3.0</FileVersion>
-    <Version>0.9.3.0</Version>
+    <AssemblyVersion>0.9.4.0</AssemblyVersion>
+    <FileVersion>0.9.4.0</FileVersion>
+    <Version>0.9.4.0</Version>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>