Compare commits

..

14 Commits

Author SHA1 Message Date
0500c9e734 Fix FQG? 2025-07-10 23:53:41 -04:00
a1ec7c8042 new command WIP 2025-07-08 15:53:50 -04:00
af3d164148 changes + new util functions 2025-07-08 15:53:29 -04:00
16bc78f676 Add table.clear 2025-07-08 15:53:11 -04:00
923e3096be HTTP retry 2025-07-08 15:53:04 -04:00
de158b45ee Print response code 2025-07-07 14:09:57 -04:00
e2d3a57629 New endpoint: Get recent world records 2025-06-27 22:08:17 -04:00
083b8960aa Normalize param name 2025-06-27 22:08:05 -04:00
7b3ab8e115 Minecraft user command: Format + fix 2025-06-24 15:08:47 -04:00
54d588b43a User (verification date guesser): Reintroduce changes that I lost 2025-06-24 15:08:47 -04:00
f072b14735 Remove unused 2025-06-24 15:08:47 -04:00
e2ee24898f Make user slash command work 2025-06-24 15:08:47 -04:00
1c08e5fd47 QueryParams guard 2025-06-24 15:08:46 -04:00
d2eded99a1 Refactor StrafesNET module (WIP, more to come) 2025-06-24 15:08:46 -04:00
8 changed files with 215 additions and 61 deletions

2
.gitignore vendored
View File

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

View File

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

View File

@ -1,6 +1,13 @@
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 = {
@ -50,7 +57,7 @@ local function NormalizeHeaders(Response)
end
end
local function Request(Method, Url, Params, RequestHeaders, RequestBody, Callback)
local function Request(Method, Url, Params, RequestHeaders, RequestBody, Callback, MaxRetries)
if not METHODS[Method] then
error("[HTTP] Method " .. Method .. " is not supported.")
end
@ -65,22 +72,50 @@ 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)
if Callback and type(Callback) == "function" then
return coroutine.wrap(function()
MaxRetries = MaxRetries or 10
local function DoRequest()
local Attempt = 0
local Delay = 2
while Attempt <= MaxRetries do
local Headers, Body = HTTPRequest(Method, RequestUrl, FormattedHeaders, RequestBody)
NormalizeHeaders(Headers)
Callback(Headers, TryDecodeJson(Body))
end)
else
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
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 Headers = {
local RequestHeaders = {
["Content-Type"] = "application/json",
["X-API-Key"] = APIKeys.StrafesNET
}
@ -22,6 +22,12 @@ 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',
@ -51,6 +57,9 @@ local STRAFESNET_API_ENDPOINTS = {
},
TIMES = {
LIST = "time",
WORLD_RECORD = {
GET = "time/worldrecord"
},
GET = "time/%d"
},
USERS = {
@ -62,34 +71,64 @@ 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, Headers)
return Request("GET", RequestUrl, Params, RequestHeaders)
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, Headers)
return Request("GET", RequestUrl, Params, RequestHeaders)
end
function StrafesNET.ListRanks(GameId, ModeId, StyleId, SortBy, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.RANKS.LIST
local Params = {
gameId = GameId,
modeId = ModeId,
styleId = StyleId,
game_id = GameId,
mode_id = ModeId,
style_id = StyleId,
sort_by = SortBy or 1,
page_size = PageSize or 10,
page_number = PageNumber or 1
}
return Request("GET", RequestUrl, Params, Headers)
return Request("GET", RequestUrl, Params, RequestHeaders)
end
function StrafesNET.ListTimes(UserId, MapId, GameId, ModeId, StyleId, SortBy, PageSize, PageNumber)
function StrafesNET.ListTimes(MapId, GameId, ModeId, StyleId, UserId, SortBy, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.TIMES.LIST
local Params = {
user_id = UserId,
@ -97,16 +136,30 @@ function StrafesNET.ListTimes(UserId, MapId, GameId, ModeId, StyleId, SortBy, Pa
game_id = GameId,
mode_id = ModeId,
style_id = StyleId,
sort_by = SortBy or 1,
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,
page_size = PageSize or 10,
page_number = PageNumber or 0
}
return Request("GET", RequestUrl, Params, Headers)
return Request("GET", RequestUrl, Params, RequestHeaders)
end
function StrafesNET.GetTime(TimeId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.TIMES.GET:format(TimeId)
return Request("GET", RequestUrl, nil, Headers)
return Request("GET", RequestUrl, nil, RequestHeaders)
end
function StrafesNET.ListUsers(StateId, PageSize, PageNumber)
@ -116,12 +169,12 @@ function StrafesNET.ListUsers(StateId, PageSize, PageNumber)
page_size = PageSize or 10,
page_number = PageNumber or 1,
}
return Request("GET", RequestUrl, Params, Headers)
return Request("GET", RequestUrl, Params, RequestHeaders)
end
function StrafesNET.GetUser(UserId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.USERS.GET:format(UserId)
return Request("GET", RequestUrl, nil, Headers)
return Request("GET", RequestUrl, nil, RequestHeaders)
end
function StrafesNET.GetUserRank(UserId, GameId, ModeId, StyleId)
@ -131,7 +184,49 @@ function StrafesNET.GetUserRank(UserId, GameId, ModeId, StyleId)
mode_id = ModeId,
style_id = StyleId,
}
return Request("GET", RequestUrl, Params, Headers)
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
end
function StrafesNET.GetRobloxInfoFromUserId(USER_ID)
@ -161,42 +256,6 @@ 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

@ -0,0 +1,56 @@
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,6 +159,7 @@ 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,6 +5,12 @@ 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()