-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMain.gd
213 lines (171 loc) · 6.94 KB
/
Main.gd
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
extends Node2D
signal interaction_create(world, bot, interaction, data)
const prefix = "gd."
var interactions = {}
var commands = {}
var command_aliases = {}
var application_commands = {}
var bookmarks = {}
var favorites = {}
func read_env():
var env = {}
var file = File.new()
var err = file.open("res://.env", File.READ)
var line = ""
while not file.eof_reached():
line = file.get_line()
var tokens = Array(line.split("="))
if(tokens.size() != 2): continue
var key = tokens.pop_front()
var value = PoolStringArray(tokens).join("=")
env[key] = value.lstrip("\"").rstrip("\"")
return env
func _ready() -> void:
var bot = DiscordBot.new()
add_child(bot)
var env = read_env()
# Try to read token from global environment variables
var token = OS.get_environment("DISCORD_BOT_TOKEN")
if not token or (token and len(token) < 10):
# Read token from local .env file
token = env["DISCORD_BOT_TOKEN"]
bot.TOKEN = token
bot.INTENTS = 4609
bot.connect("bot_ready", self, "_on_bot_ready")
bot.connect("message_create", self, "_on_message_create")
bot.connect("interaction_create", self, "_on_interaction_create")
bot.login()
_load_commands(bot)
_load_application_commands(bot)
func _on_bot_ready(bot: DiscordBot):
print("Logged in as " + bot.user.username + "#" + bot.user.discriminator)
bot.set_presence({
"activity": {
"type": "Game",
"name": "Discord.gd Tutorial v2 | Godot Engine"
}
})
# Registering commands is not needed on every run,
# only register the command if you change any options
# or if you add/remove commands
# For development use the single server command and
# once you have the commands working you can register
# them as global commands
# -----Single server (updates instantly)
#_register_application_commands(bot, "guild_id_here")
#_register_application_commands(bot, "373766421882077186")
# -----Global (may take upto 1hr to update)
_register_application_commands(bot)
func _on_message_create(bot: DiscordBot, message: Message, channel: Dictionary) -> void:
if message.author.bot or not message.content.begins_with(prefix):
return
# Make sure to use trim_prefix() instead of lstrip()
var raw_content = message.content.trim_prefix(prefix)
var tokens = []
var r = RegEx.new()
r.compile("\\S+") # Negated whitespace character class
for token in r.search_all(raw_content):
tokens.append(token.get_string())
var cmd_or_alias = tokens[0].to_lower()
tokens.remove(0) # Remove the command name from the tokens
var args = tokens
_handle_command(bot, message, channel, cmd_or_alias, args)
func _load_commands(bot: DiscordBot) -> void:
var cmd_path = "res://cmds/"
var dir = Directory.new()
if dir.open(cmd_path) != OK:
push_error("An error occurred when trying to open /cmds/ folder.")
return
dir.list_dir_begin()
while true:
var file = dir.get_next()
if file == "": # End of files
break
elif not file.begins_with(".") and (file.ends_with(".gd") or file.ends_with(".gdc")):
var script = load(cmd_path + file.get_basename() + ".gd").new()
var data = script.help
# Ensure that the commands don't have the default help values
assert(data.name != "test_name" and data.category != "test_category" and data.description != "test_description" and script.get_usage(prefix) != "test usage", "Must change default values for Command in " + file)
if not data.enabled:
continue
if script.has_method("on_ready"):
script.on_ready(self, bot)
commands[data.name] = script
if not data.has("aliases"):
continue
for alias in data.aliases:
assert(not command_aliases.has(alias), "Duplicate cmd aliases found in cmd: " + file)
command_aliases[alias] = data.name
print("Loaded " + str(commands.size()) + " cmds")
dir.list_dir_end()
func _load_application_commands(bot: DiscordBot) -> void:
var app_cmd_path = "res://application_cmds/"
var dir = Directory.new()
if dir.open(app_cmd_path) != OK:
push_error("An error occurred when trying to open /application_cmds/ folder.")
return
dir.list_dir_begin()
while true:
var file = dir.get_next()
if file == "": # End of files
break
elif not file.begins_with(".") and (file.ends_with(".gd") or file.ends_with(".gdc")):
var script = load(app_cmd_path + file.get_basename() + ".gd").new()
var data = script.data
# Ensure that the commands don't have the default help values
assert(data.name != "test_name" and data.description != "test_description", "Must change default values for ApplicationCommand in " + file)
if script.has_method("on_ready"):
script.on_ready(self, bot)
application_commands[data.name] = script
print("Loaded " + str(application_commands.size()) + " application cmds")
dir.list_dir_end()
func _register_application_commands(bot, guild_id: String = "") -> void:
var application_commands_data = []
for app_cmd in application_commands.values():
application_commands_data.append(app_cmd.data)
bot.register_commands(application_commands_data, guild_id)
func _handle_command(bot: DiscordBot, message: Message, channel: Dictionary, cmd_or_alias: String, args: Array):
var cmd = null
if command_aliases.has(cmd_or_alias):
cmd = commands[command_aliases[cmd_or_alias]]
elif commands.has(cmd_or_alias):
cmd = commands[cmd_or_alias]
if cmd == null:
return
print("CMD: " + cmd.help.name + " by " + message.author.username + "#" + message.author.discriminator + " (" + message.author.id + ")")
cmd.on_message(self, bot, message, channel, args)
func remove_components_from_interaction(interaction: DiscordInteraction, msg = ":robot: Components have timed out!") -> void:
var embed = Embed.new().set_description(msg)
var new_embeds = interaction.message.embeds + [embed]
interaction.update({
"content": interaction.message.content,
"embeds": new_embeds,
"components": []
})
func _on_interaction_create(bot: DiscordBot, interaction: DiscordInteraction):
# Handle ApplicationCommand
if interaction.is_command() or interaction.is_autocomplete():
var cmd_name = interaction.data.name
if application_commands.has(cmd_name):
# The application command was found, so execute it
var app_cmd = application_commands[cmd_name]
var options = interaction.data.options if interaction.data.has("options") else []
if interaction.is_autocomplete():
# It is an autocomplete command
app_cmd.on_autocomplete(self, bot, interaction, options)
return
print("APP_CMD: " + app_cmd.data.name + " by " + interaction.member.user.username + "#" + interaction.member.user.discriminator + " (" + interaction.member.user.id + ")")
app_cmd.execute(self, bot, interaction, options)
else:
# The application command was not found
interaction.reply({
"ephemeral": true,
"content": ":electric_plug: The requested command was not found."
})
return
var msg_id = interaction.message.id
# Emit the interaction to the normal (text) commands
if interactions.has(msg_id):
emit_signal("interaction_create", self, bot, interaction, interactions[msg_id])
else:
remove_components_from_interaction(interaction)