1. 优化头像显示,优先使用本地头像显示

2. 支持自定义导出路径、增加对导出路径的权限检测
3. 增加联系人显示
4. 滚动条滑块调整小大
5. 支持已经打开日志所在文件夹、日志增加回滚功能
This commit is contained in:
HAL 2024-11-03 00:54:25 +08:00
parent 073d586aea
commit 331cd8125d
14 changed files with 1088 additions and 921 deletions

238
app.go
View File

@ -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)
}

View File

@ -1,3 +1,10 @@
## v1.0.4
1. 优化头像显示,优先使用本地头像显示
2. 支持自定义导出路径、增加对导出路径的权限检测
3. 增加联系人显示
4. 滚动条滑块调整小大
5. 支持已经打开日志所在文件夹、日志增加回滚功能
## v1.0.3
1. 增加首次使用的引导功能
2. 增加多开微信可选择导出功能

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

503
frontend/dist/assets/index.e451d83d.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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
View File

@ -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
View File

@ -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,

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}