1
0
forked from tommy/tommy-bot

Compare commits

9 Commits
main ... main

8 changed files with 469 additions and 513 deletions

2
.gitignore vendored

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

@ -0,0 +1,88 @@
local Http = require('coro-http')
local HTTPRequest = Http.request
local json = require('json')
local METHODS = {
GET = true,
POST = true
}
local function QueryParams(Params) -- {Name = Value, ...}
if not Params then return "" end
local QueryString = "?"
for ParamName, ParamValue in next, Params do
if ParamValue ~= nil then
QueryString = QueryString .. tostring(ParamName) .. "=" .. tostring(ParamValue) .. "&"
end
end
return string.sub(QueryString, 1, -2) -- Remove last character (will always be a "&")
end
local function CreateHeaders(Headers) -- {Name = Value, ...}
if not Headers then return {} end
local RequestHeaders = {}
for HeaderName, HeaderValue in next, Headers do
RequestHeaders[#RequestHeaders + 1] = { tostring(HeaderName), tostring(HeaderValue) }
end
return RequestHeaders
end
local function TryDecodeJson(Body)
local Success, Result = pcall(json.decode, Body)
if not Success then
return Body
end
return Result
end
local function NormalizeHeaders(Response)
for Index, Header in next, Response do
if type(Header) == "table" and #Header == 2 then
local HeaderName, HeaderValue = table.unpack(Header)
Response[HeaderName] = HeaderValue
Response[Index] = nil
end
end
end
local function Request(Method, Url, Params, RequestHeaders, RequestBody, Callback)
if not METHODS[Method] then
error("[HTTP] Method " .. Method .. " is not supported.")
end
if type(Url) ~= "string" then
error("[HTTP] Url is not a string")
end
if type(RequestBody) == "table" then
RequestBody = json.encode(RequestBody)
end
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 RequestUrl = Url .. QueryString
print(RequestUrl)
if Callback and type(Callback) == "function" then
return coroutine.wrap(function()
local Headers, Body = HTTPRequest(Method, RequestUrl, FormattedHeaders, RequestBody)
NormalizeHeaders(Headers)
Callback(Headers, TryDecodeJson(Body))
end)
else
local Headers, Body = HTTPRequest(Method, RequestUrl, FormattedHeaders, RequestBody)
NormalizeHeaders(Headers)
return Headers, TryDecodeJson(Body)
end
end
return {
Request = Request
}

251
src/Modules/StrafesNET.lua Normal file

@ -0,0 +1,251 @@
local HttpRequest = require("./HttpRequest.lua")
local Request = HttpRequest.Request
local APIKeys = require("./APIKeys.lua")
local Headers = {
["Content-Type"] = "application/json",
["X-API-Key"] = APIKeys.StrafesNET
}
function L1Copy(t, b)
b = b or {}
for x, y in next, t do b[x] = y end
return b
end
local STRAFESNET_API_URL = "https://api.strafes.net/api/v1/"
local FIVEMAN_API_URL = 'https://api.fiveman1.net/v1/'
local ROBLOX_API_URL = 'https://users.roblox.com/v1/'
local ROBLOX_BADGES_API = 'https://badges.roblox.com/v1/'
local ROBLOX_PRESENCE_URL = 'https://presence.roblox.com/v1/'
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'
ROBLOX_THUMBNAIL_SIZES = {
[48] = '48x48',
[50] = '50x50',
[60] = '60x60',
[75] = '75x75',
[100] = '100x100',
[110] = '110x110',
[150] = '150x150',
[180] = '180x180',
[352] = '352x352',
[420] = '420x420',
[720] = '720x720'
}
ROBLOX_THUMBNAIL_TYPES = {
AVATAR = 'avatar',
BUST = 'avatar-bust',
HEADSHOT = 'avatar-headshot'
}
local STRAFESNET_API_ENDPOINTS = {
MAPS = {
LIST = "map",
GET = "map/%d"
},
RANKS = {
LIST = "rank"
},
TIMES = {
LIST = "time",
GET = "time/%d"
},
USERS = {
LIST = "user",
GET = "user/%d",
RANKS = {
GET = "user/%d/rank"
}
}
}
local StrafesNET = {}
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)
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)
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,
sort_by = SortBy or 1,
page_size = PageSize or 10,
page_number = PageNumber or 1
}
return Request("GET", RequestUrl, Params, Headers)
end
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,
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, Headers)
end
function StrafesNET.GetTime(TimeId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.TIMES.GET:format(TimeId)
return Request("GET", RequestUrl, nil, Headers)
end
function StrafesNET.ListUsers(StateId, PageSize, PageNumber)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.USERS.LIST
local Params = {
state_id = StateId,
page_size = PageSize or 10,
page_number = PageNumber or 1,
}
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, Headers)
end
function StrafesNET.GetUserRank(UserId, GameId, ModeId, StyleId)
local RequestUrl = STRAFESNET_API_URL .. STRAFESNET_API_ENDPOINTS.USERS.RANKS.GET:format(UserId)
local Params = {
game_id = GameId,
mode_id = ModeId,
style_id = StyleId,
}
return Request("GET", RequestUrl, Params, Headers)
end
function StrafesNET.GetRobloxInfoFromUserId(USER_ID)
if not USER_ID then return 'empty id' end
return Request("GET", ROBLOX_API_URL .. "users/" .. USER_ID)
end
function StrafesNET.GetRobloxInfoFromUsername(USERNAME)
if not USERNAME then return 'empty username' end
if #USERNAME > 32 then return 'Username too long' end
local headers, body = Request("POST", ROBLOX_API_URL .. "usernames/users", nil,
{ ["Content-Type"] = "application/json" }, { usernames = { USERNAME } })
if not body or not body.data or not body.data[1] then
return 'Username \'' .. USERNAME .. '\' not found.'
end
return StrafesNET.GetRobloxInfoFromUserId(body.data[1].id)
end
function StrafesNET.GetRobloxInfoFromDiscordId(DISCORD_ID)
if not DISCORD_ID then return 'empty id' end
-- table.foreach(DISCORD_ID, print)
local headers, body = Request("GET", FIVEMAN_API_URL .. "users/" .. DISCORD_ID)
if headers.status == "error" then return headers.messages end
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
local presence = Request("POST", ROBLOX_PRESENCE_URL .. "presence/users", { userIds = { USER_ID } }).userPresences
[1]
local last_online = Request("POST", ROBLOX_PRESENCE_URL .. "presence/last-online", { userIds = { USER_ID } })
.lastOnlineTimestamps[1]
L1Copy(last_online, presence)
return presence
end
function StrafesNET.GetUserUsernameHistory(USER_ID)
if not USER_ID then return 'empty id' end
return Request("GET", ROBLOX_API_URL .. "users/" .. USER_ID .. "/username-history",
{ limit = 50, sortOrder = 'Desc' })
end
function StrafesNET.GetBadgesAwardedDates(USER_ID, BADGE_LIST)
if not USER_ID then return 'empty id' end
return Request("GET", ROBLOX_BADGES_API .. "users/" .. USER_ID .. "/badges/awarded-dates",
{ badgeIds = table.concat(BADGE_LIST, ",") })
end
function StrafesNET.GetVerificationItemID(USER_ID)
if not USER_ID then return 'empty id' end
local headers1, body1 = Request("GET", ROBLOX_INVENTORY_API .. "users/" .. USER_ID .. "/items/Asset/102611803")
if body1.errors then return body1 end
local headers2, body2 = Request("GET", ROBLOX_INVENTORY_API .. "users/" .. USER_ID .. "/items/Asset/1567446")
if body2.errors then return body2 end
local data = {}
if body2.data and body2.data[1] then data[#data + 1] = body2.data[1] end
if body1.data and body1.data[1] then data[#data + 1] = body1.data[1] end
return { data = data }
end
function StrafesNET.GetUserThumbnail(USER_ID, TYPE, SIZE)
if not USER_ID then return 'empty id' end
local _TYPE = ROBLOX_THUMBNAIL_TYPES[TYPE] or "avatar"
local _SIZE = ROBLOX_THUMBNAIL_SIZES[SIZE] or "180x180"
return Request("GET", ROBLOX_THUMBNAIL_URL .. "users/" .. _TYPE,
{ userIds = USER_ID, size = _SIZE, format = "Png", isCircular = false })
end
return StrafesNET

@ -1,40 +0,0 @@
local http = require('coro-http')
local json = require('json')
function wait(n)local c=os.clock local t=c()while c()-t<=n do end;end
--[[
1: method
2: url
3: headers
4: body
5: options]]
local STRAFES_NET_RATELIMIMT = {
HOUR = 3000,
MINUTE = 100,
}
local remaining_timeout = 0
local function request(method,url,headers,body,options)
if type(body)=='table' then body=json.encode(body) end
local headers,body=http.request(method,url,headers,body,options)
local rbody=json.decode(body) or body
local rheaders={}
for _,t in pairs(headers) do
if type(t)=='table' then
rheaders[t[1]]=t[2]
else
rheaders[_]=t
end
end
local remaining = tonumber(rheaders['RateLimit-Remaining'])
local remaining_hour = tonumber(rheaders['X-RateLimit-Remaining-Hour'])
local reset = tonumber(rheaders['RateLimit-Reset'])
local retry_after = tonumber(rheaders['Retry-After'])
if remaining and reset then
local t = remaining==0 and reset or .38
if retry_after then t = retry_after end
wait(t)
end
return rbody,rheaders
end
-- local urlparamencode=function()
return request

@ -1,348 +0,0 @@
local http_request = require('./http.lua')
local API = {}
local API_KEY = require('./apikey.lua')
local API_HEADER = { {'Content-Type','application/json'}, { 'api-key', API_KEY }, {'secretkey', '7b320736'} }
local STRAFESNET_API_URL = 'https://api.strafes.net/v1/'
local FIVEMAN_API_URL = 'https://api.fiveman1.net/v1/'
local ROBLOX_API_URL = 'https://users.roblox.com/v1/'
local ROBLOX_BADGES_API = 'https://badges.roblox.com/v1/'
local ROBLOX_PRESENCE_URL = 'https://presence.roblox.com/v1/'
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 ROBLOX_SENS_DB = 'http://oef.ddns.net:9017/rbhop/sens/'
local RANK_CONSTANT_A, RANK_CONSTANT_B, RANK_CONSTANT_C, RANK_CONSTANT_D, RANK_CONSTANT_E = 0.215, 0.595, 0.215, 0.215, 0.71
local t=tostring
local r=function(n,nd) return tonumber(string.format('%.' .. (nd or 0) .. 'f', n))end
local GAMES={BHOP=1,SURF=2,[1]='bhop',[2]='surf'}
local STATES={[0]='Default',[1]='Whitelisted',[2]='Blacklisted',[3]='Pending'}
local RANKS={'New (1)','Newb (2)','Bad (3)','Okay (4)','Not Bad (5)','Decent (6)','Getting There (7)','Advanced (8)','Good (9)','Great (10)','Superb (11)','Amazing (12)','Sick (13)','Master (14)','Insane (15)','Majestic (16)','Baby Jesus (17)','Jesus (18)','Half God (19)','God (20)'}
local STYLES_LIST={'Autohop','Scroll','Sideways','Half-Sideways','W-Only','A-Only','Backwards'}
local STYLES={AUTOHOP=1,SCROLL=2,SIDEWAYS=3,HALFSIDEWAYS=4,WONLY=5,AONLY=6,BACKWARDS=7}
setmetatable(STYLES,{__index=function(self,i)
if type(i)=='number' then return STYLES_LIST[i] or 'Unknown' end
if i=='a' then i='auto'elseif i=='hsw'then i='half'elseif i=='s'then i='scroll'elseif i=='sw'then i='side'elseif i=='bw'then i='back'end
for ix,v in pairs(self) do
if string.sub(ix:lower(),1,#i):find(i:lower()) then
return self[ix]
end
end
end})
setmetatable(GAMES,{__index=function(self,i)
for ix,v in pairs(self) do
if tostring(ix):lower()==i:lower() then
return self[ix]
end
end
end})
API.GAMES=GAMES
API.STYLES=STYLES
API.STYLES_LIST=STYLES_LIST
API.STATES=STATES
API.ROBLOX_LOCATION_TYPES={
[0]='Mobile Website',
[1]='Mobile In-Game',
[2]='Website',
[3]='Roblox Studio',
[4]='In-Game',
[5]='XboxApp',
[6]='TeamCreate'
}
API.ROBLOX_THUMBNAIL_SIZES={
[48]='48x48',
[50]='50x50',
[60]='60x60',
[75]='75x75',
[100]='100x100',
[110]='110x110',
[150]='150x150',
[180]='180x180',
[352]='352x352',
[420]='420x420',
[720]='720x720'
}
API.ROBLOX_THUMBNAIL_TYPES = {
AVATAR='avatar',
BUST='avatar-bust',
HEADSHOT='avatar-headshot'
}
-- insyri make this BTW
-- use as local err, res = parseToURLArgs(), thanks golang for this idea
function parseToURLArgs(tb) local function Err(err) return err, nil end local function Ok(res) return nil, res end if not tb then return Err('got nothing') end if type(tb) ~= 'table' then return Err('expected table, got '..type(tb)) end local str = '?' local index = 1 for key, value in pairs(tb) do if index == 1 then str = str..key..'='..t(value) else str = str..'&'..key..'='..t(value) end index = index + 1 end return Ok(str) end
-- fiveman made these (converted to lua from python)
-- function format_helper(a,b)a=tostring(a)while#a<b do a='0'..a end;return a end
-- function formatTime(a)if a>86400000 then return'>1 day'end;local c=format_helper(a%1000,3)local d=format_helper(math.floor(a/1000)%60,2)local e=format_helper(math.floor(a/(1000*60))%60,2)local f=format_helper(math.floor(a/(1000*60*60))%24,2)if f=='00'then return e..':'..d..'.'..c else return f..':'..e..':'..d end end
function formatTime(time) -- chatgpt THIS IS FOR SECONDS! NOT MILLISECONDS
local hours = math.floor(time / 3600)
local minutes = math.floor((time % 3600) / 60)
local seconds = math.floor(time % 60)
local milliseconds = math.floor(((time % 1) * 1000)+.5)
-- if hours > 0 then
-- return string.format("%02d:%02d:%02d", hours, minutes, seconds)
-- else
-- return string.format("%02d:%02d.%03d", minutes, seconds, milliseconds)
-- end
return string.format(hours and '%02d:%02d:%02d' or '%02d:%02d.%03d', hours and hours or minutes,hours and minutes or seconds, hours and seconds or milliseconds)
end
function L1Copy(t,b) b=b or {} for x,y in next,t do b[x]=y end return b end
-- [[ STRAFESNET API ]] --
-- Get rank string from rank point
function API.FormatRank(n) return RANKS[1+math.floor(n*19)] end
-- Get skill percentage from skill point
function API.FormatSkill(n) return r(n*100,3)..'%' end
function API.FormatTime(n) return formatTime(n) end
-- Time from id.
function API:GetTime(ID)
if not ID then return 'empty id' end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/'..ID, API_HEADER)
return response,headers
end
-- Time rank from id.
function API:GetTimeRank(TIME_ID)
if not TIME_ID then return 'empty id' end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/'..TIME_ID..'/rank', API_HEADER)
return response,headers
end
-- 10 recent world records.
function API:GetRecentWrs(STYLE_ID, GAME_ID, WHITELIST_FILTER)
if not STYLE_ID or not GAME_ID then return 'empty id' end
local err, res = parseToURLArgs({style=STYLE_ID, game=GAME_ID, whitelist=WHITELIST_FILTER})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/recent/wr'..res, API_HEADER)
return response,headers
end
-- Time by map id. Sorted in ascending order.
function API:GetMapTimes(MAP_ID, STYLE_ID, PAGE)
if not MAP_ID then return 'empty id' end
local err, res = parseToURLArgs({style=STYLE_ID, page=PAGE})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/map/'..MAP_ID..res, API_HEADER)
return response,headers
end
-- Get WR of map.
function API:GetMapWr(MAP_ID, STYLE_ID)
if not MAP_ID or not STYLE_ID then return 'empty id' end
local err, res = parseToURLArgs({style=STYLE_ID})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/map/'..MAP_ID..'/wr'..res, API_HEADER)
return response,headers
end
-- Time by user id.
function API:GetUserTimes(USER_ID, MAP_ID, STYLE_ID, GAME_ID, PAGE)
if not USER_ID then return 'empty id' end
local err, res = parseToURLArgs({map=MAP_ID, style=STYLE_ID, game=GAME_ID, page=PAGE})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/user/'..USER_ID..res , API_HEADER)
return response,headers
end
-- World records by user id.
function API:GetUserWrs(USER_ID,GAME_ID,STYLE_ID)
if not USER_ID or not GAME_ID or not STYLE_ID then return 'empty id' end
local err, res = parseToURLArgs({game=GAME_ID, style=STYLE_ID})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'time/user/'..USER_ID..'/wr'..res, API_HEADER)
return response,headers
end
-- User from id.
function API:GetUser(USER_ID)
if not USER_ID then return 'empty id' end
local response,headers = http_request('GET', STRAFESNET_API_URL..'user/'..USER_ID, API_HEADER)
return response,headers
end
-- Top ranked players, paged at 50 per page.
function API:GetRanks(STYLE_ID,GAME_ID,PAGE)
if not STYLE_ID or not GAME_ID then return 'empty id' end
local err, res = parseToURLArgs({style=STYLE_ID, game=GAME_ID, page=PAGE})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'rank'..res, API_HEADER)
return response,headers
end
-- Get rank of user by their id.
function API:GetRank(USER_ID,GAME_ID,STYLE_ID)
if not USER_ID or not STYLE_ID or not GAME_ID then return 'empty id' end
local err, res = parseToURLArgs({style=STYLE_ID, game=GAME_ID})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'rank/'..USER_ID..res, API_HEADER)
return response,headers
end
-- Get list of maps.
function API:GetMaps(GAME_ID,PAGE)
if not GAME_ID then return 'empty id' end
local err, res = parseToURLArgs({game=GAME_ID, page=PAGE})
if err then return err end
local response,headers = http_request('GET', STRAFESNET_API_URL..'map'..res, API_HEADER)
return response,headers
end
-- Get map by ID.
function API:GetMap(MAP_ID)
if not MAP_ID then return 'empty id' end
local response,headers = http_request('GET', STRAFESNET_API_URL..'map/'..MAP_ID, API_HEADER)
return response,headers
end
-- [[ CUSTOM ]] --
function API:GetMapCompletionCount(MAP_ID,STYLE_ID)
if not MAP_ID or not STYLE_ID then return 'empty id' end
local _,headers = self:GetMapTimes(MAP_ID,STYLE_ID)
local pages = headers['Pagination-Count']
local res,h = self:GetMapTimes(MAP_ID,STYLE_ID,pages)
if not res then
table.foreach(h,print)
end
return ((pages-1)*200)+#res
end
--cool doggo, aidan and me
function API.CalculatePoint(rank,count) --??wtf
return RANK_CONSTANT_A*(math.exp(RANK_CONSTANT_B)-1)/(1-math.exp(math.max(-700, -RANK_CONSTANT_C*count)))*math.exp(math.max(-700, -RANK_CONSTANT_D*rank))+(1-RANK_CONSTANT_E)*(1+2*(count-rank))/(count*count)
end
function API.Pad(str,n)
n = n or 20
str = tostring(str)
return str..string.rep(' ',n-#str)
end
function API.CalculateDifference(v1,v2)
return math.abs(v1-v2)
end
function API.CalculateDifferencePercent(v1,v2)
return math.abs((1-(v1/v2))*100)..'%'
end
function API:GetUserFromAny(user,message)
local str = user:match('^["\'](.+)[\'"]$')
local num = user:match('^(%d+)$')
if str then
local roblox_user=self:GetRobloxInfoFromUsername(str)
if not roblox_user.id then return 'User not found' end
return roblox_user
elseif num then
local roblox_user = self: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=self: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=self: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=self:GetRobloxInfoFromUsername(user)
if not roblox_user.id then return 'User not found' end
return roblox_user
end
return 'Something went wrong (this should generally not happen)'
end
-- [[ ROBLOX / FIVEMAN AND OTHER APIs ]] --
function API:GetRobloxInfoFromUserId(USER_ID)
if not USER_ID then return 'empty id' end
local response,headers = http_request('GET', ROBLOX_API_URL..'users/'..USER_ID, API_HEADER)
return response,headers
end
function API:GetRobloxInfoFromUsername(USERNAME)
if not USERNAME then return 'empty username' end
if #USERNAME > 32 then return 'Username too long' end
local response,headers = http_request('POST', ROBLOX_API_URL..'usernames/users', API_HEADER, {usernames={USERNAME}})
if not response.data[1] then return 'Username \''..USERNAME..'\' not found.' end
return self:GetRobloxInfoFromUserId(response.data[1].id)
end
function API:GetRobloxInfoFromDiscordId(DISCORD_ID)
if not DISCORD_ID then return 'empty id' end
local response,headers = http_request('GET', FIVEMAN_API_URL..'users/'..DISCORD_ID, API_HEADER)
if response.status=='error' then return response.messages end
local response2 = http_request('GET', ROBLOX_API_URL..'users/'..response.result.robloxId, API_HEADER)
return response2
end
function API:GetUserOnlineStatus(USER_ID)
if not USER_ID then return 'empty id' end
local response1 = http_request('POST', ROBLOX_PRESENCE_URL..'presence/users', API_HEADER, {userIds={USER_ID}}).userPresences[1] --For LastLocation
local response2 = http_request('POST', ROBLOX_PRESENCE_URL..'presence/last-online', API_HEADER, {userIds={USER_ID}}).lastOnlineTimestamps[1] --For a more accurate LastOnline
L1Copy(response2, response1)
return response1
end
function API:GetUserUsernameHistory(USER_ID)
if not USER_ID then return 'empty id' end
local err, res = parseToURLArgs({limit=50,sortOrder='Desc'})
if err then return err end
local response1 = http_request('GET', ROBLOX_API_URL..'users/'..USER_ID..'/username-history'..res,API_HEADER)
return response1
end
function API:GetBadgesAwardedDates(USER_ID,BADGE_LIST)
if not USER_ID then return 'empty id' end
local err,res = parseToURLArgs({badgeIds=table.concat(BADGE_LIST,',')})
if err then return end
local response,headers = http_request('GET',ROBLOX_BADGES_API..'users/'..USER_ID..'/badges/awarded-dates'..res)
return response,headers
end
function API:GetVerificationItemID(USER_ID)
if not USER_ID then return 'empty id' end
local response1,headers1 = http_request('GET', ROBLOX_INVENTORY_API..'users/'..USER_ID.."/items/Asset/102611803", API_HEADER)
if response1.errors then return response1,headers1 end
local response2,headers2 = http_request('GET', ROBLOX_INVENTORY_API..'users/'..USER_ID.."/items/Asset/1567446", API_HEADER)
if response2.errors then return response2,headers2 end
local data = {}
data[#data+1] = response2.data[1] -- Do the older item first if present
data[#data+1] = response1.data[1]
return {data=data},headers1
end
function API:GetUserThumbnail(USER_ID,TYPE,SIZE) -- https://thumbnails.roblox.com/v1/users/avatar?userIds=1455906620&size=180x180&format=Png&isCircular=false
if not USER_ID then return 'empty id' end
local _TYPE = self.ROBLOX_THUMBNAIL_TYPES[TYPE] or 'avatar'
local _SIZE = self.ROBLOX_THUMBNAIL_SIZES[SIZE] or '180x180'
local err, res = parseToURLArgs({userIds=USER_ID,size=_SIZE,format='Png',isCircular=false})
if err then return err end
local response,headers = http_request('GET', ROBLOX_THUMBNAIL_URL..'users/'.._TYPE..res, API_HEADER)
return response,headers
end
function API:GetGroups(USER_ID)
if not USER_ID then return 'empty id' end
local response,headers = http_request('GET',string.format(ROBLOX_GROUPS_ROLES_URL,USER_ID))
return response,headers
end
function API:GetSensWithParams(ARGUMENTS)
local hasarg = false
for _,val in next,ARGUMENTS do
if val then
hasarg=true
end
end
ARGUMENTS.format='json'
local err,res = parseToURLArgs(ARGUMENTS)
if err then return err end
local response,headers=http_request('GET',ROBLOX_SENS_DB..(hasarg and 'get' or 'get/all')..res)
return response,headers
end
return API

@ -1,6 +1,7 @@
local Discordia = require('discordia') local Discordia = require('discordia')
local json = require('json') local json = require('json')
local http_request = require('../Modules/http.lua') local HttpRequest = require('../Modules/HttpRequest.lua')
local Request = HttpRequest.Request
local SubCommandHandler = require('../Modules/SubCommandHandler.lua') local SubCommandHandler = require('../Modules/SubCommandHandler.lua')
Discordia.extensions() Discordia.extensions()
@ -12,8 +13,10 @@ local MinecraftSubCommandHandler = SubCommandHandler.new()
local MinecraftMainCommand = SlashCommandTools.slashCommand('minecraft', 'Minecraft server related commands') 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 MinecraftStatusSubCommand = SlashCommandTools.subCommand('status',
local MinecraftSetIpSubCommand = SlashCommandTools.subCommand('setip', 'Set the preferred Minecraft server IP address for this server') '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') local MinecraftSetIpOptions = SlashCommandTools.string('ip', 'The IP address of the server')
MinecraftSetIpOptions:setRequired(true) MinecraftSetIpOptions:setRequired(true)
@ -54,17 +57,20 @@ MinecraftSubCommandHandler:AddSubCommand(MinecraftStatusSubCommand.name, functio
end end
local ServerIPStr = ServerMinecraftData.IP .. ':' .. ServerMinecraftData.PORT local ServerIPStr = ServerMinecraftData.IP .. ':' .. ServerMinecraftData.PORT
local Response, Headers = http_request('GET', ('https://api.mcsrvstat.us/3/%s'):format(ServerIPStr)) local Headers, Body = Request("GET", ('https://api.mcsrvstat.us/3/%s'):format(ServerIPStr), nil,
{ ["User-Agent"] = "tommy-bot/1.0 Main-Release" })
local IsOnline = Response.online if not Headers.code == 200 then
return error("Something went wrong")
end
local IsOnline = Body.online
local EmbedData local EmbedData
if IsOnline then if IsOnline then
local MaxPlayers = Response.players.max local MaxPlayers = Body.players.max
local OnlinePlayers = Response.players.online local OnlinePlayers = Body.players.online
local AnonymousPlayers = OnlinePlayers local AnonymousPlayers = OnlinePlayers
local Players = {} local Players = {}
if OnlinePlayers > 0 then if OnlinePlayers > 0 then
for PlayerIndex, PlayerData in next, Response.players.list do for PlayerIndex, PlayerData in next, Body.players.list do
table.insert(Players, PlayerData.name) table.insert(Players, PlayerData.name)
AnonymousPlayers = AnonymousPlayers - 1 AnonymousPlayers = AnonymousPlayers - 1
end end
@ -78,7 +84,7 @@ MinecraftSubCommandHandler:AddSubCommand(MinecraftStatusSubCommand.name, functio
end end
EmbedData = { EmbedData = {
title = 'Server Status for ' .. ServerIPStr, title = 'Server Status for ' .. ServerIPStr,
description = Response.motd.clean[1]..' ('..Response.version..')', description = Body.motd.clean[1] .. ' (' .. Body.version .. ')',
fields = { fields = {
{ name = 'Players', value = OnlinePlayers .. '/' .. MaxPlayers, inline = true }, { name = 'Players', value = OnlinePlayers .. '/' .. MaxPlayers, inline = true },
{ name = 'List of players', value = table.concat(Players, '\n'), inline = true } { name = 'List of players', value = table.concat(Players, '\n'), inline = true }

@ -4,7 +4,7 @@ local Discordia = require('discordia')
local Date = Discordia.Date local Date = Discordia.Date
Discordia.extensions() Discordia.extensions()
local API = require('../Modules/strafes_net.lua') local StrafesNET = require('../Modules/StrafesNET.lua')
local UserCommand = SlashCommandTools.slashCommand('user', 'Looks up specified user on Roblox') local UserCommand = SlashCommandTools.slashCommand('user', 'Looks up specified user on Roblox')
@ -58,7 +58,6 @@ local IDToDate = { --Terrible ranges but it's all we have
{ 232802028144, FromYMD("2024-04-08") }, { 232802028144, FromYMD("2024-04-08") },
{ 234886704167, FromYMD("2024-06-28") }, { 234886704167, FromYMD("2024-06-28") },
{ 241580400713, FromYMD("2025-02-16") }, { 241580400713, FromYMD("2025-02-16") },
{244356127782, FromYMD("2025-05-18")},
} }
--We assume linear interpolation since anything more complex I can't process --We assume linear interpolation since anything more complex I can't process
local function linterp(i1, i2, m) local function linterp(i1, i2, m)
@ -95,16 +94,16 @@ local function Callback(Interaction, Command, Args)
local user_id = Args.user_id local user_id = Args.user_id
local member = Args.member local member = Args.member
if username then if username then
user_info = API:GetRobloxInfoFromUsername(username) _, user_info = StrafesNET.GetRobloxInfoFromUsername(username)
elseif user_id then elseif user_id then
user_info = API:GetRobloxInfoFromUserId(user_id) _, user_info = StrafesNET.GetRobloxInfoFromUserId(user_id)
elseif member then elseif member then
user_info = API:GetRobloxInfoFromDiscordId(member.id) _, user_info = StrafesNET.GetRobloxInfoFromDiscordId(member.id)
end end
else else
local user = Interaction.member or Interaction.user local user = Interaction.member or Interaction.user
if user then if user then
user_info = API:GetRobloxInfoFromDiscordId(user.id) _, user_info = StrafesNET.GetRobloxInfoFromDiscordId(user.id)
end end
end end
if not user_info.id then if not user_info.id then
@ -112,7 +111,6 @@ local function Callback(Interaction, Command, Args)
end end
local description = user_info.description == '' and 'This user has no description' or user_info.description local description = user_info.description == '' and 'This user has no description' or user_info.description
-- table.foreach(user_info,print)
local created = tostring(Date.fromISO(user_info.created):toSeconds()) local created = tostring(Date.fromISO(user_info.created):toSeconds())
local current = Date():toSeconds() local current = Date():toSeconds()
local accountAge = round((current - created) / 86400) local accountAge = round((current - created) / 86400)
@ -121,7 +119,8 @@ local function Callback(Interaction, Command, Args)
local name = user_info.name local name = user_info.name
local displayName = user_info.displayName local displayName = user_info.displayName
local usernameHistory = API:GetUserUsernameHistory(id).data or {} local usernameHistoryHeaders, usernameHistoryBody = StrafesNET.GetUserUsernameHistory(id)
local usernameHistory = usernameHistoryBody.data or {}
local usernameHistoryTable = {} local usernameHistoryTable = {}
for index, usernameObj in next, usernameHistory do for index, usernameObj in next, usernameHistory do
table.insert(usernameHistoryTable, usernameObj.name) table.insert(usernameHistoryTable, usernameObj.name)
@ -135,7 +134,7 @@ local function Callback(Interaction, Command, Args)
if onlineStatus_info.userPresenceType == 2 then LastLocation = "Ingame" end if onlineStatus_info.userPresenceType == 2 then LastLocation = "Ingame" end
local LastOnline = 0 --Date.fromISO(onlineStatus_info.lastOnline):toSeconds() local LastOnline = 0 --Date.fromISO(onlineStatus_info.lastOnline):toSeconds()
local verificationAssetId = API:GetVerificationItemID(id) local verificationAssetId = StrafesNET.GetVerificationItemID(id)
local verificationDate = "Not verified" local verificationDate = "Not verified"
if verificationAssetId.errors then if verificationAssetId.errors then
verificationDate = "Failed to fetch" verificationDate = "Failed to fetch"
@ -149,7 +148,7 @@ local function Callback(Interaction, Command, Args)
end end
end end
local badgeRequest = API:GetBadgesAwardedDates(id,Badges) local _, badgeRequest = StrafesNET.GetBadgesAwardedDates(id, Badges)
local badgeData = badgeRequest.data local badgeData = badgeRequest.data
-- local badgesDates = {} -- local badgesDates = {}
@ -160,11 +159,11 @@ local function Callback(Interaction, Command, Args)
local awardedDate = tonumber(Date.fromISO(badge.awardedDate):toSeconds()) local awardedDate = tonumber(Date.fromISO(badge.awardedDate):toSeconds())
if firstBadgeDate > awardedDate then if firstBadgeDate > awardedDate then
firstBadge = badgeId firstBadge = badgeId
firstBadgeDate=awardedDate
end end
-- badgesDates[badgeId]=awardedDate -- badgesDates[badgeId]=awardedDate
end end
local userThumbnail = API:GetUserThumbnail(id).data[1] local userThumbnailHeaders, userThumbnailBody = StrafesNET.GetUserThumbnail(id)
local userThumbnail = userThumbnailBody.data[1]
local embed = { local embed = {
title = displayName .. ' (@' .. name .. ')', title = displayName .. ' (@' .. name .. ')',