Compare commits

..

5 Commits

Author SHA1 Message Date
2b4b5d39ed User (verification date guesser): Reintroduce changes that I lost 2025-06-24 00:07:43 -04:00
0b05c9a8ba Remove unused 2025-06-24 00:07:24 -04:00
8735d6bfa8 Make user slash command work 2025-06-23 23:59:08 -04:00
ba4c807644 QueryParams guard 2025-06-23 23:59:01 -04:00
c71c9229e8 Refactor StrafesNET module (WIP, more to come) 2025-06-23 23:59:01 -04:00
9 changed files with 90 additions and 250 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
discordia.log
gateway.json
APIKeys.lua
API_Keys.lua
Token.lua
deps
sensdb.lua

3
src/Modules/APIKeys.lua Normal file
View File

@ -0,0 +1,3 @@
return {
StrafesNET = "strafe_3fdb3338a3b05a64566f20df51a4e343"
}

View File

@ -1,13 +1,6 @@
local Http = require('coro-http')
local HTTPRequest = Http.request
local Timer = require("timer")
local Sleep = Timer.sleep
local function Wait(n)
return Sleep(n * 1000)
end
local json = require('json')
local METHODS = {
@ -57,7 +50,7 @@ local function NormalizeHeaders(Response)
end
end
local function Request(Method, Url, Params, RequestHeaders, RequestBody, Callback, MaxRetries)
local function Request(Method, Url, Params, RequestHeaders, RequestBody, Callback)
if not METHODS[Method] then
error("[HTTP] Method " .. Method .. " is not supported.")
end
@ -72,50 +65,22 @@ local function Request(Method, Url, Params, RequestHeaders, RequestBody, Callbac
local QueryString = QueryParams(Params) -- at worse (I think), this is an empty string (which cannot mess up the request)
local FormattedHeaders = CreateHeaders(RequestHeaders) -- at worse, this will just be an empty table (which cannot mess up the request)
local FormattedHeaders = CreateHeaders(RequestHeaders) -- At worse, this will just be an empty table (which cannot mess up the request)
local RequestUrl = Url .. QueryString
print(RequestUrl)
MaxRetries = MaxRetries or 10
local function DoRequest()
local Attempt = 0
local Delay = 2
while Attempt <= MaxRetries do
if Callback and type(Callback) == "function" then
return coroutine.wrap(function()
local Headers, Body = HTTPRequest(Method, RequestUrl, FormattedHeaders, RequestBody)
NormalizeHeaders(Headers)
print("Attempt:", Attempt + 1, "Status code:", Headers.code)
-- we will assume <400 = success i guess
if Headers.code and Headers.code < 400 then
return Headers, TryDecodeJson(Body)
end
Attempt = Attempt + 1
if Attempt > MaxRetries then
break
end
print("Request failed, retrying in " .. Delay .. " seconds...")
Wait(Delay)
Delay = Delay * 2 -- exponential back-off
end
Callback(Headers, TryDecodeJson(Body))
end)
else
local Headers, Body = HTTPRequest(Method, RequestUrl, FormattedHeaders, RequestBody)
NormalizeHeaders(Headers)
return Headers, TryDecodeJson(Body)
end
if Callback and type(Callback) == "function" then
return coroutine.wrap(function()
local Headers, DecodedBody = DoRequest()
Callback(Headers, DecodedBody)
end)
else
return DoRequest()
end
end
return {

View File

@ -2,7 +2,7 @@ local HttpRequest = require("./HttpRequest.lua")
local Request = HttpRequest.Request
local APIKeys = require("./APIKeys.lua")
local RequestHeaders = {
local Headers = {
["Content-Type"] = "application/json",
["X-API-Key"] = APIKeys.StrafesNET
}
@ -22,12 +22,6 @@ local ROBLOX_THUMBNAIL_URL = 'https://thumbnails.roblox.com/v1/'
local ROBLOX_INVENTORY_API = 'https://inventory.roblox.com/v1/'
local ROBLOX_GROUPS_ROLES_URL = 'https://groups.roblox.com/v2/users/%s/groups/roles'
local GAME_IDS = {
BHOP = 1,
SURF = 2,
-- FLY_TRIALS = 5
}
ROBLOX_THUMBNAIL_SIZES = {
[48] = '48x48',
[50] = '50x50',
@ -57,9 +51,6 @@ local STRAFESNET_API_ENDPOINTS = {
},
TIMES = {
LIST = "time",
WORLD_RECORD = {
GET = "time/worldrecord"
},
GET = "time/%d"
},
USERS = {
@ -71,64 +62,34 @@ local STRAFESNET_API_ENDPOINTS = {
}
}
local RankConstants = {
Magic1 = 0.7,
Magic2 = 0.22313016
}
local StrafesNET = {}
StrafesNET.GameIds = GAME_IDS
function StrafesNET.CalculatePoints(Rank, Count)
local ExpMagic2 = math.exp(RankConstants.Magic2)
local Num1 = ExpMagic2 - 1.0
local ExpDenomExp = math.max(-700.0, -RankConstants.Magic2 * Count)
local Denom1 = 1.0 - math.exp(ExpDenomExp)
local ExpRankExp = math.max(-700.0, -RankConstants.Magic2 * Rank)
local ExpRank = math.exp(ExpRankExp)
local Part1 = RankConstants.Magic1 * (Num1 / Denom1) * ExpRank
local Part2 = (1.0 - RankConstants.Magic1) * (1.0 + 2.0 * (Count - Rank)) / (Count * Count)
return Part1 + Part2
end
function StrafesNET.CalculateSkill(Rank, Count)
local Denominator = Count - 1
if Denominator == 0 then
return 0
else
return (Count - Rank) / Denominator
end
end
function StrafesNET.ListMaps(GameId, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.MAPS.LIST
local Params = { game_id = GameId, page_size = PageSize or 10, page_number = PageNumber or 1 }
return Request("GET", RequestUrl, Params, RequestHeaders)
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.GetMap(MapId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.MAPS.GET:format(MapId)
local Params = { id = MapId }
return Request("GET", RequestUrl, Params, RequestHeaders)
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.ListRanks(GameId, ModeId, StyleId, SortBy, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.RANKS.LIST
local Params = {
game_id = GameId,
mode_id = ModeId,
style_id = StyleId,
gameId = GameId,
modeId = ModeId,
styleId = StyleId,
sort_by = SortBy or 1,
page_size = PageSize or 10,
page_number = PageNumber or 1
}
return Request("GET", RequestUrl, Params, RequestHeaders)
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.ListTimes(MapId, GameId, ModeId, StyleId, UserId, SortBy, PageSize, PageNumber)
function StrafesNET.ListTimes(UserId, MapId, GameId, ModeId, StyleId, SortBy, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.TIMES.LIST
local Params = {
user_id = UserId,
@ -136,30 +97,16 @@ function StrafesNET.ListTimes(MapId, GameId, ModeId, StyleId, UserId, SortBy, Pa
game_id = GameId,
mode_id = ModeId,
style_id = StyleId,
sort_by = SortBy or 0,
page_size = PageSize or 10,
page_number = PageNumber or 1
}
return Request("GET", RequestUrl, Params, RequestHeaders)
end
function StrafesNET.GetWorldRecords(UserId, MapId, GameId, ModeId, StyleId, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.TIMES.WORLD_RECORD.GET
local Params = {
user_id = UserId,
map_id = MapId,
game_id = GameId,
mode_id = ModeId,
style_id = StyleId,
sort_by = SortBy or 1,
page_size = PageSize or 10,
page_number = PageNumber or 0
}
return Request("GET", RequestUrl, Params, RequestHeaders)
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.GetTime(TimeId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.TIMES.GET:format(TimeId)
return Request("GET", RequestUrl, nil, RequestHeaders)
return Request("GET", RequestUrl, nil, Headers)
end
function StrafesNET.ListUsers(StateId, PageSize, PageNumber)
@ -169,12 +116,12 @@ function StrafesNET.ListUsers(StateId, PageSize, PageNumber)
page_size = PageSize or 10,
page_number = PageNumber or 1,
}
return Request("GET", RequestUrl, Params, RequestHeaders)
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.GetUser(UserId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.USERS.GET:format(UserId)
return Request("GET", RequestUrl, nil, RequestHeaders)
return Request("GET", RequestUrl, nil, Headers)
end
function StrafesNET.GetUserRank(UserId, GameId, ModeId, StyleId)
@ -184,49 +131,7 @@ function StrafesNET.GetUserRank(UserId, GameId, ModeId, StyleId)
mode_id = ModeId,
style_id = StyleId,
}
return Request("GET", RequestUrl, Params, RequestHeaders)
end
-- util stuff or something
function StrafesNET.GetMapCompletionCount(MapId, GameId, ModeId, StyleId)
local Headers, Response = StrafesNET.ListTimes(MapId, GameId, ModeId, StyleId)
if Headers.code >= 400 then
return error("HTTP Error while getting map completion count")
end
return Response.pagination.total_items
end
function StrafesNET.GetAllUserTimes(UserId, GameId, ModeId, StyleId)
local Times = {}
local CurrentPage = 1
local Headers, Response = StrafesNET.ListTimes(nil, GameId, ModeId, StyleId, UserId, 0, 100, CurrentPage)
if Headers.code >= 400 then
return error("HTTP error while getting times for something")
end
for TimeIndex, Time in next, Response.data do
Times[Time.id] = Time
end
local TotalPages = Response.pagination.total_pages
while CurrentPage < TotalPages do
CurrentPage = CurrentPage + 1
local _Headers, _Response = StrafesNET.ListTimes(nil, GameId, ModeId, StyleId, UserId, 0, 100, CurrentPage)
if _Headers.code >= 400 then
return error("HTTP error while getting times for something")
end
for _, Time in next, _Response.data do
Times[Time.id] = Time
end
end
return Times
end
function StrafesNET.GetAllMaps()
local Maps = {}
-- TODO
return Maps
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.GetRobloxInfoFromUserId(USER_ID)
@ -256,6 +161,42 @@ function StrafesNET.GetRobloxInfoFromDiscordId(DISCORD_ID)
return Request("GET", ROBLOX_API_URL .. "users/" .. body.result.robloxId)
end
function StrafesNET.GetUserFromAny(user, message)
local str = user:match('^["\'](.+)[\'"]$')
local num = user:match('^(%d+)$')
if str then
local roblox_user = StrafesNET.GetRobloxInfoFromUsername(str)
if not roblox_user.id then return 'User not found' end
return roblox_user
elseif num then
local roblox_user = StrafesNET.GetRobloxInfoFromUserId(user)
if not roblox_user.id then return 'Invalid user id' end
return roblox_user
elseif user == 'me' then
local me = message.author
local roblox_user = StrafesNET.GetRobloxInfoFromDiscordId(me.id)
if not roblox_user.id then
return
'You are not registered with the fiveman1 api, use !link with the rbhop bot to link your roblox account'
end
return roblox_user
elseif user:match('<@%d+>') then
local user_id = user:match('<@(%d+)>')
local member = message.guild:getMember(user_id)
local roblox_user = StrafesNET.GetRobloxInfoFromDiscordId(member.id)
if not roblox_user.id then
return
'User is not registered with the fiveman1 api, use !link with the rbhop bot to link your roblox account'
end
return roblox_user
else
local roblox_user = StrafesNET.GetRobloxInfoFromUsername(user)
if not roblox_user.id then return 'User not found' end
return roblox_user
end
end
function StrafesNET.GetUserOnlineStatus(USER_ID)
if not USER_ID then return 'empty id' end

View File

View File

@ -1,7 +1,6 @@
local Discordia = require('discordia')
local json = require('json')
local HttpRequest = require('../Modules/HttpRequest.lua')
local Request = HttpRequest.Request
local http_request = require('../Modules/http.lua')
local SubCommandHandler = require('../Modules/SubCommandHandler.lua')
Discordia.extensions()
@ -13,10 +12,8 @@ local MinecraftSubCommandHandler = SubCommandHandler.new()
local MinecraftMainCommand = SlashCommandTools.slashCommand('minecraft', 'Minecraft server related commands')
local MinecraftStatusSubCommand = SlashCommandTools.subCommand('status',
'Get the Minecraft server status according to the preferred IP address set for this server')
local MinecraftSetIpSubCommand = SlashCommandTools.subCommand('setip',
'Set the preferred Minecraft server IP address for this server')
local MinecraftStatusSubCommand = SlashCommandTools.subCommand('status', 'Get the Minecraft server status according to the preferred IP address set for this server')
local MinecraftSetIpSubCommand = SlashCommandTools.subCommand('setip', 'Set the preferred Minecraft server IP address for this server')
local MinecraftSetIpOptions = SlashCommandTools.string('ip', 'The IP address of the server')
MinecraftSetIpOptions:setRequired(true)
@ -56,49 +53,46 @@ MinecraftSubCommandHandler:AddSubCommand(MinecraftStatusSubCommand.name, functio
return Interaction:reply('There is no data for this Discord server', true)
end
local ServerIPStr = ServerMinecraftData.IP .. ':' .. ServerMinecraftData.PORT
local Headers, Body = Request("GET", ('https://api.mcsrvstat.us/3/%s'):format(ServerIPStr), nil,
{ ["User-Agent"] = "tommy-bot/1.0 Main-Release" })
if not Headers.code == 200 then
return error("Something went wrong")
end
local IsOnline = Body.online
local ServerIPStr = ServerMinecraftData.IP..':'..ServerMinecraftData.PORT
local Response, Headers = http_request('GET', ('https://api.mcsrvstat.us/3/%s'):format(ServerIPStr))
local IsOnline = Response.online
local EmbedData
if IsOnline then
local MaxPlayers = Body.players.max
local OnlinePlayers = Body.players.online
local MaxPlayers = Response.players.max
local OnlinePlayers = Response.players.online
local AnonymousPlayers = OnlinePlayers
local Players = {}
if OnlinePlayers > 0 then
for PlayerIndex, PlayerData in next, Body.players.list do
if OnlinePlayers>0 then
for PlayerIndex, PlayerData in next, Response.players.list do
table.insert(Players, PlayerData.name)
AnonymousPlayers = AnonymousPlayers - 1
AnonymousPlayers = AnonymousPlayers-1
end
else
table.insert(Players, 'No players online')
end
if AnonymousPlayers > 0 then
if AnonymousPlayers>0 then
for AnonymousPlayerIndex = 1, AnonymousPlayers do
table.insert(Players, 'Anonymous Player')
end
end
EmbedData = {
title = 'Server Status for ' .. ServerIPStr,
description = Body.motd.clean[1] .. ' (' .. Body.version .. ')',
fields = {
{ name = 'Players', value = OnlinePlayers .. '/' .. MaxPlayers, inline = true },
{ name = 'List of players', value = table.concat(Players, '\n'), inline = true }
},
color = COLOURS.GREEN
}
title = 'Server Status for '..ServerIPStr,
description = Response.motd.clean[1]..' ('..Response.version..')',
fields = {
{name = 'Players', value = OnlinePlayers..'/'..MaxPlayers, inline = true},
{name = 'List of players', value = table.concat(Players, '\n'), inline = true}
},
color = COLOURS.GREEN
}
else
EmbedData = {
title = 'Server Status for ' .. ServerIPStr,
title = 'Server Status for '..ServerIPStr,
description = 'Server is offline',
color = COLOURS.RED
}
end
return Interaction:reply({ embed = EmbedData })
return Interaction:reply({embed = EmbedData})
end)
MinecraftSubCommandHandler:AddSubCommand(MinecraftSetIpSubCommand.name, function(Interaction, Command, Args)
@ -113,18 +107,18 @@ MinecraftSubCommandHandler:AddSubCommand(MinecraftSetIpSubCommand.name, function
if not ServerIP then
return Interaction:reply('Invalid server IP')
end
local ServerPort = ServerIPStr:match(ServerIP .. ':(%d+)') or 25565
local ServerPort = ServerIPStr:match(ServerIP..':(%d+)') or 25565
local GuildMinecraftData = { IP = ServerIP, PORT = ServerPort }
local GuildMinecraftData = {IP = ServerIP, PORT = ServerPort}
local GlobalMinecraftData = json.decode(io.open('minecraft_data.json', 'r'):read('*a'))
local GlobalMinecraftData = json.decode(io.open('minecraft_data.json','r'):read('*a'))
GlobalMinecraftData[GuildId] = GuildMinecraftData
io.open('minecraft_data.json', 'w+'):write(json.encode(GlobalMinecraftData)):close()
io.open('minecraft_data.json','w+'):write(json.encode(GlobalMinecraftData)):close()
return Interaction:reply('Successfully added `' .. ServerIP .. ':' .. ServerPort .. '` for ServerId=' .. GuildId)
return Interaction:reply('Successfully added `'..ServerIP..':'..ServerPort..'` for ServerId='..GuildId)
end)
return {
Command = MinecraftMainCommand,
Callback = MinecraftSubCommandHandler:GetMainCommandCallback()
}
}

View File

@ -1,56 +0,0 @@
local SlashCommandTools = require('discordia-slash').util.tools()
local Discordia = require('discordia')
local Date = Discordia.Date
Discordia.extensions()
local StrafesNET = require('../Modules/StrafesNET.lua')
local CalculateCommand = SlashCommandTools.slashCommand('calculate', 'Calculate rank and skill points')
local UsernameOption = SlashCommandTools.string('username', 'Username to look up')
local UserIdOption = SlashCommandTools.integer('user_id', 'User ID to look up')
local MemberOption = SlashCommandTools.user('member', 'User to look up')
local GameIdOption = SlashCommandTools.integer
CalculateCommand:addOption(UsernameOption)
CalculateCommand:addOption(UserIdOption)
CalculateCommand:addOption(MemberOption)
local function Callback(Interaction, Command, Args)
local UserInfo
if Args then
if Args.username then
local Headers, Response = StrafesNET.GetRobloxInfoFromUsername(Args.username)
if Headers.code < 400 then
UserInfo = Response
end
elseif Args.user_id then
local Headers, Response = StrafesNET.GetRobloxInfoFromUserId(Args.user_id)
if Headers.code < 400 then
UserInfo = Response
end
elseif Args.member then
local Headers, Response = StrafesNET.GetRobloxInfoFromDiscordId(Args.member.id)
if Headers.code < 400 then
UserInfo = Response
end
end
else
local Headers, Response = StrafesNET.GetRobloxInfoFromDiscordId((Interaction.member or Interaction.user).id)
if Headers.code < 400 then
UserInfo = Response
end
end
if UserInfo == nil then
error("SOMETHING WENT REALLY WRONG")
end
-- Add args for game/style etc and grab all times and grab all placements
end
return {
Command = CalculateCommand,
Callback = Callback
}

View File

@ -159,7 +159,6 @@ local function Callback(Interaction, Command, Args)
local awardedDate = tonumber(Date.fromISO(badge.awardedDate):toSeconds())
if firstBadgeDate > awardedDate then
firstBadge = badgeId
firstBadgeDate = awardedDate
end
-- badgesDates[badgeId]=awardedDate
end

View File

@ -5,12 +5,6 @@ local CommandCollector = require('./Modules/CommandCollector.lua')
local Client = Discordia.Client():useApplicationCommands()
Discordia.extensions()
table.clear = function(t)
for k in pairs(t) do
t[k] = nil
end
end
local MessageCommandCollector = CommandCollector.new('Message'):Collect()
local SlashCommandCollector = CommandCollector.new('Slash'):Collect()
local UserCommandCollector = CommandCollector.new('User'):Collect()