forked from tommy/tommy-bot
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
7b3ab8e115
|
|||
54d588b43a
|
|||
f072b14735
|
|||
e2ee24898f
|
|||
1c08e5fd47
|
|||
d2eded99a1
|
|||
5eb9f82bfe
|
|||
708a5364a9
|
|||
328a9ed5b7 |
.gitignore
src
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
discordia.log
|
||||
gateway.json
|
||||
apikey.lua
|
||||
APIKeys.lua
|
||||
Token.lua
|
||||
deps
|
||||
sensdb.lua
|
||||
|
88
src/Modules/HttpRequest.lua
Normal file
88
src/Modules/HttpRequest.lua
Normal file
@ -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
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 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')
|
||||
Discordia.extensions()
|
||||
|
||||
@ -12,8 +13,10 @@ 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)
|
||||
@ -53,46 +56,49 @@ 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 Response, Headers = http_request('GET', ('https://api.mcsrvstat.us/3/%s'):format(ServerIPStr))
|
||||
|
||||
local IsOnline = Response.online
|
||||
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 EmbedData
|
||||
if IsOnline then
|
||||
local MaxPlayers = Response.players.max
|
||||
local OnlinePlayers = Response.players.online
|
||||
local MaxPlayers = Body.players.max
|
||||
local OnlinePlayers = Body.players.online
|
||||
local AnonymousPlayers = OnlinePlayers
|
||||
local Players = {}
|
||||
if OnlinePlayers>0 then
|
||||
for PlayerIndex, PlayerData in next, Response.players.list do
|
||||
if OnlinePlayers > 0 then
|
||||
for PlayerIndex, PlayerData in next, Body.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 = 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
|
||||
}
|
||||
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
|
||||
}
|
||||
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)
|
||||
@ -107,15 +113,15 @@ 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 {
|
||||
|
@ -4,7 +4,7 @@ local Discordia = require('discordia')
|
||||
local Date = Discordia.Date
|
||||
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')
|
||||
|
||||
@ -17,52 +17,51 @@ UserCommand:addOption(UserIdOption)
|
||||
UserCommand:addOption(MemberOption)
|
||||
|
||||
Badges = {
|
||||
'275640532', --Bhop, pre-group
|
||||
'363928432', --Surf, pre-group
|
||||
'275640532', --Bhop, pre-group
|
||||
'363928432', --Surf, pre-group
|
||||
'2124614454', --Bhop, post-group
|
||||
'2124615096', --Surf, post-group
|
||||
}
|
||||
BadgesToName = {
|
||||
[275640532]='old bhop',
|
||||
[363928432]='old surf',
|
||||
[2124614454]='new bhop',
|
||||
[2124615096]='new surf',
|
||||
[275640532] = 'old bhop',
|
||||
[363928432] = 'old surf',
|
||||
[2124614454] = 'new bhop',
|
||||
[2124615096] = 'new surf',
|
||||
}
|
||||
local function round(x,n)
|
||||
return string.format('%.'..(n or 0)..'f',x)
|
||||
local function round(x, n)
|
||||
return string.format('%.' .. (n or 0) .. 'f', x)
|
||||
end
|
||||
|
||||
local function FromYMD(ymd)
|
||||
return Date.fromISO(ymd.."T00:00:00")[1]
|
||||
return Date.fromISO(ymd .. "T00:00:00")[1]
|
||||
end
|
||||
local function leftpad(s,n,p)
|
||||
return string.rep(p,n-#tostring(s))..s
|
||||
local function leftpad(s, n, p)
|
||||
return string.rep(p, n - #tostring(s)) .. s
|
||||
end
|
||||
local function ToYMD(seconds)
|
||||
return "<t:"..seconds..":R>"
|
||||
return "<t:" .. seconds .. ":R>"
|
||||
end
|
||||
local IDToDate = { --Terrible ranges but it's all we have
|
||||
-- {1000000000, FromYMD("2006-01-01")}, --I guess?
|
||||
-- {1864564055, FromYMD("2008-08-04")},
|
||||
{1228821079, FromYMD("2013-02-07")}, -- asomstephano12344 (mass scanning near sign removal date)
|
||||
{3800920136, FromYMD("2016-04-16")},
|
||||
{9855616205, FromYMD("2017-04-02")},
|
||||
{30361018662, FromYMD("2018-11-14")},
|
||||
{32665806459, FromYMD("2019-01-07")},
|
||||
{34758058773, FromYMD("2019-02-24")},
|
||||
{65918261258, FromYMD("2020-06-05")},
|
||||
{171994717435, FromYMD("2023-03-24")},
|
||||
{173210319088, FromYMD("2023-04-14")},
|
||||
{206368884641, FromYMD("2023-07-16")},
|
||||
{229093879745, FromYMD("2024-01-02")},
|
||||
{232802028144, FromYMD("2024-04-08")},
|
||||
{234886704167, FromYMD("2024-06-28")},
|
||||
{241580400713, FromYMD("2025-02-16")},
|
||||
{244356127782, FromYMD("2025-05-18")},
|
||||
{ 1228821079, FromYMD("2013-02-07") }, -- asomstephano12344 (mass scanning near sign removal date)
|
||||
{ 3800920136, FromYMD("2016-04-16") },
|
||||
{ 9855616205, FromYMD("2017-04-02") },
|
||||
{ 30361018662, FromYMD("2018-11-14") },
|
||||
{ 32665806459, FromYMD("2019-01-07") },
|
||||
{ 34758058773, FromYMD("2019-02-24") },
|
||||
{ 65918261258, FromYMD("2020-06-05") },
|
||||
{ 171994717435, FromYMD("2023-03-24") },
|
||||
{ 173210319088, FromYMD("2023-04-14") },
|
||||
{ 206368884641, FromYMD("2023-07-16") },
|
||||
{ 229093879745, FromYMD("2024-01-02") },
|
||||
{ 232802028144, FromYMD("2024-04-08") },
|
||||
{ 234886704167, FromYMD("2024-06-28") },
|
||||
{ 241580400713, FromYMD("2025-02-16") },
|
||||
}
|
||||
--We assume linear interpolation since anything more complex I can't process
|
||||
local function linterp(i1, i2, m)
|
||||
return math.floor(i1 + (i2-i1)*m)
|
||||
return math.floor(i1 + (i2 - i1) * m)
|
||||
end
|
||||
local function GuessDateFromAssetID(InstanceID, AssetID)
|
||||
local note = ""
|
||||
@ -70,72 +69,72 @@ local function GuessDateFromAssetID(InstanceID, AssetID)
|
||||
note = " (Verification Sign)"
|
||||
end
|
||||
for i = #IDToDate, 1, -1 do --Newest to oldest
|
||||
local ID,Time = unpack(IDToDate[i])
|
||||
local ID, Time = unpack(IDToDate[i])
|
||||
if ID < InstanceID then
|
||||
if not IDToDate[i+1] then
|
||||
if not IDToDate[i + 1] then
|
||||
-- Screw it we ball, just do unjustified interpolation
|
||||
local ID1, Time1 = unpack(IDToDate[#IDToDate-1])
|
||||
local ID1, Time1 = unpack(IDToDate[#IDToDate - 1])
|
||||
local ID2, Time2 = unpack(IDToDate[#IDToDate])
|
||||
return "Around "..ToYMD(linterp(Time1, Time2, (InstanceID-ID1)/(ID2-ID1)))..note
|
||||
return "Around " .. ToYMD(linterp(Time1, Time2, (InstanceID - ID1) / (ID2 - ID1))) .. note
|
||||
end
|
||||
local ParentID, ParentTime = unpack(IDToDate[i+1])
|
||||
return "Around "..ToYMD(linterp(Time, ParentTime, (InstanceID-ID)/(ParentID-ID)))..note
|
||||
local ParentID, ParentTime = unpack(IDToDate[i + 1])
|
||||
return "Around " .. ToYMD(linterp(Time, ParentTime, (InstanceID - ID) / (ParentID - ID))) .. note
|
||||
end
|
||||
end
|
||||
-- Screw it we ball, just do unjustified interpolation
|
||||
local ID1, Time1 = unpack(IDToDate[1])
|
||||
local ID2, Time2 = unpack(IDToDate[2])
|
||||
return "Around "..ToYMD(linterp(Time1, Time2, (InstanceID-ID1)/(ID2-ID1)))..note
|
||||
return "Around " .. ToYMD(linterp(Time1, Time2, (InstanceID - ID1) / (ID2 - ID1))) .. note
|
||||
end
|
||||
|
||||
local function Callback(Interaction, Command, Args)
|
||||
local user_info
|
||||
if Args then
|
||||
local username = Args.username
|
||||
local user_id = Args.user_id
|
||||
local member = Args.member
|
||||
if username then
|
||||
user_info = API:GetRobloxInfoFromUsername(username)
|
||||
elseif user_id then
|
||||
user_info = API:GetRobloxInfoFromUserId(user_id)
|
||||
elseif member then
|
||||
user_info = API:GetRobloxInfoFromDiscordId(member.id)
|
||||
end
|
||||
else
|
||||
local user = Interaction.member or Interaction.user
|
||||
if user then
|
||||
user_info = API:GetRobloxInfoFromDiscordId(user.id)
|
||||
end
|
||||
end
|
||||
local user_info
|
||||
if Args then
|
||||
local username = Args.username
|
||||
local user_id = Args.user_id
|
||||
local member = Args.member
|
||||
if username then
|
||||
_, user_info = StrafesNET.GetRobloxInfoFromUsername(username)
|
||||
elseif user_id then
|
||||
_, user_info = StrafesNET.GetRobloxInfoFromUserId(user_id)
|
||||
elseif member then
|
||||
_, user_info = StrafesNET.GetRobloxInfoFromDiscordId(member.id)
|
||||
end
|
||||
else
|
||||
local user = Interaction.member or Interaction.user
|
||||
if user then
|
||||
_, user_info = StrafesNET.GetRobloxInfoFromDiscordId(user.id)
|
||||
end
|
||||
end
|
||||
if not user_info.id then
|
||||
return error("User not found")
|
||||
end
|
||||
|
||||
local description = user_info.description=='' and 'This user has no description' or user_info.description
|
||||
-- table.foreach(user_info,print)
|
||||
local description = user_info.description == '' and 'This user has no description' or user_info.description
|
||||
local created = tostring(Date.fromISO(user_info.created):toSeconds())
|
||||
local current = Date():toSeconds()
|
||||
local accountAge = round((current-created)/86400)
|
||||
local accountAge = round((current - created) / 86400)
|
||||
local isBanned = user_info.isBanned
|
||||
local id = user_info.id
|
||||
local name = user_info.name
|
||||
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 = {}
|
||||
for index,usernameObj in next,usernameHistory do
|
||||
table.insert(usernameHistoryTable,usernameObj.name)
|
||||
for index, usernameObj in next, usernameHistory do
|
||||
table.insert(usernameHistoryTable, usernameObj.name)
|
||||
end
|
||||
local usernameHistoryString = table.concat(usernameHistoryTable,', ')
|
||||
local usernameHistoryString = table.concat(usernameHistoryTable, ', ')
|
||||
|
||||
local onlineStatus_info = {lastLocation="Unknown", lastOnline=0, userPresenceType=-1}
|
||||
local onlineStatus_info = { lastLocation = "Unknown", lastOnline = 0, userPresenceType = -1 }
|
||||
-- table.foreach(onlineStatus_info,print)
|
||||
|
||||
local LastLocation = onlineStatus_info.lastLocation
|
||||
if onlineStatus_info.userPresenceType==2 then LastLocation="Ingame" end
|
||||
local LastOnline = 0--Date.fromISO(onlineStatus_info.lastOnline):toSeconds()
|
||||
if onlineStatus_info.userPresenceType == 2 then LastLocation = "Ingame" end
|
||||
local LastOnline = 0 --Date.fromISO(onlineStatus_info.lastOnline):toSeconds()
|
||||
|
||||
local verificationAssetId = API:GetVerificationItemID(id)
|
||||
local verificationAssetId = StrafesNET.GetVerificationItemID(id)
|
||||
local verificationDate = "Not verified"
|
||||
if verificationAssetId.errors then
|
||||
verificationDate = "Failed to fetch"
|
||||
@ -149,49 +148,49 @@ local function Callback(Interaction, Command, Args)
|
||||
end
|
||||
end
|
||||
|
||||
local badgeRequest = API:GetBadgesAwardedDates(id,Badges)
|
||||
local _, badgeRequest = StrafesNET.GetBadgesAwardedDates(id, Badges)
|
||||
local badgeData = badgeRequest.data
|
||||
|
||||
-- local badgesDates = {}
|
||||
|
||||
local firstBadge,firstBadgeDate = 0,math.huge
|
||||
for _,badge in next,badgeData do
|
||||
local firstBadge, firstBadgeDate = 0, math.huge
|
||||
for _, badge in next, badgeData do
|
||||
local badgeId = badge.badgeId
|
||||
local awardedDate = tonumber(Date.fromISO(badge.awardedDate):toSeconds())
|
||||
if firstBadgeDate>awardedDate then
|
||||
firstBadge=badgeId
|
||||
firstBadgeDate=awardedDate
|
||||
if firstBadgeDate > awardedDate then
|
||||
firstBadge = badgeId
|
||||
end
|
||||
-- badgesDates[badgeId]=awardedDate
|
||||
end
|
||||
local userThumbnail = API:GetUserThumbnail(id).data[1]
|
||||
local userThumbnailHeaders, userThumbnailBody = StrafesNET.GetUserThumbnail(id)
|
||||
local userThumbnail = userThumbnailBody.data[1]
|
||||
|
||||
local embed = {
|
||||
title = displayName..' (@'..name..')',
|
||||
url = 'https://roblox.com/users/'..id..'/profile',
|
||||
title = displayName .. ' (@' .. name .. ')',
|
||||
url = 'https://roblox.com/users/' .. id .. '/profile',
|
||||
thumbnail = {
|
||||
url = userThumbnail.imageUrl,
|
||||
},
|
||||
fields = {
|
||||
{name='ID',value=id,inline=true},
|
||||
{name='Account Age',value=accountAge..' days',inline=true},
|
||||
{name='Created',value='<t:'..round(created)..':R>',inline=true},
|
||||
{name='Verified Email',value=verificationDate,inline=true},
|
||||
{name='Last Online',value='<t:'..round(LastOnline)..':R>',inline=true},
|
||||
{name='Last Location',value=LastLocation,inline=true},
|
||||
{name='Banned',value=isBanned,inline=true},
|
||||
{name='Description',value=description,inline=false},
|
||||
{name='Username History ('..#usernameHistoryTable..(#usernameHistoryTable==50 and '*' or '')..')',value=usernameHistoryString,inline=false},
|
||||
{ name = 'ID', value = id, inline = true },
|
||||
{ name = 'Account Age', value = accountAge .. ' days', inline = true },
|
||||
{ name = 'Created', value = '<t:' .. round(created) .. ':R>', inline = true },
|
||||
{ name = 'Verified Email', value = verificationDate, inline = true },
|
||||
{ name = 'Last Online', value = '<t:' .. round(LastOnline) .. ':R>', inline = true },
|
||||
{ name = 'Last Location', value = LastLocation, inline = true },
|
||||
{ name = 'Banned', value = isBanned, inline = true },
|
||||
{ name = 'Description', value = description, inline = false },
|
||||
{ name = 'Username History (' .. #usernameHistoryTable .. (#usernameHistoryTable == 50 and '*' or '') .. ')', value = usernameHistoryString, inline = false },
|
||||
}
|
||||
}
|
||||
if firstBadge and firstBadgeDate~=math.huge then
|
||||
table.insert(embed.fields,{name='FQG',value=BadgesToName[firstBadge],inline=true})
|
||||
table.insert(embed.fields,{name='Joined',value='<t:'..round(firstBadgeDate)..':R>',inline=true})
|
||||
if firstBadge and firstBadgeDate ~= math.huge then
|
||||
table.insert(embed.fields, { name = 'FQG', value = BadgesToName[firstBadge], inline = true })
|
||||
table.insert(embed.fields, { name = 'Joined', value = '<t:' .. round(firstBadgeDate) .. ':R>', inline = true })
|
||||
end
|
||||
Interaction:reply({embed=embed})
|
||||
Interaction:reply({ embed = embed })
|
||||
end
|
||||
|
||||
return {
|
||||
Command = UserCommand,
|
||||
Callback = Callback
|
||||
Command = UserCommand,
|
||||
Callback = Callback
|
||||
}
|
10
src/main.lua
10
src/main.lua
@ -16,10 +16,10 @@ Client:on('ready', function()
|
||||
end)
|
||||
|
||||
local function RunCallback(Callback, Interaction, Command, Args)
|
||||
local Success, Return = pcall(Callback, Interaction, Command, Args)
|
||||
if not Success then
|
||||
Interaction:reply('Error encountered when trying to run command: '..tostring(Return), true)
|
||||
end
|
||||
local Success, Return = pcall(Callback, Interaction, Command, Args)
|
||||
if not Success then
|
||||
Interaction:reply('Error encountered when trying to run command: ' .. tostring(Return), true)
|
||||
end
|
||||
end
|
||||
|
||||
Client:on('slashCommand', function(Interaction, Command, Args)
|
||||
@ -43,4 +43,4 @@ Client:on('userCommand', function(Interaction, Command, Member)
|
||||
end
|
||||
end)
|
||||
|
||||
Client:run('Bot '..Token)
|
||||
Client:run('Bot ' .. Token)
|
||||
|
Reference in New Issue
Block a user