Merge pull request #11 from dowoge/global-slash-commands

Global slash commands
This commit is contained in:
tommy 2024-07-10 15:55:41 -04:00 committed by GitHub
commit c9bdd6a8b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 348 additions and 759 deletions

View File

@ -1,3 +1,3 @@
.\exes\lit.exe install SinisterRectus/discordia
.\exes\lit.exe install GitSparTV/discordia-slash
git clone https://github.com/Bilal2453/discordia-interactions.git ./deps/discordia-interactions
git clone git@github.com:GitSparTV/discordia-slash.git ./deps/discordia-slash
git clone git@github.com:Bilal2453/discordia-interactions.git ./deps/discordia-interactions

View File

@ -0,0 +1,15 @@
local SlashCommandTools = require('discordia-slash').util.tools()
local GetProfilePictureCommand = SlashCommandTools.messageCommand('Copy message', 'Says the same exact message (text only)')
local function Callback(Interaction, Command, Message)
local MessageContent = Message.content
if MessageContent then
return Interaction:reply(MessageContent)
end
end
return {
Command = GetProfilePictureCommand,
Callback = Callback
}

View File

@ -0,0 +1,139 @@
local Discordia = require('discordia')
local json = require('json')
local http_request = require('../Modules/http.lua')
Discordia.extensions()
local ApplicationCommandOptionTypes = Discordia.enums.appCommandOptionType
local SlashCommandTools = require('discordia-slash').util.tools()
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 MinecraftSetIpOptions = SlashCommandTools.string('ip', 'The IP address of the server')
MinecraftSetIpOptions:setRequired(true)
MinecraftSetIpSubCommand:addOption(MinecraftSetIpOptions)
MinecraftMainCommand:addOption(MinecraftSetIpSubCommand)
MinecraftMainCommand:addOption(MinecraftStatusSubCommand)
local COLOURS = {
GREEN = 0x00ff00,
RED = 0xff0000
}
--initialize minecraft ip data
local MinecraftDataFile = io.open('minecraft_data.json', 'r')
if not MinecraftDataFile or (MinecraftDataFile and MinecraftDataFile:read('*a') == '') then
print('no such file exists! so make it')
io.open('minecraft_data.json', 'w+'):write(json.encode({})):close()
end
if MinecraftDataFile then
MinecraftDataFile:close()
end
local SubCommandCallbacks = {}
local function Status(Interaction, Command, Args)
local GuildId = Interaction.guild and Interaction.guild.id
if not GuildId then
return Interaction:reply('You cannot use this command outside of a Discord server', true)
end
local GlobalMinecraftData = json.decode(io.open('minecraft_data.json', 'r'):read('*a'))
if not GlobalMinecraftData then
return Interaction:reply('Could not read server data', true)
end
local ServerMinecraftData = GlobalMinecraftData[GuildId]
if not ServerMinecraftData then
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 EmbedData
if IsOnline then
local MaxPlayers = Response.players.max
local OnlinePlayers = Response.players.online
local AnonymousPlayers = OnlinePlayers
local Players = {}
if OnlinePlayers>0 then
for PlayerIndex, PlayerData in next, Response.players.list do
table.insert(Players, PlayerData.name)
AnonymousPlayers = AnonymousPlayers-1
end
else
table.insert(Players, 'No players online')
end
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
}
else
EmbedData = {
title = 'Server Status for '..ServerIPStr,
description = 'Server is offline',
color = COLOURS.RED
}
end
return Interaction:reply({embed = EmbedData})
end
local function SetIp(Interaction, Command, Args)
local ServerIPStr = Args.ip
local GuildId = Interaction.guild and Interaction.guild.id
if not GuildId then
return Interaction:reply('You cannot use this command outside of a Discord server')
end
local ServerIP = ServerIPStr:match("(%d+%.%d+%.%d+%.%d+)") or ServerIPStr:match("(%w*%.?%w+%.%w+)")
if not ServerIP then
return Interaction:reply('Invalid server IP')
end
local ServerPort = ServerIPStr:match(ServerIP..':(%d+)') or 25565
local GuildMinecraftData = {IP = ServerIP, PORT = ServerPort}
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()
return Interaction:reply('Successfully added `'..ServerIP..':'..ServerPort..'` for ServerId='..GuildId)
end
SubCommandCallbacks.status = Status
SubCommandCallbacks.setip = SetIp
local function Callback(Interaction, Command, Args)
local SubCommandOption = Command.options[1]
if SubCommandOption.type == ApplicationCommandOptionTypes.subCommand then
local SubCommandName = SubCommandOption.name
local SubCommandCallback = SubCommandCallbacks[SubCommandName]
local SubCommandArgs = Args[SubCommandName]
if SubCommandCallback then
SubCommandCallback(Interaction, Command, SubCommandArgs)
end
end
end
return {
Command = MinecraftMainCommand,
Callback = Callback
}

View File

@ -1,30 +1,21 @@
local discordia=require('discordia')
local date = discordia.Date
local API=require('./../strafes_net.lua')
local commands=require('./../commands.lua')
--[[
{
"description": "string",
"created": "2022-08-22T02:55:01.607Z",
"isBanned": true,
"externalAppDisplayName": "string",
"hasVerifiedBadge": true,
"id": 0,
"name": "string",
"displayName": "string"
}]]
--[[
{"GameId":null,
"IsOnline":false,
"LastLocation":"Offline",
"LastOnline":"2022-08-21T22:32:23.4-05:00",
"LocationType":2,
"PlaceId":null,
"VisitorId":1455906620,
"PresenceType":0,
"UniverseId":null,
"Visibility":0}
]]
local SlashCommandTools = require('discordia-slash').util.tools()
local Discordia = require('discordia')
local Date = Discordia.Date
Discordia.extensions()
local API = require('../Modules/strafes_net.lua')
local UserCommand = SlashCommandTools.slashCommand('user', 'Looks up specified user on Roblox')
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')
UserCommand:addOption(UsernameOption)
UserCommand:addOption(UserIdOption)
UserCommand:addOption(MemberOption)
Badges = {
'275640532', --Bhop, pre-group
'363928432', --Surf, pre-group
@ -42,7 +33,7 @@ local function round(x,n)
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
@ -84,18 +75,30 @@ local function GuessDateFromAssetID(AssetID)
return "Before "..ToYMD(IDToDate[1][2])
end
discordia.extensions()
commands:Add('user',{},'user <username|mention|"me">', function(t)
local args=t.args
local message=t.message
local user=args[1] or 'me'
local user_info=API:GetUserFromAny(user,message)
if type(user_info)=='string' then return message:reply('```'..user_info..'```') 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 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 current = date():toSeconds()
local created = tostring(Date.fromISO(user_info.created):toSeconds())
local current = Date():toSeconds()
local accountAge = round((current-created)/86400)
local isBanned = user_info.isBanned
local id = user_info.id
@ -114,7 +117,7 @@ commands:Add('user',{},'user <username|mention|"me">', function(t)
local LastLocation = onlineStatus_info.lastLocation
if onlineStatus_info.userPresenceType==2 then LastLocation="Ingame" end
local LastOnline = date.fromISO(onlineStatus_info.lastOnline):toSeconds()
local LastOnline = Date.fromISO(onlineStatus_info.lastOnline):toSeconds()
local verificationAssetId = API:GetVerificationItemID(id)
local verificationDate = "Not verified"
@ -132,7 +135,7 @@ commands:Add('user',{},'user <username|mention|"me">', function(t)
local firstBadge,firstBadgeDate = 0,math.huge
for _,badge in next,badgeData do
local badgeId = badge.badgeId
local awardedDate = tonumber(date.fromISO(badge.awardedDate):toSeconds())
local awardedDate = tonumber(Date.fromISO(badge.awardedDate):toSeconds())
if firstBadgeDate>awardedDate then
firstBadge=badgeId
firstBadgeDate=awardedDate
@ -163,5 +166,10 @@ commands:Add('user',{},'user <username|mention|"me">', function(t)
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
message:reply({embed=embed})
end)
Interaction:reply({embed=embed})
end
return {
Command = UserCommand,
Callback = Callback
}

View File

@ -0,0 +1,17 @@
local SlashCommandTools = require('discordia-slash').util.tools()
local PongCommand = SlashCommandTools.slashCommand('ping', 'Replies with pong')
local MessageOption = SlashCommandTools.string('message', 'What the bot will append to the message')
PongCommand:addOption(MessageOption)
local function Callback(Interaction, Command, Args)
local Message = Args.message
return Interaction:reply('Pong! '..Message)
end
return {
Command = PongCommand,
Callback = Callback
}

View File

@ -0,0 +1,15 @@
local SlashCommandTools = require('discordia-slash').util.tools()
local GetProfilePictureCommand = SlashCommandTools.userCommand('Get profile picture', 'Gets user avatar')
local function Callback(Interaction, Command, Member)
local AvatarURL = Member:getAvatarURL(1024)
if AvatarURL then
return Interaction:reply(AvatarURL, true)
end
end
return {
Command = GetProfilePictureCommand,
Callback = Callback
}

View File

@ -1,80 +1,45 @@
local discordia = require('discordia')
local dcmd = require('discordia-slash')
local token = require('./modules/token.lua')
local commands=require('./modules/commands.lua')
local prefix = ','
local client = discordia.Client()
client:useSlashCommands()
_G.dcmd=dcmd
_G.client = client
_G.locked = false
local Discordia = require('discordia')
local DiscordiaSlash = require('discordia-slash')
local Token = require('./Modules/Token.lua')
local CommandCollector = require('./Modules/CommandCollector.lua')
local Client = Discordia.Client():useApplicationCommands()
Discordia.extensions()
discordia.extensions()
local MessageCommandCollector = CommandCollector.new('Message'):Collect()
local SlashCommandCollector = CommandCollector.new('Slash'):Collect()
local UserCommandCollector = CommandCollector.new('User'):Collect()
client:on('ready',function()
commands:INIT()
local f=io.open('restart.txt','r+'):read()
local t=tostring(f):split(',')
if #t==3 then
client:getGuild(t[1]):getChannel(t[2]):send(
{
content='bot ready',
reference={
message=client:getChannel(t[2]):getMessage(t[3]),
mention=true
}
}
)
io.open('restart.txt','w+'):write(''):close()
else
print('restart.txt is empty or something so probably a first start')
end
Client:on('ready', function()
-- local GlobalCommands = Client:getGlobalApplicationCommands()
-- for CommandId in pairs(GlobalCommands) do
-- Client:deleteGlobalApplicationCommand(CommandId)
-- end
MessageCommandCollector:Publish(Client)
SlashCommandCollector:Publish(Client)
UserCommandCollector:Publish(Client)
end)
function parseMentions(message)
local content=message.content
local usersMentioned={}
if #message.mentionedUsers>0 then
for user in message.mentionedUsers:iter() do
usersMentioned[user.id]=user
end
end
local msgSplit=content:split(' ')
for i,v in next, msgSplit do
if v:match('<@![0-9]+>') then
local id=v:match('<@!([0-9]+)>')
if usersMentioned[id] then
msgSplit[i]=usersMentioned[id].mentionString
end
end
end
return table.concat(msgSplit,' ') or '',usersMentioned
end
client:on('messageCreate', function(message)
if message.author.bot then return end
local content,mentions=parseMentions(message)
if content:sub(1,#prefix)==prefix and content~=prefix then
local cmd=content:sub(#prefix+1,#content)
local args=cmd:split(' ')
local cmdName=args[1]
table.remove(args,1)
local command=commands.command_list[cmdName]
if command~=nil then
if message.guild~=nil then
local tb
local s,e=xpcall(function()
command.exec({message=message,args=args,mentions=mentions,t={client,discordia,token}})
end,function(err)
tb = debug.traceback()
return err
end)
if not s then
message:reply('tripped : '..e:split('/')[#e:split('/')])
print(e,tb)
end
end
end
Client:on('slashCommand', function(Interaction, Command, Args)
local SlashCommand = SlashCommandCollector:Get(Command.name)
if SlashCommand then
SlashCommand.Callback(Interaction, Command, Args)
end
end)
client:run('Bot '..token)
Client:on('messageCommand', function(Interaction, Command, Message)
local MessageCommand = MessageCommandCollector:Get(Command.name)
if MessageCommand then
MessageCommand.Callback(Interaction, Command, Message)
end
end)
Client:on('userCommand', function(Interaction, Command, Member)
local UserCommand = UserCommandCollector:Get(Command.name)
if UserCommand then
UserCommand.Callback(Interaction, Command, Member)
end
end)
Client:run('Bot '..Token)

View File

@ -0,0 +1,73 @@
local RELATIVE_PATH_TO_COMMANDS = '../' --this is because the require function will call to a path relative to this current file :D
local IGNORE_STARTING_FILE_NAME = '_'
local CommandCollector = {}
CommandCollector.__index = CommandCollector
function CommandCollector.new(Prefix)
local self = setmetatable({}, CommandCollector)
self.Prefix = Prefix
self.Collected = false
self.Collection = {}
return self
end
function CommandCollector:Get(CommandName)
for CommandIndex, CommandData in next, self.Collection do
if CommandName == CommandData.Command.name then
return CommandData
end
end
end
function CommandCollector:Collect()
if self.Collected then
print('Command collector for', self.Prefix, 'commands was already collected')
return
end
local CommandsContainerPath = self.Prefix..'Commands/'
for File in io.popen('dir "./src/'..CommandsContainerPath..'" /b'):lines() do
if File:sub(1, 1) ~= IGNORE_STARTING_FILE_NAME then
local Success, Return = pcall(require, RELATIVE_PATH_TO_COMMANDS..CommandsContainerPath..File)
if Success then
if not Return.Command or not Return.Callback then
print('Malformed command data in', CommandsContainerPath..File, 'Reason: returned command data table is missing a Command or Callback field')
return
end
print('Loaded', CommandsContainerPath..File)
table.insert(self.Collection, {Command = Return.Command, Callback = Return.Callback})
else
print('Error loading', CommandsContainerPath..File, 'Error:', Return)
end
end
end
print('Loaded a total of '..#self.Collection..' '..self.Prefix..' command'..(#self.Collection ~= 1 and 's' or ''))
self.Collected = true
return self
end
function CommandCollector:Publish(Client)
if not Client.createGlobalApplicationCommand then
print('Client does not have the method \'createGlobalApplicationCommand\'')
return
end
for CommandIndex, CommandData in next, self.Collection do
local Success, Return = pcall(Client.createGlobalApplicationCommand, Client, CommandData.Command)
if Success then
print('Published command', CommandData.Command.name)
else
print('Failed to publish command', CommandData.Command.name, 'Error:', Return)
end
end
return self
end
return CommandCollector

View File

@ -1,34 +0,0 @@
local discordia=require('discordia')
discordia.extensions()
local commands={command_list={}}
setmetatable(commands.command_list,{__index=function(self,index)
for i,v in pairs(self) do
for i2,v2 in pairs(v.alias) do
if v2==index then
return self[i]
end
end
end
return nil
end})
function commands:Add(name,alias,desc,exec)
name=type(name)=='string' and name or ('Command'..#self.command_list)
self.command_list[name]={
name=name,
alias=type(alias)=='table'and alias or {'None'},
desc=type(desc)=='string'and desc or ('No description provided'),
exec=type(exec)=='function'and exec or function(message)
return message:reply('No command assigned')
end
}
return self.command_list[name]
end
function commands:Get(name)
return self.command_list[name]
end
function commands:INIT()
for file in io.popen([[dir "./src/modules/commands" /b]]):lines() do require('./commands/'..file) end
print('commands done')
end
return commands

View File

@ -1,14 +0,0 @@
local discordia=require('discordia')
local commands=require('./../commands.lua')
discordia.extensions()
commands:Add('cmds',{'commands','cmd','help'},'Returns a list of all commands',function(t)
local final='```\n'
for i,v in pairs(commands.command_list) do
local name=v.name
local alias=table.concat(v.alias,', ') or 'None'
local desc=v.desc
final=final..'Name: '..name..'\nDescription: '..desc..'\n'..'Aliases: '..alias..'\n\n'
end
final=final..'```'
t.message:reply(final)
end)

View File

@ -1,78 +0,0 @@
local discordia=require('discordia')
local commands=require('./../commands.lua')
discordia.extensions()
function split(s,d)
local t,c,i={},'',0
for k in s:gmatch('.') do
i=i+1
if k==d and string.sub(s,i+1)~='' then
t[#t+1]=c
c=''
goto continue
end
c=c..k
::continue::
end
t[#t+1]=c
return t
end
function clearTmp()
for file in io.popen([[dir "./tmp" /b]]):lines() do
if file then
os.remove('./tmp/'..file)
end
end
end
function isTmpEmpty()
local dir = io.popen([[dir "./tmp" /b]]):read()
return dir==nil, dir, dir~=nil and split(dir,'\n') or {}
end
commands:Add('sc',{},'download soundcloud song (usage: "sc [link]")', function(t)
local args = t.args
local message = t.message
if args[1] then
if args[1]:match('https://soundcloud.com/[%w-_]+/[%w-_]+') then
clearTmp()
local link=args[1]:match('https://soundcloud.com/[%w-_]+/[%w-_]+')
message:reply('Attempting to download song from <'..link..'>')
local filepath = ''
local s=io.popen('ytdl.exe -o "./tmp/%(uploader_id)s-%(display_id)s.%(ext)s" '..link)
local songName
repeat
local str = s:read()
if str then
local tag = str:match('^%[(.+)%]')
if tag=='soundcloud' then
local song = str:match('^%[soundcloud%] (.+):')
if song:match('%d+')~=song then
songName = song:match('.+/(.+)')
end
end
end
until s:read()==nil
s:close()
if type(songName)=='string' and songName~='' then
message:reply('found song: '..songName)
local empty,file = isTmpEmpty()
if not empty then
message:reply({file='./tmp/'..file})
os.remove('./tmp/'..file)
end
end
else
message:reply('Invalid URL')
end
else
message:reply('No URL provided')
end
end)
-- commands:Add('ct',{},'',function()
-- clearTmp()
-- end)
-- commands:Add('ft',{},'',function()
-- filterTmp()
-- end)

View File

@ -1,28 +0,0 @@
local discordia=require('discordia')
local API=require('./../strafes_net.lua')
local commands=require('./../commands.lua')
discordia.extensions()
commands:Add('map',{},'get map info', function(t)
local args = t.args
local message = t.message
local game = API.GAMES[args[1]]
local map
if not game then
local str = table.concat(args,' ')
map = API.MAPS[1][str] or API.MAPS[2][str]
else
map = API.MAPS[game][table.concat(args,' ',2)]
end
if not map then return message:reply('```No map found```') end
local formatted_message = '```'..
'Map: '..map.DisplayName..' ('..API.GAMES[map.Game]..')\n'..
'ID: '..map.ID..'\n'..
'Creator: '..map.Creator..'\n'..
'PlayCount: '..map.PlayCount..'\n'..
'Published: '..os.date('%A, %B %d %Y @ %I:%M (%p)',map.Date)..
'```'
return message:reply(formatted_message)
end)

View File

@ -1,71 +0,0 @@
-- local discordia=require('discordia')
-- local API=require('./../strafes_net.lua')
-- discordia.extensions()
-- API.MAPS={}
-- local function insert(t, value)
-- local start, ending, mid, state = 1, #t, 1, 0
-- while start <= ending do
-- mid = math.floor((start + ending) / 2)
-- if #value.DisplayName < #t[mid].DisplayName then
-- ending, state = mid - 1, 0
-- else
-- start, state = mid + 1, 1
-- end
-- end
-- table.insert(t, mid + state, value)
-- end
-- for _, game in next, API.GAMES do
-- if type(tonumber(game)) == 'number' then
-- local count = 0 -- add into the maps table afterwards
-- local maps = {}
-- local res, headers = API:GetMaps(game)
-- local pages = tonumber(headers['Pagination-Count'])
-- count = count + #res
-- for _, v in next, res do
-- insert(maps, v)
-- end
-- if pages > 1 then
-- for i = 2, pages do
-- res, headers = API:GetMaps(game, i)
-- count = count + #res
-- for _, j in next, res do
-- insert(maps, j)
-- end
-- end
-- end
-- setmetatable(maps, {__index = function(self, k)
-- if k=='count' then return self.count end
-- -- Just to make sure it goes in the right order
-- if type(k)=='string' then
-- for i = 1, self.count do
-- local v = self[i]
-- if type(v) == 'table' and v.DisplayName:lower():find(tostring(k:gsub('%%', '%%%%'):gsub('^%^', '%%^'):gsub('%$$', '%%$'):gsub('%(', '%%('):gsub('%)', '%%)'):gsub('%.', '%%.'):gsub('%[', '%%['):gsub('%]', '%%]'):gsub('%*', '%%*'):gsub('%+', '%%+'):gsub('%-', '%%-'):gsub('%?', '%%?')):lower()) then
-- return v
-- end
-- end
-- elseif type(k)=='number' then
-- for i = 1, self.count do
-- local v = self[i]
-- if type(v) == 'table' and v.ID==k then
-- return v
-- end
-- end
-- end
-- end})
-- maps.count = count
-- API.MAPS[game] = maps
-- print('map init done for game:', API.GAMES[game], 'count:', API.MAPS[game].count)
-- end
-- end

View File

@ -1,114 +0,0 @@
local discordia=require('discordia')
local json=require('json')
local http_request=require('./../http.lua')
local Commands=require('./../commands.lua')
discordia.extensions()
local COLOURS={
GREEN=0x00ff00,
RED=0xff0000
}
--initialize minecraft ip data
local MinecraftDataFile=io.open('minecraft_data.json','r')
if not MinecraftDataFile or (MinecraftDataFile and MinecraftDataFile:read('*a')=='') then
print('no such file exists! so make it')
io.open('minecraft_data.json','w+'):write(json.encode({})):close()
end
if MinecraftDataFile then
MinecraftDataFile:close()
end
Commands:Add('setip',{},'set ip for status',function(CommandData)
local CommandArgs=CommandData.args
local CommandMessage=CommandData.message
local ServerIPStr=CommandArgs[1]
if not ServerIPStr then
return CommandMessage:reply('No IP provided')
end
local ServerIP=ServerIPStr:match("(%d+%.%d+%.%d+%.%d+)") or ServerIPStr:match("(%w*%.?%w+%.%w+)")
if not ServerIP then
return CommandMessage:reply('Invalid server IP')
end
local ServerPort=ServerIPStr:match(ServerIP..':(%d+)') or 25565
local GuildId=CommandMessage.guild.id
if not GuildId then
return CommandMessage:reply('You cannot use this command outside of a Discord server')
end
local GuildMinecraftData={IP=ServerIP,PORT=ServerPort}
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()
return CommandMessage:reply({
content='Successfully added `'..ServerIP..':'..ServerPort..'` for ServerId='..GuildId,
reference={
message=CommandMessage,
mention=true
}
})
end)
Commands:Add('status',{},'get status for minecraft server',function(CommandData)
local CommandMessage=CommandData.message
local GuildId=CommandMessage.guild.id
if not GuildId then
return CommandMessage:reply('You cannot use this command outside of a Discord server')
end
local GlobalMinecraftData=json.decode(io.open('minecraft_data.json','r'):read('*a'))
if not GlobalMinecraftData then
return CommandMessage:reply('Could not read server data')
end
local ServerMinecraftData=GlobalMinecraftData[GuildId]
if not ServerMinecraftData then
return CommandMessage:reply('There is no data for this Discord server')
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 EmbedData
if IsOnline then
local MaxPlayers=Response.players.max
local OnlinePlayers=Response.players.online
local AnonymousPlayers=OnlinePlayers
local Players={}
if OnlinePlayers>0 then
for PlayerIndex,PlayerData in next,Response.players.list do
table.insert(Players,PlayerData.name)
AnonymousPlayers=AnonymousPlayers-1
end
else
table.insert(Players,'No players online')
end
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
}
else
EmbedData={
title='Server Status for '..ServerIPStr,
description='Server is offline',
color=COLOURS.RED
}
end
return CommandMessage:reply({embed=EmbedData})
end)

View File

@ -1,60 +0,0 @@
local discordia=require('discordia')
local API=require('./../strafes_net.lua')
local commands=require('./../commands.lua')
local pad = API.Pad
discordia.extensions()
-- args: user, game, style, map
commands:Add('pb', {}, 'get placement on map', function(t)
local args = t.args
local message = t.message
if #args < 4 then return message:reply('invalid arguments') end
local user = API:GetUserFromAny(args[1],message)
local sn_info = API:GetUser(user.id)
local game = API.GAMES[args[2]]
local style = API.STYLES[args[3]]
local map = API.MAPS[game][table.concat(args,' ',4)]
-- i love checks
if not game then return message:reply('invalid game') end
if not style then return message:reply('invalid style') end
if not map then return message:reply('invalid map') end
if not sn_info.ID then return message:reply('```No data with StrafesNET is associated with that user.```') end
if sn_info.State==2 then return message:reply('```This user is currently blacklisted```') end
local time = API:GetUserTimes(user.id, map.ID, style, game)[1]
if not time then return message:reply('```No time was found.```') end
local rank = API:GetTimeRank(time.ID).Rank
local count = tonumber(API:GetMapCompletionCount(time.Map, style))
if not rank or not count then
rank = 1
count = 1
end
local time_formatted = API.FormatTime(time.Time)
local date = os.date("%x", time.Date)
local placement = rank .. '/' .. count
local points = API.CalculatePoint(rank, count)
local t_n, d_n, p_n= #time_formatted, 8, math.max(#placement, 10)
local first_line = 'PB Time for map: '..map.DisplayName..' ('..API.GAMES[game]..', '..API.STYLES_LIST[style]..')'
local second_line = pad('Time:', t_n + 1) .. '| '
.. pad('Date:', d_n + 1) .. '| '
.. pad('Placement:', p_n + 1) .. '| '
.. 'Points:'
local third_line = pad(time_formatted, t_n + 1) .. '| '
.. pad(date, d_n + 1) .. '| '
.. pad(placement, p_n + 1) .. '| '
.. tostring(points)
return message:reply('```' .. first_line .. '\n' .. second_line .. '\n' .. third_line .. '```')
end)

View File

@ -1,40 +0,0 @@
local discordia=require('discordia')
local API=require('./../strafes_net.lua')
local commands=require('./../commands.lua')
function dump(a,b,c,d)b=b or 50;d=d or("DUMP START "..tostring(a))c=c or 0;for e,f in next,a do local g;if type(f)=="string"then g="\""..f.."\""else g=tostring(f)end;d=d.."\nD "..string.rep(" ",c*2)..tostring(e)..": "..g;if type(f)=="table"then if c>=b then d=d.." [ ... ]"else d=dump(f,b,c+1,d)end end end;return d end
discordia.extensions()
commands:Add('rank',{},'rank <username|mention|"me"> <game> <style>', function(t)
local args=t.args
local message=t.message
if #args<3 then return message:reply('invalid arguments') end
local user=args[1]
local game=API.GAMES[args[2]]
local style=API.STYLES[args[3]]
if not game then return message:reply('invalid game') end
if not style then return message:reply('invalid style') end
user = API:GetUserFromAny(user,message)
local sn_info = API:GetUser(user.id)
if not sn_info.ID then return message:reply('```No data with StrafesNET is associated with that user.```') end
if sn_info.State==2 then return message:reply('```This user is currently blacklisted```') end
local rank = API:GetRank(user.id,game,style)
local rank_string = API.FormatRank(rank.Rank)
local skill = API.FormatSkill(rank.Skill)
local formatted_message = '```'..
'Name: '..user.displayName..' ('..user.name..')\n'..
'Style: '..API.STYLES_LIST[rank.Style]..'\n'..
'Rank: '..rank_string..'\n'..
'Skill: '..skill..'\n'..
'Placement: '..rank.Placement..'\n'..
'State: '..API.STATES[sn_info.State]..'\n'..
'```'
message:reply(formatted_message)
end)

View File

@ -1,22 +0,0 @@
local discordia=require('discordia')
local commands=require('./../commands.lua')
discordia.extensions()
function wait(n)local c=os.clock local t=c()while c()-t < n do end;end
commands:Add('restart',{},"restart bot [dev]", function(t)
if t.message.author==t.t[1].owner then
t.message:addReaction('👍')
t.t[1]:stop()
wait(1.5)
io.open('restart.txt','w+'):write(t.message.guild.id..','..t.message.channel.id..','..t.message.id):close()
os.execute('.\\exes\\luvit ./src/main.lua')
end
end)
commands:Add('leave',{},'leave',function(t)
if t.message.author==t.t[1].owner then
t.message:delete()
local left = t.message.guild:leave()
if left then
print('left')
end
end
end)

View File

@ -1,132 +0,0 @@
local discordia=require('discordia')
local API=require('./../strafes_net.lua')
local commands=require('./../commands.lua')
function sleep(n) local t = os.clock() while os.clock()-t <= n do end end
discordia.extensions()
local pad = API.Pad
commands:Add('skill',{},'skill <username|mention|"me"> <game> <style> <sort?=skill|point>', function(t)
local args=t.args
local message=t.message
if not _G.locked then
if #args<3 then return message:reply('usage: `skill <username|mention|"me"> <game> <style> <sort?="skill"|"point">`') end
local user=args[1]
local game=API.GAMES[args[2]]
local style=API.STYLES[args[3]]
if not game then return message:reply('invalid game') end
if not style then return message:reply('invalid style') end
local sort = args[4]
if type(sort)=='string' and not sort:lower():find('skill') and not sort:lower():find('point') then
return message:reply('invalid sort option, valid options are "skill" or "point"')
elseif sort==nil then
sort = 'skill'
end
print('getting user')
local user = API:GetUserFromAny(user,message)
if type(user)=='string' then return message:reply('```'..user..'```') end
local sn_info = API:GetUser(user.id)
if not sn_info.ID then return message:reply('```No data with StrafesNET is associated with that user.```') end
if sn_info.State==2 then return message:reply('```This user is currently blacklisted```') end
print(user.name,user.id,API.GAMES[game],API.STYLES[style]:lower())
_G.locked = true
_G.current = {name=user.name,game=API.GAMES[game],style=API.STYLES[style]:lower()}
local times = {}
local res,rheaders = API:GetUserTimes(user.id,nil,style,game)
if #res~=0 then
local pages = tonumber(rheaders['Pagination-Count'])
for _,v in next,res do
table.insert(times,v)
end
if pages>1 then
for i=2,pages do
print('getting times page',i)
res,rheaders = API:GetUserTimes(user.id,nil,style,game,i)
for _,v in next,res do
table.insert(times,v)
end
end
end
print('times:',#times)
t.message:reply('ETA: '..(math.floor(#times*3/100))..' minutes '..((#times*3)%60)..' seconds (found '..#times..' times out of '..API.MAPS[game].count..' maps)')
local test_a,test_b = 0,0
for _,time in next,times do
local rank = API:GetTimeRank(time.ID).Rank
local count = tonumber(API:GetMapCompletionCount(time.Map,style))
if not rank or not count then
print('NO RANK OR COUNT')
print(rank,count)
rank = 1
count = 1
end
time.Points = API.CalculatePoint(rank,count)
time.Rank = rank
time.MapCompletionCount = count
time.SkillRaw = rank == 1 and 1 or (count-rank)/(count-1)
time.Skill = API.FormatSkill(time.SkillRaw)
test_a=test_a+(count-rank)
test_b=test_b+(count-1)
end
table.sort(times,sort:find('skill') and function(t1,t2)
return t1.SkillRaw<t2.SkillRaw
end or sort:find('point') and function(t1,t2)
return t1.Points<t2.Points
end)
local points = 0
for _,time in next,times do
points = points+time.Points
end
local skillFinal = (test_a)/(test_b-1)
local msg = 'Average Skill: '..API.FormatSkill(math.clamp(skillFinal,0,1))..'\n'..
'Points: '..points..'\n'..
pad('Map',50)..' | '..pad('Points')..' | '..pad('Skill',7)..' | '.. pad('Placement',14)..' | Time\n\n'
for _,time in next,times do
-- msg = msg..'['..time.Rank..'/'..time.MapCompletionCount..'] '..time.Map..' ('..time.Skill..')\n'
local mapStr = API.MAPS[game][time.Map].DisplayName..' ('..time.Map..')'
local skill = time.Skill
local point = time.Points
local rankStr = time.Rank..'/'..time.MapCompletionCount
local timeStr = API.FormatTime(time.Time)
msg = msg.. pad(mapStr,50)..' | '..pad(point)..' | '..pad(skill,7)..' | '.. pad(rankStr,14)..' | '..timeStr..'\n'
end
local txt = './skill-'..API.GAMES[game]..'-'..API.STYLES[style]:lower()..'-'..user.name..'.txt'
local file=io.open(txt,'w+')
file:write(msg)
file:close()
message:reply({
file=txt,
reference={
message=message,
mention=true
}
})
os.remove(txt)
_G.locked = false
else
message:reply('```No times found for that user.```')
_G.locked = false
end
else
--_G.current = {name=user.name,game=API.GAMES[game],style=API.STYLES[style]:lower()}
message:reply('Bot is currently in use, please try again later ('.._G.current.name..' for '.._G.current.game..' in '.._G.current.style..')')
end
end)
commands:Add('compare',{},'compare n1 n2', function(t)
local args=t.args
local message=t.message
local n1 = args[1]
local n2 = args[2]
local compared = API.CalculateDifference(n1,n2)
local compared_percent = API.CalculateDifferencePercent(n1,n2)
message:reply(tostring(compared)..' ('..compared_percent..')')
end)
commands:Add('calc',{},nil,function(t)
local args=t.args
local message=t.message
local rank = args[1]
local count = args[2]
local points=API.CalculatePoint(rank,count)
local skill=API.FormatSkill(rank == 1 and 1 or (count-rank)/(count-1))
message:reply('```Points: '..points..'\nSkill: '..skill..'```')
end )

View File

@ -1,50 +0,0 @@
local discordia=require('discordia')
local API=require('./../strafes_net.lua')
local commands=require('./../commands.lua')
local pad = API.Pad
discordia.extensions()
-- args: game, style, map
commands:Add('wr', {}, 'get map wr', function(t)
local args = t.args
local message = t.message
if #args < 3 then return message:reply('invalid arguments') end
local game = API.GAMES[args[1]]
local style = API.STYLES[args[2]]
local map = API.MAPS[game][table.concat(args,' ',3)]
if not game then return message:reply('invalid game') end
if not style then return message:reply('invalid style') end
if not map then return message:reply('invalid map') end
local time = API:GetMapWr(map.ID,style)
if not time then return message:reply('No time was found') end
local user = API:GetRobloxInfoFromUserId(time.User)
local username = user.name
local time_formatted = API.FormatTime(time.Time)
local date = os.date("%x", time.Date)
local count = tonumber(API:GetMapCompletionCount(time.Map, style))
local points = tostring(API.CalculatePoint(1, count))
-- Username: | Time: | Points: | Date:
local n_n,t_n,p_n = 20,#time_formatted,#points
local first_line = 'WR Time for map: '..map.DisplayName..' ( 1/'..count..' ) ['..API.GAMES[game]..', '..API.STYLES_LIST[style]..']'
local second_line = pad('Username:', n_n + 1) .. '| '
.. pad('Time:', t_n + 1) .. '| '
.. pad('Points:',p_n + 1).. '| '
.. 'Date:'
local third_line = pad(username, n_n + 1) .. '| '
.. pad(time_formatted, t_n + 1) .. '| '
.. pad(points, p_n + 1) .. '| '
.. date
return message:reply('```' .. first_line .. '\n' .. second_line .. '\n' .. third_line .. '```')
end)