Skip to content

Commit 42431f2

Browse files
authored
Merge pull request #10 from michaelassaf/feature/file-execution
Add file execution functionality
2 parents b62467b + 52669f6 commit 42431f2

File tree

1 file changed

+74
-33
lines changed

1 file changed

+74
-33
lines changed

main.go

+74-33
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import (
44
"context"
55
"flag"
66
"fmt"
7+
"io"
78
"log"
9+
"net/http"
810
"os"
911
"os/signal"
12+
"path/filepath"
1013
"regexp"
1114
"strings"
1215

@@ -25,6 +28,12 @@ var (
2528
pclient *piston.Client
2629
)
2730

31+
const (
32+
cblock int = iota
33+
cgist
34+
cfile
35+
)
36+
2837
// code execution ouptput
2938
var o chan string
3039

@@ -72,29 +81,35 @@ func executionHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
7281

7382
// extract code block from message and execute code
7483
var responseContent string
75-
if lang, codeBlock := codeBlockExtractor(m.Content); lang != "" || codeBlock != "" {
84+
ctype, lang, codeBlock := codeBlockExtractor(m.Message)
85+
if lang != "" || codeBlock != "" {
7686
go exec(m.ChannelID, codeBlock, m.Reference(), lang)
7787
responseContent = <-o
7888
} else {
7989
return
8090
}
8191

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",
95101
},
96102
},
97103
},
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,
98113
})
99114
}
100115

@@ -111,7 +126,7 @@ func reExecuctionHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
111126

112127
// extract code block from message and execute code
113128
var responseContent string
114-
if lang, codeBlock := codeBlockExtractor(m.Content); lang != "" || codeBlock != "" {
129+
if _, lang, codeBlock := codeBlockExtractor(m); lang != "" || codeBlock != "" {
115130
go exec(i.ChannelID, codeBlock, i.Message.Reference(), lang)
116131
responseContent = <-o
117132
} else {
@@ -157,46 +172,72 @@ func exec(channelID string, code string, messageReference *discordgo.MessageRefe
157172
},
158173
)
159174
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)
162176
}
163177

164178
o <- fmt.Sprintf(">>> Output [%s - %s]\n```\n%s\n```\n", output.Language, output.Version, output.GetOutput())
165179
}
166180

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
169184
// this is based on a writing standard in discord for writing code in a paragraph message block
170185
// example message: ```go ... ```
171186
rcb, _ := regexp.Compile("run```.*")
187+
// syntax for executing a gist
172188
rg, _ := regexp.Compile("run https://gist.github.com/.*/.*")
173189
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 *.*")
182192

193+
c := strings.Split(mc, "\n")
194+
for bi, bb := range c {
183195
// extract code block to execute
184196
if rcb.MatchString(bb) {
185-
lang := strings.TrimPrefix(string(rcb.Find([]byte(content))), "run```")
197+
lang := strings.TrimPrefix(string(rcb.Find([]byte(mc))), "run```")
186198
// find end of code block
187199
var codeBlock string
188200
endBlockRegx, _ := regexp.Compile("```")
189-
subArray := c[bi+1:]
190201

191-
for ei, eb := range subArray {
202+
sa := c[bi+1:]
203+
for ei, eb := range sa {
192204
if endBlockRegx.Match([]byte(eb)) {
193205
// 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)
196236
}
237+
return cfile, lang, string(codeBlock)
197238
}
198239
}
199240
}
200241

201-
return "", ""
242+
return -1, "", ""
202243
}

0 commit comments

Comments
 (0)