init
39
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: Wails build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
# Match any new tag
|
||||
- '*'
|
||||
|
||||
env:
|
||||
# Necessary for most environments as build failure can occur due to OOM issues
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
# Failure in one platform build won't impact the others
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build:
|
||||
- name: 'wechatDataBackup.exe'
|
||||
platform: 'windows/amd64'
|
||||
os: 'windows-latest'
|
||||
|
||||
runs-on: ${{ matrix.build.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build wails
|
||||
uses: dAppServer/wails-build-action@v2.2
|
||||
id: build
|
||||
with:
|
||||
build-name: ${{ matrix.build.name }}
|
||||
sign: false
|
||||
build-platform: ${{ matrix.build.platform }}
|
||||
package: true
|
||||
go-version: '1.21'
|
33
.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# After editing .gitignore to match the ignored files, you can do < git ls-files -ci --exclude-standard >
|
||||
# to see the files that are included in the exclude lists; you can then do
|
||||
#
|
||||
# @ Linux/MacOS: < git ls-files -ci --exclude-standard -z | xargs -0 git rm --cached >
|
||||
# @ Windows (PowerShell): < git ls-files -ci --exclude-standard | % { git rm --cached "$_" } >
|
||||
# @ Windows (cmd.exe): < for /F "tokens=*" %a in ('git ls-files -ci --exclude-standard') do @git rm --cached "%a" >
|
||||
# ...to re-init the ignore list
|
||||
|
||||
# Wails directories
|
||||
build/bin
|
||||
frontend/wailsjs
|
||||
frontend/node_modules
|
||||
|
||||
# Wails junk files
|
||||
.syso
|
||||
|
||||
# Go files
|
||||
go.sum
|
||||
|
||||
# IDEs
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
# System enviroment variables
|
||||
env
|
||||
*/.DS_Store
|
||||
|
||||
|
||||
# runtime
|
||||
User
|
||||
config.json
|
||||
wechatDataBackup.exe
|
||||
app.log
|
66
README.md
@ -1,2 +1,68 @@
|
||||
# wechatDataBackup
|
||||
PC微信聊天记录数据导出工具
|
||||
|
||||
* 基于wails开发 + React前端,实现PC端微信聊天记录一键导出功能。
|
||||
* 导出后数据可以做永久化保存,即使微信停止支持,聊天记录也可以随时查看。
|
||||
* 前端界面尽量与微信界面保持一致,减少使用成本。
|
||||
* 理论上支持所有Windows 32/64位微信版本。
|
||||
|
||||
效果图如下:
|
||||
|
||||

|
||||
|
||||
## 使用方法
|
||||
1. 下载release可执行文件直接打开
|
||||
2. 下载源码自行编译可执行文件 [安装wails环境](https://wails.io/zh-Hans/docs/gettingstarted/installation)
|
||||
```shell
|
||||
git clone https://github.com/git-jiadong/wechatDataBackup.git
|
||||
cd wechatDataBackup
|
||||
wails build
|
||||
```
|
||||
编译成功后在可执行二进制文件路径`build\bin\wechatDataBackup.exe`
|
||||
|
||||
3. 导出聊天记录
|
||||
电脑登陆微信,然后打开`wechatDataBackup.exe`后按照如图提示导出
|
||||

|
||||
|
||||
## 功能
|
||||
本项目目前的规划与实现进度:
|
||||
- [x] 支持图片消息
|
||||
- [x] 支持视频消息
|
||||
- [x] 支持链接消息
|
||||
- [x] 支持文件消息
|
||||
- [x] 支持原始表情显示
|
||||
- [x] 支持按类型检索
|
||||
- [x] 支持日期检索
|
||||
- [x] 支持按群成员检索
|
||||
- [x] 支持增量式导出
|
||||
- [ ] 支持更多消息类型显示
|
||||
- [ ] 图片查看器重绘
|
||||
- [ ] 实现头像或表情预先下载(实现完全离线查看)
|
||||
- [ ] 聊天报告
|
||||
- [ ] AI本地模型应用
|
||||
- [ ] 导出数据本地加密
|
||||
- ...
|
||||
如果遇到什么问题,或者有更好的建议与优化点欢迎给作者提 [ISSUE](https://github.com/git-jiadong/wechatDataBackup/issues)
|
||||
|
||||
|
||||
### 常见问题
|
||||
Q: 支持手机端的聊天记录备份吗?<br>
|
||||
A: 手机端可以使用聊天数据迁移功能,将手机的数据迁移到电脑后再将数据导出 [迁移聊天记录](https://mp.weixin.qq.com/s?src=11×tamp=1724572247&ver=5465&signature=j1TNxZAx48TdBzc6KJIHInIvXlBhwSAlQ4XGowKeyijZ2gsmXyOb2Zpg9qfVyMdGrte0SwU9kT8xCDgFBI7CR7fqCHpHuZzpv3O77gSkV3mbxmFdPKfW7Fq89CzHPQr0&new=1)<br>
|
||||
Q: 导出的数据比PC微信里面看到的少,数据不完整<br>
|
||||
A: 这是由于可能数据存在于内存中还没有回写到磁盘导致的,退出微信时会将内存的数据全部回写到磁盘,导出数据时最好退出重新登陆一次微信,保证数据都在磁盘中再导出即可。<br>
|
||||
|
||||
## 免责声明
|
||||
**⚠️ 本项目仅供学习、研究使用,严禁商业使用**<br/>
|
||||
**⚠️ 用于网络安全用途的,请确保在国家法律法规下使用**<br/>
|
||||
**⚠️ 本项目完全免费,问你要钱的都是骗子**<br/>
|
||||
**⚠️ 使用本项目初衷是作者研究微信数据库的运行使用,您使用本软件导致的后果,包含但不限于数据损坏,记录丢失等问题,作者不承担相关责任。**<br/>
|
||||
**⚠️ 因软件特殊性质,请在使用时获得微信账号所有人授权,你当确保不侵犯他人个人隐私权,后果自行承担**<br/>
|
||||
|
||||
## 前端代码
|
||||
由于前端代码不成熟,前端界面代码暂时不公开。
|
||||
|
||||
## 参考/引用
|
||||
- 微信数据库解密和数据库的使用 [PyWxDump](https://github.com/xaoyaoo/PyWxDump/tree/master)
|
||||
- silk语音消息解码 [silk-v3-decoder](https://github.com/kn007/silk-v3-decoder)
|
||||
- PCM转MP3 [lame](https://github.com/viert/lame.git)
|
||||
- Dat图片解码 [wechatDatDecode](https://github.com/liuggchen/wechatDatDecode)
|
304
app.go
Normal file
@ -0,0 +1,304 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"wechatDataBackup/pkg/utils"
|
||||
"wechatDataBackup/pkg/wechat"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultConfig = "config"
|
||||
configDefaultUserKey = "userConfig.defaultUser"
|
||||
configUsersKey = "userConfig.users"
|
||||
appVersion = "v1.0.0"
|
||||
)
|
||||
|
||||
// App struct
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
info wechat.WeChatInfo
|
||||
provider *wechat.WechatDataProvider
|
||||
defaultUser string
|
||||
users []string
|
||||
}
|
||||
|
||||
type WeChatInfo struct {
|
||||
ProcessID uint32 `json:"PID"`
|
||||
FilePath string `json:"FilePath"`
|
||||
AcountName string `json:"AcountName"`
|
||||
Version string `json:"Version"`
|
||||
Is64Bits bool `json:"Is64Bits"`
|
||||
DBKey string `json:"DBkey"`
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
func NewApp() *App {
|
||||
a := &App{}
|
||||
|
||||
viper.SetConfigName(defaultConfig)
|
||||
viper.SetConfigType("json")
|
||||
viper.AddConfigPath(".")
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
a.defaultUser = viper.GetString(configDefaultUserKey)
|
||||
a.users = viper.GetStringSlice(configUsersKey)
|
||||
// log.Println(a.defaultUser)
|
||||
// log.Println(a.users)
|
||||
} else {
|
||||
log.Println("not config exist")
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// startup is called when the app starts. The context is saved
|
||||
// so we can call the runtime methods
|
||||
func (a *App) startup(ctx context.Context) {
|
||||
a.ctx = ctx
|
||||
}
|
||||
|
||||
func (a *App) beforeClose(ctx context.Context) (prevent bool) {
|
||||
dialog, err := runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.QuestionDialog,
|
||||
Title: "Quit?",
|
||||
Message: "Are you sure you want to quit?",
|
||||
})
|
||||
|
||||
if err != nil || dialog == "Yes" {
|
||||
a.provider.WechatWechatDataProviderClose()
|
||||
a.provider = nil
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *App) GetWeChatAllInfo() string {
|
||||
a.info, _ = wechat.GetWeChatAllInfo()
|
||||
|
||||
var info WeChatInfo
|
||||
info.ProcessID = a.info.ProcessID
|
||||
info.FilePath = a.info.FilePath
|
||||
info.AcountName = a.info.AcountName
|
||||
info.Version = a.info.Version
|
||||
info.Is64Bits = a.info.Is64Bits
|
||||
info.DBKey = a.info.DBKey
|
||||
|
||||
infoStr, _ := json.Marshal(info)
|
||||
log.Println(string(infoStr))
|
||||
|
||||
return string(infoStr)
|
||||
}
|
||||
|
||||
func (a *App) ExportWeChatAllData(full bool) {
|
||||
|
||||
if a.provider != nil {
|
||||
a.provider.WechatWechatDataProviderClose()
|
||||
a.provider = nil
|
||||
}
|
||||
|
||||
progress := make(chan string)
|
||||
go func() {
|
||||
|
||||
_, err := os.Stat(".\\User")
|
||||
if err != nil {
|
||||
os.Mkdir(".\\User", os.ModeDir)
|
||||
}
|
||||
|
||||
expPath := ".\\User\\" + a.info.AcountName
|
||||
_, err = os.Stat(expPath)
|
||||
if err == nil {
|
||||
if !full {
|
||||
os.RemoveAll(expPath + "\\Msg")
|
||||
} else {
|
||||
os.RemoveAll(expPath)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = os.Stat(expPath)
|
||||
if err != nil {
|
||||
os.Mkdir(expPath, os.ModeDir)
|
||||
}
|
||||
|
||||
go wechat.ExportWeChatAllData(a.info, expPath, progress)
|
||||
|
||||
for p := range progress {
|
||||
log.Println(p)
|
||||
runtime.EventsEmit(a.ctx, "exportData", p)
|
||||
}
|
||||
|
||||
if len(a.defaultUser) == 0 {
|
||||
a.defaultUser = a.info.AcountName
|
||||
}
|
||||
|
||||
hasUser := false
|
||||
for _, user := range a.users {
|
||||
if user == a.info.AcountName {
|
||||
hasUser = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasUser {
|
||||
a.users = append(a.users, a.info.AcountName)
|
||||
}
|
||||
a.setCurrentConfig()
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *App) createWechatDataProvider(resPath 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
|
||||
}
|
||||
|
||||
provider, err := wechat.CreateWechatDataProvider(resPath)
|
||||
if err != nil {
|
||||
log.Println("CreateWechatDataProvider failed:", resPath)
|
||||
return err
|
||||
}
|
||||
|
||||
a.provider = provider
|
||||
// infoJson, _ := json.Marshal(a.provider.SelfInfo)
|
||||
// runtime.EventsEmit(a.ctx, "selfInfo", string(infoJson))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) WeChatInit() {
|
||||
expPath := ".\\User\\" + a.defaultUser
|
||||
if a.createWechatDataProvider(expPath) == 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 ""
|
||||
}
|
||||
log.Printf("pageIndex: %d\n", pageIndex)
|
||||
list, err := a.provider.WeChatGetSessionList(pageIndex, pageSize)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
listStr, _ := json.Marshal(list)
|
||||
log.Println("GetWechatSessionList:", 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 {
|
||||
return "{\"Total\":0, \"Rows\":[]}"
|
||||
}
|
||||
dire := wechat.Message_Search_Forward
|
||||
if direction == "backward" {
|
||||
dire = wechat.Message_Search_Backward
|
||||
} else if direction == "both" {
|
||||
dire = wechat.Message_Search_Both
|
||||
}
|
||||
list, err := a.provider.WeChatGetMessageListByTime(userName, time, pageSize, dire)
|
||||
if err != nil {
|
||||
log.Println("WeChatGetMessageList failed:", err)
|
||||
return ""
|
||||
}
|
||||
listStr, _ := json.Marshal(list)
|
||||
log.Println("GetWechatMessageList:", list.Total)
|
||||
|
||||
return string(listStr)
|
||||
}
|
||||
|
||||
func (a *App) GetWechatMessageListByKeyWord(userName string, time int64, keyword string, msgType string, pageSize int) string {
|
||||
log.Println("GetWechatMessageListByKeyWord:", userName, pageSize, time, msgType)
|
||||
if len(userName) == 0 {
|
||||
return "{\"Total\":0, \"Rows\":[]}"
|
||||
}
|
||||
list, err := a.provider.WeChatGetMessageListByKeyWord(userName, time, keyword, msgType, pageSize)
|
||||
if err != nil {
|
||||
log.Println("WeChatGetMessageListByKeyWord failed:", err)
|
||||
return ""
|
||||
}
|
||||
listStr, _ := json.Marshal(list)
|
||||
log.Println("WeChatGetMessageListByKeyWord:", list.Total, list.KeyWord)
|
||||
|
||||
return string(listStr)
|
||||
}
|
||||
|
||||
func (a *App) GetWechatMessageDate(userName string) string {
|
||||
log.Println("GetWechatMessageDate:", userName)
|
||||
if len(userName) == 0 {
|
||||
return "{\"Total\":0, \"Date\":[]}"
|
||||
}
|
||||
|
||||
messageData, err := a.provider.WeChatGetMessageDate(userName)
|
||||
if err != nil {
|
||||
log.Println("GetWechatMessageDate:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
messageDataStr, _ := json.Marshal(messageData)
|
||||
log.Println("GetWechatMessageDate:", messageData.Total)
|
||||
|
||||
return string(messageDataStr)
|
||||
}
|
||||
|
||||
func (a *App) setCurrentConfig() {
|
||||
viper.Set(configDefaultUserKey, a.defaultUser)
|
||||
viper.Set(configUsersKey, a.users)
|
||||
err := viper.SafeWriteConfig()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
type userList struct {
|
||||
Users []string `json:"Users"`
|
||||
}
|
||||
|
||||
func (a *App) GetWeChatUserList() string {
|
||||
|
||||
l := userList{}
|
||||
l.Users = a.users
|
||||
|
||||
usersStr, _ := json.Marshal(l)
|
||||
str := string(usersStr)
|
||||
log.Println("users:", str)
|
||||
return str
|
||||
}
|
||||
|
||||
func (a *App) OpenFileOrExplorer(filePath string, explorer bool) string {
|
||||
// if root, err := os.Getwd(); err == nil {
|
||||
// filePath = root + filePath[1:]
|
||||
// }
|
||||
// log.Println("OpenFileOrExplorer:", filePath)
|
||||
err := utils.OpenFileOrExplorer(filePath, explorer)
|
||||
if err != nil {
|
||||
return "{\"result\": \"OpenFileOrExplorer failed\", \"status\":\"failed\"}"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("{\"result\": \"%s\", \"status\":\"OK\"}", "")
|
||||
}
|
||||
|
||||
func (a *App) GetWeChatRoomUserList(roomId string) string {
|
||||
userlist, err := a.provider.WeChatGetChatRoomUserList(roomId)
|
||||
if err != nil {
|
||||
log.Println("WeChatGetChatRoomUserList:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
userListStr, _ := json.Marshal(userlist)
|
||||
|
||||
return string(userListStr)
|
||||
}
|
||||
|
||||
func (a *App) GetAppVersion() string {
|
||||
return appVersion
|
||||
}
|
35
build/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Build Directory
|
||||
|
||||
The build directory is used to house all the build files and assets for your application.
|
||||
|
||||
The structure is:
|
||||
|
||||
* bin - Output directory
|
||||
* darwin - macOS specific files
|
||||
* windows - Windows specific files
|
||||
|
||||
## Mac
|
||||
|
||||
The `darwin` directory holds files specific to Mac builds.
|
||||
These may be customised and used as part of the build. To return these files to the default state, simply delete them
|
||||
and
|
||||
build with `wails build`.
|
||||
|
||||
The directory contains the following files:
|
||||
|
||||
- `Info.plist` - the main plist file used for Mac builds. It is used when building using `wails build`.
|
||||
- `Info.dev.plist` - same as the main plist file but used when building using `wails dev`.
|
||||
|
||||
## Windows
|
||||
|
||||
The `windows` directory contains the manifest and rc files used when building with `wails build`.
|
||||
These may be customised for your application. To return these files to the default state, simply delete them and
|
||||
build with `wails build`.
|
||||
|
||||
- `icon.ico` - The icon used for the application. This is used when building using `wails build`. If you wish to
|
||||
use a different icon, simply replace this file with your own. If it is missing, a new `icon.ico` file
|
||||
will be created using the `appicon.png` file in the build directory.
|
||||
- `installer/*` - The files used to create the Windows installer. These are used when building using `wails build`.
|
||||
- `info.json` - Application details used for Windows builds. The data here will be used by the Windows installer,
|
||||
as well as the application itself (right click the exe -> properties -> details)
|
||||
- `wails.exe.manifest` - The main application manifest file.
|
BIN
build/appicon.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
68
build/darwin/Info.dev.plist
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>{{.Info.ProductName}}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{{.Name}}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.wails.{{.Name}}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>{{.Info.ProductVersion}}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>{{.Info.Comments}}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>{{.Info.ProductVersion}}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>iconfile</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>true</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>{{.Info.Copyright}}</string>
|
||||
{{if .Info.FileAssociations}}
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
{{range .Info.FileAssociations}}
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>{{.Ext}}</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>{{.Name}}</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>{{.Role}}</string>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>{{.IconName}}</string>
|
||||
</dict>
|
||||
{{end}}
|
||||
</array>
|
||||
{{end}}
|
||||
{{if .Info.Protocols}}
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
{{range .Info.Protocols}}
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.wails.{{.Scheme}}</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>{{.Scheme}}</string>
|
||||
</array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>{{.Role}}</string>
|
||||
</dict>
|
||||
{{end}}
|
||||
</array>
|
||||
{{end}}
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
63
build/darwin/Info.plist
Normal file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>{{.Info.ProductName}}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{{.Name}}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.wails.{{.Name}}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>{{.Info.ProductVersion}}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>{{.Info.Comments}}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>{{.Info.ProductVersion}}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>iconfile</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.13.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>true</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>{{.Info.Copyright}}</string>
|
||||
{{if .Info.FileAssociations}}
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
{{range .Info.FileAssociations}}
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>{{.Ext}}</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>{{.Name}}</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>{{.Role}}</string>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>{{.IconName}}</string>
|
||||
</dict>
|
||||
{{end}}
|
||||
</array>
|
||||
{{end}}
|
||||
{{if .Info.Protocols}}
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
{{range .Info.Protocols}}
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.wails.{{.Scheme}}</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>{{.Scheme}}</string>
|
||||
</array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>{{.Role}}</string>
|
||||
</dict>
|
||||
{{end}}
|
||||
</array>
|
||||
{{end}}
|
||||
</dict>
|
||||
</plist>
|
BIN
build/windows/icon.ico
Normal file
After Width: | Height: | Size: 411 KiB |
15
build/windows/info.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"fixed": {
|
||||
"file_version": "{{.Info.ProductVersion}}"
|
||||
},
|
||||
"info": {
|
||||
"0000": {
|
||||
"ProductVersion": "{{.Info.ProductVersion}}",
|
||||
"CompanyName": "{{.Info.CompanyName}}",
|
||||
"FileDescription": "{{.Info.ProductName}}",
|
||||
"LegalCopyright": "{{.Info.Copyright}}",
|
||||
"ProductName": "{{.Info.ProductName}}",
|
||||
"Comments": "{{.Info.Comments}}"
|
||||
}
|
||||
}
|
||||
}
|
114
build/windows/installer/project.nsi
Normal file
@ -0,0 +1,114 @@
|
||||
Unicode true
|
||||
|
||||
####
|
||||
## Please note: Template replacements don't work in this file. They are provided with default defines like
|
||||
## mentioned underneath.
|
||||
## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo.
|
||||
## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually
|
||||
## from outside of Wails for debugging and development of the installer.
|
||||
##
|
||||
## For development first make a wails nsis build to populate the "wails_tools.nsh":
|
||||
## > wails build --target windows/amd64 --nsis
|
||||
## Then you can call makensis on this file with specifying the path to your binary:
|
||||
## For a AMD64 only installer:
|
||||
## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app.exe
|
||||
## For a ARM64 only installer:
|
||||
## > makensis -DARG_WAILS_ARM64_BINARY=..\..\bin\app.exe
|
||||
## For a installer with both architectures:
|
||||
## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe
|
||||
####
|
||||
## The following information is taken from the ProjectInfo file, but they can be overwritten here.
|
||||
####
|
||||
## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}"
|
||||
## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}"
|
||||
## !define INFO_PRODUCTNAME "MyProduct" # Default "{{.Info.ProductName}}"
|
||||
## !define INFO_PRODUCTVERSION "1.0.0" # Default "{{.Info.ProductVersion}}"
|
||||
## !define INFO_COPYRIGHT "Copyright" # Default "{{.Info.Copyright}}"
|
||||
###
|
||||
## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
|
||||
## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||
####
|
||||
## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html
|
||||
####
|
||||
## Include the wails tools
|
||||
####
|
||||
!include "wails_tools.nsh"
|
||||
|
||||
# The version information for this two must consist of 4 parts
|
||||
VIProductVersion "${INFO_PRODUCTVERSION}.0"
|
||||
VIFileVersion "${INFO_PRODUCTVERSION}.0"
|
||||
|
||||
VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
|
||||
VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
|
||||
VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}"
|
||||
VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}"
|
||||
VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
|
||||
VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
|
||||
|
||||
# Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware
|
||||
ManifestDPIAware true
|
||||
|
||||
!include "MUI.nsh"
|
||||
|
||||
!define MUI_ICON "..\icon.ico"
|
||||
!define MUI_UNICON "..\icon.ico"
|
||||
# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
|
||||
!define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
|
||||
# !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer
|
||||
!insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
|
||||
!insertmacro MUI_PAGE_INSTFILES # Installing page.
|
||||
!insertmacro MUI_PAGE_FINISH # Finished installation page.
|
||||
|
||||
!insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer
|
||||
|
||||
## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1
|
||||
#!uninstfinalize 'signtool --file "%1"'
|
||||
#!finalize 'signtool --file "%1"'
|
||||
|
||||
Name "${INFO_PRODUCTNAME}"
|
||||
OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
|
||||
InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder).
|
||||
ShowInstDetails show # This will always show the installation details.
|
||||
|
||||
Function .onInit
|
||||
!insertmacro wails.checkArchitecture
|
||||
FunctionEnd
|
||||
|
||||
Section
|
||||
!insertmacro wails.setShellContext
|
||||
|
||||
!insertmacro wails.webview2runtime
|
||||
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
!insertmacro wails.files
|
||||
|
||||
CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||
|
||||
!insertmacro wails.associateFiles
|
||||
!insertmacro wails.associateCustomProtocols
|
||||
|
||||
!insertmacro wails.writeUninstaller
|
||||
SectionEnd
|
||||
|
||||
Section "uninstall"
|
||||
!insertmacro wails.setShellContext
|
||||
|
||||
RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
|
||||
|
||||
RMDir /r $INSTDIR
|
||||
|
||||
Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
|
||||
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
|
||||
|
||||
!insertmacro wails.unassociateFiles
|
||||
!insertmacro wails.unassociateCustomProtocols
|
||||
|
||||
!insertmacro wails.deleteUninstaller
|
||||
SectionEnd
|
249
build/windows/installer/wails_tools.nsh
Normal file
@ -0,0 +1,249 @@
|
||||
# DO NOT EDIT - Generated automatically by `wails build`
|
||||
|
||||
!include "x64.nsh"
|
||||
!include "WinVer.nsh"
|
||||
!include "FileFunc.nsh"
|
||||
|
||||
!ifndef INFO_PROJECTNAME
|
||||
!define INFO_PROJECTNAME "{{.Name}}"
|
||||
!endif
|
||||
!ifndef INFO_COMPANYNAME
|
||||
!define INFO_COMPANYNAME "{{.Info.CompanyName}}"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTNAME
|
||||
!define INFO_PRODUCTNAME "{{.Info.ProductName}}"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTVERSION
|
||||
!define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}"
|
||||
!endif
|
||||
!ifndef INFO_COPYRIGHT
|
||||
!define INFO_COPYRIGHT "{{.Info.Copyright}}"
|
||||
!endif
|
||||
!ifndef PRODUCT_EXECUTABLE
|
||||
!define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
|
||||
!endif
|
||||
!ifndef UNINST_KEY_NAME
|
||||
!define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||
!endif
|
||||
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
|
||||
|
||||
!ifndef REQUEST_EXECUTION_LEVEL
|
||||
!define REQUEST_EXECUTION_LEVEL "admin"
|
||||
!endif
|
||||
|
||||
RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
||||
|
||||
!ifdef ARG_WAILS_AMD64_BINARY
|
||||
!define SUPPORTS_AMD64
|
||||
!endif
|
||||
|
||||
!ifdef ARG_WAILS_ARM64_BINARY
|
||||
!define SUPPORTS_ARM64
|
||||
!endif
|
||||
|
||||
!ifdef SUPPORTS_AMD64
|
||||
!ifdef SUPPORTS_ARM64
|
||||
!define ARCH "amd64_arm64"
|
||||
!else
|
||||
!define ARCH "amd64"
|
||||
!endif
|
||||
!else
|
||||
!ifdef SUPPORTS_ARM64
|
||||
!define ARCH "arm64"
|
||||
!else
|
||||
!error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY"
|
||||
!endif
|
||||
!endif
|
||||
|
||||
!macro wails.checkArchitecture
|
||||
!ifndef WAILS_WIN10_REQUIRED
|
||||
!define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later."
|
||||
!endif
|
||||
|
||||
!ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED
|
||||
!define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
|
||||
!endif
|
||||
|
||||
${If} ${AtLeastWin10}
|
||||
!ifdef SUPPORTS_AMD64
|
||||
${if} ${IsNativeAMD64}
|
||||
Goto ok
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
!ifdef SUPPORTS_ARM64
|
||||
${if} ${IsNativeARM64}
|
||||
Goto ok
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
IfSilent silentArch notSilentArch
|
||||
silentArch:
|
||||
SetErrorLevel 65
|
||||
Abort
|
||||
notSilentArch:
|
||||
MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}"
|
||||
Quit
|
||||
${else}
|
||||
IfSilent silentWin notSilentWin
|
||||
silentWin:
|
||||
SetErrorLevel 64
|
||||
Abort
|
||||
notSilentWin:
|
||||
MessageBox MB_OK "${WAILS_WIN10_REQUIRED}"
|
||||
Quit
|
||||
${EndIf}
|
||||
|
||||
ok:
|
||||
!macroend
|
||||
|
||||
!macro wails.files
|
||||
!ifdef SUPPORTS_AMD64
|
||||
${if} ${IsNativeAMD64}
|
||||
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}"
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
!ifdef SUPPORTS_ARM64
|
||||
${if} ${IsNativeARM64}
|
||||
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}"
|
||||
${EndIf}
|
||||
!endif
|
||||
!macroend
|
||||
|
||||
!macro wails.writeUninstaller
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
SetRegView 64
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
|
||||
!macroend
|
||||
|
||||
!macro wails.deleteUninstaller
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
SetRegView 64
|
||||
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||
!macroend
|
||||
|
||||
!macro wails.setShellContext
|
||||
${If} ${REQUEST_EXECUTION_LEVEL} == "admin"
|
||||
SetShellVarContext all
|
||||
${else}
|
||||
SetShellVarContext current
|
||||
${EndIf}
|
||||
!macroend
|
||||
|
||||
# Install webview2 by launching the bootstrapper
|
||||
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
|
||||
!macro wails.webview2runtime
|
||||
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
|
||||
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
|
||||
!endif
|
||||
|
||||
SetRegView 64
|
||||
# If the admin key exists and is not empty then webview2 is already installed
|
||||
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||
${If} $0 != ""
|
||||
Goto ok
|
||||
${EndIf}
|
||||
|
||||
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
|
||||
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
|
||||
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||
${If} $0 != ""
|
||||
Goto ok
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
SetDetailsPrint both
|
||||
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
|
||||
SetDetailsPrint listonly
|
||||
|
||||
InitPluginsDir
|
||||
CreateDirectory "$pluginsdir\webview2bootstrapper"
|
||||
SetOutPath "$pluginsdir\webview2bootstrapper"
|
||||
File "tmp\MicrosoftEdgeWebview2Setup.exe"
|
||||
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
|
||||
|
||||
SetDetailsPrint both
|
||||
ok:
|
||||
!macroend
|
||||
|
||||
# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b
|
||||
!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
|
||||
; Backup the previously associated file class
|
||||
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" ""
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0"
|
||||
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}"
|
||||
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open"
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}`
|
||||
!macroend
|
||||
|
||||
!macro APP_UNASSOCIATE EXT FILECLASS
|
||||
; Backup the previously associated file class
|
||||
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup`
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0"
|
||||
|
||||
DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}`
|
||||
!macroend
|
||||
|
||||
!macro wails.associateFiles
|
||||
; Create file associations
|
||||
{{range .Info.FileAssociations}}
|
||||
!insertmacro APP_ASSOCIATE "{{.Ext}}" "{{.Name}}" "{{.Description}}" "$INSTDIR\{{.IconName}}.ico" "Open with ${INFO_PRODUCTNAME}" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\""
|
||||
|
||||
File "..\{{.IconName}}.ico"
|
||||
{{end}}
|
||||
!macroend
|
||||
|
||||
!macro wails.unassociateFiles
|
||||
; Delete app associations
|
||||
{{range .Info.FileAssociations}}
|
||||
!insertmacro APP_UNASSOCIATE "{{.Ext}}" "{{.Name}}"
|
||||
|
||||
Delete "$INSTDIR\{{.IconName}}.ico"
|
||||
{{end}}
|
||||
!macroend
|
||||
|
||||
!macro CUSTOM_PROTOCOL_ASSOCIATE PROTOCOL DESCRIPTION ICON COMMAND
|
||||
DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}"
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "" "${DESCRIPTION}"
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "URL Protocol" ""
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\DefaultIcon" "" "${ICON}"
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell" "" ""
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open" "" ""
|
||||
WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open\command" "" "${COMMAND}"
|
||||
!macroend
|
||||
|
||||
!macro CUSTOM_PROTOCOL_UNASSOCIATE PROTOCOL
|
||||
DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}"
|
||||
!macroend
|
||||
|
||||
!macro wails.associateCustomProtocols
|
||||
; Create custom protocols associations
|
||||
{{range .Info.Protocols}}
|
||||
!insertmacro CUSTOM_PROTOCOL_ASSOCIATE "{{.Scheme}}" "{{.Description}}" "$INSTDIR\${PRODUCT_EXECUTABLE},0" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\""
|
||||
|
||||
{{end}}
|
||||
!macroend
|
||||
|
||||
!macro wails.unassociateCustomProtocols
|
||||
; Delete app custom protocol associations
|
||||
{{range .Info.Protocols}}
|
||||
!insertmacro CUSTOM_PROTOCOL_UNASSOCIATE "{{.Scheme}}"
|
||||
{{end}}
|
||||
!macroend
|
15
build/windows/wails.exe.manifest
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<assemblyIdentity type="win32" name="com.wails.{{.Name}}" version="{{.Info.ProductVersion}}.0" processorArchitecture="*"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
BIN
frontend/dist/assets/001_微笑.1ec7a344.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/002_撇嘴.0279218b.png
vendored
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
frontend/dist/assets/003_色.e92bb91a.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/004_发呆.8d849292.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/005_得意.72060784.png
vendored
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/dist/assets/006_流泪.f52e5f23.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
frontend/dist/assets/007_害羞.414541cc.png
vendored
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
frontend/dist/assets/008_闭嘴.4b6c78a6.png
vendored
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
frontend/dist/assets/009_睡.75e64219.png
vendored
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
frontend/dist/assets/010_大哭.2cd2fee3.png
vendored
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
frontend/dist/assets/011_尴尬.70cfea1c.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/dist/assets/012_发怒.b88ce021.png
vendored
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
frontend/dist/assets/013_调皮.f3363541.png
vendored
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
frontend/dist/assets/014_呲牙.3cd1fb7c.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/015_惊讶.c9eb5e15.png
vendored
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/dist/assets/016_难过.5d872489.png
vendored
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
frontend/dist/assets/017_囧.59ee6551.png
vendored
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
frontend/dist/assets/018_抓狂.d1646df8.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/dist/assets/019_吐.51bb226f.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/020_偷笑.59941b0b.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/021_愉快.47582f99.png
vendored
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
frontend/dist/assets/022_白眼.ca492234.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/023_傲慢.651b4c79.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/024_困.4556c7db.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
frontend/dist/assets/025_惊恐.ed5cfeab.png
vendored
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/dist/assets/026_憨笑.6d317a05.png
vendored
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
frontend/dist/assets/027_悠闲.cef28253.png
vendored
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
frontend/dist/assets/028_咒骂.a26d48fa.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/029_疑问.aaa09269.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/dist/assets/030_嘘.40e8213d.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/031_晕.44e3541a.png
vendored
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
frontend/dist/assets/032_衰.1a3910a6.png
vendored
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
frontend/dist/assets/033_骷髅.3c9202dc.png
vendored
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
frontend/dist/assets/034_敲打.b2798ca7.png
vendored
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
frontend/dist/assets/035_再见.db23652c.png
vendored
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/dist/assets/036_擦汗.b46fa893.png
vendored
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
frontend/dist/assets/037_抠鼻.64bc8033.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/038_鼓掌.2a84e4c7.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/039_坏笑.4998b91f.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/040_右哼哼.27d8126d.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/dist/assets/041_鄙视.7e22890d.png
vendored
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
frontend/dist/assets/042_委屈.a5caf83a.png
vendored
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
frontend/dist/assets/043_快哭了.62b1b67c.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/044_阴险.3697222b.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/045_亲亲.dfa6bbdf.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/046_可怜.634845ad.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/047_笑脸.ab25a28c.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/048_生病.cd7aadb3.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/049_脸红.9ecb5c1c.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/050_破涕为笑.a3d2342d.png
vendored
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
frontend/dist/assets/051_恐惧.7af18313.png
vendored
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
frontend/dist/assets/052_失望.87e0479b.png
vendored
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/dist/assets/053_无语.6220ee7c.png
vendored
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/dist/assets/054_嘿哈.2116e692.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/dist/assets/055_捂脸.28f3a0d3.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/dist/assets/056_奸笑.9cf99423.png
vendored
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
frontend/dist/assets/057_机智.93c3d05a.png
vendored
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
frontend/dist/assets/058_皱眉.efe09ed7.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/059_耶.a6bc3d2b.png
vendored
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
frontend/dist/assets/060_吃瓜.a2a158de.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/061_加油.77c81f5b.png
vendored
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
frontend/dist/assets/062_汗.be95535c.png
vendored
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
frontend/dist/assets/063_天啊.a8355bf9.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/064_Emm.787be530.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/065_社会社会.a5f5902a.png
vendored
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
frontend/dist/assets/066_旺柴.7825a175.png
vendored
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/dist/assets/067_好的.a9fffc64.png
vendored
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
frontend/dist/assets/068_打脸.560c8d1f.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/069_哇.74cdcc27.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/070_翻白眼.ecb744e2.png
vendored
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
frontend/dist/assets/071_666.281fb9b6.png
vendored
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
frontend/dist/assets/072_让我看看.cee96a9f.png
vendored
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
frontend/dist/assets/073_叹气.712846f3.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
frontend/dist/assets/074_苦涩.4edf4f58.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/075_裂开.3fb97804.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/dist/assets/076_嘴唇.59b9c0be.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
frontend/dist/assets/077_爱心.a09c823b.png
vendored
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/dist/assets/078_心碎.9a4fb37d.png
vendored
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
frontend/dist/assets/079_拥抱.e529a46b.png
vendored
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
frontend/dist/assets/080_强.64fe98a8.png
vendored
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
frontend/dist/assets/081_弱.07ddf3a5.png
vendored
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
frontend/dist/assets/082_握手.aeb86265.png
vendored
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
frontend/dist/assets/083_胜利.e9ff0663.png
vendored
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/dist/assets/084_抱拳.0ae5f316.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
frontend/dist/assets/085_勾引.a4c3a7b4.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
frontend/dist/assets/086_拳头.2829fdbe.png
vendored
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
frontend/dist/assets/087_OK.fc42db3d.png
vendored
Normal file
After Width: | Height: | Size: 6.7 KiB |