Evaluate gdscript code
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
extends Node

const prefix := "!e"

var expr := Expression.new()

const PRINT_FUNCS = {
	"print": "",
	"prints": " ",
	"printt": "\\t",
	"printraw": "",
}

const BLACKLISTED_OBJS = [
	"OS.",
	"File.",
	"GDScript.",
	"ClassDB.",
	"Expression.",
]

var script_append := "\n#----INSERTED-----#\n\nvar output: String"

const print_func_template := """
func {name}(arg1 = '', arg2 = '', arg3 = '', arg4 = '', arg5 = '', arg6 = '', arg7 = '', arg8 = '', arg9 = '') -> void:
	# Also call the built-in printing function
	{name}(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
	for argument in [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9]:
		output += str(argument) + '{separator}'
	output += '{end}'
"""

var output_message = "Output: ```%s```"

var func_replacements = [
	[compile("print\\s*\\("), "self.print("],
	[compile("prints\\s*\\("), "self.prints("],
	[compile("printt\\s*\\("), "self.printt("],
	[compile("printraw\\s*\\("), "self.printraw("],
]


func compile(source: String) -> RegEx:
	var reg := RegEx.new()
	reg.compile(source)
	return reg


func _ready():
	var bot := DiscordBot.new()
	add_child(bot)
	var file = File.new()
	var err = file.open("res://token", File.READ)
	var token
	if err == OK:
		token = file.get_as_text()
	elif OS.has_environment("TOKEN"):
		token = OS.get_environment("TOKEN")
	else:
		push_error("token missing")
	file.close()
	bot.TOKEN = token
	bot.connect("bot_ready", self, "_on_bot_ready")
	bot.connect("message_create", self, "_on_message_create")
	bot.login()
	for print_func in PRINT_FUNCS:
		script_append += print_func_template.format(
			{
				name = print_func,
				separator = PRINT_FUNCS[print_func],
				end = "" if print_func == "printraw" else "\\n",
			}
		)


func _on_bot_ready(bot: DiscordBot):
	bot.set_presence({"activity": {"type": "Game", "name": "Evaluating GDScript"}})
	print("Logged in as " + bot.user.username + "#" + bot.user.discriminator)
	print("Listening on " + str(bot.channels.size()) + " channels and " + str(bot.guilds.size()) + " guilds.")


func _on_message_create(bot: DiscordBot, message: Message, _channel: Dictionary):
	var split = message.content.split(" ")
	if message.author.bot or not prefix in split[0]:
		return
	var reg := compile("`+([^`]+)`+")
	var res = reg.search(message.content)
	var code: String = res.strings[1] if res else PoolStringArray(Array(split).slice(1, len(split))).join(" ")
	var lines = code.split("\n")
	if res and lines[0] in ["swift", "py", "c", "c++"]:
		lines.remove(0)
		code = lines.join("\n")

	for b in BLACKLISTED_OBJS:
		code = code.replace(b, "Node.")

	code = code.strip_edges()

	var original_code = code  # save it for later

	var error
	if lines.size() > 1 or "." in code:
		if code.find("func _ready()") == -1:
			code = "func _ready():\n" + code.indent("\t") + "\n"

		if not "extends" in lines[0]:
			code = "extends Node\n" + code

		for row in func_replacements:
			code = row[0].sub(code, row[1], true)

		code += script_append

		var script := GDScript.new()
		script.source_code = code
		error = script.reload()
		if error == OK:
			var instance: Node = script.new()
			if is_instance_valid(instance):
				add_child(instance)
				yield(get_tree(), "idle_frame")
				var o = instance.output.strip_edges()
				bot.reply(message, output_message % (o if o else "null"))
				instance.queue_free()
				return
	else:
		var e: int = expr.parse(code, [])

		if e == OK:
			var o = str(expr.execute([], null, true))
			if not expr.has_execute_failed():
				bot.reply(message, output_message % (o if o else "null"))
				return
		error = expr.get_error_text()

	bot.reply(message, "Execution of \n```swift\n%s\n``` Has failed. Error: %s" % [original_code, error])