Skip to content

Commit 3d15c7c

Browse files
committed
scriptcat 骨架 & GMXHR GmNotification
1 parent 23f7fa4 commit 3d15c7c

12 files changed

Lines changed: 447 additions & 35 deletions

File tree

cmd/scriptcat/exec.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package main
22

33
import (
4+
"io/ioutil"
5+
46
"github.com/scriptscat/cloudcat/pkg/scriptcat"
7+
"github.com/scriptscat/cloudcat/pkg/utils"
58
"github.com/spf13/cobra"
6-
"io/ioutil"
79
)
810

911
type execCmd struct {
12+
cookiefile string
1013
}
1114

1215
func newExecCmd() *execCmd {
@@ -19,6 +22,7 @@ func (e *execCmd) Commands() []*cobra.Command {
1922
Short: "执行一个脚本猫脚本",
2023
RunE: e.exec,
2124
}
25+
ret.Flags().StringVarP(&e.cookiefile, "cookiefile", "c", "", "设置cookie文件")
2226

2327
return []*cobra.Command{ret}
2428
}
@@ -35,5 +39,14 @@ func (e *execCmd) exec(cmd *cobra.Command, args []string) error {
3539
return err
3640
}
3741

42+
opts := make([]scriptcat.Option, 0)
43+
if e.cookiefile != "" {
44+
jar, err := utils.ReadCookie(e.cookiefile)
45+
if err != nil {
46+
return err
47+
}
48+
opts = append(opts, scriptcat.WithCookie(jar))
49+
}
50+
3851
return sc.Run(string(script))
3952
}

pkg/executor/context.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ func NewContext(executor *Executor, opt ...Option) (*Context, error) {
1616
iso: executor.iso,
1717
global: global,
1818
}
19-
for _, o := range opt {
20-
o(options)
21-
}
2219
ctx, err := v8go.NewContext(executor.iso, options.global)
2320
if err != nil {
2421
return nil, err
2522
}
23+
options.ctx = ctx
24+
for _, o := range opt {
25+
o(options)
26+
if options.err != nil {
27+
return nil, err
28+
}
29+
}
2630
return &Context{
2731
ctx: ctx,
2832
}, nil

pkg/executor/executor_test.go

Lines changed: 0 additions & 14 deletions
This file was deleted.

pkg/executor/gm.go

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,44 @@
11
package executor
22

33
import (
4-
"net/http"
5-
4+
"github.com/sirupsen/logrus"
65
"rogchap.com/v8go"
76
)
87

9-
func globalFunc(opts *Options, name string, callback v8go.FunctionCallback) {
10-
f, err := v8go.NewFunctionTemplate(opts.iso, callback)
11-
if err != nil {
12-
opts.err = err
13-
return
14-
}
15-
if err := opts.global.Set(name, f); err != nil {
16-
opts.err = err
17-
}
18-
}
19-
20-
func GmXmlHttpRequest(jar http.CookieJar) Option {
8+
func GmNotification() Option {
219
return func(opts *Options) {
22-
globalFunc(opts, "GM_xmlhttpRequest", func(info *v8go.FunctionCallbackInfo) *v8go.Value {
10+
globalFunc(opts, "GM_notification", func(info *v8go.FunctionCallbackInfo) *v8go.Value {
11+
if len(info.Args()) == 0 {
12+
return nil
13+
}
14+
var title, text string
15+
if info.Args()[0].IsObject() {
16+
arg1, err := info.Args()[0].AsObject()
17+
if err != nil {
18+
return nil
19+
}
20+
title = getObjString(arg1, "title")
21+
text = getObjString(arg1, "text")
22+
if fn := getFunction(arg1, "ondone"); fn != nil {
23+
fn.Call()
24+
}
25+
if len(info.Args()) == 2 && info.Args()[1].IsFunction() {
26+
fn, err := info.Args()[0].AsFunction()
27+
if err != nil {
28+
return nil
29+
}
30+
fn.Call()
31+
}
32+
} else {
33+
switch len(info.Args()) {
34+
case 2:
35+
text = getString(info.Args()[1])
36+
fallthrough
37+
case 1:
38+
title = getString(info.Args()[0])
39+
}
40+
}
41+
opts.log(logrus.InfoLevel, "Notification: %v %v", title, text)
2342

2443
return nil
2544
})

pkg/executor/gmxhr.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package executor
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"net/url"
10+
"strings"
11+
"time"
12+
13+
"github.com/sirupsen/logrus"
14+
"rogchap.com/v8go"
15+
)
16+
17+
func globalFunc(opts *Options, name string, callback v8go.FunctionCallback) {
18+
f, err := v8go.NewFunctionTemplate(opts.iso, callback)
19+
if err != nil {
20+
opts.err = err
21+
return
22+
}
23+
if err := opts.ctx.Global().Set(name, f.GetFunction(opts.ctx)); err != nil {
24+
opts.err = err
25+
}
26+
}
27+
28+
func GmXmlHttpRequest(jar http.CookieJar) Option {
29+
return func(opts *Options) {
30+
globalFunc(opts, "GM_xmlhttpRequest", func(info *v8go.FunctionCallbackInfo) *v8go.Value {
31+
//TODO: 实现代理等
32+
cli := &http.Client{
33+
Transport: nil,
34+
CheckRedirect: nil,
35+
Jar: jar,
36+
Timeout: time.Second * 30,
37+
}
38+
if len(info.Args()) != 1 {
39+
opts.log(logrus.ErrorLevel, "GMXHR number of parameters")
40+
return nil
41+
}
42+
arg := info.Args()[0]
43+
if !arg.IsObject() {
44+
opts.log(logrus.ErrorLevel, "GMXHR arg not object")
45+
return nil
46+
}
47+
details := arg.Object()
48+
var err error
49+
defer func() {
50+
if err != nil {
51+
if onerror := getFunction(details, "onerror"); onerror != nil {
52+
_, _ = onerror.Call()
53+
}
54+
}
55+
}()
56+
method := strings.ToUpper(getObjString(details, "method"))
57+
u := getObjString(details, "url")
58+
var data io.Reader
59+
if method != "GET" {
60+
data = bytes.NewBufferString(getObjString(details, "data"))
61+
}
62+
req, err := http.NewRequest(method, u, data)
63+
if err != nil {
64+
opts.log(logrus.ErrorLevel, "GMXHR New Request: %v", err)
65+
return nil
66+
}
67+
if cookie := getObjString(details, "cookie"); cookie != "" {
68+
if anonymous := getObjBool(details, "anonymous"); !anonymous && jar != nil {
69+
u, _ := url.Parse(u)
70+
cookies := jar.Cookies(u)
71+
for _, v := range cookies {
72+
cookie = fmt.Sprintf("%v=%v;", v.Name, v.Value) + cookie
73+
}
74+
}
75+
req.Header.Add("Cookie", cookie)
76+
}
77+
78+
if headers := getObject(details, "headers"); headers != nil {
79+
headersMap := make(map[string]string)
80+
b, _ := headers.MarshalJSON()
81+
if err := json.Unmarshal(b, &headersMap); err == nil {
82+
for k, v := range headersMap {
83+
req.Header.Set(k, v)
84+
}
85+
}
86+
}
87+
88+
if timeout := getNumber(details, "timeout"); timeout != 0 {
89+
cli.Timeout = time.Duration(timeout) * time.Millisecond
90+
}
91+
92+
go func() {
93+
var err error
94+
defer func() {
95+
if err != nil {
96+
if onerror := getFunction(details, "onerror"); onerror != nil {
97+
_, _ = onerror.Call()
98+
}
99+
}
100+
}()
101+
resp, err := cli.Do(req)
102+
if err != nil {
103+
opts.log(logrus.ErrorLevel, "GMXHR Request: %v", err)
104+
if err == http.ErrHandlerTimeout {
105+
if ontimeout := getFunction(details, "ontimeout"); ontimeout != nil {
106+
_, _ = ontimeout.Call()
107+
}
108+
return
109+
}
110+
return
111+
}
112+
113+
// 处理resp
114+
if onload := getFunction(details, "onload"); onload != nil {
115+
var xhrResp *v8go.ObjectTemplate
116+
xhrResp, err = v8go.NewObjectTemplate(opts.iso)
117+
if err != nil {
118+
opts.log(logrus.ErrorLevel, "GMXHR Respond: %v", err)
119+
if onerror := getFunction(details, "onerror"); onerror != nil {
120+
_, _ = onerror.Call()
121+
}
122+
return
123+
}
124+
xhrResp.Set("finalUrl", u)
125+
xhrResp.Set("readyState", 4)
126+
responseHeaders := ""
127+
for k, v := range resp.Header {
128+
for _, v := range v {
129+
responseHeaders = responseHeaders + fmt.Sprintf("%s: %s\n", k, v)
130+
}
131+
}
132+
xhrResp.Set("responseHeaders", responseHeaders)
133+
var body []byte
134+
body, err = io.ReadAll(resp.Body)
135+
xhrResp.Set("status", resp.StatusCode)
136+
xhrResp.Set("responseText", string(body))
137+
138+
var arg *v8go.Object
139+
if arg, err = xhrResp.NewInstance(info.Context()); err != nil {
140+
opts.log(logrus.ErrorLevel, "GMXHR Respond: %v", err)
141+
return
142+
}
143+
144+
if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
145+
if v, err := v8go.JSONParse(info.Context(), string(body)); err != nil {
146+
arg.Set("response", string(body))
147+
} else {
148+
arg.Set("response", v)
149+
}
150+
} else {
151+
arg.Set("response", string(body))
152+
}
153+
154+
onload.Call(arg)
155+
}
156+
157+
}()
158+
return nil
159+
})
160+
}
161+
}

pkg/executor/gmxhr_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package executor
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
"testing"
7+
8+
"github.com/sirupsen/logrus"
9+
"github.com/stretchr/testify/assert"
10+
"rogchap.com/v8go"
11+
)
12+
13+
func TestGmXmlHttpRequest(t *testing.T) {
14+
iso, _ := NewExecutor()
15+
16+
ctx, _ := NewContext(iso, WithLogger(logrus.StandardLogger().Logf), GmNotification(), GmXmlHttpRequest(nil), Console())
17+
18+
ret, err := iso.Run(ctx, `
19+
function main() {
20+
return new Promise(resolve => {
21+
GM_xmlhttpRequest({
22+
method: 'GET',
23+
url: 'https://api.bilibili.com/x/web-interface/nav',
24+
headers: {
25+
"Referer": "https://www.bilibili.com",
26+
"Origin": "https://www.bilibili.com"
27+
},
28+
onload(xhr) {
29+
console.log("qwe123",xhr,xhr.response,xhr.response.code);
30+
switch (xhr.response.code) {
31+
case -101:
32+
GM_notification({
33+
title: 'bilibili自动签到 - ScriptCat',
34+
text: '哔哩哔哩签到失败,账号未登录,请先登录',
35+
});
36+
resolve();
37+
break;
38+
default:
39+
resolve();
40+
}
41+
},
42+
onerror() {
43+
console.log("error");
44+
resolve();
45+
}
46+
})
47+
})
48+
}
49+
main();
50+
`)
51+
assert.Nil(t, err)
52+
p, err := ret.AsPromise()
53+
assert.Nil(t, err)
54+
l := sync.WaitGroup{}
55+
l.Add(1)
56+
p.Then(func(info *v8go.FunctionCallbackInfo) *v8go.Value {
57+
l.Done()
58+
return nil
59+
})
60+
p.Catch(func(info *v8go.FunctionCallbackInfo) *v8go.Value {
61+
l.Done()
62+
return nil
63+
})
64+
l.Wait()
65+
assert.Equal(t, v8go.Fulfilled, p.State())
66+
fmt.Println(p.Result().String())
67+
}

0 commit comments

Comments
 (0)