@@ -4,9 +4,12 @@ import (
4
4
"context"
5
5
"flag"
6
6
"fmt"
7
+ "io"
7
8
"log"
9
+ "net/http"
8
10
"os"
9
11
"os/signal"
12
+ "path/filepath"
10
13
"regexp"
11
14
"strings"
12
15
25
28
pclient * piston.Client
26
29
)
27
30
31
+ const (
32
+ cblock int = iota
33
+ cgist
34
+ cfile
35
+ )
36
+
28
37
// code execution ouptput
29
38
var o chan string
30
39
@@ -72,29 +81,35 @@ func executionHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
72
81
73
82
// extract code block from message and execute code
74
83
var responseContent string
75
- if lang , codeBlock := codeBlockExtractor (m .Content ); lang != "" || codeBlock != "" {
84
+ ctype , lang , codeBlock := codeBlockExtractor (m .Message )
85
+ if lang != "" || codeBlock != "" {
76
86
go exec (m .ChannelID , codeBlock , m .Reference (), lang )
77
87
responseContent = <- o
78
88
} else {
79
89
return
80
90
}
81
91
82
- // send initial reply message containing output of code execution
83
- // "Run" button is injected in the message so the user may re run their code
84
- _ , _ = s .ChannelMessageSendComplex (m .ChannelID , & discordgo.MessageSend {
85
- Content : responseContent ,
86
- Reference : m .Reference (),
87
- Components : []discordgo.MessageComponent {
88
- discordgo.ActionsRow {
89
- Components : []discordgo.MessageComponent {
90
- discordgo.Button {
91
- Label : "Run" ,
92
- Style : discordgo .SuccessButton ,
93
- CustomID : "run" ,
94
- },
92
+ // only add run button for code block and gist execution
93
+ var runButton []discordgo.MessageComponent
94
+ if ctype != cfile {
95
+ runButton = []discordgo.MessageComponent {discordgo.ActionsRow {
96
+ Components : []discordgo.MessageComponent {
97
+ discordgo.Button {
98
+ Label : "Run" ,
99
+ Style : discordgo .SuccessButton ,
100
+ CustomID : "run" ,
95
101
},
96
102
},
97
103
},
104
+ }
105
+ }
106
+
107
+ // send initial reply message containing output of code execution
108
+ // "Run" button is injected in the message so the user may re run their code
109
+ _ , _ = s .ChannelMessageSendComplex (m .ChannelID , & discordgo.MessageSend {
110
+ Content : responseContent ,
111
+ Reference : m .Reference (),
112
+ Components : runButton ,
98
113
})
99
114
}
100
115
@@ -111,7 +126,7 @@ func reExecuctionHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
111
126
112
127
// extract code block from message and execute code
113
128
var responseContent string
114
- if lang , codeBlock := codeBlockExtractor (m . Content ); lang != "" || codeBlock != "" {
129
+ if _ , lang , codeBlock := codeBlockExtractor (m ); lang != "" || codeBlock != "" {
115
130
go exec (i .ChannelID , codeBlock , i .Message .Reference (), lang )
116
131
responseContent = <- o
117
132
} else {
@@ -157,46 +172,72 @@ func exec(channelID string, code string, messageReference *discordgo.MessageRefe
157
172
},
158
173
)
159
174
if err != nil {
160
- fmt .Println (err .Error ())
161
- _ , _ = s .ChannelMessageSendReply (channelID , err .Error (), messageReference )
175
+ o <- fmt .Sprintf (">>> Execution Failed [%s - %s]\n ```\n %s\n ```\n " , output .Language , output .Version , err )
162
176
}
163
177
164
178
o <- fmt .Sprintf (">>> Output [%s - %s]\n ```\n %s\n ```\n " , output .Language , output .Version , output .GetOutput ())
165
179
}
166
180
167
- func codeBlockExtractor (content string ) (string , string ) {
168
- // check to see if we are executing a code block
181
+ func codeBlockExtractor (m * discordgo.Message ) (int , string , string ) {
182
+ mc := m .Content
183
+ // syntax for executing a code block
169
184
// this is based on a writing standard in discord for writing code in a paragraph message block
170
185
// example message: ```go ... ```
171
186
rcb , _ := regexp .Compile ("run```.*" )
187
+ // syntax for executing a gist
172
188
rg , _ := regexp .Compile ("run https://gist.github.com/.*/.*" )
173
189
rgist , _ := regexp .Compile ("run https://gist.github.com/.*/" )
174
- c := strings .Split (content , "\n " )
175
- for bi , bb := range c {
176
- // extract gist language and code to execute
177
- if rg .MatchString (bb ) {
178
- gistID := rgist .ReplaceAllString (bb , "" )
179
- gist , _ , _ := gclient .Gists .Get (ctx , gistID )
180
- return strings .ToLower (* gist .Files ["helloworld.go" ].Language ), * gist .Files ["helloworld.go" ].Content
181
- }
190
+ // syntax for executing an attached file
191
+ rf , _ := regexp .Compile ("run *.*" )
182
192
193
+ c := strings .Split (mc , "\n " )
194
+ for bi , bb := range c {
183
195
// extract code block to execute
184
196
if rcb .MatchString (bb ) {
185
- lang := strings .TrimPrefix (string (rcb .Find ([]byte (content ))), "run```" )
197
+ lang := strings .TrimPrefix (string (rcb .Find ([]byte (mc ))), "run```" )
186
198
// find end of code block
187
199
var codeBlock string
188
200
endBlockRegx , _ := regexp .Compile ("```" )
189
- subArray := c [bi + 1 :]
190
201
191
- for ei , eb := range subArray {
202
+ sa := c [bi + 1 :]
203
+ for ei , eb := range sa {
192
204
if endBlockRegx .Match ([]byte (eb )) {
193
205
// create code block to execute
194
- codeBlock = strings .Join (subArray [:ei ], "\n " )
195
- return lang , codeBlock
206
+ codeBlock = strings .Join (sa [:ei ], "\n " )
207
+ return cblock , lang , codeBlock
208
+ }
209
+ }
210
+ }
211
+ // extract gist language and code to execute
212
+ if rg .MatchString (bb ) {
213
+ gistID := rgist .ReplaceAllString (bb , "" )
214
+ gist , _ , err := gclient .Gists .Get (ctx , gistID )
215
+ if err != nil {
216
+ log .Printf ("Failed to obtain gist: %v\n " , err )
217
+ }
218
+ return cgist , strings .ToLower (* gist .Files ["helloworld.go" ].Language ), * gist .Files ["helloworld.go" ].Content
219
+ }
220
+ // extract file language and code to execute
221
+ if rf .MatchString (bb ) {
222
+ if len (m .Attachments ) > 0 {
223
+ // handle 1 file in message attachments
224
+ f := m .Attachments [0 ]
225
+ // get language from extension
226
+ lang := strings .TrimLeft (filepath .Ext (f .Filename ), "." )
227
+ // get code from file
228
+ resp , err := http .Get (f .URL )
229
+ if err != nil {
230
+ log .Printf ("Failed GET http call to file attachment URL: %v\n " , err )
231
+ }
232
+ defer resp .Body .Close ()
233
+ codeBlock , err := io .ReadAll (resp .Body )
234
+ if err != nil {
235
+ log .Printf ("Failed to obtain code from response body: %v\n " , err )
196
236
}
237
+ return cfile , lang , string (codeBlock )
197
238
}
198
239
}
199
240
}
200
241
201
- return "" , ""
242
+ return - 1 , "" , ""
202
243
}
0 commit comments