-
Notifications
You must be signed in to change notification settings - Fork 2k
/
code_runner.go
221 lines (209 loc) · 7.81 KB
/
code_runner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Package runcode 基于 https://tool.runoob.com 的在线运行代码
package runcode
import (
"errors"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
control "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/zbputils/control/order"
"github.com/tidwall/gjson"
)
var (
templates = map[string]string{
"py2": "print 'Hello World!'",
"ruby": "puts \"Hello World!\";",
"rb": "puts \"Hello World!\";",
"php": "<?php\n\techo 'Hello World!';\n?>",
"javascript": "console.log(\"Hello World!\");",
"js": "console.log(\"Hello World!\");",
"node.js": "console.log(\"Hello World!\");",
"scala": "object Main {\n def main(args:Array[String])\n {\n println(\"Hello World!\")\n }\n\t\t\n}",
"go": "package main\n\nimport \"fmt\"\n\nfunc main() {\n fmt.Println(\"Hello, World!\")\n}",
"c": "#include <stdio.h>\n\nint main()\n{\n printf(\"Hello, World! \n\");\n return 0;\n}",
"c++": "#include <iostream>\nusing namespace std;\n\nint main()\n{\n cout << \"Hello World\";\n return 0;\n}",
"cpp": "#include <iostream>\nusing namespace std;\n\nint main()\n{\n cout << \"Hello World\";\n return 0;\n}",
"java": "public class HelloWorld {\n public static void main(String []args) {\n System.out.println(\"Hello World!\");\n }\n}",
"rust": "fn main() {\n println!(\"Hello World!\");\n}",
"rs": "fn main() {\n println!(\"Hello World!\");\n}",
"c#": "using System;\nnamespace HelloWorldApplication\n{\n class HelloWorld\n {\n static void Main(string[] args)\n {\n Console.WriteLine(\"Hello World!\");\n }\n }\n}",
"cs": "using System;\nnamespace HelloWorldApplication\n{\n class HelloWorld\n {\n static void Main(string[] args)\n {\n Console.WriteLine(\"Hello World!\");\n }\n }\n}",
"csharp": "using System;\nnamespace HelloWorldApplication\n{\n class HelloWorld\n {\n static void Main(string[] args)\n {\n Console.WriteLine(\"Hello World!\");\n }\n }\n}",
"shell": "echo 'Hello World!'",
"bash": "echo 'Hello World!'",
"erlang": "% escript will ignore the first line\n\nmain(_) ->\n io:format(\"Hello World!~n\").",
"perl": "print \"Hello, World!\n\";",
"python": "print(\"Hello, World!\")",
"py": "print(\"Hello, World!\")",
"swift": "var myString = \"Hello, World!\"\nprint(myString)",
"lua": "var myString = \"Hello, World!\"\nprint(myString)",
"pascal": "runcode Hello;\nbegin\n writeln ('Hello, world!')\nend.",
"kotlin": "fun main(args : Array<String>){\n println(\"Hello World!\")\n}",
"kt": "fun main(args : Array<String>){\n println(\"Hello World!\")\n}",
"r": "myString <- \"Hello, World!\"\nprint ( myString)",
"vb": "Module Module1\n\n Sub Main()\n Console.WriteLine(\"Hello World!\")\n End Sub\n\nEnd Module",
"typescript": "const hello : string = \"Hello World!\"\nconsole.log(hello)",
"ts": "const hello : string = \"Hello World!\"\nconsole.log(hello)",
}
table = map[string][2]string{
"py2": {"0", "py"},
"ruby": {"1", "rb"},
"rb": {"1", "rb"},
"php": {"3", "php"},
"javascript": {"4", "js"},
"js": {"4", "js"},
"node.js": {"4", "js"},
"scala": {"5", "scala"},
"go": {"6", "go"},
"c": {"7", "c"},
"c++": {"7", "cpp"},
"cpp": {"7", "cpp"},
"java": {"8", "java"},
"rust": {"9", "rs"},
"rs": {"9", "rs"},
"c#": {"10", "cs"},
"cs": {"10", "cs"},
"csharp": {"10", "cs"},
"shell": {"10", "sh"},
"bash": {"10", "sh"},
"erlang": {"12", "erl"},
"perl": {"14", "pl"},
"python": {"15", "py3"},
"py": {"15", "py3"},
"swift": {"16", "swift"},
"lua": {"17", "lua"},
"pascal": {"18", "pas"},
"kotlin": {"19", "kt"},
"kt": {"19", "kt"},
"r": {"80", "r"},
"vb": {"84", "vb"},
"typescript": {"1010", "ts"},
"ts": {"1010", "ts"},
}
)
func init() {
control.Register("runcode", order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "在线代码运行: \n" +
">runcode [language] [code block]\n" +
"模板查看: \n" +
">runcode [language] help\n" +
"支持语种: \n" +
"Go || Python || C/C++ || C# || Java || Lua \n" +
"JavaScript || TypeScript || PHP || Shell \n" +
"Kotlin || Rust || Erlang || Ruby || Swift \n" +
"R || VB || Py2 || Perl || Pascal || Scala",
}).ApplySingle(ctxext.DefaultSingle).OnRegex(`^>runcode\s(.+?)\s([\s\S]+)$`).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
language := ctx.State["regex_matched"].([]string)[1]
language = strings.ToLower(language)
if runType, exist := table[language]; !exist {
// 不支持语言
ctx.SendChain(
message.Text("> ", ctx.Event.Sender.NickName, "\n"),
message.Text("语言不是受支持的编程语种呢~"),
)
} else {
// 执行运行
block := ctx.State["regex_matched"].([]string)[2]
block = message.UnescapeCQCodeText(block)
if block == "help" {
// 输出模板
ctx.SendChain(
message.Text("> ", ctx.Event.Sender.NickName, " ", language, "-template:\n"),
message.Text(
">runcode ", language, "\n",
templates[language],
),
)
} else {
if output, err := runCode(block, runType); err != nil {
// 运行失败
ctx.SendChain(
message.Text("> ", ctx.Event.Sender.NickName, "\n"),
message.Text("ERROR: ", err),
)
} else {
// 运行成功
ctx.SendChain(
message.Text("> ", ctx.Event.Sender.NickName, "\n"),
message.Text(output),
)
}
}
}
})
}
func runCode(code string, runType [2]string) (string, error) {
// 对菜鸟api发送数据并返回结果
api := "https://tool.runoob.com/compile2.php"
header := http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded; charset=UTF-8"},
"Referer": []string{"https://c.runoob.com/"},
"User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0"},
}
val := url.Values{
"code": []string{code},
"token": []string{"4381fe197827ec87cbac9552f14ec62a"},
"stdin": []string{""},
"language": []string{runType[0]},
"fileext": []string{runType[1]},
}
// 发送请求
client := &http.Client{
Timeout: 15 * time.Second,
}
request, _ := http.NewRequest("POST", api, strings.NewReader(val.Encode()))
request.Header = header
body, err := client.Do(request)
if err != nil {
return "", err
}
defer body.Body.Close()
if body.StatusCode != http.StatusOK {
return "", errors.New("code not 200")
}
res, err := ioutil.ReadAll(body.Body)
if err != nil {
return "", err
}
// 结果处理
content := gjson.ParseBytes(res)
if e := content.Get("errors").Str; e != "\n\n" {
return "", errors.New(cutTooLong(clearNewLineSuffix(e)))
}
output := content.Get("output").Str
return cutTooLong(clearNewLineSuffix(output)), nil
}
// 清除末尾多余的换行符
func clearNewLineSuffix(text string) string {
for strings.HasSuffix(text, "\n") {
text = text[:len(text)-1]
}
return text
}
// 截断过长文本
func cutTooLong(text string) string {
temp := []rune(text)
count := 0
for i := range temp {
switch {
case temp[i] == 13 && i < len(temp)-1 && temp[i+1] == 10:
// 匹配 \r\n 跳过,等 \n 自己加
case temp[i] == 10:
count++
case temp[i] == 13:
count++
}
if count > 30 || i > 1000 {
temp = append(temp[:i-1], []rune("\n............\n............")...)
break
}
}
return string(temp)
}