Skip to content

Commit b712209

Browse files
committed
feat: implement scan for more information and optimize output
1 parent 77fc612 commit b712209

File tree

7 files changed

+432
-16
lines changed

7 files changed

+432
-16
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
**Todo:**
2020

21-
- [ ] scan more information
21+
- [x] scan more information (need networking)
2222
- [x] json beautify
2323
- [ ] javascript beautify
2424
- [ ] html beautify

cmd/scan-tui.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"github.com/charmbracelet/bubbles/help"
6+
"github.com/charmbracelet/bubbles/key"
7+
"github.com/charmbracelet/bubbles/progress"
8+
"github.com/charmbracelet/bubbles/table"
9+
tea "github.com/charmbracelet/bubbletea"
10+
"github.com/charmbracelet/lipgloss"
11+
"github.com/fatih/color"
12+
"regexp"
13+
"strconv"
14+
"strings"
15+
"wxapkg/util"
16+
)
17+
18+
var baseStyle = lipgloss.NewStyle().
19+
BorderStyle(lipgloss.ThickBorder()).
20+
BorderForeground(lipgloss.Color("240"))
21+
22+
type scanTui struct {
23+
table table.Model
24+
raw []util.WxidInfo
25+
selected *util.WxidInfo
26+
progress progress.Model
27+
}
28+
29+
func newScanTui(wxidInfo []util.WxidInfo) *scanTui {
30+
var prog = progress.New(progress.WithScaledGradient("#FF7CCB", "#FDFF8C"))
31+
32+
var rows = make([]table.Row, 0, len(wxidInfo))
33+
for _, info := range wxidInfo {
34+
rows = append(rows, []string{
35+
info.Nickname,
36+
info.PrincipalName,
37+
info.Description,
38+
})
39+
}
40+
41+
var title = color.New(color.FgMagenta, color.Bold).Sprint
42+
columns := []table.Column{
43+
{Title: title("Name"), Width: 20},
44+
{Title: title("Developer"), Width: 30},
45+
{Title: title("Description"), Width: 40},
46+
}
47+
prog.Width = 0
48+
for _, c := range columns {
49+
prog.Width += c.Width
50+
}
51+
prog.Width += len(columns) * 2
52+
53+
var height = 10
54+
if len(rows) < height {
55+
height = len(rows)
56+
}
57+
t := table.New(
58+
table.WithColumns(columns),
59+
table.WithRows(rows),
60+
table.WithFocused(true),
61+
table.WithHeight(height),
62+
)
63+
t.Rows()
64+
65+
s := table.DefaultStyles()
66+
s.Header = s.Header.
67+
BorderStyle(lipgloss.NormalBorder()).
68+
BorderForeground(lipgloss.Color("240")).
69+
BorderBottom(true).
70+
Bold(false)
71+
s.Selected = s.Selected.
72+
Foreground(lipgloss.Color("229")).
73+
Background(lipgloss.Color("57")).
74+
Bold(false)
75+
t.SetStyles(s)
76+
77+
return &scanTui{
78+
table: t,
79+
raw: wxidInfo,
80+
progress: prog,
81+
}
82+
}
83+
84+
func (s *scanTui) Init() tea.Cmd {
85+
return nil
86+
}
87+
88+
func (s *scanTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
89+
var cmd tea.Cmd
90+
switch msg := msg.(type) {
91+
case tea.KeyMsg:
92+
switch msg.String() {
93+
case "esc":
94+
if s.table.Focused() {
95+
s.table.Blur()
96+
} else {
97+
s.table.Focus()
98+
}
99+
case "q", "ctrl+c":
100+
return s, tea.Quit
101+
case "enter":
102+
s.selected = &s.raw[s.table.Cursor()]
103+
return s, tea.Quit
104+
}
105+
}
106+
s.table, cmd = s.table.Update(msg)
107+
return s, cmd
108+
}
109+
110+
func (s *scanTui) renderProgress() string {
111+
var prog = s.progress.ViewAs(float64(s.table.Cursor()+1) / float64(len(s.raw)))
112+
var p = regexp.MustCompile(`\d{1,3}%`).FindString(prog)
113+
format := "%" + strconv.Itoa(len(p)) + "s"
114+
newStr := fmt.Sprintf(format, fmt.Sprintf("%d/%d", s.table.Cursor()+1, len(s.raw)))
115+
prog = strings.Replace(prog, p, newStr, 1)
116+
117+
return prog
118+
}
119+
120+
func (s *scanTui) renderDetail() string {
121+
var result = ""
122+
123+
var info = s.raw[s.table.Cursor()]
124+
125+
var link = color.New(color.Italic, color.Underline).Sprint
126+
var title = color.New(color.FgMagenta, color.Bold).Sprint
127+
var content = color.CyanString
128+
129+
if info.Error != "" {
130+
result += title(" error: ") + color.RedString(info.Error) + "\n"
131+
}
132+
133+
if info.Error == "" {
134+
result += title(" wxid: ") + content(info.Wxid) + "\n"
135+
result += title(" Name: ") + content(info.Nickname) + "\n"
136+
result += title(" Developer: ") + content(info.PrincipalName) + "\n"
137+
result += title(" Description: ") + content(info.Description) + "\n"
138+
}
139+
140+
result += title(" Location: ") + content(link(info.Location)) + "\n"
141+
142+
if info.Error == "" {
143+
result += title(" Avatar: ") + content(link(info.Avatar)) + "\n"
144+
}
145+
146+
result += title(" All information see '") + content(".\\"+util.CachePath) + title("'")
147+
148+
return result
149+
}
150+
151+
func (s *scanTui) renderHelp() string {
152+
return help.New().ShortHelpView([]key.Binding{
153+
key.NewBinding(
154+
key.WithKeys("enter"),
155+
key.WithHelp("enter", "unpack"),
156+
),
157+
key.NewBinding(
158+
key.WithKeys("up", "k"),
159+
key.WithHelp("↑/k", "move up"),
160+
),
161+
key.NewBinding(
162+
key.WithKeys("down", "j"),
163+
key.WithHelp("↓/j", "move down"),
164+
),
165+
key.NewBinding(
166+
key.WithKeys("q", "ctrl+c"),
167+
key.WithHelp("q", "exit"),
168+
),
169+
})
170+
}
171+
172+
func (s *scanTui) View() string {
173+
var result = ""
174+
result += "" + s.renderProgress() + "\n"
175+
result += baseStyle.Render(s.table.View()) + "\n"
176+
result += s.renderDetail() + "\n"
177+
result += "\n " + s.renderHelp() + "\n"
178+
179+
return result
180+
}

cmd/scan.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package cmd
22

33
import (
44
"fmt"
5+
tea "github.com/charmbracelet/bubbletea"
56
"github.com/fatih/color"
67
"github.com/spf13/cobra"
78
"os"
89
"path/filepath"
910
"regexp"
11+
"wxapkg/util"
1012
)
1113

1214
var scanCmd = &cobra.Command{
@@ -27,14 +29,39 @@ var scanCmd = &cobra.Command{
2729
return
2830
}
2931

32+
var wxidInfos = make([]util.WxidInfo, 0, len(files))
3033
for _, file := range files {
3134
if !file.IsDir() || !regAppId.MatchString(file.Name()) {
3235
continue
3336
}
3437

35-
var id = regAppId.FindStringSubmatch(file.Name())[1]
36-
fmt.Printf("%s %s\n", color.GreenString(id), color.CyanString(filepath.Join(root, file.Name()))) // todo scan file
38+
var wxid = regAppId.FindStringSubmatch(file.Name())[1]
39+
info, err := util.WxidQuery.Query(wxid)
40+
info.Location = filepath.Join(root, file.Name())
41+
info.Wxid = wxid
42+
if err != nil {
43+
info.Error = fmt.Sprintf("%v", err)
44+
}
45+
46+
wxidInfos = append(wxidInfos, info)
47+
}
48+
49+
var tui = newScanTui(wxidInfos)
50+
if _, err := tea.NewProgram(tui, tea.WithAltScreen()).Run(); err != nil {
51+
color.Red("Error running program: %v", err)
52+
os.Exit(1)
3753
}
54+
55+
if tui.selected == nil {
56+
return
57+
}
58+
59+
output := tui.selected.Wxid
60+
_ = unpackCmd.Flags().Set("root", tui.selected.Location)
61+
_ = unpackCmd.Flags().Set("output", output)
62+
detailFilePath := filepath.Join(output, "detail.json")
63+
unpackCmd.Run(unpackCmd, []string{"detailFilePath", detailFilePath})
64+
_ = os.WriteFile(detailFilePath, []byte(tui.selected.Json()), 0600)
3865
},
3966
}
4067

cmd/unpack.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ import (
1717
"os"
1818
"path/filepath"
1919
"regexp"
20+
"sort"
2021
"sync"
2122
"wxapkg/util"
2223
)
2324

24-
var logger = color.New()
2525
var programName = filepath.Base(os.Args[0])
2626
var unpackCmd = &cobra.Command{
2727
Use: "unpack",
@@ -38,6 +38,8 @@ var unpackCmd = &cobra.Command{
3838
dirs, err := os.ReadDir(root)
3939
util.Fatal(err)
4040

41+
color.Cyan("[+] unpack root '%s' with %d threads\n", root, thread)
42+
4143
var allFileCount = 0
4244
for _, subDir := range dirs {
4345
subOutput := filepath.Join(output, subDir.Name())
@@ -51,14 +53,29 @@ var unpackCmd = &cobra.Command{
5153
util.Fatal(err)
5254
allFileCount += fileCount
5355

54-
logger.Println(color.YellowString("\r[+] unpacked %5d files from '%s'", fileCount, file))
56+
rel, _ := filepath.Rel(filepath.Dir(root), file)
57+
color.Yellow("\r[+] unpacked %5d files from '%s'", fileCount, rel)
5558
}
5659
}
5760

58-
logger.Println(color.CyanString("[+] all %d files saved to '%s'", allFileCount, output))
59-
logger.Println(color.CyanString("[+] statistics:"))
61+
color.Cyan("[+] all %d files saved to '%s'\n", allFileCount, output)
62+
if len(args) == 2 && "detailFilePath" == args[0] {
63+
color.Cyan("[+] mini program detail info saved to '%s'\n", args[1])
64+
}
65+
66+
color.Cyan("[+] extension statistics:\n")
67+
68+
var keys [][]interface{}
6069
for k, v := range exts {
61-
logger.Println(color.CyanString(" - %5d %-5s files", v, k))
70+
keys = append(keys, []interface{}{k, v})
71+
}
72+
73+
sort.Slice(keys, func(i, j int) bool {
74+
return keys[i][1].(int) > keys[j][1].(int)
75+
})
76+
77+
for _, kk := range keys {
78+
color.Cyan(" - %-5s %5d\n", kk[0], kk[1])
6279
}
6380
},
6481
}
@@ -128,6 +145,7 @@ func unpack(decryptedData []byte, unpackRoot string, thread int) (int, error) {
128145
wg.Add(thread)
129146
var locker = sync.Mutex{}
130147
var count = 0
148+
var colorPrint = color.New()
131149
for i := 0; i < thread; i++ {
132150
go func() {
133151
defer wg.Done()
@@ -146,10 +164,9 @@ func unpack(decryptedData []byte, unpackRoot string, thread int) (int, error) {
146164
err = os.WriteFile(outputFilePath, beautify, 0600)
147165
util.Fatal(err)
148166

149-
//color.Green("(%d/%d) saved '%s'", i+1, fileCount, outputFilePath)
150167
locker.Lock()
151168
count++
152-
logger.Printf(color.GreenString("\runpack %d/%d", count, fileCount))
169+
_, _ = colorPrint.Print(color.GreenString("\runpack %d/%d", count, fileCount))
153170
locker.Unlock()
154171
}
155172
}()
@@ -192,7 +209,7 @@ func fileBeautify(name string, data []byte) (result []byte) {
192209
func parseWxid(root string) (string, error) {
193210
var regAppId = regexp.MustCompile(`(wx[0-9a-f]{16})`)
194211
if !regAppId.MatchString(filepath.Base(root)) {
195-
return "", errors.New("the path is not a mimi program path")
212+
return "", errors.New("the path is not a mini program path")
196213
}
197214

198215
return regAppId.FindStringSubmatch(filepath.Base(root))[1], nil

go.mod

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,34 @@ module wxapkg
33
go 1.19
44

55
require (
6+
github.com/charmbracelet/bubbles v0.15.0
7+
github.com/charmbracelet/bubbletea v0.24.1
8+
github.com/charmbracelet/lipgloss v0.7.1
69
github.com/fatih/color v1.15.0
710
github.com/spf13/cobra v1.7.0
811
github.com/tidwall/pretty v1.2.1
12+
github.com/wux1an/fake-useragent v1.1.0
913
golang.org/x/crypto v0.7.0
1014
)
1115

1216
require (
17+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
18+
github.com/charmbracelet/harmonica v0.2.0 // indirect
19+
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
1320
github.com/inconshreveable/mousetrap v1.1.0 // indirect
21+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
1422
github.com/mattn/go-colorable v0.1.13 // indirect
15-
github.com/mattn/go-isatty v0.0.17 // indirect
23+
github.com/mattn/go-isatty v0.0.19 // indirect
24+
github.com/mattn/go-localereader v0.0.1 // indirect
25+
github.com/mattn/go-runewidth v0.0.14 // indirect
26+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
27+
github.com/muesli/cancelreader v0.2.2 // indirect
28+
github.com/muesli/reflow v0.3.0 // indirect
29+
github.com/muesli/termenv v0.15.1 // indirect
30+
github.com/rivo/uniseg v0.4.4 // indirect
1631
github.com/spf13/pflag v1.0.5 // indirect
17-
golang.org/x/sys v0.6.0 // indirect
32+
golang.org/x/sync v0.2.0 // indirect
33+
golang.org/x/sys v0.8.0 // indirect
34+
golang.org/x/term v0.8.0 // indirect
35+
golang.org/x/text v0.9.0 // indirect
1836
)

0 commit comments

Comments
 (0)