1. 优化头像显示,优先使用本地头像显示
2. 支持自定义导出路径、增加对导出路径的权限检测 3. 增加联系人显示 4. 滚动条滑块调整小大 5. 支持已经打开日志所在文件夹、日志增加回滚功能
This commit is contained in:
parent
073d586aea
commit
331cd8125d
238
app.go
238
app.go
@ -5,8 +5,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"wechatDataBackup/pkg/utils"
|
||||
"wechatDataBackup/pkg/wechat"
|
||||
|
||||
@ -18,9 +20,37 @@ const (
|
||||
defaultConfig = "config"
|
||||
configDefaultUserKey = "userConfig.defaultUser"
|
||||
configUsersKey = "userConfig.users"
|
||||
appVersion = "v1.0.3"
|
||||
configExportPathKey = "exportPath"
|
||||
appVersion = "v1.0.4"
|
||||
)
|
||||
|
||||
type FileLoader struct {
|
||||
http.Handler
|
||||
FilePrefix string
|
||||
}
|
||||
|
||||
func NewFileLoader(prefix string) *FileLoader {
|
||||
return &FileLoader{FilePrefix: prefix}
|
||||
}
|
||||
|
||||
func (h *FileLoader) SetFilePrefix(prefix string) {
|
||||
h.FilePrefix = prefix
|
||||
log.Println("SetFilePrefix", h.FilePrefix)
|
||||
}
|
||||
|
||||
func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||
var err error
|
||||
requestedFilename := h.FilePrefix + "\\" + strings.TrimPrefix(req.URL.Path, "/")
|
||||
// log.Println("Requesting file:", requestedFilename)
|
||||
fileData, err := os.ReadFile(requestedFilename)
|
||||
if err != nil {
|
||||
res.WriteHeader(http.StatusBadRequest)
|
||||
res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))
|
||||
}
|
||||
|
||||
res.Write(fileData)
|
||||
}
|
||||
|
||||
// App struct
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
@ -29,6 +59,8 @@ type App struct {
|
||||
defaultUser string
|
||||
users []string
|
||||
firstStart bool
|
||||
|
||||
FLoader *FileLoader
|
||||
}
|
||||
|
||||
type WeChatInfo struct {
|
||||
@ -51,21 +83,38 @@ type WeChatAccountInfos struct {
|
||||
Total int `json:"Total"`
|
||||
}
|
||||
|
||||
type ErrorMessage struct {
|
||||
ErrorStr string `json:"error"`
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
func NewApp() *App {
|
||||
a := &App{}
|
||||
|
||||
a.FLoader = NewFileLoader(".\\")
|
||||
viper.SetConfigName(defaultConfig)
|
||||
viper.SetConfigType("json")
|
||||
viper.AddConfigPath(".")
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
a.defaultUser = viper.GetString(configDefaultUserKey)
|
||||
a.users = viper.GetStringSlice(configUsersKey)
|
||||
prefix := viper.GetString(configExportPathKey)
|
||||
if prefix != "" {
|
||||
log.Println("SetFilePrefix", prefix)
|
||||
a.FLoader.SetFilePrefix(prefix)
|
||||
}
|
||||
|
||||
a.scanAccountByPath(prefix)
|
||||
// log.Println(a.defaultUser)
|
||||
// log.Println(a.users)
|
||||
} else {
|
||||
if a.scanAccountByPath(".\\") != nil {
|
||||
log.Println("not config exist")
|
||||
}
|
||||
}
|
||||
log.Printf("default: %s users: %v\n", a.defaultUser, a.users)
|
||||
if len(a.users) == 0 {
|
||||
a.firstStart = true
|
||||
log.Println("not config exist")
|
||||
}
|
||||
|
||||
return a
|
||||
@ -93,6 +142,11 @@ func (a *App) GetWeChatAllInfo() string {
|
||||
infoList.Info = make([]WeChatInfo, 0)
|
||||
infoList.Total = 0
|
||||
|
||||
if a.provider != nil {
|
||||
a.provider.WechatWechatDataProviderClose()
|
||||
a.provider = nil
|
||||
}
|
||||
|
||||
a.infoList = wechat.GetWeChatAllInfo()
|
||||
for i := range a.infoList.Info {
|
||||
var info WeChatInfo
|
||||
@ -135,12 +189,13 @@ func (a *App) ExportWeChatAllData(full bool, acountName string) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := os.Stat(".\\User")
|
||||
prefixExportPath := a.FLoader.FilePrefix + "\\User\\"
|
||||
_, err := os.Stat(prefixExportPath)
|
||||
if err != nil {
|
||||
os.Mkdir(".\\User", os.ModeDir)
|
||||
os.Mkdir(prefixExportPath, os.ModeDir)
|
||||
}
|
||||
|
||||
expPath := ".\\User\\" + pInfo.AcountName
|
||||
expPath := prefixExportPath + pInfo.AcountName
|
||||
_, err = os.Stat(expPath)
|
||||
if err == nil {
|
||||
if !full {
|
||||
@ -177,7 +232,7 @@ func (a *App) ExportWeChatAllData(full bool, acountName string) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *App) createWechatDataProvider(resPath string) error {
|
||||
func (a *App) createWechatDataProvider(resPath string, prefix string) error {
|
||||
if a.provider != nil && a.provider.SelfInfo != nil && filepath.Base(resPath) == a.provider.SelfInfo.UserName {
|
||||
log.Println("WechatDataProvider not need create:", a.provider.SelfInfo.UserName)
|
||||
return nil
|
||||
@ -189,7 +244,7 @@ func (a *App) createWechatDataProvider(resPath string) error {
|
||||
log.Println("createWechatDataProvider WechatWechatDataProviderClose")
|
||||
}
|
||||
|
||||
provider, err := wechat.CreateWechatDataProvider(resPath)
|
||||
provider, err := wechat.CreateWechatDataProvider(resPath, prefix)
|
||||
if err != nil {
|
||||
log.Println("CreateWechatDataProvider failed:", resPath)
|
||||
return err
|
||||
@ -202,22 +257,29 @@ func (a *App) createWechatDataProvider(resPath string) error {
|
||||
}
|
||||
|
||||
func (a *App) WeChatInit() {
|
||||
expPath := ".\\User\\" + a.defaultUser
|
||||
if a.createWechatDataProvider(expPath) == nil {
|
||||
if len(a.defaultUser) == 0 {
|
||||
log.Println("not defaultUser")
|
||||
return
|
||||
}
|
||||
|
||||
expPath := a.FLoader.FilePrefix + "\\User\\" + a.defaultUser
|
||||
prefixPath := "\\User\\" + a.defaultUser
|
||||
wechat.ExportWeChatHeadImage(expPath)
|
||||
if a.createWechatDataProvider(expPath, prefixPath) == nil {
|
||||
infoJson, _ := json.Marshal(a.provider.SelfInfo)
|
||||
runtime.EventsEmit(a.ctx, "selfInfo", string(infoJson))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetWechatSessionList(pageIndex int, pageSize int) string {
|
||||
expPath := ".\\User\\" + a.defaultUser
|
||||
if a.createWechatDataProvider(expPath) != nil {
|
||||
return ""
|
||||
if a.provider == nil {
|
||||
log.Println("provider not init")
|
||||
return "{\"Total\":0}"
|
||||
}
|
||||
log.Printf("pageIndex: %d\n", pageIndex)
|
||||
list, err := a.provider.WeChatGetSessionList(pageIndex, pageSize)
|
||||
if err != nil {
|
||||
return ""
|
||||
return "{\"Total\":0}"
|
||||
}
|
||||
|
||||
listStr, _ := json.Marshal(list)
|
||||
@ -225,6 +287,22 @@ func (a *App) GetWechatSessionList(pageIndex int, pageSize int) string {
|
||||
return string(listStr)
|
||||
}
|
||||
|
||||
func (a *App) GetWechatContactList(pageIndex int, pageSize int) string {
|
||||
if a.provider == nil {
|
||||
log.Println("provider not init")
|
||||
return "{\"Total\":0}"
|
||||
}
|
||||
log.Printf("pageIndex: %d\n", pageIndex)
|
||||
list, err := a.provider.WeChatGetContactList(pageIndex, pageSize)
|
||||
if err != nil {
|
||||
return "{\"Total\":0}"
|
||||
}
|
||||
|
||||
listStr, _ := json.Marshal(list)
|
||||
log.Println("WeChatGetContactList:", list.Total)
|
||||
return string(listStr)
|
||||
}
|
||||
|
||||
func (a *App) GetWechatMessageListByTime(userName string, time int64, pageSize int, direction string) string {
|
||||
log.Println("GetWechatMessageList:", userName, pageSize, time, direction)
|
||||
if len(userName) == 0 {
|
||||
@ -284,6 +362,7 @@ func (a *App) GetWechatMessageDate(userName string) string {
|
||||
func (a *App) setCurrentConfig() {
|
||||
viper.Set(configDefaultUserKey, a.defaultUser)
|
||||
viper.Set(configUsersKey, a.users)
|
||||
viper.Set(configExportPathKey, a.FLoader.FilePrefix)
|
||||
err := viper.SafeWriteConfig()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@ -314,7 +393,9 @@ func (a *App) OpenFileOrExplorer(filePath string, explorer bool) string {
|
||||
// filePath = root + filePath[1:]
|
||||
// }
|
||||
// log.Println("OpenFileOrExplorer:", filePath)
|
||||
err := utils.OpenFileOrExplorer(filePath, explorer)
|
||||
|
||||
path := a.FLoader.FilePrefix + filePath
|
||||
err := utils.OpenFileOrExplorer(path, explorer)
|
||||
if err != nil {
|
||||
return "{\"result\": \"OpenFileOrExplorer failed\", \"status\":\"failed\"}"
|
||||
}
|
||||
@ -349,13 +430,14 @@ func (a *App) GetWechatLocalAccountInfo() string {
|
||||
infos.Total = 0
|
||||
infos.CurrentAccount = a.defaultUser
|
||||
for i := range a.users {
|
||||
resPath := ".\\User\\" + a.users[i]
|
||||
resPath := a.FLoader.FilePrefix + "\\User\\" + a.users[i]
|
||||
if _, err := os.Stat(resPath); err != nil {
|
||||
log.Println("GetWechatLocalAccountInfo:", resPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
info, err := wechat.WechatGetAccountInfo(resPath, a.users[i])
|
||||
prefixResPath := "\\User\\" + a.users[i]
|
||||
info, err := wechat.WechatGetAccountInfo(resPath, prefixResPath, a.users[i])
|
||||
if err != nil {
|
||||
log.Println("GetWechatLocalAccountInfo", err)
|
||||
continue
|
||||
@ -386,3 +468,127 @@ func (a *App) WechatSwitchAccount(account string) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *App) GetExportPathStat() string {
|
||||
path := a.FLoader.FilePrefix
|
||||
log.Println("utils.GetPathStat ++")
|
||||
stat, err := utils.GetPathStat(path)
|
||||
log.Println("utils.GetPathStat --")
|
||||
if err != nil {
|
||||
log.Println("GetPathStat error:", path, err)
|
||||
var msg ErrorMessage
|
||||
msg.ErrorStr = fmt.Sprintf("%s:%v", path, err)
|
||||
msgStr, _ := json.Marshal(msg)
|
||||
return string(msgStr)
|
||||
}
|
||||
|
||||
statString, _ := json.Marshal(stat)
|
||||
|
||||
return string(statString)
|
||||
}
|
||||
|
||||
func (a *App) ExportPathIsCanWrite() bool {
|
||||
path := a.FLoader.FilePrefix
|
||||
return utils.PathIsCanWriteFile(path)
|
||||
}
|
||||
|
||||
func (a *App) OpenExportPath() {
|
||||
path := a.FLoader.FilePrefix
|
||||
runtime.BrowserOpenURL(a.ctx, path)
|
||||
}
|
||||
|
||||
func (a *App) OpenDirectoryDialog() string {
|
||||
dialogOptions := runtime.OpenDialogOptions{
|
||||
Title: "选择导出路径",
|
||||
}
|
||||
selectedDir, err := runtime.OpenDirectoryDialog(a.ctx, dialogOptions)
|
||||
if err != nil {
|
||||
log.Println("OpenDirectoryDialog:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if selectedDir == "" {
|
||||
log.Println("Cancel selectedDir")
|
||||
return ""
|
||||
}
|
||||
|
||||
if selectedDir == a.FLoader.FilePrefix {
|
||||
log.Println("same path No need SetFilePrefix")
|
||||
return ""
|
||||
}
|
||||
|
||||
if !utils.PathIsCanWriteFile(selectedDir) {
|
||||
log.Println("PathIsCanWriteFile:", selectedDir, "error")
|
||||
return ""
|
||||
}
|
||||
|
||||
a.FLoader.SetFilePrefix(selectedDir)
|
||||
log.Println("OpenDirectoryDialog:", selectedDir)
|
||||
a.scanAccountByPath(selectedDir)
|
||||
return selectedDir
|
||||
}
|
||||
|
||||
func (a *App) scanAccountByPath(path string) error {
|
||||
infos := WeChatAccountInfos{}
|
||||
infos.Info = make([]wechat.WeChatAccountInfo, 0)
|
||||
infos.Total = 0
|
||||
infos.CurrentAccount = ""
|
||||
|
||||
userPath := path + "\\User\\"
|
||||
if _, err := os.Stat(userPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirs, err := os.ReadDir(userPath)
|
||||
if err != nil {
|
||||
log.Println("ReadDir", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range dirs {
|
||||
if !dirs[i].Type().IsDir() {
|
||||
continue
|
||||
}
|
||||
log.Println("dirs[i].Name():", dirs[i].Name())
|
||||
resPath := path + "\\User\\" + dirs[i].Name()
|
||||
prefixResPath := "\\User\\" + dirs[i].Name()
|
||||
info, err := wechat.WechatGetAccountInfo(resPath, prefixResPath, dirs[i].Name())
|
||||
if err != nil {
|
||||
log.Println("GetWechatLocalAccountInfo", err)
|
||||
continue
|
||||
}
|
||||
|
||||
infos.Info = append(infos.Info, *info)
|
||||
infos.Total += 1
|
||||
}
|
||||
|
||||
users := make([]string, 0)
|
||||
for i := 0; i < infos.Total; i++ {
|
||||
users = append(users, infos.Info[i].AccountName)
|
||||
}
|
||||
|
||||
a.users = users
|
||||
found := false
|
||||
for i := range a.users {
|
||||
if a.defaultUser == a.users[i] {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
a.defaultUser = ""
|
||||
}
|
||||
if a.defaultUser == "" && len(a.users) > 0 {
|
||||
a.defaultUser = a.users[0]
|
||||
}
|
||||
|
||||
if len(a.users) > 0 {
|
||||
a.setCurrentConfig()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) OepnLogFileExplorer() {
|
||||
utils.OpenFileOrExplorer(".\\app.log", true)
|
||||
}
|
||||
|
@ -1,3 +1,10 @@
|
||||
## v1.0.4
|
||||
1. 优化头像显示,优先使用本地头像显示
|
||||
2. 支持自定义导出路径、增加对导出路径的权限检测
|
||||
3. 增加联系人显示
|
||||
4. 滚动条滑块调整小大
|
||||
5. 支持已经打开日志所在文件夹、日志增加回滚功能
|
||||
|
||||
## v1.0.3
|
||||
1. 增加首次使用的引导功能
|
||||
2. 增加多开微信可选择导出功能
|
||||
|
503
frontend/dist/assets/index.425764f5.js
vendored
503
frontend/dist/assets/index.425764f5.js
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index.abf5d552.css
vendored
1
frontend/dist/assets/index.abf5d552.css
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index.b10575d2.css
vendored
1
frontend/dist/assets/index.b10575d2.css
vendored
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index.bee21395.css
vendored
Normal file
1
frontend/dist/assets/index.bee21395.css
vendored
Normal file
File diff suppressed because one or more lines are too long
353
frontend/dist/assets/index.c5ad2349.js
vendored
353
frontend/dist/assets/index.c5ad2349.js
vendored
File diff suppressed because one or more lines are too long
503
frontend/dist/assets/index.e451d83d.js
vendored
Normal file
503
frontend/dist/assets/index.e451d83d.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
frontend/dist/index.html
vendored
4
frontend/dist/index.html
vendored
@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>wechatDataBackup</title>
|
||||
<script type="module" crossorigin src="/assets/index.425764f5.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.b10575d2.css">
|
||||
<script type="module" crossorigin src="/assets/index.e451d83d.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.bee21395.css">
|
||||
</head>
|
||||
<body >
|
||||
<div id="root"></div>
|
||||
|
1
go.mod
1
go.mod
@ -15,6 +15,7 @@ require (
|
||||
github.com/wailsapp/wails/v2 v2.9.1
|
||||
golang.org/x/sys v0.20.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
40
main.go
40
main.go
@ -2,51 +2,35 @@ package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
//go:embed all:frontend/dist
|
||||
var assets embed.FS
|
||||
|
||||
type FileLoader struct {
|
||||
http.Handler
|
||||
}
|
||||
|
||||
func NewFileLoader() *FileLoader {
|
||||
return &FileLoader{}
|
||||
}
|
||||
|
||||
func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||
var err error
|
||||
requestedFilename := strings.TrimPrefix(req.URL.Path, "/")
|
||||
// println("Requesting file:", requestedFilename)
|
||||
fileData, err := os.ReadFile(requestedFilename)
|
||||
if err != nil {
|
||||
res.WriteHeader(http.StatusBadRequest)
|
||||
res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))
|
||||
}
|
||||
|
||||
res.Write(fileData)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// log output format
|
||||
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
|
||||
}
|
||||
|
||||
func main() {
|
||||
file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
defer file.Close()
|
||||
logJack := &lumberjack.Logger{
|
||||
Filename: "./app.log",
|
||||
MaxSize: 5,
|
||||
MaxBackups: 1,
|
||||
MaxAge: 30,
|
||||
Compress: false,
|
||||
}
|
||||
defer logJack.Close()
|
||||
|
||||
multiWriter := io.MultiWriter(file, os.Stdout)
|
||||
multiWriter := io.MultiWriter(logJack, os.Stdout)
|
||||
// 设置日志输出目标为文件
|
||||
log.SetOutput(multiWriter)
|
||||
log.Println("====================== wechatDataBackup ======================")
|
||||
@ -62,7 +46,7 @@ func main() {
|
||||
Height: 768,
|
||||
AssetServer: &assetserver.Options{
|
||||
Assets: assets,
|
||||
Handler: NewFileLoader(),
|
||||
Handler: app.FLoader,
|
||||
},
|
||||
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
||||
OnStartup: app.startup,
|
||||
|
@ -5,12 +5,22 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/browser"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
type PathStat struct {
|
||||
Path string `json:"path"`
|
||||
Total uint64 `json:"total"`
|
||||
Free uint64 `json:"free"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
}
|
||||
|
||||
func getDefaultProgram(fileExtension string) (string, error) {
|
||||
key, err := registry.OpenKey(registry.CLASSES_ROOT, fmt.Sprintf(`.%s`, fileExtension), registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
@ -75,3 +85,38 @@ func OpenFileOrExplorer(filePath string, explorer bool) error {
|
||||
fmt.Println("Command executed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPathStat(path string) (PathStat, error) {
|
||||
pathStat := PathStat{}
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return pathStat, err
|
||||
}
|
||||
|
||||
stat, err := disk.Usage(absPath)
|
||||
if err != nil {
|
||||
return pathStat, err
|
||||
}
|
||||
|
||||
pathStat.Path = stat.Path
|
||||
pathStat.Total = stat.Total
|
||||
pathStat.Used = stat.Used
|
||||
pathStat.Free = stat.Free
|
||||
pathStat.UsedPercent = stat.UsedPercent
|
||||
|
||||
return pathStat, nil
|
||||
}
|
||||
|
||||
func PathIsCanWriteFile(path string) bool {
|
||||
|
||||
filepath := fmt.Sprintf("%s\\CanWrite.txt", path)
|
||||
file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
file.Close()
|
||||
os.Remove(filepath)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -49,6 +49,11 @@ type wechatMediaMSG struct {
|
||||
Buf []byte
|
||||
}
|
||||
|
||||
type wechatHeadImgMSG struct {
|
||||
userName string
|
||||
Buf []byte
|
||||
}
|
||||
|
||||
func GetWeChatAllInfo() *WeChatInfoList {
|
||||
list := GetWeChatInfo()
|
||||
|
||||
@ -73,6 +78,121 @@ func ExportWeChatAllData(info WeChatInfo, expPath string, progress chan<- string
|
||||
exportWeChatBat(info, expPath, progress)
|
||||
exportWeChatVideoAndFile(info, expPath, progress)
|
||||
exportWeChatVoice(info, expPath, progress)
|
||||
exportWeChatHeadImage(info, expPath, progress)
|
||||
}
|
||||
|
||||
func exportWeChatHeadImage(info WeChatInfo, expPath string, progress chan<- string) {
|
||||
progress <- "{\"status\":\"processing\", \"result\":\"export WeChat Head Image\", \"progress\": 81}"
|
||||
|
||||
headImgPath := fmt.Sprintf("%s\\FileStorage\\HeadImage", expPath)
|
||||
if _, err := os.Stat(headImgPath); err != nil {
|
||||
if err := os.MkdirAll(headImgPath, 0644); err != nil {
|
||||
log.Printf("MkdirAll %s failed: %v\n", headImgPath, err)
|
||||
progress <- fmt.Sprintf("{\"status\":\"error\", \"result\":\"%v error\"}", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
handleNumber := int64(0)
|
||||
fileNumber := int64(0)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var reportWg sync.WaitGroup
|
||||
quitChan := make(chan struct{})
|
||||
MSGChan := make(chan wechatHeadImgMSG, 100)
|
||||
go func() {
|
||||
for {
|
||||
miscDBPath := fmt.Sprintf("%s\\Msg\\Misc.db", expPath)
|
||||
_, err := os.Stat(miscDBPath)
|
||||
if err != nil {
|
||||
log.Println("no exist:", miscDBPath)
|
||||
break
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlite3", miscDBPath)
|
||||
if err != nil {
|
||||
log.Printf("open %s failed: %v\n", miscDBPath, err)
|
||||
break
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.QueryRow("select count(*) from ContactHeadImg1;").Scan(&fileNumber)
|
||||
if err != nil {
|
||||
log.Println("select count(*) failed", err)
|
||||
break
|
||||
}
|
||||
log.Println("ContactHeadImg1 fileNumber", fileNumber)
|
||||
rows, err := db.Query("select ifnull(usrName,'') as usrName, ifnull(smallHeadBuf,'') as smallHeadBuf from ContactHeadImg1;")
|
||||
if err != nil {
|
||||
log.Printf("Query failed: %v\n", err)
|
||||
break
|
||||
}
|
||||
|
||||
msg := wechatHeadImgMSG{}
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&msg.userName, &msg.Buf)
|
||||
if err != nil {
|
||||
log.Println("Scan failed: ", err)
|
||||
break
|
||||
}
|
||||
|
||||
MSGChan <- msg
|
||||
}
|
||||
break
|
||||
}
|
||||
close(MSGChan)
|
||||
}()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for msg := range MSGChan {
|
||||
imgPath := fmt.Sprintf("%s\\%s.headimg", headImgPath, msg.userName)
|
||||
for {
|
||||
// log.Println("imgPath:", imgPath, len(msg.Buf))
|
||||
_, err := os.Stat(imgPath)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if len(msg.userName) == 0 || len(msg.Buf) == 0 {
|
||||
break
|
||||
}
|
||||
err = os.WriteFile(imgPath, msg.Buf[:], 0666)
|
||||
if err != nil {
|
||||
log.Println("WriteFile:", imgPath, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
atomic.AddInt64(&handleNumber, 1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
reportWg.Add(1)
|
||||
go func() {
|
||||
defer reportWg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-quitChan:
|
||||
log.Println("WeChat Head Image report progress end")
|
||||
return
|
||||
default:
|
||||
if fileNumber != 0 {
|
||||
filePercent := float64(handleNumber) / float64(fileNumber)
|
||||
totalPercent := 81 + (filePercent * (100 - 81))
|
||||
totalPercentStr := fmt.Sprintf("{\"status\":\"processing\", \"result\":\"export WeChat Head Image doing\", \"progress\": %d}", int(totalPercent))
|
||||
progress <- totalPercentStr
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
close(quitChan)
|
||||
reportWg.Wait()
|
||||
progress <- "{\"status\":\"processing\", \"result\":\"export WeChat Head Image end\", \"progress\": 100}"
|
||||
}
|
||||
|
||||
func exportWeChatVoice(info WeChatInfo, expPath string, progress chan<- string) {
|
||||
@ -171,7 +291,7 @@ func exportWeChatVoice(info WeChatInfo, expPath string, progress chan<- string)
|
||||
return
|
||||
default:
|
||||
filePercent := float64(handleNumber) / float64(fileNumber)
|
||||
totalPercent := 61 + (filePercent * (100 - 61))
|
||||
totalPercent := 61 + (filePercent * (80 - 61))
|
||||
totalPercentStr := fmt.Sprintf("{\"status\":\"processing\", \"result\":\"export WeChat voice doing\", \"progress\": %d}", int(totalPercent))
|
||||
progress <- totalPercentStr
|
||||
time.Sleep(time.Second)
|
||||
@ -182,7 +302,7 @@ func exportWeChatVoice(info WeChatInfo, expPath string, progress chan<- string)
|
||||
wg.Wait()
|
||||
close(quitChan)
|
||||
reportWg.Wait()
|
||||
progress <- "{\"status\":\"processing\", \"result\":\"export WeChat voice end\", \"progress\": 100}"
|
||||
progress <- "{\"status\":\"processing\", \"result\":\"export WeChat voice end\", \"progress\": 80}"
|
||||
}
|
||||
|
||||
func exportWeChatVideoAndFile(info WeChatInfo, expPath string, progress chan<- string) {
|
||||
@ -793,3 +913,31 @@ func getPathFileNumber(targetPath string, fileSuffix string) int64 {
|
||||
|
||||
return number
|
||||
}
|
||||
|
||||
func ExportWeChatHeadImage(exportPath string) {
|
||||
progress := make(chan string)
|
||||
info := WeChatInfo{}
|
||||
|
||||
miscDBPath := fmt.Sprintf("%s\\Msg\\Misc.db", exportPath)
|
||||
_, err := os.Stat(miscDBPath)
|
||||
if err != nil {
|
||||
log.Println("no exist:", miscDBPath)
|
||||
return
|
||||
}
|
||||
|
||||
headImgPath := fmt.Sprintf("%s\\FileStorage\\HeadImage", exportPath)
|
||||
if _, err := os.Stat(headImgPath); err == nil {
|
||||
log.Println("has HeadImage")
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
exportWeChatHeadImage(info, exportPath, progress)
|
||||
close(progress)
|
||||
}()
|
||||
|
||||
for p := range progress {
|
||||
log.Println(p)
|
||||
}
|
||||
log.Println("ExportWeChatHeadImage done")
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ type WeChatUserInfo struct {
|
||||
NickName string `json:"NickName"`
|
||||
SmallHeadImgUrl string `json:"SmallHeadImgUrl"`
|
||||
BigHeadImgUrl string `json:"BigHeadImgUrl"`
|
||||
LocalHeadImgUrl string `json:"LocalHeadImgUrl"`
|
||||
IsGroup bool `json:"IsGroup"`
|
||||
}
|
||||
|
||||
type WeChatSession struct {
|
||||
@ -75,7 +77,7 @@ type WeChatSession struct {
|
||||
Content string `json:"Content"`
|
||||
UserInfo WeChatUserInfo `json:"UserInfo"`
|
||||
Time uint64 `json:"Time"`
|
||||
IsGroup bool `json:IsGroup`
|
||||
IsGroup bool `json:"IsGroup"`
|
||||
}
|
||||
|
||||
type WeChatSessionList struct {
|
||||
@ -144,6 +146,19 @@ type WeChatUserList struct {
|
||||
Total int `json:"Total"`
|
||||
}
|
||||
|
||||
type WeChatContact struct {
|
||||
WeChatUserInfo
|
||||
PYInitial string
|
||||
QuanPin string
|
||||
RemarkPYInitial string
|
||||
RemarkQuanPin string
|
||||
}
|
||||
|
||||
type WeChatContactList struct {
|
||||
Users []WeChatContact `json:"Users"`
|
||||
Total int `json:"Total"`
|
||||
}
|
||||
|
||||
type WeChatAccountInfo struct {
|
||||
AccountName string `json:"AccountName"`
|
||||
AliasName string `json:"AliasName"`
|
||||
@ -151,6 +166,7 @@ type WeChatAccountInfo struct {
|
||||
NickName string `json:"NickName"`
|
||||
SmallHeadImgUrl string `json:"SmallHeadImgUrl"`
|
||||
BigHeadImgUrl string `json:"BigHeadImgUrl"`
|
||||
LocalHeadImgUrl string `json:"LocalHeadImgUrl"`
|
||||
}
|
||||
|
||||
type wechatMsgDB struct {
|
||||
@ -161,14 +177,16 @@ type wechatMsgDB struct {
|
||||
}
|
||||
|
||||
type WechatDataProvider struct {
|
||||
resPath string
|
||||
microMsg *sql.DB
|
||||
resPath string
|
||||
prefixResPath string
|
||||
microMsg *sql.DB
|
||||
|
||||
msgDBs []*wechatMsgDB
|
||||
userInfoMap map[string]WeChatUserInfo
|
||||
userInfoMtx sync.Mutex
|
||||
|
||||
SelfInfo *WeChatUserInfo
|
||||
SelfInfo *WeChatUserInfo
|
||||
ContactList *WeChatContactList
|
||||
}
|
||||
|
||||
const (
|
||||
@ -181,13 +199,40 @@ func (a byTime) Len() int { return len(a) }
|
||||
func (a byTime) Less(i, j int) bool { return a[i].startTime > a[j].startTime }
|
||||
func (a byTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func CreateWechatDataProvider(resPath string) (*WechatDataProvider, error) {
|
||||
type byName []WeChatContact
|
||||
|
||||
func (c byName) Len() int { return len(c) }
|
||||
func (c byName) Less(i, j int) bool {
|
||||
var a, b string
|
||||
if c[i].RemarkQuanPin != "" {
|
||||
a = c[i].RemarkQuanPin
|
||||
} else {
|
||||
a = c[i].QuanPin
|
||||
}
|
||||
|
||||
if c[j].RemarkQuanPin != "" {
|
||||
b = c[j].RemarkQuanPin
|
||||
} else {
|
||||
b = c[j].QuanPin
|
||||
}
|
||||
|
||||
return strings.Compare(a, b) < 0
|
||||
}
|
||||
func (c byName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
|
||||
func CreateWechatDataProvider(resPath string, prefixRes string) (*WechatDataProvider, error) {
|
||||
provider := &WechatDataProvider{}
|
||||
provider.resPath = resPath
|
||||
provider.prefixResPath = prefixRes
|
||||
provider.msgDBs = make([]*wechatMsgDB, 0)
|
||||
log.Println(resPath)
|
||||
|
||||
userName := filepath.Base(resPath)
|
||||
MicroMsgDBPath := resPath + "\\Msg\\" + MicroMsgDB
|
||||
if _, err := os.Stat(MicroMsgDBPath); err != nil {
|
||||
log.Println("CreateWechatDataProvider failed", MicroMsgDBPath, err)
|
||||
return provider, err
|
||||
}
|
||||
microMsg, err := sql.Open("sqlite3", MicroMsgDBPath)
|
||||
if err != nil {
|
||||
log.Printf("open db %s error: %v", MicroMsgDBPath, err)
|
||||
@ -217,11 +262,19 @@ func CreateWechatDataProvider(resPath string) (*WechatDataProvider, error) {
|
||||
}
|
||||
provider.userInfoMap = make(map[string]WeChatUserInfo)
|
||||
provider.microMsg = microMsg
|
||||
provider.SelfInfo, err = provider.WechatGetUserInfoByName(userName)
|
||||
provider.SelfInfo, err = provider.WechatGetUserInfoByNameOnCache(userName)
|
||||
if err != nil {
|
||||
log.Printf("WechatGetUserInfoByName %s failed: %v", userName, err)
|
||||
return provider, err
|
||||
}
|
||||
|
||||
provider.ContactList, err = provider.wechatGetAllContact()
|
||||
if err != nil {
|
||||
log.Println("wechatGetAllContact failed", err)
|
||||
return provider, err
|
||||
}
|
||||
sort.Sort(byName(provider.ContactList.Users))
|
||||
log.Println("Contact number:", provider.ContactList.Total)
|
||||
provider.userInfoMap[userName] = *provider.SelfInfo
|
||||
log.Println("resPath:", provider.resPath)
|
||||
return provider, nil
|
||||
@ -256,7 +309,7 @@ func (P *WechatDataProvider) WechatGetUserInfoByName(name string) (*WeChatUserIn
|
||||
return info, err
|
||||
}
|
||||
|
||||
log.Printf("UserName %s, Alias %s, ReMark %s, NickName %s\n", UserName, Alias, ReMark, NickName)
|
||||
// log.Printf("UserName %s, Alias %s, ReMark %s, NickName %s\n", UserName, Alias, ReMark, NickName)
|
||||
|
||||
var smallHeadImgUrl, bigHeadImgUrl string
|
||||
querySql = fmt.Sprintf("select ifnull(smallHeadImgUrl,'') as smallHeadImgUrl, ifnull(bigHeadImgUrl,'') as bigHeadImgUrl from ContactHeadImgUrl where usrName='%s';", UserName)
|
||||
@ -272,7 +325,13 @@ func (P *WechatDataProvider) WechatGetUserInfoByName(name string) (*WeChatUserIn
|
||||
info.NickName = NickName
|
||||
info.SmallHeadImgUrl = smallHeadImgUrl
|
||||
info.BigHeadImgUrl = bigHeadImgUrl
|
||||
info.IsGroup = strings.HasSuffix(UserName, "@chatroom")
|
||||
|
||||
localHeadImgPath := fmt.Sprintf("%s\\FileStorage\\HeadImage\\%s.headimg", P.resPath, name)
|
||||
relativePath := fmt.Sprintf("%s\\FileStorage\\HeadImage\\%s.headimg", P.prefixResPath, name)
|
||||
if _, err = os.Stat(localHeadImgPath); err == nil {
|
||||
info.LocalHeadImgUrl = relativePath
|
||||
}
|
||||
// log.Println(info)
|
||||
return info, nil
|
||||
}
|
||||
@ -299,7 +358,7 @@ func (P *WechatDataProvider) WeChatGetSessionList(pageIndex int, pageSize int) (
|
||||
continue
|
||||
}
|
||||
if len(strContent) == 0 {
|
||||
log.Printf("%s cotent nil\n", strUsrName)
|
||||
// log.Printf("%s cotent nil\n", strUsrName)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -308,7 +367,7 @@ func (P *WechatDataProvider) WeChatGetSessionList(pageIndex int, pageSize int) (
|
||||
session.Content = strContent
|
||||
session.Time = nTime
|
||||
session.IsGroup = strings.HasSuffix(strUsrName, "@chatroom")
|
||||
info, err := P.WechatGetUserInfoByName(strUsrName)
|
||||
info, err := P.WechatGetUserInfoByNameOnCache(strUsrName)
|
||||
if err != nil {
|
||||
log.Printf("WechatGetUserInfoByName %s failed\n", strUsrName)
|
||||
continue
|
||||
@ -321,6 +380,29 @@ func (P *WechatDataProvider) WeChatGetSessionList(pageIndex int, pageSize int) (
|
||||
return List, nil
|
||||
}
|
||||
|
||||
func (P *WechatDataProvider) WeChatGetContactList(pageIndex int, pageSize int) (*WeChatUserList, error) {
|
||||
List := &WeChatUserList{}
|
||||
List.Users = make([]WeChatUserInfo, 0)
|
||||
|
||||
if P.ContactList.Total <= pageIndex*pageSize {
|
||||
return List, nil
|
||||
}
|
||||
end := (pageIndex * pageSize) + pageSize
|
||||
if end > P.ContactList.Total {
|
||||
end = P.ContactList.Total
|
||||
}
|
||||
|
||||
log.Printf("P.ContactList.Total %d, start %d, end %d", P.ContactList.Total, pageIndex*pageSize, end)
|
||||
var info WeChatUserInfo
|
||||
for _, contact := range P.ContactList.Users[pageIndex*pageSize : end] {
|
||||
info = contact.WeChatUserInfo
|
||||
List.Users = append(List.Users, info)
|
||||
List.Total += 1
|
||||
}
|
||||
|
||||
return List, nil
|
||||
}
|
||||
|
||||
func (P *WechatDataProvider) WeChatGetMessageListByTime(userName string, time int64, pageSize int, direction Message_Search_Direction) (*WeChatMessageList, error) {
|
||||
|
||||
List := &WeChatMessageList{}
|
||||
@ -583,23 +665,23 @@ func (P *WechatDataProvider) wechatMessageExtraHandle(msg *WeChatMessage) {
|
||||
}
|
||||
case 3:
|
||||
if len(ext.Field2) > 0 && (msg.Type == Wechat_Message_Type_Picture || msg.Type == Wechat_Message_Type_Video || msg.Type == Wechat_Message_Type_Misc) {
|
||||
msg.ThumbPath = P.resPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
msg.ThumbPath = P.prefixResPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
}
|
||||
case 4:
|
||||
if len(ext.Field2) > 0 {
|
||||
if msg.Type == Wechat_Message_Type_Misc && msg.SubType == Wechat_Misc_Message_File {
|
||||
msg.FileInfo.FilePath = P.resPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
msg.FileInfo.FilePath = P.prefixResPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
msg.FileInfo.FileName = filepath.Base(ext.Field2)
|
||||
} else if msg.Type == Wechat_Message_Type_Picture || msg.Type == Wechat_Message_Type_Video || msg.Type == Wechat_Message_Type_Misc {
|
||||
msg.ImagePath = P.resPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
msg.VideoPath = P.resPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
msg.ImagePath = P.prefixResPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
msg.VideoPath = P.prefixResPath + ext.Field2[len(P.SelfInfo.UserName):]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if msg.Type == Wechat_Message_Type_Voice {
|
||||
msg.VoicePath = fmt.Sprintf("%s\\FileStorage\\Voice\\%d.mp3", P.resPath, msg.MsgSvrId)
|
||||
msg.VoicePath = fmt.Sprintf("%s\\FileStorage\\Voice\\%d.mp3", P.prefixResPath, msg.MsgSvrId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -892,7 +974,50 @@ func (P *WechatDataProvider) WechatGetUserInfoByNameOnCache(name string) (*WeCha
|
||||
return pinfo, nil
|
||||
}
|
||||
|
||||
func WechatGetAccountInfo(resPath, accountName string) (*WeChatAccountInfo, error) {
|
||||
func (P *WechatDataProvider) wechatGetAllContact() (*WeChatContactList, error) {
|
||||
List := &WeChatContactList{}
|
||||
List.Users = make([]WeChatContact, 0)
|
||||
|
||||
querySql := fmt.Sprintf("select ifnull(UserName,'') as UserName,Reserved1,Reserved2,ifnull(PYInitial,'') as PYInitial,ifnull(QuanPin,'') as QuanPin,ifnull(RemarkPYInitial,'') as RemarkPYInitial,ifnull(RemarkQuanPin,'') as RemarkQuanPin from Contact desc;")
|
||||
dbRows, err := P.microMsg.Query(querySql)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return List, err
|
||||
}
|
||||
defer dbRows.Close()
|
||||
|
||||
var UserName string
|
||||
var Reserved1, Reserved2 int
|
||||
for dbRows.Next() {
|
||||
var Contact WeChatContact
|
||||
err = dbRows.Scan(&UserName, &Reserved1, &Reserved2, &Contact.PYInitial, &Contact.QuanPin, &Contact.RemarkPYInitial, &Contact.RemarkQuanPin)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if Reserved1 != 1 || Reserved2 != 1 {
|
||||
// log.Printf("%s is not your contact", UserName)
|
||||
continue
|
||||
}
|
||||
info, err := P.WechatGetUserInfoByNameOnCache(UserName)
|
||||
if err != nil {
|
||||
log.Printf("WechatGetUserInfoByName %s failed\n", UserName)
|
||||
continue
|
||||
}
|
||||
|
||||
if info.NickName == "" && info.ReMark == "" {
|
||||
continue
|
||||
}
|
||||
Contact.WeChatUserInfo = *info
|
||||
List.Users = append(List.Users, Contact)
|
||||
List.Total += 1
|
||||
}
|
||||
|
||||
return List, nil
|
||||
}
|
||||
|
||||
func WechatGetAccountInfo(resPath, prefixRes, accountName string) (*WeChatAccountInfo, error) {
|
||||
MicroMsgDBPath := resPath + "\\Msg\\" + MicroMsgDB
|
||||
if _, err := os.Stat(MicroMsgDBPath); err != nil {
|
||||
log.Println("MicroMsgDBPath:", MicroMsgDBPath, err)
|
||||
@ -934,6 +1059,11 @@ func WechatGetAccountInfo(resPath, accountName string) (*WeChatAccountInfo, erro
|
||||
info.SmallHeadImgUrl = smallHeadImgUrl
|
||||
info.BigHeadImgUrl = bigHeadImgUrl
|
||||
|
||||
localHeadImgPath := fmt.Sprintf("%s\\FileStorage\\HeadImage\\%s.headimg", resPath, accountName)
|
||||
relativePath := fmt.Sprintf("%s\\FileStorage\\HeadImage\\%s.headimg", prefixRes, accountName)
|
||||
if _, err = os.Stat(localHeadImgPath); err == nil {
|
||||
info.LocalHeadImgUrl = relativePath
|
||||
}
|
||||
// log.Println(info)
|
||||
return info, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user