-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.lua
More file actions
208 lines (166 loc) · 6.55 KB
/
init.lua
File metadata and controls
208 lines (166 loc) · 6.55 KB
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
local STATUS_PLAYERS = "%d player%s %s" -- Watches n player(s) race/battle
local STATUS_EMPTY = "an empty map" -- Watches an empty map
local STATUS_ERROR = "until you'll help me"
local STATUS_GAMEMODE = os.getenv("SRB2KART_STATUS_GAMEMODE") -- If nil, chooses between "race" or "battle"
local SLASHPLAYERS_GAMEMODE = os.getenv("SRB2KART_SLASHPLAYERS_GAMEMODE") -- If nil, chooses between "racing" or "battling"
local SEED_PLAYER = os.getenv("SRB2KART_SEEDPLAYER") -- If there is only one player with this name on server, don't count them in status
local COMMAND_PREFIX = os.getenv("DISCORD_CMDPREFIX") or ""
local CMD_PLAYERS = COMMAND_PREFIX.."players"
local CMD_GAMEMODE = COMMAND_PREFIX.."gamemode"
local STATUS_UPDATE_INTERVAL = 5*1000
local discordia = require("discordia")
local dcmd = require("discordia-slash")
local dcmdtools = dcmd.util.tools()
local timer = require("timer")
local wrap = coroutine.wrap
local client = discordia.Client({
autoReconnect = tonumber(os.getenv("DISCORD_NOAUTORECONNECT") or "0") == 0,
}):useApplicationCommands()
local SRB2Kart = require("./srb2kart.lua")
local server = SRB2Kart:new(os.getenv("SRB2KART_ADDRESS") or "127.0.0.1", os.getenv("SRB2KART_PROTO") or "srb2kart-16p", os.getenv("SRB2KART_GAMEMODEFILE"))
local function getStatusGamemode(gametype)
if STATUS_GAMEMODE then return STATUS_GAMEMODE end
-- Special case for DRRR
if type(gametype) == "string" then return gametype:lower() end
return gametype == 2 and "race" or "battle"
end
local function getSlashplayersGamemode(gametype)
if SLASHPLAYERS_GAMEMODE then return SLASHPLAYERS_GAMEMODE end
-- Special case for DRRR
if type(gametype) == "string" then
-- Just so race and battle is consistent with kart
if gametype:find("Race") then
gametype = 2
elseif gametype:find("Battle") then
gametype = 0
else
return "playing "..gametype:lower()
end
end
return gametype == 2 and "racing" or "battling"
end
-- Not 100% sure if this is necessary
local function fixname(name)
local nterm = name:find('\000')
if nterm then
name = name:sub(1, nterm-1)
end
return name
end
-- This prob should be done in better way. Have to use coroutine because setStatus yields and that causes error because
-- you can't do that from c function (which happens when using timer)
local function updateStatus()
server:askInfo()
local numplayers = server.serverinfo.numplayers
if numplayers > 0 and SEED_PLAYER then
for _, p in ipairs(server.playerinfo) do
if fixname(p.name) == SEED_PLAYER then
numplayers = numplayers - 1
break
end
end
end
if server:isInfoExpired() then
client:setStatus(discordia.enums.status.doNotDisturb)
client:setActivity({
name = STATUS_ERROR,
type = discordia.enums.activityType.watching,
})
elseif numplayers == 0 then
client:setStatus(discordia.enums.status.online)
client:setActivity({
name = STATUS_EMPTY,
type = discordia.enums.activityType.watching,
})
else
local text = STATUS_PLAYERS:format(numplayers, numplayers > 1 and 's' or '', getStatusGamemode(server.serverinfo.gametype or server.serverinfo.gametypename))
client:setStatus(discordia.enums.status.online)
client:setActivity({
name = text,
type = discordia.enums.activityType.watching,
})
end
end
local function deleteUnsupportedCommands(supported)
for id, cmd in pairs(client:getGlobalApplicationCommands()) do
if not supported[cmd.name] then
client:deleteGlobalApplicationCommand(id)
end
end
end
client:on("ready", function()
timer.setInterval(STATUS_UPDATE_INTERVAL, function() wrap(updateStatus)() end)
local supported = {}
local function registerCmd(name, desc)
local cmd = dcmdtools.slashCommand(name, desc)
client:createGlobalApplicationCommand(cmd)
supported[name] = true
end
-- Is it fine that i create those each restart?
registerCmd(CMD_PLAYERS, "Get player info")
if server.gamemodefile ~= nil then
registerCmd(CMD_GAMEMODE, "Get current gamemode")
end
deleteUnsupportedCommands(supported)
wrap(function()
client:setStatus(discordia.enums.status.idle)
client:setActivity()
end)()
end)
local function joinnames(names, verb)
if #names == 0 then return "No one is "..verb end
local last = table.remove(names)
if #names == 0 then
return last.." is "..verb
else
return table.concat(names, ", ").." and "..last.." are "..verb
end
end
client:on("slashCommand", function(ia, cmd, args)
if server:isInfoExpired() then
ia:reply("Server is currently unavailable.")
return
end
if cmd.name == CMD_PLAYERS then
local playing = {}
local spec = {}
local resp = ""
local verb = getSlashplayersGamemode(server.serverinfo.gametype or server.serverinfo.gametypename)
local map = "Unknown"
if server.serverinfo.maptitle then
map = fixname(server.serverinfo.maptitle):gsub('^%s*(.-)%s*$', '%1') -- Remove embedded zeros and trim the title
-- Append zone, if needed
if server.serverinfo.iszone ~= 0 then
map = map.." Zone"
end
end
for _, p in ipairs(server.playerinfo) do
if p.node ~= 255 then
table.insert(p.team == 0 and playing or spec, fixname(p.name))
end
end
-- This is ugly lol
if #playing == 0 then
if #spec == 0 then
resp = "No one is "..verb..", map is "..map.."."
else
resp = joinnames(spec, "watching").." at "..map.."."
end
else
if #spec == 0 then
resp = joinnames(playing, verb).." at "..map.."."
else
resp = joinnames(playing, verb)..", "..joinnames(spec, "watching").." at "..map.."."
end
end
ia:reply(resp)
elseif cmd.name == CMD_GAMEMODE then
local gamemodes = server:getGamemodes()
if gamemodes == nil then
ia:reply("Unable to fetch gamemodes right now.")
else
ia:reply(string.format("Current gamemode%s: %s.", #gamemodes > 1 and "s are" or " is", table.concat(gamemodes, ", ")))
end
end
end)
client:run("Bot "..(os.getenv("DISCORD_TOKEN") or error("DISCORD_TOKEN env variable is required")))