Compare commits

..

No commits in common. "07501e7fa0c3a0ac9c9ca158e4375517aa27acd8" and "3dd57d3bdc222b2ad644b7af84613049e7453145" have entirely different histories.

53 changed files with 3686 additions and 2362 deletions

View File

@ -1,13 +1,11 @@
[![forthebadge](https://forthebadge.com/images/badges/powered-by-black-magic.svg)](https://forthebadge.com) [![made-with-luvit.svg](https://512mb.org/git/Yessiest/badges/raw/branch/master/badges/made-with-luvit.svg)](https://forthebadge.com/generator/) [![forthebadge](https://forthebadge.com/images/badges/powered-by-black-magic.svg)](https://forthebadge.com) [![made-with-luvit.svg](https://512mb.org/git/Yessiest/badges/raw/branch/master/badges/made-with-luvit.svg)](https://forthebadge.com/generator/)
# 512mb.org-bot # 512mb.org-bot
Yet another 512mb bot. It's running on a discord library for the luvit framework called [Discordia](https://github.com/SinisterRectus/discordia). For guides on using particular features of the bot check out the [wiki](https://github.com/512mb-org/512mb.org-bot/wiki). yet another 512mb bot. it's running on a discord library for the luvit framework called [Discordia](https://github.com/SinisterRectus/discordia)
# Self-hosting # Self-hosting
1. Get a bot token and place it in ./token place the bot token in the "./token" file, change server id in bot.lua to the id of your server.
2. Install libqalculate and compile luaqalc compile [luvit](https://luvit.io) and run bot.lua.
3. Place the ``libqalculator.so`` in ./libraries/
4. Get [luvit](https://luvit.io) and run ``bot.lua`` using the ``luvit`` binary
# License # License
``` ```

View File

@ -16,7 +16,7 @@ local servers = {}
--create server --create server
local server = import("classes.server-handler") local server = import("classes.server-handler")
client:on("ready",function() client:on("ready",function()
print("Starting bot") print("starting test")
for _,id in pairs(server_ids) do for _,id in pairs(server_ids) do
if not servers[id] then if not servers[id] then
servers[id] = server(client,client:getGuild(id),{ servers[id] = server(client,client:getGuild(id),{
@ -46,4 +46,4 @@ if not tempfile then
end end
local nstr = tempfile:read("*l") local nstr = tempfile:read("*l")
tempfile:close() tempfile:close()
client:run('Bot '..nstr:match("^%s*(.+)%s*$")) client:run('Bot '..nstr)

BIN
libraries/.tasklib.lua.swp Normal file

Binary file not shown.

View File

@ -1,153 +1,203 @@
--rewrite this lib (P.S: done) --rewrite this lib (P.S: done)
--P.S: air stands for Advanced Input Recognition, although technically it's not all that advanced --P.S: air stands for Advanced Input Recognition, although technically it's not all that advanced
local parse_string = require("string_parse")
local table_utils = require("table-utils")
air = {} air = {}
object_types = { air.match_strings = function(string)
["voiceChannel"] = function(id,client,guild_id) local strings = {}
local guild = client:getGuild(guild_id) string = string:gsub("\"(.-[^\\])\"",function(capt)
local channel = guild:getChannel(id:match("(%d+)[^%d]*$")) string_id = string_id + 1
if tostring(channel):match("^GuildVoiceChannel: ") then strings["%str"..string_id] = capt:gsub("\\\"","\"")
return true,channel return " %str"..string_id
else end)
return false return string,strings
end end
end,
["textChannel"] = function(id,client,guild_id) local function parse_strings(thing,strings)
local guild = client:getGuild(guild_id) --find all strings and replace them with string ids with no spaces
local channel = guild:getChannel(id:match("(%d+)[^%d]*$")) local strings = strings or {}
if tostring(channel):match("^GuildTextChannel: ") then local string_count = 0
return true,channel local function get_string(text)
else string_count = string_count + 1
return false local id = "&;"..tostring(string_count)..";&"
end strings[string_count] = text
end, return id
["messageLink"] = function(id,client,guild_id) end
local guild = client:getGuild(guild_id) thing = thing:gsub("(\")\"",get_string)
local channelId,messageId = id:match("(%d+)/(%d+)[^%d]*$") thing = thing:gsub("(\".-[^\\])\"",get_string)
channel = guild:getChannel(channelId) thing = thing:gsub("(\')\'",get_string)
if tostring(channel):find("GuildTextChannel") then thing = thing:gsub("(\'.-[^\\])\'",get_string)
message = channel:getMessage(messageId) return thing,strings
if message then end
return true,message --this table will look up special types
end special_case = {
end ["voiceChannel"] = function(id,client,guild_id)
return false local guild = client:getGuild(guild_id)
end, local channel = guild:getChannel(id:match("(%d+)[^%d]*$"))
["role"] = function(id,client,guild_id) if tostring(channel):match("^GuildVoiceChannel: ") then
local guild = client:getGuild(guild_id) return true,channel
local role = guild:getRole(id:match("(%d+)[^%d]*$")) else
if role then return false
return true,role
else
return false
end
end,
["member"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local member = guild:getMember(id:match("(%d+)[^%d]*$"))
if member then
return true,member
else
return false
end
end,
["emoji"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local emoji = guild:getEmoji(id:match("(%d+)[^%d]*$"))
if emoji then
return true,emoji
else
return false
end
end,
["ban"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local ban = guild:getBan(id:match("(%d+)[^%d]*$"))
if ban then
return true,ban
else
return false
end
end,
["channel"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local channel = guild:getChannel(id:match("(%d+)[^%d]*$"))
if channel then
return true,channel
else
return false
end
end,
["user"] = function(id,client,guild_id)
local user = client:getUser(id:match("(%d+)[^%d]*$"))
if user then
return true,user
end
return false
end,
["id"] = function(id)
if tonumber(id:match("(%d+)[^%d]*$")) and tostring(id:match("(%d+)[^%d]*$")):len() > 10 then
return true,id
end
return false
end,
["string"] = function(str)
if str:match("^[\"'].*[\"']$") then
return true, str:match("^[\"'](.*)[\"']$")
end
return true,str
end,
["number"] = function(n)
local number = tonumber(n)
return (number ~= nil), number
end end
end,
["textChannel"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local channel = guild:getChannel(id:match("(%d+)[^%d]*$"))
if tostring(channel):match("^GuildTextChannel: ") then
return true,channel
else
return false
end
end,
["messageLink"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local channelId,messageId = id:match("(%d+)/(%d+)[^%d]*$")
channel = guild:getChannel(channelId)
if tostring(channel):find("GuildTextChannel") then
message = channel:getMessage(messageId)
if message then
return true,message
end
end
return false
end,
["role"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local role = guild:getRole(id:match("(%d+)[^%d]*$"))
if role then
return true,role
else
return false
end
end,
["member"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local member = guild:getMember(id:match("(%d+)[^%d]*$"))
if member then
return true,member
else
return false
end
end,
["emoji"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local emoji = guild:getEmoji(id:match("(%d+)[^%d]*$"))
if emoji then
return true,emoji
else
return false
end
end,
["ban"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local ban = guild:getBan(id:match("(%d+)[^%d]*$"))
if ban then
return true,ban
else
return false
end
end,
["channel"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local channel = guild:getChannel(id:match("(%d+)[^%d]*$"))
if channel then
return true,channel
else
return false
end
end,
["user"] = function(id,client,guild_id)
local user = client:getUser(id:match("(%d+)[^%d]*$"))
if user then
return true,user
end
return false
end,
["id"] = function(id)
if tonumber(id:match("(%d+)[^%d]*$")) and tostring(id:match("(%d+)[^%d]*$")):len() > 10 then
return true,id
end
return false
end
} }
air.parse = function(string,argmatch,client,guild_id) air.parse = function(string,argmatch,client,guild_id)
local strings = parse_string(string,"[\"']") local args,opts = {},{}
local argmatch = table_utils.shallowcopy(argmatch or {}) local string_id = 0
local tokens,args,opts = {},{},{} local strings = {}
-- Tokenize string = string:gsub("[%s\n]+\"\"",function(capt)
for k,v in pairs(strings) do string_id = string_id + 1
local padded_string = v:match("^%s*(.+)%s*$") strings["%str"..string_id] = ""
if padded_string:match("^[\"'].*[\"']$") then return " %str"..string_id
table.insert(tokens,padded_string) end)
else string = string:gsub("[%s\n]+\"(.-[^\\])\"",function(capt)
v:gsub("%S+",function(text) string_id = string_id + 1
table.insert(tokens,text) strings["%str"..string_id] = capt:gsub("\\\"","\"")
end) return " %str"..string_id
end end)
string = string:gsub("[%s\n]+%-%-(%w+)=\"\"",function(name)
opts[name] = ""
return ""
end)
string = string:gsub("[%s\n]+%-%-(%w+)=\"(.-[^\\])\"",function(name,value)
opts[name] = value:gsub("\\\"","\"")
return ""
end)
string = string:gsub("[%s\n]+%-%-(%w+)=(%S+)",function(name,value)
opts[name] = value
return ""
end)
string = string:gsub("[%s\n]+%-%-(%w+)",function(name)
opts[name] = true
return ""
end)
string = string:gsub("[%s\n]+%-(%w+)",function(args)
args:gsub(".",function(key)
opts[key] = true
end)
return ""
end)
string:gsub("([^%s\n]+)",function(match)
table.insert(args,match)
end)
for k,v in pairs(args) do
if v:match("%%str%d+") then
if strings[v] then
args[k] = strings[v]
end
end end
-- Remove opts and match arguments end
for k,v in pairs(tokens) do if argmatch and #argmatch > 0 then
if v:match("^%-%-%w+=$") then local match,err = false
local optname = table.remove(tokens,k):match("^%-%-(%w+)=$") local new_args = {}
local value = tokens[k] for k,v in pairs(argmatch) do
opts[optname] = value if not args[k] then
elseif v:match("^%-%-%w+$") then match = false
local optname = v:match("^%-%-(%w+)$") err = "Missing arguments: "..table.concat(argmatch,", ",k)
opts[optname] = true break
elseif v:match("^%-%w+$") then end
local opts = v:gsub("%w",function(c) if v == "number" and tonumber(args[k]) then
opts[c] = true match = true
end) new_args[k] = tonumber(args[k])
else elseif v == "string" then
local arg = table.remove(argmatch,1) match = true
if arg then new_args[k] = args[k]
local status,obj = object_types[arg](v,client,guild_id) elseif special_case[v] then
if not status then match,new_args[k] = special_case[v](args[k],client,guild_id)
return false, args, opts, "Mismatched argument "..tostring(#arg)..": "..arg.." expected." else
end match = false
table.insert(args,obj) end
else if match == false then
table.insert(args,select(2,object_types["string"](v))) err = "Type mismatch for argument "..k..": "..argmatch[k].." expected."
end break
end end
end end
if #argmatch > 0 then for k,v in pairs(args) do
return false, args, opts, "Missing arguments: "..table.concat(argmatch,", ") if not new_args[k] then
new_args[k] = v
end
end end
return true, args, opts return match,new_args,opts,err
else
return true,args,opts
end
end end
return air return air

View File

@ -1,4 +1,5 @@
class = require("baseclass") class = require("baseclass")
color = require("tty-colors")
tests = {} tests = {}
tests[1] = function() tests[1] = function()
print("Basic class initialization test") print("Basic class initialization test")
@ -78,7 +79,8 @@ print("Deteceted "..#tests.." tests. Starting now.")
OK = 0 OK = 0
for k,v in pairs(tests) do for k,v in pairs(tests) do
status,errcode = pcall(v) status,errcode = pcall(v)
print("TEST #"..k.." "..((status and "OK") or "ERROR")..(((not status) and errcode) or "")) stat_color = (status and "green") or "red"
print(color("TEST #"..k.." "..((status and "OK") or "ERROR")..(((not status) and errcode) or ""),stat_color))
if status then if status then
OK = OK + 1 OK = OK + 1
end end

View File

@ -0,0 +1 @@
../../tty-colors.lua

View File

@ -6,87 +6,87 @@ local class = import("classes.baseclass")
local table_utils = import("table-utils") local table_utils = import("table-utils")
local acl = class("ACL") local acl = class("ACL")
function acl:__init() function acl:__init()
self.user_rules = {} self.user_rules = {}
self.group_rules = {} self.group_rules = {}
end end
function acl:set_user_rule(user_id,status) function acl:set_user_rule(user_id,status)
assert( assert(
(status == nil) or (status == 0) or (status == -1) or (status == 1), (status == nil) or (status == 0) or (status == -1) or (status == 1),
"invalid status setting" "invalid status setting"
) )
self.user_rules[user_id] = status self.user_rules[user_id] = status
end end
function acl:set_group_rule(group_id,status) function acl:set_group_rule(group_id,status)
assert( assert(
(status == nil) or (status == 0) or (status == -1) or (status == 1), (status == nil) or (status == 0) or (status == -1) or (status == 1),
"invalid status setting" "invalid status setting"
) )
self.group_rules[group_id] = status self.group_rules[group_id] = status
end end
function acl:check_user(user_id) function acl:check_user(user_id)
if self.user_rules[user_id] and self.user_rules[user_id] ~= 0 then if self.user_rules[user_id] and self.user_rules[user_id] ~= 0 then
return true,(self.user_rules[user_id] == 1) return true,(self.user_rules[user_id] == 1)
else else
return false return false
end end
end end
function acl:check_group(groups) function acl:check_group(groups)
local allow = false local allow = false
local found = false local found = false
for k,v in pairs(groups) do for k,v in pairs(groups) do
if self.group_rules[v] then if self.group_rules[v] then
found = true found = true
allow = self.group_rules[v] allow = self.group_rules[v]
end
end end
return found,(allow and allow == 1) end
return found,(allow and allow == 1)
end end
function acl:export_all_lists() function acl:export_all_lists()
local lists = { local lists = {
users = "", users = "",
groups = "" groups = ""
} }
for k,v in pairs(self.user_rules) do for k,v in pairs(self.user_rules) do
lists.users = lists.users..k..":"..tostring(v)..";\n" lists.users = lists.users..k..":"..tostring(v)..";\n"
end end
for k,v in pairs(self.group_rules) do for k,v in pairs(self.group_rules) do
lists.groups = lists.groups..k..":"..tostring(v)..";\n" lists.groups = lists.groups..k..":"..tostring(v)..";\n"
end end
return lists return lists
end end
function acl:export_user_list() function acl:export_user_list()
local list = "" local list = ""
for k,v in pairs(self.user_rules) do for k,v in pairs(self.user_rules) do
list = list..k..":"..tostring(v)..";\n" list = list..k..":"..tostring(v)..";\n"
end end
return list return list
end end
function acl:export_group_list() function acl:export_group_list()
local list = "" local list = ""
for k,v in pairs(self.group_rules) do for k,v in pairs(self.group_rules) do
list = list..k..":"..tostring(v)..";\n" list = list..k..":"..tostring(v)..";\n"
end end
return list return list
end end
function acl:export_snapshot() function acl:export_snapshot()
return { return {
user_rules = bot_utils.deepcopy(self.user_rules), user_rules = bot_utils.deepcopy(self.user_rules),
group_rules = bot_utils.deepcopy(self.group_rules) group_rules = bot_utils.deepcopy(self.group_rules)
} }
end end
function acl:import_snapshot(t) function acl:import_snapshot(t)
self.user_rules = t.user_rules self.user_rules = t.user_rules
self.group_rules = t.group_rules self.group_rules = t.group_rules
end end
function acl:import_user_list(list) function acl:import_user_list(list)
list:gsub("(%w+):(%d+)",function(id,status) list:gsub("(%w+):(%d+)",function(id,status)
self.user_rules[id] = status self.user_rules[id] = status
end) end)
end end
function acl:import_group_list(list) function acl:import_group_list(list)
list:gsub("(%w+):(%d+)",function(id,status) list:gsub("(%w+):(%d+)",function(id,status)
self.group_rules[id] = status self.group_rules[id] = status
end) end)
end end
return acl return acl

View File

@ -1,32 +1,32 @@
--class generator (for the purpose of creating classes) --class generator (for the purpose of creating classes)
return function(name) return function(name)
local new_class = {}
new_class.__classname = name or "Object"
new_class.__index = new_class
new_class.__new = function(self,...)
local obj = {}
--set metamethod proetection measures
setmetatable(obj,{__index = function(obj,key)
if key:find("^__") then
return nil
else
return self[key]
end
end,
__name = new_class.__classname})
if self.__init then
self.__init(obj,...)
end
return obj
end
new_class.extend = function(self,name)
local new_class = {} local new_class = {}
new_class.__classname = name or "Object" new_class.__classname = name or "Object"
new_class.__index = new_class new_class.__index = new_class
new_class.__new = function(self,...) setmetatable(new_class,{__index = self,__call = function(...) return new_class.__new(...) end, __name = new_class.__classname.." (class)"})
local obj = {}
--set metamethod proetection measures
setmetatable(obj,{__index = function(obj,key)
if key:find("^__") then
return nil
else
return self[key]
end
end,
__name = new_class.__classname})
if self.__init then
self.__init(obj,...)
end
return obj
end
new_class.extend = function(self,name)
local new_class = {}
new_class.__classname = name or "Object"
new_class.__index = new_class
setmetatable(new_class,{__index = self,__call = function(...) return new_class.__new(...) end, __name = new_class.__classname.." (class)"})
return new_class
end
--make our class callable; on call, it will initialize a new instance of itself
setmetatable(new_class,{__call = function(...) return new_class.__new(...) end, __name = new_class.__classname.." (class)"})
return new_class return new_class
end
--make our class callable; on call, it will initialize a new instance of itself
setmetatable(new_class,{__call = function(...) return new_class.__new(...) end, __name = new_class.__classname.." (class)"})
return new_class
end end

View File

@ -6,69 +6,85 @@ local discordia = import("discordia")
local enum_perms = discordia.enums.permission local enum_perms = discordia.enums.permission
--The following method extends the ACL class to work with rule-specific features, --The following method extends the ACL class to work with rule-specific features,
--such as the role position --such as the role position
function command_acl:check_group(highest_role) function command_acl:check_group(roles)
local allow = self.group_rules[tostring(highest_role.id)] local found = false
return found,(allow and allow == 1) local highest_role = nil
local highest_role_status = nil
for k,v in pairs(roles) do
if self.group_rules[v.id] then
found = true
if not highest_role then
highest_role = v
highest_role_status = self.group_rules[v.id]
end
if v.position > highest_role.position then
highest_role = v
highest_role_status = self.group_rules[v.id]
end
end
end
local allow = highest_role_status
return found,(allow and allow == 1)
end end
--The following methods extend the ACL class to add the "perm" permissions --The following methods extend the ACL class to add the "perm" permissions
--(the fallback when no rule/user permissions are found) --(the fallback when no rule/user permissions are found)
function command_acl:__init() function command_acl:__init()
self.user_rules = {} self.user_rules = {}
self.group_rules = {} self.group_rules = {}
self.perm_rules = {} self.perm_rules = {}
end end
function command_acl:check_perm(perms) function command_acl:check_perm(perms)
local output = true local output = true
for k,v in pairs(self.perm_rules) do for k,v in pairs(self.perm_rules) do
if (bit.band(perms[1],enum_perms[v]) == 0) then if (bit.band(perms[1],enum_perms[v]) == 0) then
output = false output = false
end end
end end
return output return output
end end
function command_acl:set_perm_rules(list) function command_acl:set_perm_rules(list)
assert(type(list)=="table","table expected, got "..type(list)) assert(type(list)=="table","table expected, got "..type(list))
self.perm_rules = list self.perm_rules = list
end end
function command_acl:export_all_lists() function command_acl:export_all_lists()
local lists = { local lists = {
users = "", users = "",
groups = "", groups = "",
perm = "" perm = ""
} }
for k,v in pairs(self.user_rules) do for k,v in pairs(self.user_rules) do
lists.users = lists.users..k..":"..tostring(v)..";\n" lists.users = lists.users..k..":"..tostring(v)..";\n"
end end
for k,v in pairs(self.group_rules) do for k,v in pairs(self.group_rules) do
lists.groups = lists.groups..k..":"..tostring(v)..";\n" lists.groups = lists.groups..k..":"..tostring(v)..";\n"
end end
for k,v in pairs(self.perm_rules) do for k,v in pairs(self.perm_rules) do
lists.perm = lists.perm..k..":"..tostring(v)..";\n" lists.perm = lists.perm..k..":"..tostring(v)..";\n"
end end
return lists return lists
end end
function command_acl:export_perm_list() function command_acl:export_perm_list()
local list = "" local list = ""
for k,v in pairs(self.perm_rules) do for k,v in pairs(self.perm_rules) do
list = list..k..":"..tostring(v)..";\n" list = list..k..":"..tostring(v)..";\n"
end end
return list return list
end end
function command_acl:export_snapshot() function command_acl:export_snapshot()
return { return {
user_rules = table_utils.deepcopy(self.user_rules), user_rules = table_utils.deepcopy(self.user_rules),
group_rules = table_utils.deepcopy(self.group_rules), group_rules = table_utils.deepcopy(self.group_rules),
perm_rules = table_utils.deepcopy(self.perm_rules) perm_rules = table_utils.deepcopy(self.perm_rules)
} }
end end
function command_acl:import_snapshot(t) function command_acl:import_snapshot(t)
self.user_rules = t.user_rules self.user_rules = t.user_rules
self.group_rules = t.group_rules self.group_rules = t.group_rules
self.perm_rules = t.perm_rules self.perm_rules = t.perm_rules
end end
function command_acl:import_perm_list() function command_acl:import_perm_list()
list:gsub("(%w+):(%d+)",function(id,status) list:gsub("(%w+):(%d+)",function(id,status)
self.perm_rules[id] = status self.perm_rules[id] = status
end) end)
end end
return command_acl return command_acl

View File

View File

@ -9,95 +9,108 @@ local command_handler = class("Command-handler")
local table_utils = import("table-utils") local table_utils = import("table-utils")
local purify = import("purify") local purify = import("purify")
function command_handler:__init(parent_server) function command_handler:__init(parent_server)
self.server_handler = assert(parent_server,"parent server handler not provided") self.server_handler = assert(parent_server,"parent server handler not provided")
self.pool = {} self.command_pool = {}
self.prefixes = {} self.prefixes = {}
self.meta = { self.command_meta = {
plugins = {}, plugins = {},
categories = {} categories = {}
} }
end end
function command_handler:add_prefix(prefix) function command_handler:add_prefix(prefix)
self.prefixes[prefix] = purify.purify_escapes(prefix) local purified_prefix = purify.purify_escapes(prefix)
return true self.prefixes[purified_prefix] = purified_prefix
return true
end end
function command_handler:remove_prefix(prefix) function command_handler:remove_prefix(prefix)
if self.prefixes[prefix] and table_utils.count(self.prefixes) > 1 then local purified_prefix = purify.purify_escapes(prefix)
self.prefixes[prefix] = nil if self.prefixes[purified_prefix] or table_utils.count(self.prefixes) <= 1 then
return true self.prefix[purified_prefix] = nil
end return true
if not self.prefixes[prefix] then else
return false, "Prefix not found" return false, (
end (self.prefixes[purified_prefix] and "No such prefix") or
return false, "Cannot remove last remaining prefix!" "Cannot remove the last remaining prefix"
)
end
end end
function command_handler:get_prefixes() function command_handler:get_prefixes()
return table_utils.deepcopy(self.prefixes) return table_utils.deepcopy(self.prefixes)
end end
function command_handler:add_command(command) function command_handler:add_command(command)
assert(type(command) == "table","command object expected") assert(type(command) == "table","command object expected")
if self.pool[command.name] then local purified_name = purify.purify_escapes(command.name)
return false, "Already have a command with the same name" self.command_pool[purified_name] = command
end if not self.command_meta.plugins[command.parent.name] then
self.pool[command.name] = command self.command_meta.plugins[command.parent.name] = {}
if not self.meta.plugins[command.parent.name] then end
self.meta.plugins[command.parent.name] = {} if not self.command_meta.categories[command.options.category] then
end self.command_meta.categories[command.options.category] = {}
self.meta.plugins[command.parent.name][command.name] = command.name end
if not self.meta.categories[command.category] then table.insert(self.command_meta.categories[command.options.category],command.name)
self.meta.categories[command.category] = {} table.insert(self.command_meta.plugins[command.parent.name],command.name)
end return command
self.meta.categories[command.category][command.name] = command.name
return command
end end
function command_handler:remove_command(command) function command_handler:remove_command(command)
assert(type(command) == "table","command object expected") assert(type(command) == "table","command object expected")
if not self.pool[command.name] then local purified_name = purify.purify_escapes(command.name)
return false if self.command_pool[purified_name] then
local command = self.command_pool[purified_name]
--not exactly optimal, but lists are lists. can't do much about them.
table_utils.remove_value(self.command_meta.plugins[command.parent.name],command.name)
if #self.command_meta.plugins[command.parent.name] == 0 then
self.command_meta.plugins[command.parent.name] = nil
end end
self.pool[command.name] = nil table_utils.remove_value(self.command_meta.categories[command.options.category],command.name)
self.meta.categories[command.category][command.name] = nil if #self.command_meta.categories[command.options.category] == 0 then
self.meta.plugins[command.parent.name][command.name] = nil self.command_meta.categories[command.options.category] = nil
end
self.command_pool[purified_name] = nil
return true
else
return false
end
end end
function command_handler:get_command(name) function command_handler:get_command(name)
return self.pool[name] local purified_name = purify.purify_escapes(assert(type(name) == "string") and name)
if self.command_pool[purified_name] then
return self.command_pool[purified_name]
else
return false
end
end end
function command_handler:get_commands(name) function command_handler:get_commands(name)
local list = {} local list = {}
for k,v in pairs(self.pool) do for k,v in pairs(self.command_pool) do
table.insert(list,k) table.insert(list,k)
end end
return list return list
end end
function command_handler:get_metadata() function command_handler:get_commands_metadata()
local plugins,categories = {},{} return table_utils.deepcopy(self.command_meta)
for k,v in pairs(self.meta.plugins) do
plugins[k] = table_utils.listcopy(v)
end
for k,v in pairs(self.meta.categories) do
categories[k] = table_utils.listcopy(v)
end
return {
plugins = plugins,
categories = categories
}
end end
function command_handler:handle(message,ignore_flag) function command_handler:handle(message)
local content = message.content for name,command in pairs(self.command_pool) do
local prefix = "" if command.options.regex then
local command if message.content:match(command.options.regex) then
for k,v in pairs(self.prefixes) do command:exec(message)
if content:match("^"..v) then return
prefix = k end
else
if command.options.prefix then
for _,prefix in pairs(self.prefixes) do
if message.content:match("^"..prefix..name.."$") or message.content:match("^"..prefix..name.."%s") then
command:exec(message)
return
end
end end
end else
command = content:sub(prefix:len()+1,-1):match("^[%-_%w]+") if message.content:match("^"..name.."$") or message.content:match("^"..name.."%s") then
if self.pool[command] then command:exec(message)
if (prefix == "") and self.pool[command].options.prefix == false then return
self.pool[command]:exec(message,ignore_flag)
elseif (prefix ~= "") and self.pool[command].options.prefix == true then
self.pool[command]:exec(message,ignore_flag)
end end
end
end end
end
end end
return command_handler return command_handler

View File

@ -7,170 +7,178 @@ local command = class("Command")
local acl = import("classes.command-acl") local acl = import("classes.command-acl")
local discordia = import("discordia") local discordia = import("discordia")
function command:__init(name,callback) function command:__init(name,callback)
assert(name:match("^[-_%w]+$"),"Name can only contain alphanumeric characters, underscores or dashes") self.rules = acl()
self.rules = acl() self.name = name
self.name = name self.timer = discordia.Date():toMilliseconds()
self.timer = discordia.Date():toMilliseconds() self.options = {
self.category = "None" allow_bots = false, --allow bots to execute the command
self.options = { typing_decorator = false, --set if the bot should be "typing" while the command executes
allow_bots = false, --allow bots to execute the command category = "None", --set category for the command
typing_decorator = false, --set if the bot should be "typing" while the command executes prefix = true, --if true and if regex isn't enabled, check for prefix at the start. if not, don't check for prefix
prefix = true, --if true check for prefix at the start. if not, don't check for prefix regex = false, --check if the message matches this regular expression (should be a string)
timeout = 1000, --set the timeout for a command no_parsing = false, --check if you want to disable the message argument parsing process
} timeout = 1000, --set the timeout for a command
if type(callback) == "table" then }
for k,v in pairs(callback.options or {}) do if type(callback) == "table" then
self.options[k] = v for k,v in pairs(callback.options or {}) do
end self.options[k] = v
self.callback = callback.exec
if callback.category then
self.category = callback.category
end
self.args = callback.args or self.args
if callback.users then
for k,v in pairs(callback.users) do
self.rules:set_user_rule(k,v)
end
end
if callback.roles then
for k,v in pairs(callback.roles) do
self.rules:set_group_rule(k,v)
end
end
callback.perms = callback.perms and self.rules:set_perm_rules(callback.perms)
callback.help = callback.help and self:set_help(callback.help,callback.usage)
elseif type(callback) == "function" then
self.callback = callback
end end
self.callback = callback.exec
self.args = callback.args or self.args
if callback.users then
for k,v in pairs(callback.users) do
self.rules:set_user_rule(k,v)
end
end
if callback.roles then
for k,v in pairs(callback.roles) do
self.rules:set_group_rule(k,v)
end
end
if callback.perms then
self.rules:set_perm_rules(callback.perms)
end
if callback.help then
self:set_help(callback.help,callback.usage)
end
elseif type(callback) == "function" then
self.callback = callback
end
end end
--set the callback to be called on comm:exec(msg) --set the callback to be called on comm:exec(msg)
function command:set_callback(fn) function command:set_callback(fn)
assert(type(fn) == "function","function expected, got "..type(fn)) assert(type(fn) == "function","function expected, got "..type(fn))
self.callback = fn self.callback = fn
return self return self
end end
--generate help using only description and usage, or nothing at all --generate help using only description and usage, or nothing at all
function command:generate_help(description,usage) function command:generate_help(description,usage)
assert(not description or (type(description) == "string"),"Description should be either string or nil, got "..type(description)) assert(not description or (type(description) == "string"),"Description should be either string or nil, got "..type(description))
assert(not usage or (type(usage) == "string"),"Usage should be either string or nil, got "..type(usage)) assert(not usage or (type(usage) == "string"),"Usage should be either string or nil, got "..type(usage))
local backup_usage_str local backup_usage_str
if self.args then if self.args then
backup_usage_str = self.name.." <"..table.concat(self.args,"> <")..">" backup_usage_str = self.name.." <"..table.concat(self.args,"> <")..">"
else else
backup_usage_str = "not defined" backup_usage_str = "not defined"
end end
local permissions = table.concat(self.rules:export_snapshot()["perms"] or {"All"},"\n") local permissions = table.concat(self.rules:export_snapshot()["perms"] or {},"\n")
self.help = {embed = { if permissions == "" then
title = "Help for ``"..self.name.."``", permissions = "All"
description = description, end
fields = { self.help = {embed = {
{name = "Usage: ",value = usage or backup_usage_str}, title = "Help for ``"..self.name.."``",
{name = "Perms: ",value = permissions} description = description,
} fields = {
}} {name = "Usage: ",value = usage or backup_usage_str},
return self {name = "Perms: ",value = permissions}
}
}}
return self
end end
--set the help message to be sent --set the help message to be sent
function command:set_help(obj,usage) function command:set_help(obj,usage)
if type(obj) == "table" then if type(obj) == "string" then
self.help = obj self:generate_help(obj,usage)
else elseif type(obj) == "table" then
self:generate_help(obj, self.help = obj
(type(usage) == "string" and usage) else
or "No description provided.") error("Type "..type(obj).." cannot be set as a help message")
end end
return self return self
end end
--print the help message, or generate it if there is none --print the help message, or generate it if there is none
function command:get_help() function command:get_help()
if not self.help then if not self.help then
self:generate_help("Description not defined") self:generate_help("Description not defined")
end end
return self.help return self.help
end end
function command:set_timeout_callback(fn) function command:set_timeout_callback(fn)
assert(type(fn) == "function","function expected, got "..type(fn)) assert(type(fn) == "function","function expected, got "..type(fn))
self.timeout_callback = fn self.timeout_callback = fn
return self return self
end end
--check the permissions for command --check the permissions for command
function command:check_permissions(message,special_flag) function command:check_permissions(message)
local ctime = discordia.Date():toMilliseconds() if message.author.bot and (not self.options.allow_bots) then
if (ctime-self.options.timeout < self.timer) and (not ignore_flag) then return false
if self.timeout_callback then end
self.timeout_callback(message) if discordia.Date():toMilliseconds()-self.options.timeout < self.timer then
end if self.timeout_callback then
return false self.timeout_callback(fn)
return false
end end
self.timer = discordia.Date():toMilliseconds() end
-- user rules first, group second, permission rules last self.timer = discordia.Date():toMilliseconds()
if ignore_flag == 2 then if self.rules:check_user(message.author.id) then
return true local found,allow = self.rules:check_user(message.author.id)
end return allow
local User, allowUser = self.rules:check_user(tostring(message.author.id)) end
local Group, allowGroup = self.rules:check_group(message.member.highestRole) if self.rules:check_group(message.member.roles) then
if User then local found,allow = self.rules:check_group(message.member.roles)
return allowUser return allow
end end
if Group then return self.rules:check_perm(message.member:getPermissions(message.channel))
return allowGroup
end
return self.rules:check_perm(message.member:getPermissions(message.channel))
end end
--the main entry point for the command - execute the callback within after --the main entry point for the command - execute the callback within after
--multiple checks --multiple checks
function command:exec(message,ignore_flag) function command:exec(message,args,opts)
if message.author.bot and (not self.options.allow_bots) then local exec = self.callback
return false if not self.callback then
error("Callback not set for command "..self.name)
end
if self.decorator then
self.callback = self.decorator(self.callback)
end
local content
if self.options.regex then
content = message.content
else
local strstart,strend = message.content:find(self.name,1,true)
content = message.content:sub(strend+1,-1)
end
if self:check_permissions(message) then
if self.options.typing_decorator then
message.channel:broadcastTyping()
end end
if self:check_permissions(message,ignore_flag) then local status,args,opts,err = import("air").parse(content,self.args,message.client,message.guild.id)
local exec = self.callback if status then
if not self.callback then local callst,status,response = pcall(self.callback,message,args,opts)
error("Callback not set for command "..self.name) if callst then
end if type(status) == "boolean" then
if self.decorator then if status then
self.callback = self.decorator(self.callback) message:addReaction("")
else
message:addReaction("")
end
end end
local strstart,strend = message.content:find(self.name,1,true) else
content = message.content:sub(strend+1,-1) message:addReaction("⚠️")
if self.options.typing_decorator then message:reply("An internal error occured: "..status)
message.channel:broadcastTyping() end
end else
local status,args,opts,err = import("air").parse(content,self.args,message.client,message.guild.id) message:addReaction("")
if status then message:reply(err)
local callst,status,response = pcall(self.callback,message,args,opts)
if callst then
if type(status) == "boolean" then
message:addReaction((status and "") or "")
end
return
end
message:addReaction("⚠️")
message:reply("An internal error occured: "..status)
return
end
message:addReaction("")
message:reply(err)
return
end end
else
message:addReaction("") message:addReaction("")
end
end end
--add decorators for the callback --add decorators for the callback
function command:set_decorator(fn) function command:set_decorator(fn)
assert(type(fn) == "function","a decorator function expected, got "..type(fn)) assert(type(fn) == "function","a decorator function expected, got "..type(fn))
self.decorator = fn self.decorator = fn
return self return self
end end
--get a list of all properties of the command --get a list of all properties of the command
function command:get_properties() function command:get_properties()
return { return {
name = self.name, name = self.name,
category = self.options.category, category = self.options.category,
args = table_utils.deepcopy(self.args), args = table_utils.deepcopy(self.args),
help = table_utils.deepcopy(self.help), help = table_utils.deepcopy(self.help),
prefix = self.prefix prefix = self.prefix
} }
end end
return command return command

View File

@ -2,94 +2,94 @@ local class = import("classes.baseclass")
local emitter_proxy = class("EmitterProxy") local emitter_proxy = class("EmitterProxy")
function emitter_proxy:__init(emitter) function emitter_proxy:__init(emitter)
self.original = emitter self.original = emitter
self.callback_pool = {} self.callback_pool = {}
end end
function emitter_proxy:on(event,callback) function emitter_proxy:on(event,callback)
if not self.callback_pool[event] then if not self.callback_pool[event] then
self.callback_pool[event] = {} self.callback_pool[event] = {}
end end
self.callback_pool[event][callback] = callback self.callback_pool[event][callback] = callback
self.original:on(event,callback) self.original:on(event,callback)
return callback return callback
end end
function emitter_proxy:once(event,callback) function emitter_proxy:once(event,callback)
if not self.callback_pool[event] then if not self.callback_pool[event] then
self.callback_pool[event] = {} self.callback_pool[event] = {}
end end
local wrapper = function(...) local wrapper = function(...)
callback(...) callback(...)
self.callback_pool[event][callback] = nil self.callback_pool[event][callback] = nil
end end
self.callback_pool[event][callback] = wrapper self.callback_pool[event][callback] = wrapper
self.callback_pool[event][wrapper] = wrapper self.callback_pool[event][wrapper] = wrapper
self.original:once(event,wrapper) self.original:once(event,wrapper)
return callback return callback
end end
function emitter_proxy:removeListener(event,callback) function emitter_proxy:removeListener(event,callback)
if self.callback_pool[event] and self.callback_pool[event][callback] then if self.callback_pool[event] and self.callback_pool[event][callback] then
self.callback_pool[event][callback] = nil self.callback_pool[event][callback] = nil
self.original:removeListener(event,callback) self.original:removeListener(event,callback)
end end
end end
function emitter_proxy:removeAllListeners(event,callback) function emitter_proxy:removeAllListeners(event,callback)
if self.callback_pool[event] then if self.callback_pool[event] then
for k,v in pairs(self.callback_pool[event]) do for k,v in pairs(self.callback_pool[event]) do
self.original:removeListener(event,v) self.original:removeListener(event,v)
end
self.callback_pool[event] = nil
end end
self.callback_pool[event] = nil
end
end end
function emitter_proxy:listeners(event) function emitter_proxy:listeners(event)
local copy = {} local copy = {}
if self.callback_pool[event] then if self.callback_pool[event] then
for k,v in pairs(self.callback_pool[event]) do for k,v in pairs(self.callback_pool[event]) do
table.insert(copy,v) table.insert(copy,v)
end
end end
return copy end
return copy
end end
function emitter_proxy:listenerCount(event) function emitter_proxy:listenerCount(event)
local count = 0 local count = 0
if event then if event then
if self.callback_pool[event] then if self.callback_pool[event] then
for k,v in pairs(self.callback_pool[event]) do for k,v in pairs(self.callback_pool[event]) do
count = count + 1 count = count + 1
end end
end
else
for k,v in pairs(self.callback_pool) do
for k2,v2 in pairs(v) do
count = count + 1
end
end
end end
return count else
for k,v in pairs(self.callback_pool) do
for k2,v2 in pairs(v) do
count = count + 1
end
end
end
return count
end end
function emitter_proxy:propogate(event,emitter) function emitter_proxy:propogate(event,emitter)
if not self.callback_pool[event] then if not self.callback_pool[event] then
self.callback_pool[event] = {} self.callback_pool[event] = {}
end end
local emitter_propogate_handler = function(...) local emitter_propogate_handler = function(...)
emitter:emit(event,...) emitter:emit(event,...)
end end
self.callback_pool[event][emitter_propogate_handler] = emitter_propogate_handler self.callback_pool[event][emitter_propogate_handler] = emitter_propogate_handler
self.original:on(event,emitter_propogate_handler) self.original:on(event,emitter_propogate_handler)
return emitter_propogate_handler return emitter_propogate_handler
end end
function emitter_proxy:destroy() function emitter_proxy:destroy()
for k,v in pairs(self.callback_pool) do for k,v in pairs(self.callback_pool) do
for k2,v2 in pairs(v) do for k2,v2 in pairs(v) do
self.original:removeListener(k,v2) self.original:removeListener(k,v2)
end
end end
end
end end
return emitter_proxy return emitter_proxy

View File

@ -0,0 +1,12 @@
local interface = {}
interface.wrapper = function(client,guild_id)
local new_i = {}
new_i.message = {}
new_i.message.get = function(channel,id)
local new_m = {}
local message = client.getMessage(id)
local new_m.content = message.content
local new_m.created_at = message.createdAt
local new_m.attachments = {}
for k,v in pairs(message.attachments) do
table.insert(new_m

View File

@ -0,0 +1,90 @@
local RPC_server = import("classes.RPC-server")
local class = import("classes.baseclass")
local monitor = class("Monitor")
--we only generate proxies as far as 1 object deep.
--to provide seamlessness, metamethods that request object proxies from their
--pointers may be used on the client side
--pointers here mean tables that contain the __id and __type properties.
--they do not hold any info on the object besides its class name and id
--a lookup table of all classes that we do not ignore. we exclude client and containers
--because they might break the sandboxing. we *do not* want that.
local allowed_types = {
["guild"] = true,
["member"] = true,
["emoji"] = true,
["message"] = true,
["channel"] = true,
["role"] = true,
["user"] = true,
["invite"] = true,
["guildtextchannel"] = true,
["textchannel"] = true,
["iterable"] = true,
["cache"] = true,
["arrayiterable"] = true,
["filteretediterable"] = true,
["secondarycache"] = true,
["weakcache"] = true,
["tableiterable"] = true,
}
--a lookup table of classes that can be explicitly converted to arrays.
local iterable_types = {
["iterable"] = true,
["cache"] = true,
["arrayiterable"] = true,
["filteretediterable"] = true,
["secondarycache"] = true,
["weakcache"] = true,
["tableiterable"] = true,
}
local comprehend_object = function(object)
local output
if (type(object) == "table") and (object.__class) then
--our object is an instance of a class
local class = object.__class.__name:lower()
if allowed_types[class] and (not iterable_types[class]) then
--our object can only be pointed to
output = {__id = object[k].id, __type = class}
else
--our object can be converted to an array
end
else
--our object is either an atomic data type, a string or a table.
end
end
local create_proxy = function(object)
local output = {}
for k,v in pairs(getmetatable(object).__getters) do
end
end
local proto_api = {
msg = {
get = function(channel,id)
channel:getMessage(id)
end,
},
guild = {
},
member = {
},
channel = {
}
}
function monitor:__init(guild,options)
assert(guild,"No guild provided")
assert(options,"No options provided (arg 2)")

View File

@ -11,133 +11,131 @@ local json = import("json")
local core = import("core") local core = import("core")
local emitter_proxy = import("classes.emitter-proxy") local emitter_proxy = import("classes.emitter-proxy")
local table_utils = import("table-utils") local table_utils = import("table-utils")
local log = import("logging")
function plugin_handler:__init(parent_server) function plugin_handler:__init(parent_server)
assert(parent_server,"server handler to assign the plugin handler to has not been provided") assert(parent_server,"server handler to assign the plugin handler to has not been provided")
self.server_handler = parent_server self.server_handler = parent_server
self.plugins = {} self.plugins = {}
self.plugin_info = {} self.plugin_info = {}
self.plugin_paths = {} self.plugin_paths = {}
self.server_handler.event_emitter:on("serverSaveConfig",function() self.server_handler.event_emitter:on("serverSaveConfig",function()
log("SERVER", "Saving plugins configs") print("[SERVER] Saving plugins configs")
for name,plugin in pairs(self.plugins) do for name,plugin in pairs(self.plugins) do
self:save_plugin_config(name) self:save_plugin_config(name)
end end
end) end)
end end
function plugin_handler:load_plugin_config(name) function plugin_handler:load_plugin_config(name)
return file.readJSON(self.server_handler.config_path..name..".json",{}) return file.readJSON(self.server_handler.config_path..name..".json",{})
end end
function plugin_handler:save_plugin_config(name) function plugin_handler:save_plugin_config(name)
if self.plugins[name] then if self.plugins[name] then
file.writeJSON(self.server_handler.config_path..name..".json",self.plugins[name].__env.config) file.writeJSON(self.server_handler.config_path..name..".json",self.plugins[name].__env.config)
end end
end end
function plugin_handler:add_plugin_folder(path) function plugin_handler:add_plugin_folder(path)
assert(type(path) == "string","path should be a string, got "..type(path)) assert(type(path) == "string","path should be a string, got "..type(path))
table.insert(self.plugin_paths,path) table.insert(self.plugin_paths,path)
end end
function plugin_handler:scan_folder(path) function plugin_handler:scan_folder(path)
local file = io.open(path.."/meta.json","r") local file = io.open(path.."/meta.json","r")
if file then if file then
local metadata,code,err = json.decode(file:read("*a")) local metadata,code,err = json.decode(file:read("*a"))
if metadata and metadata.name then if metadata and metadata.name then
self.plugin_info[metadata.name] = metadata self.plugin_info[metadata.name] = metadata
self.plugin_info[metadata.name].path = path.."/" self.plugin_info[metadata.name].path = path.."/"
self.plugin_info[metadata.name].loaded = false self.plugin_info[metadata.name].loaded = false
end
file:close()
else
for k,v in pairs({"/init.lua","/main.lua"}) do
local file = io.open(path..v,"r")
if file then
local name = path:match("[^/]+$")
self.plugin_info[name] = {["main"]=v:gsub("/","")}
self.plugin_info[name].path = path.."/"
self.plugin_info[name].loaded = false
file:close()
end
end
end end
file:close()
else
for k,v in pairs({"/init.lua","/main.lua"}) do
local file = io.open(path..v,"r")
if file then
local name = path:match("[^/]+$")
self.plugin_info[name] = {["main"]=v:gsub("/","")}
self.plugin_info[name].path = path.."/"
self.plugin_info[name].loaded = false
file:close()
end
end
end
end end
function plugin_handler:update_plugin_info() function plugin_handler:update_plugin_info()
for k,v in pairs(self.plugin_paths) do for k,v in pairs(self.plugin_paths) do
if file.existsDir(v) then if file.existsDir(v) then
file.ls(v):gsub("[^\n]+",function(c) file.ls(v):gsub("[^\n]+",function(c)
self:scan_folder(v..c) self:scan_folder(v..c)
end) end)
end
end end
end
end end
function plugin_handler:list_loadable() function plugin_handler:list_loadable()
return table_utils.deepcopy(self.plugin_info) return table_utils.deepcopy(self.plugin_info)
end end
function plugin_handler:load(name) function plugin_handler:load(name)
if not self.plugin_info[name] then if not self.plugin_info[name] then
return false, "No such plugin" return false, "No such plugin"
end end
if not self.plugin_info[name].main then if not self.plugin_info[name].main then
return false, "Plugin metadata entry doesn't specify the main file path or main file isn't found" return false, "Plugin metadata entry doesn't specify the main file path or main file isn't found"
end end
if self.plugin_info[name].loaded then if self.plugin_info[name].loaded then
return false, "Plugin is already loaded" return false, "Plugin is already loaded"
end end
local environment = setmetatable({ local environment = setmetatable({
id = self.server_handler.id, id = self.server_handler.id,
globals = self.server_handler.config, globals = self.server_handler.config,
signals = emitter_proxy(self.server_handler.signal_emitter), signals = emitter_proxy(self.server_handler.signal_emitter),
client = self.server_handler.client, client = self.server_handler.client,
events = emitter_proxy(self.server_handler.event_emitter), events = emitter_proxy(self.server_handler.event_emitter),
discordia = import("discordia"), discordia = import("discordia"),
server = self.server_handler, server = self.server_handler,
command_handler = self.server_handler.command_handler, command_handler = self.server_handler.command_handler,
plugin_handler = self.server_handler.plugin_handler, plugin_handler = self.server_handler.plugin_handler,
log = log, log = function() end,
config = self:load_plugin_config(name), config = self:load_plugin_config(name),
import = import, import = import,
},{__index = _G}) },{__index = _G})
local plugin_meta = self.plugin_info[name] local plugin_meta = self.plugin_info[name]
if file.exists(plugin_meta.path..plugin_meta.main) then if file.exists(plugin_meta.path..plugin_meta.main) then
environment["plugin_path"] = plugin_meta.path environment["plugin_path"] = plugin_meta.path
local plugin_content = file.read(plugin_meta.path..plugin_meta.main,"*a") local plugin_content = file.read(plugin_meta.path..plugin_meta.main,"*a")
local plugin_loader,err = load(plugin_content,"plugin loader: "..plugin_meta.path..plugin_meta.main,nil,environment) local plugin_loader,err = load(plugin_content,"plugin loader: "..plugin_meta.path..plugin_meta.main,nil,environment)
if plugin_loader then if plugin_loader then
local plugin_object = plugin_loader() local plugin_object = plugin_loader()
if plugin_object then if plugin_object then
plugin_object.name = name plugin_object.name = name
plugin_object:load(environment) plugin_object:load(environment)
self.plugins[name] = plugin_object self.plugins[name] = plugin_object
self.plugins[name].__env = environment self.plugins[name].__env = environment
self.plugin_info[name].loaded = true self.plugin_info[name].loaded = true
return true return true
else else
return false, "Plugin object missing" return false, "Plugin object missing"
end end
else
return false, err
end
else else
return false, "File specified as the main file is inaccessible" return false, err
end end
else
return false, "File specified as the main file is inaccessible"
end
end end
function plugin_handler:unload(name) function plugin_handler:unload(name)
if self.plugins[name] then if self.plugins[name] then
self.plugins[name].__env.signals:destroy() self.plugins[name].__env.signals:destroy()
self.plugins[name].__env.events:destroy() self.plugins[name].__env.events:destroy()
self.plugins[name]:unload() self.plugins[name]:unload()
self.plugin_info[name].loaded = false self.plugin_info[name].loaded = false
return true return true
else else
return false,"Plugin is not loaded" return false,"Plugin is not loaded"
end end
end end
return plugin_handler return plugin_handler

View File

@ -2,81 +2,58 @@ local class = import("classes.baseclass")
local plugin = class("Plugin") local plugin = class("Plugin")
function plugin:__init() function plugin:__init()
self.command_pool = {} self.command_pool = {}
self.config = {} self.config = {}
end end
function plugin:load(environment) function plugin:load(environment)
self.command_handler = environment.server.command_handler self.command_handler = environment.server.command_handler
for k,v in pairs(self.command_pool) do for k,v in pairs(self.command_pool) do
self.command_handler:add_command(v) self.command_handler:add_command(v)
end end
end end
function plugin:unload() function plugin:unload()
if self.removal_callback then if self.removal_callback then
self.removal_callback() self.removal_callback()
end end
for k,v in pairs(self.command_pool) do for k,v in pairs(self.command_pool) do
self.command_handler:remove_command(v) self.command_handler:remove_command(v)
end end
end end
function plugin:for_all_commands(fn) function plugin:for_all_commands(fn)
assert(type(fn)=="function","function expected, got "..type(fn)) assert(type(fn)=="function","function expected, got "..type(fn))
for k,v in pairs(self.command_pool) do for k,v in pairs(self.command_pool) do
fn(v) fn(v)
end end
end end
function plugin:for_every_new_command(fn) function plugin:for_every_new_command(fn)
assert(type(fn)=="function","function expected, got "..type(fn)) assert(type(fn)=="function","function expected, got "..type(fn))
self.decorator = fn self.decorator = fn
end end
function plugin:add_command(command_object) function plugin:add_command(command_object)
if self.decorator then if self.decorator then
self.fn(command_object) self.fn(command_object)
end end
command_object.parent = self command_object.parent = self
self.command_pool[command_object] = command_object self.command_pool[command_object] = command_object
--in post init state: we request the command handler to add the commands --in post init state: we request the command handler to add the commands
--that way, we can link our plugin back to the command handler --that way, we can link our plugin back to the command handler
if self.command_handler then if self.command_handler then
self.command_handler:add_command(command_object) self.command_handler:add_command(command_object)
end end
end end
function plugin:remove_command(command_object) function plugin:remove_command(command_object)
if self.command_pool[command_object] then if self.command_pool[command_object] then
self.command_pool[command_object] = nil self.command_pool[command_object] = nil
end end
--remove command after post-init state --remove command after post-init state
if self.command_handler then if self.command_handler then
self.command_handler:remove_command(command_object) self.command_handler:remove_command(command_object)
end end
end end
function plugin:load_helpdb(path)
local helpdb_file = io.open(path,r)
local helpdb,err = load(helpdb_file:read("*a") or "","helpdb "..path,nil,
setmetatable({
require = require,
import = import
},{
__index = _G
})
)
helpdb_file:close()
if not helpdb then
error(err)
end
helpdb = helpdb()
self:for_all_commands(function(command)
if helpdb[command.name] then
command:set_help(helpdb[command.name])
end
end)
end
return plugin return plugin

View File

@ -6,99 +6,98 @@ local command_handler = import("classes.command-handler")
local file = import("file") local file = import("file")
local eventlist = import("eventlist") local eventlist = import("eventlist")
local discordia = import("discordia") local discordia = import("discordia")
local log = import("logging")
local function check_partitioning(id,...) local function check_partitioning(id,...)
args = {...} args = {...}
v = args[1] v = args[1]
if type(v) == "table" and v.guild and v.guild.id == id then if type(v) == "table" and v.guild and v.guild.id == id then
return true return true
elseif not (type(v) == "table") then elseif not (type(v) == "table") then
return true return true
elseif type(v) == "table" and (not v.guild) and (tostring(v):find("Guild: ")) and v.id == id then elseif type(v) == "table" and (not v.guild) and (tostring(v):find("Guild: ")) and v.id == id then
return true return true
elseif type(v) == "table" and (not v.guild) and (v.message) and (v.message.guild.id == id) then elseif type(v) == "table" and (not v.guild) and (v.message) and (v.message.guild.id == id) then
return true return true
else else
return false return false
end end
end end
function server_handler:__init(client,guild,options) function server_handler:__init(client,guild,options)
assert(type(client) == "table","discordia client expected, got "..type(client)) assert(type(client) == "table","discordia client expected, got "..type(client))
self.client = client self.client = client
self.uptime = discordia.Date() self.uptime = discordia.Date()
self.event_emitter = core.Emitter:new() self.event_emitter = core.Emitter:new()
self.signal_emitter = core.Emitter:new() self.signal_emitter = core.Emitter:new()
self.plugin_handler = plugin_handler(self) self.plugin_handler = plugin_handler(self)
self.command_handler = command_handler(self) self.command_handler = command_handler(self)
self.id = guild.id self.id = guild.id
--conifgurable properties --conifgurable properties
self.config_path = options.path or "./servers/%id/" self.config_path = options.path or "./servers/%id/"
self.autosave = options.path or true self.autosave = options.path or true
self.autosave_frequency = options.autosave_frequency or 10 self.autosave_frequency = options.autosave_frequency or 10
self.plugin_search_paths = options.plugin_search_paths or {"./plugins/"} self.plugin_search_paths = options.plugin_search_paths or {"./plugins/"}
self.default_plugins = options.default_plugins or {"plugins"} self.default_plugins = options.default_plugins or {"test"}
self.default_prefixes = options.default_prefixes or {"&","<@"..self.client.user.id.."> ","."} self.default_prefixes = options.default_prefixes or {"&","<@!"..self.client.user.id..">"}
self.config = {} self.config = {}
self.config_path = self.config_path:gsub("%%id",self.id) self.config_path = self.config_path:gsub("%%id",self.id)
self:load_config() self:load_config()
self.config["prefix"] = self.config["prefix"] or self.default_prefixes[1] or "(missing prefix)" self.config["prefix"] = self.config["prefix"] or self.default_prefixes[1] or "(missing prefix)"
self.message_counter = 0 self.message_counter = 0
if self.autosave then if self.autosave then
self.client:on("messageCreate",function(msg)
self.message_counter = self.message_counter + 1
if math.fmod(self.message_counter,self.autosave_frequency) == 0 then
self:save_config()
end
end)
end
if not file.existsDir(self.config_path) then
os.execute("mkdir -p "..self.config_path)
end
for k,v in pairs(eventlist) do
self.client:on(v,function(...)
--check if the event is for this server, and then emit.
if check_partitioning(self.id,...) then
self.event_emitter:emit(v,...)
end
end)
end
self.client:on("messageCreate",function(msg) self.client:on("messageCreate",function(msg)
if msg.guild and msg.guild.id == self.id then self.message_counter = self.message_counter + 1
self.command_handler:handle(msg) if math.fmod(self.message_counter,self.autosave_frequency) == 0 then
end self:save_config()
end
end) end)
for _,path in pairs(self.plugin_search_paths) do end
self.plugin_handler:add_plugin_folder(path) if not file.existsDir(self.config_path) then
end os.execute("mkdir -p "..self.config_path)
self.plugin_handler:update_plugin_info() end
for _,plugin_name in pairs(self.default_plugins) do for k,v in pairs(eventlist) do
log("SERVER", "Loading plugin: "..tostring(plugin_name).." - ", self.plugin_handler:load(plugin_name)) self.client:on(v,function(...)
end --check if the event is for this server, and then emit.
for _,prefix in pairs(self.default_prefixes) do if check_partitioning(self.id,...) then
self.command_handler:add_prefix(prefix) self.event_emitter:emit(v,...)
end
end)
end
self.client:on("messageCreate",function(msg)
if msg.guild and msg.guild.id == self.id then
self.command_handler:handle(msg)
end end
end)
for _,path in pairs(self.plugin_search_paths) do
self.plugin_handler:add_plugin_folder(path)
end
self.plugin_handler:update_plugin_info()
for _,plugin_name in pairs(self.default_plugins) do
print("[SERVER] Loading plugin: "..tostring(plugin_name).." - ", self.plugin_handler:load(plugin_name))
end
for _,prefix in pairs(self.default_prefixes) do
self.command_handler:add_prefix(prefix)
end
end end
function server_handler:load_config(path) function server_handler:load_config(path)
log("SERVER", "Loading config") print("[SERVER] Loading config")
if path then if path then
self.config = file.readJSON(path,{}) self.config = file.readJSON(path,{})
else else
self.config = file.readJSON(self.config_path.."config.json") self.config = file.readJSON(self.config_path.."config.json")
end end
self.event_emitter:emit("serverLoadConfig",self.config) self.event_emitter:emit("serverLoadConfig",self.config)
end end
function server_handler:save_config(path) function server_handler:save_config(path)
log("SERVER", "Saving config") print("[SERVER] Saving config")
if path then if path then
file.writeJSON(path,self.config) file.writeJSON(path,self.config)
else else
file.writeJSON(self.config_path.."config.json",self.config) file.writeJSON(self.config_path.."config.json",self.config)
end end
self.event_emitter:emit("serverSaveConfig",self.config) self.event_emitter:emit("serverSaveConfig",self.config)
end end
return server_handler return server_handler

View File

@ -8,7 +8,6 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]] ]]
local find_strings = require("string_parse")
local safe_regex = function(str,pattern) local safe_regex = function(str,pattern)
local status,ret = pcall(string.match,str,pattern) local status,ret = pcall(string.match,str,pattern)
@ -19,7 +18,6 @@ end
if _VERSION=="Lua 5.1" then if _VERSION=="Lua 5.1" then
table.unpack = unpack table.unpack = unpack
end end
local cron = { local cron = {
directive_handler = nil directive_handler = nil
} }
@ -202,6 +200,50 @@ local syntax = {
end} end}
} }
local find_strings = function(text)
-- Find 2 string delimiters.
-- Partition text into before and after if the string is empty
-- Partition text into before, string and after if the string isn't empty
local strings = {text}
while strings[#strings]:match("[\"'/]") do
local string = strings[#strings]
-- Opening character for a string
local open_pos = string:find("[\"'/]")
local open_char = string:sub(open_pos,open_pos)
if strings[#strings]:sub(open_pos+1,open_pos+1) == open_char then
-- Empty string
local text_before = string:sub(1,open_pos-1)
local text_after = string:sub(open_pos+2,-1)
strings[#strings] = text_before
table.insert(strings,open_char..open_char)
table.insert(strings,text_after)
else
-- Non-empty string
local text_before = string:sub(1,open_pos-1)
local _,closing_position = string:sub(open_pos,-1):find("[^\\]"..open_char)
if not closing_position then
break
else
closing_position = closing_position+open_pos-1
end
local text_string = string:sub(open_pos,closing_position)
local text_after = string:sub(closing_position+1,-1)
strings[#strings] = text_before
table.insert(strings,text_string)
table.insert(strings,text_after)
end
end
for k,v in pairs(strings) do
if v:len() == 0 then
table.remove(strings,k)
end
end
return strings
-- P.S: This one is the best one i've written. Sure it looks clunky, but it
-- does exactly what I expect it to do - handle cases when there are string
-- delimiters inside other strings. Lovely. Also kinda horrifying.
end
local startfrom = function(pos,t) local startfrom = function(pos,t)
local newtable = {} local newtable = {}
for i = pos,#t do for i = pos,#t do
@ -222,6 +264,7 @@ cron._split = function(text)
return tokens return tokens
end end
cron._split_with_strings = function(text) cron._split_with_strings = function(text)
-- Parse strings -- Parse strings
local nt = find_strings(text) local nt = find_strings(text)
@ -274,9 +317,6 @@ cron.parse_directive = function(tokens)
break break
end end
end end
if stop then
break
end
end end
-- We use a delimiter so that command start wouldn't be ambiguous -- We use a delimiter so that command start wouldn't be ambiguous
-- Rather than defining an amount of arguments to directives, we -- Rather than defining an amount of arguments to directives, we

View File

@ -1,48 +1,48 @@
return function(message,overrides) return function(message,overrides)
assert(type(message) == "table","table expected, got "..type(message)) assert(type(message) == "table","table expected, got "..type(message))
assert(type(overrides) == "table","table expected for arg#2, got "..type(overrides)) assert(type(overrides) == "table","table expected for arg#2, got "..type(overrides))
local fake = {content = message.content, local fake = {content = message.content,
author = message.author, author = message.author,
member = message.guild:getMember(message.author.id), member = message.guild:getMember(message.author.id),
channel = message.channel, channel = message.channel,
client = message.client, client = message.client,
guild = message.guild, guild = message.guild,
delete = function() message:delete() end, delete = function() message:delete() end,
reply = function(thing,...) reply = function(thing,...)
message.channel:send(...) message.channel:send(...)
end, end,
createdAt = message.createdAt, createdAt = message.createdAt,
_parent = message.parent, _parent = message.parent,
parent = message.parent, parent = message.parent,
id = message.id, id = message.id,
attachment = message.attachment, attachment = message.attachment,
attachments = message.attachments, attachments = message.attachments,
cleanContent = message.cleanContent, cleanContent = message.cleanContent,
editedTimestamp = message.editedTimestamp, editedTimestamp = message.editedTimestamp,
embed = message.embed, embed = message.embed,
embeds = message.embeds, embeds = message.embeds,
link = message.link, link = message.link,
mentionedChannels = message.mentionedChannels, mentionedChannels = message.mentionedChannels,
mentionedEmojis = message.mentionedEmojis, mentionedEmojis = message.mentionedEmojis,
mentionedRoles = message.mentionedRoles, mentionedRoles = message.mentionedRoles,
mentionedUsers = message.mentionedUsers, mentionedUsers = message.mentionedUsers,
nonce = message.nonce, nonce = message.nonce,
oldContent = message.oldContent, oldContent = message.oldContent,
pinned = message.pinned, pinned = message.pinned,
reactions = message.reactions, reactions = message.reactions,
tts = message.tts, tts = message.tts,
type = message.type, type = message.type,
webhookId = message.webhookId, webhookId = message.webhookId,
addReaction = function(self,...) addReaction = function(self,...)
message:addReaction(...) message:addReaction(...)
end, end,
removeReaction = function(self,...) removeReaction = function(self,...)
message:removeReaction(...) message:removeReaction(...)
end, end,
emulated = true emulated = true
} }
for k,v in pairs(overrides) do for k,v in pairs(overrides) do
fake[k] = v fake[k] = v
end end
return fake return fake
end end

View File

@ -2,112 +2,112 @@
local file = {} local file = {}
local json local json
if pcall(import,"json") then if pcall(import,"json") then
json = import("json") json = import("json")
elseif pcall(require,"json") then elseif pcall(require,"json") then
json = require("json") json = require("json")
end end
file.safe = true file.safe = true
file.read = function(filename,mode) file.read = function(filename,mode)
assert(type(filename) == "string","string expected, got "..type(filename)) assert(type(filename) == "string","string expected, got "..type(filename))
local mode = mode or "*a" local mode = mode or "*a"
local temp_file,err = io.open(filename,r) local temp_file,err = io.open(filename,r)
if err then if err then
if not file.safe then error(err) else if not file.safe then error(err) else
ret_string = "" ret_string = ""
end
else
ret_string = temp_file:read(mode)
temp_file:close()
end end
return ret_string,err else
ret_string = temp_file:read(mode)
temp_file:close()
end
return ret_string,err
end end
file.write = function(filename,write) file.write = function(filename,write)
assert(type(filename) == "string", "string expected, got "..type(filename)) assert(type(filename) == "string", "string expected, got "..type(filename))
assert(type(write) == "string", "string expected for argument #2, got "..type(write)) assert(type(write) == "string", "string expected for argument #2, got "..type(write))
local temp_file,err = io.open(filename,"w+") local temp_file,err = io.open(filename,"w+")
local status = false local status = false
if err then if err then
if not file.safe then error(err) else if not file.safe then error(err) else
status = false status = false
end
else
temp_file:write(write)
temp_file:close()
status = true
end end
return status,err else
temp_file:write(write)
temp_file:close()
status = true
end
return status,err
end end
file.exists = function(filename) file.exists = function(filename)
local file = io.open(filename,"r") local file = io.open(filename,"r")
if file then if file then
file:close() file:close()
return true return true
else else
return false return false
end end
end end
file.existsDir = function(filename) file.existsDir = function(filename)
local file = io.open(filename.."/stuff","w") local file = io.open(filename.."/stuff","w")
if file then if file then
file:close() file:close()
os.remove(filename.."/stuff") os.remove(filename.."/stuff")
return true return true
else else
return false return false
end end
end end
file.ls = function(path) file.ls = function(path)
if file.existsDir(path) then if file.existsDir(path) then
local ls_handle = io.popen("ls -1 "..path,"r") local ls_handle = io.popen("ls -1 "..path,"r")
local ls_data = ls_handle:read("*a") local ls_data = ls_handle:read("*a")
ls_handle:close() ls_handle:close()
return ls_data return ls_data
else else
return false, "No such file or directory" return false, "No such file or directory"
end end
end end
if json then if json then
file.readJSON = function(filename,default) file.readJSON = function(filename,default)
assert(type(filename) == "string","string expected, got "..type(filename)) assert(type(filename) == "string","string expected, got "..type(filename))
local json_data,err = file.read(filename,"*a") local json_data,err = file.read(filename,"*a")
local table_data, status local table_data, status
if err then if err then
if not file.safe then error(err) else if not file.safe then error(err) else
status = err status = err
table_data = default or {} table_data = default or {}
end end
else else
table_data,_,err = json.decode(json_data) table_data,_,err = json.decode(json_data)
if not table_data then if not table_data then
if not file.safe then error(err) else if not file.safe then error(err) else
status = err status = err
table_data = default or {} table_data = default or {}
end
end
end end
return table_data, status end
end end
file.writeJSON = function(filename,table_data) return table_data, status
assert(type(filename) == "string","string expected, got "..type(filename)) end
assert(type(table_data) == "table","table expected, got "..type(table_data)) file.writeJSON = function(filename,table_data)
local status = false assert(type(filename) == "string","string expected, got "..type(filename))
local status,json_object,_,err = pcall(function() return json.encode(table_data) end) assert(type(table_data) == "table","table expected, got "..type(table_data))
if not status then local status = false
if not file.safe then error(err) else local status,json_object,_,err = pcall(function() return json.encode(table_data) end)
status = false if not status then
err = json_object if not file.safe then error(err) else
end status = false
else err = json_object
if json_object then end
status,err = file.write(filename,json_object) else
end if json_object then
end status,err = file.write(filename,json_object)
return status, err end
end end
return status, err
end
end end
return file return file

View File

@ -1,39 +1,39 @@
--Luvit's deadly sin - a library that fixes the dumbest problem in luvit --Luvit's deadly sin - a library that fixes the dumbest problem in luvit
--That is, unability to load core modules from the files that were required --That is, unability to load core modules from the files that were required
return function(reqfunc) return function(reqfunc)
local function import(path) local function import(path)
local paths = {} local paths = { }
package.path:gsub("[^;]+",function(path) package.path:gsub("[^;]+",function(path)
table.insert(paths,path) table.insert(paths,path)
end) end)
local filename = path:gsub("%.","/") local filename = path:gsub("%.","/")
local file = io.open(filename..".lua","r") local file = io.open(filename..".lua","r")
local iterator = 0 local iterator = 0
local last_filename = "" local last_filename = ""
while not file do while not file do
iterator = iterator + 1 iterator = iterator + 1
if paths[iterator] then if paths[iterator] then
file = io.open(paths[iterator]:gsub("%?",filename),"r") file = io.open(paths[iterator]:gsub("%?",filename),"r")
last_filename = paths[iterator] last_filename = paths[iterator]
else else
break break
end end
end
if not file then
return reqfunc(path)
else
content = file:read("*a")
local f,err = load(content,"import: "..filename,nil,setmetatable({
require = reqfunc,
import = import,
},{__index = _G}))
if err then
error("[import: "..filename.."] "..tostring(err))
end
return f()
end
end end
return import if not file then
return reqfunc(path)
else
content = file:read("*a")
local func,err = load(content,"import: "..filename,nil,setmetatable({
require = reqfunc,
import = import,
},{__index = _G}))
if err then
error(err)
end
return func()
end
end
return import
end end
--[[ --[[
Usage: Usage:

View File

@ -1,26 +0,0 @@
local logging_facilities = {
["ALIAS"] = "0;32",
["REACTIONS"] = "0;32",
["SERVER"] = "1;34",
["ERROR"] = "1;31",
["WARNING"] = "1;33"
}
local clear = "\27[0m"
local concat = function(tab,separator)
local text = ""
local separator = separator or "\9"
for k,v in pairs(tab) do
text = text..tostring(v)..separator
end
return text:sub(1,-1-separator:len())
end
return function(facility, ...)
local effect = "\27["
if logging_facilities[facility] then
effect = effect..logging_facilities[facility].."m"
else
effect = effect.."1m"
end
print(os.date("%Y-%m-%d %H:%M:%S | ")..effect.."["..facility.."]"..clear.."\9| "..concat({...}))
end

@ -1 +1 @@
Subproject commit 23fcea17c2eb380b6a6b1ba848d5bc7ebdc38b05 Subproject commit 9e6f09050f9ebecc4ffcea1234b920468254a79b

View File

@ -1,123 +1,123 @@
local markov = {} local markov = {}
local function node(relations) local function node(relations)
local node = {} local node = {}
local total = 0 local total = 0
for k,v in pairs(relations) do for k,v in pairs(relations) do
total = total + v.occurences total = total + v.occurences
end end
for k,v in pairs(relations) do for k,v in pairs(relations) do
node[k] = {probability = v.occurences/total,occurences = v.occurences} node[k] = {probability = v.occurences/total,occurences = v.occurences}
end end
return node return node
end end
local function escape(str) local function escape(str)
return str:gsub("([%%%*%(%)%^%.%[%]%+%-%$%?])","%%%1") return str:gsub("([%%%*%(%)%^%.%[%]%+%-%$%?])","%%%1")
end end
local function register_words(str,word_list) local function register_words(str,word_list)
local word_list = word_list or {} local word_list = word_list or {}
str:gsub("%S+",function(word) str:gsub("%S+",function(word)
if not word_list[word] then if not word_list[word] then
word_list[word] = {} word_list[word] = {}
end
local current_word = word_list[word]
local escaped_word = escape(word)
str:gsub("%s+" .. escaped_word .. "%s+(%S+)",function(word2)
if not current_word[word2] then
current_word[word2] = {}
end end
local current_word = word_list[word] if not current_word[word2].occurences then
local escaped_word = escape(word) current_word[word2].occurences = 1
str:gsub("%s+" .. escaped_word .. "%s+(%S+)",function(word2) else
if not current_word[word2] then current_word[word2].occurences = current_word[word2].occurences + 1
current_word[word2] = {} end
end end)
if not current_word[word2].occurences then end)
current_word[word2].occurences = 1 for k,v in pairs(word_list) do
else word_list[k] = node(v)
current_word[word2].occurences = current_word[word2].occurences + 1 end
end return word_list
end)
end)
for k,v in pairs(word_list) do
word_list[k] = node(v)
end
return word_list
end end
local table_length = function(tab) local table_length = function(tab)
local len = 0 local len = 0
for k,v in pairs(tab) do for k,v in pairs(tab) do
len = len + 1 len = len + 1
end end
return len return len
end end
function markov.walk(self,start) function markov.walk(self,start)
if not self.init then if not self.init then
error("Attempted to use method on uninitialized instances") error("Attempted to use an instance method on an uninitialized instance")
end end
local random = math.random(0,1e7)/1e7 local random = math.random(0,1e7)/1e7
local words = {} local words = {}
words.count = 0 words.count = 0
local word = nil local word = nil
if self.net[start] then if self.net[start] then
while (words.count < 1) and (table_length(self.net[start]) > 0) do while (words.count < 1) and (table_length(self.net[start]) > 0) do
for k,v in pairs(self.net[start]) do for k,v in pairs(self.net[start]) do
if (random <= v.probability) then if (random <= v.probability) then
words.count = words.count + 1 words.count = words.count + 1
table.insert(words,k) table.insert(words,k)
end
end
random = math.random(0,1e7)/1e7
end end
end
random = math.random(0,1e7)/1e7
end end
if words.count > 0 then end
word = words[math.random(1,#words)] if words.count > 0 then
end word = words[math.random(1,#words)]
return word end
return word
end end
function markov.expand_vocabulary(self,source) function markov.expand_vocabulary(self,source)
if not self.init then if not self.init then
error("Attempted to use method on uninitialized instances") error("Attempted to use an instance method on an uninitialized instance")
end end
self.net = register_words(source,self.net) self.net = register_words(source,self.net)
end end
function markov.save_state(self) function markov.save_state(self)
return self.net return self.net
end end
function markov.load_state(self,new_state) function markov.load_state(self,new_state)
self.net = new_state self.net = new_state
end end
function markov.run(self,start,count) function markov.run(self,start,count)
if not self.init then if not self.init then
error("Attempted to use an instance method on an uninitialized instance") error("Attempted to use an instance method on an uninitialized instance")
end
if not start then
for k,v in pairs(self.net) do
start = k
break
end end
if not start then end
for k,v in pairs(self.net) do local sequence = ""
start = k local current_word = start
break while current_word do
end sequence = sequence..current_word.." "
local _,counter = sequence:gsub("(%S+)","%1")
current_word = self:walk(current_word)
if counter > (count or 200) then
sequence = sequence:sub(1,-2).."..."
break
end end
local sequence = "" end
local current_word = start return sequence
while current_word do
sequence = sequence..current_word.." "
local _,counter = sequence:gsub("(%S+)","%1")
current_word = self:walk(current_word)
if counter > (count or 200) then
sequence = sequence:sub(1,-2).."..."
break
end
end
return sequence
end end
function markov.new(str) function markov.new(str)
local self = setmetatable({},{__index = markov}) local self = setmetatable({},{__index = markov})
self.net = register_words(str or "") self.net = register_words(str or "")
self.init = true self.init = true
return self return self
end end
return markov return markov

View File

@ -1,34 +1,32 @@
--string purifier library --string purifier library
local purify = {} local purify = {}
purify.purify_pings = function(msg,input) purify.purify_pings = function(msg,input)
local text = input local text = input
while text:match("<@(%D*)(%d*)>") do while text:match("<@(%D*)(%d*)>") do
local obj,id = text:match("<@(%D*)(%d*)>") local obj,id = text:match("<@(%D*)(%d*)>")
local substitution = "" local substitution = ""
if obj:match("!") then if obj:match("!") then
local member = msg.guild:getMember(id) local member = msg.guild:getMember(id)
if member then if member then
substitution = "@"..member.name substitution = "@"..member.name
end end
elseif obj:match("&") then elseif obj:match("&") then
local role = msg.guild:getRole(id) local role = msg.guild:getRole(id)
if role then if role then
substitution = "@"..role.name substitution = "@"..role.name
end end
end
if substitution == "" then
substitution = "<\\@"..obj..id..">"
end
text = text:gsub("<@(%D*)"..id..">",substitution)
end end
text = text:gsub("@everyone","") if substitution == "" then
text = text:gsub("@here","") substitution = "<\\@"..obj..id..">"
return text end
text = text:gsub("<@(%D*)"..id..">",substitution)
end
return text
end end
purify.purify_escapes = function(text) purify.purify_escapes = function(text)
local match = "([%(%)%.%%%+%-%*%?%[%]%^%$])" local match = "([%(%)%.%%%+%-%*%?%[%]%^%$])"
return text:gsub(match,"%%%1") return text:gsub(match,"%%%1")
end end
return purify return purify

View File

@ -1,44 +0,0 @@
return function(text,custom_strings)
local delimiters = custom_strings or "[\"'/]"
-- Find 2 string delimiters.
-- Partition text into before and after if the string is empty
-- Partition text into before, string and after if the string isn't empty
local strings = {text}
while strings[#strings]:match(delimiters) do
local string = strings[#strings]
-- Opening character for a string
local open_pos = string:find(delimiters)
local open_char = string:sub(open_pos,open_pos)
if strings[#strings]:sub(open_pos+1,open_pos+1) == open_char then
-- Empty string
local text_before = string:sub(1,open_pos-1)
local text_after = string:sub(open_pos+2,-1)
strings[#strings] = text_before
table.insert(strings,open_char..open_char)
table.insert(strings,text_after)
else
-- Non-empty string
local text_before = string:sub(1,open_pos-1)
local _,closing_position = string:sub(open_pos,-1):find("[^\\]"..open_char)
if not closing_position then
break
else
closing_position = closing_position+open_pos-1
end
local text_string = string:sub(open_pos,closing_position)
local text_after = string:sub(closing_position+1,-1)
strings[#strings] = text_before
table.insert(strings,text_string)
table.insert(strings,text_after)
end
end
for k,v in pairs(strings) do
if v:len() == 0 then
table.remove(strings,k)
end
end
return strings
-- P.S: This one is the best one i've written. Sure it looks clunky, but it
-- does exactly what I expect it to do - handle cases when there are string
-- delimiters inside other strings. Lovely. Also kinda horrifying.
end

View File

@ -15,50 +15,42 @@ utilities.deepcopy = function(orig)
return copy return copy
end end
utilities.slice = function(list,start,list_end) utilities.slice = function(list,start,list_end)
local output = {} local output = {}
for I = (start or 1),(list_end or #table) do for I = (start or 1),(list_end or #table) do
table.insert(output,list[I]) table.insert(output,list[I])
end end
return output return output
end end
utilities.shallowcopy = function(orig) utilities.shallowcopy = function(orig)
local copy = {} local copy = {}
for k,v in pairs(orig) do for k,v in pairs(orig) do
copy[k] = v copy[k] = v
end end
return copy return copy
end
utilities.listcopy = function(orig)
local list = {}
for k,v in pairs(orig) do
table.insert(list,v)
end
return list
end end
--overwrite the original table's properties with new properties --overwrite the original table's properties with new properties
utilities.overwrite = function(original,overwrite) utilities.overwrite = function(original,overwrite)
local new = utilities.shallowcopy(original) local new = utilities.shallowcopy(original)
for k,v in pairs(overwrite) do for k,v in pairs(overwrite) do
new[k] = v new[k] = v
end end
return new return new
end end
--merge all objects passed as arguments into a table. --merge all objects passed as arguments into a table.
--if the object is a table, merge all of it's contents with the table --if the object is a table, merge all of it's contents with the table
utilities.merge = function(...) utilities.merge = function(...)
local args = {...} local args = {...}
local new = {} local new = {}
for k,v in pairs(args) do for k,v in pairs(args) do
if type(v) == "table" then if type(v) == "table" then
for k2,v2 in pairs(v) do for k2,v2 in pairs(v) do
table.insert(new,v2) table.insert(new,v2)
end end
else else
table.insert(new,v) table.insert(new,v)
end
end end
return new end
return new
end end
utilities.remove_value = function(tb,v) utilities.remove_value = function(tb,v)
local id_to_remove = nil local id_to_remove = nil

View File

@ -1,9 +1,9 @@
return function(sec) return function(sec)
local hours = math.floor(sec/3600) local hours = math.floor(sec/3600)
local minutes = math.floor((sec - hours*3600)/60) local minutes = math.floor((sec - hours*3600)/60)
local seconds = sec - (hours*3600) - (minutes*60) local seconds = sec - (hours*3600) - (minutes*60)
hours = ((hours < 10) and ("0"..hours)) or hours hours = ((hours < 10) and ("0"..hours)) or hours
minutes = ((minutes < 10) and ("0"..minutes)) or minutes minutes = ((minutes < 10) and ("0"..minutes)) or minutes
seconds = ((seconds < 10) and ("0"..seconds)) or seconds seconds = ((seconds < 10) and ("0"..seconds)) or seconds
return ((tonumber(hours) > 0 and hours..":") or "")..minutes..":"..seconds return ((tonumber(hours) > 0 and hours..":") or "")..minutes..":"..seconds
end end

View File

@ -26,7 +26,10 @@ local triggerOnce = function(evname,args,vars)
end end
end end
event_emitter:on("messageCreate",function(msg) client:on("messageCreate",function(msg)
if (not msg.guild) or (tostring(msg.guild.id) ~= tostring(id)) then
return
end
local content = msg.content local content = msg.content
local user = msg.author.id local user = msg.author.id
local channelid = msg.channel.id local channelid = msg.channel.id
@ -42,7 +45,10 @@ event_emitter:on("messageCreate",function(msg)
triggerOnce("messageOnce",{content,user,channelid},args) triggerOnce("messageOnce",{content,user,channelid},args)
end) end)
event_emitter:on("userBan",function(user,guild) client:on("userBan",function(user,guild)
if tostring(guild.id) ~= tostring(id) then
return
end
args = { args = {
["%$USER"] = user.id, ["%$USER"] = user.id,
["%$USERNAME"] = user.name ["%$USERNAME"] = user.name
@ -53,7 +59,10 @@ event_emitter:on("userBan",function(user,guild)
triggerOnce("banOnce",{user.id},args) triggerOnce("banOnce",{user.id},args)
end) end)
event_emitter:on("userUnban",function(user,guild) client:on("userUnban",function(user,guild)
if tostring(guild.id) ~= tostring(id) then
return
end
args = { args = {
["%$USER"] = user.id, ["%$USER"] = user.id,
["%$USERNAME"] = user.name ["%$USERNAME"] = user.name
@ -64,11 +73,14 @@ event_emitter:on("userUnban",function(user,guild)
triggerOnce("unbanOnce",{user.id},args) triggerOnce("unbanOnce",{user.id},args)
end) end)
event_emitter:on("memberJoin", function(member) client:on("memberJoin", function(member)
if tostring(member.guild.id) ~= tostring(id) then
return
end
args = { args = {
["%$USER"] = member.id, ["%$USER"] = member.id,
["%$USERNAME"] = member.name, ["%$USERNAME"] = member.name,
["%$AGE"] = discordia.Date():toSeconds()-member.user.createdAt, ["%$AGE"] = member.user.createdAt,
["%$DISCRIM"] = member.user.discriminator, ["%$DISCRIM"] = member.user.discriminator,
["%$TAG"] = member.user.tag ["%$TAG"] = member.user.tag
} }
@ -76,21 +88,24 @@ event_emitter:on("memberJoin", function(member)
trigger("join",{ trigger("join",{
member.id, member.id,
member.name, member.name,
discordia.Date():toSeconds()-member.user.createdAt member.user.createdAt
},args) },args)
-- @joinOnce: userid, username, age, $USER, $USERNAME, $AGE, $DISCRIM, $TAG -- @joinOnce: userid, username, age, $USER, $USERNAME, $AGE, $DISCRIM, $TAG
triggerOnce("joinOnce",{ triggerOnce("joinOnce",{
member.id, member.id,
member.name, member.name,
discordia.Date():toSeconds()-member.user.createdAt member.user.createdAt
},args) },args)
end) end)
event_emitter:on("memberLeave", function(member) client:on("memberLeave", function(member)
if tostring(member.guild.id) ~= tostring(id) then
return
end
args = { args = {
["%$USER"] = member.id, ["%$USER"] = member.id,
["%$USERNAME"] = member.name, ["%$USERNAME"] = member.name,
["%$AGE"] = discordia.Date():toSeconds()-member.user.createdAt, ["%$AGE"] = member.user.createdAt,
["%$DISCRIM"] = member.user.discriminator, ["%$DISCRIM"] = member.user.discriminator,
["%$TAG"] = member.user.tag, ["%$TAG"] = member.user.tag,
["%$GUILDTIME"] = member.joinedAt, ["%$GUILDTIME"] = member.joinedAt,

View File

@ -1,51 +0,0 @@
return {
["event"] = {embed={
title = "Add a cron event",
description = "https://github.com/512mb-org/512mb.org-bot/wiki/Events-and-cronjobs",
fields = {
{name = "Usage:",value = "event ..."},
{name = "Perms:",value = "administrator"},
}
}},
["delay"] = {embed={
title = "Delay a command",
description = "Delay fromat is <number><unit>, where unit is one of the follwing:\n\"h\" - hour,\n\"m\" - minute,\n\"d\" - day,\n\"w\" - week,\n\"y\" - year",
fields = {
{name = "Usage:",value = "delay <delayformat> <command>"},
{name = "Perms:",value = "any"},
}
}},
["events"] = {embed={
title = "View your running events",
description = "nuff said.",
fields = {
{name = "Usage:",value = "events <page>"},
{name = "Perms:",value = "any"},
}
}},
["user-events"] = {embed={
title = "View running events of a certain user",
description = "nuff said.",
fields = {
{name = "Usage:",value = "user-events <user> <page>"},
{name = "Perms:",value = "administrator"},
}
}},
["remove-event"] = {embed={
title = "Remove an event",
description = "nuff said.",
fields = {
{name = "Usage:",value = "remove-event <id>"},
{name = "Perms:",value = "any"},
}
}},
["remove-user-event"] = {embed={
title = "Remove an event from a user",
description = "nuff said.",
fields = {
{name = "Usage:",value = "remove-user-event <user> <id>"},
{name = "Perms:",value = "administrator"},
}
}},
["date"] = "Print current date and time"
}

View File

@ -4,8 +4,6 @@ local plugin = pluginc("cron")
local cron = import("cron") local cron = import("cron")
local fake_message = import("fake_message") local fake_message = import("fake_message")
local md5 = import("md5") local md5 = import("md5")
local discordia = import("discordia")
local event_emitter = events
local events = { local events = {
timer = {}, timer = {},
event = {} event = {}
@ -15,24 +13,21 @@ local exec = function(v,command)
local channel = client:getChannel(v.channel) local channel = client:getChannel(v.channel)
if not channel then if not channel then
log("ERROR","Unable to retrieve event channel: "..tostring(v.channel)) log("ERROR","Unable to retrieve event channel: "..tostring(v.channel))
log("ERROR","Failed event: "..command)
return return
end end
local msg = channel:getMessage(v.id) local msg = channel:getMessage(v.id)
if not msg then if not msg then
log("ERROR","Unable to retrieve event message: "..tostring(v.id)) log("ERROR","Unable to retrieve event message: "..tostring(v.id))
log("ERROR","Failed event: "..command)
return return
end end
if not msg.member then if not msg.member then
log("ERROR","Unable to retrieve event creator: "..tostring(v.user.id)) log("ERROR","Unable to retrieve event creator: "..tostring(v.user.id))
log("ERROR","Failed event: "..command)
return return
end end
command_handler:handle(fake_message(msg,{ command_handler:handle(fake_message(msg,{
delete = function() end, delete = function() end,
content = command content = command
}),1) }))
end end
if not config.events then if not config.events then
@ -60,7 +55,7 @@ local create_event = function(msg,cronjob,create_entry)
user = tostring(msg.author.id), user = tostring(msg.author.id),
type = functype type = functype
} }
if create_entry then return true,hash end if create_entry then return true end
if not config.events.event[event_name] then config.events.event[event_name] = {} end if not config.events.event[event_name] then config.events.event[event_name] = {} end
config.events.event[event_name][hash] = { config.events.event[event_name][hash] = {
comm = arg, comm = arg,
@ -77,7 +72,7 @@ local create_event = function(msg,cronjob,create_entry)
user = tostring(msg.author.id), user = tostring(msg.author.id),
type = functype type = functype
} }
if create_entry then return true,hash end if create_entry then return true end
config.events.timer[hash] = { config.events.timer[hash] = {
comm = arg, comm = arg,
channel = tostring(msg.channel.id), channel = tostring(msg.channel.id),
@ -161,20 +156,16 @@ for k,v in pairs(config.events.timer) do
local status,hash = create_event(message,v.comm,true) local status,hash = create_event(message,v.comm,true)
--orphan events with mismatching hashes --orphan events with mismatching hashes
if status and (hash ~= k) then if status and (hash ~= k) then
log("WARNING", "Hash mismatch, orphaning event.") log("WARNING", "Hash mismatch, orpahning event.")
events.timer[k] = nil events.timer[k] = nil
config.events.timer[k] = nil config.events.timer[k] = nil
create_event(message,v.comm) create_event(message,v.comm)
end end
else else
log("ERROR","No message with id "..v.id) log("ERROR","No message with id "..v.id)
log("ERROR","Event id: "..k..".\nEvent description: ")
print(v.comm)
end end
else else
log("ERROR","No channel with id "..v.channel) log("ERROR","No channel with id "..v.channel)
log("ERROR","Event id: "..k..".\nEvent description: ")
print(v.comm)
end end
end end
@ -189,28 +180,35 @@ for _,evtype in pairs(config.events.event) do
local status,hash = create_event(message,v.comm,true) local status,hash = create_event(message,v.comm,true)
--orphan events with mismatching hashes --orphan events with mismatching hashes
if status and (hash ~= k) then if status and (hash ~= k) then
log("WARNING", "Hash mismatch, orphaning event.") log("WARNING", "Hash mismatch, orpahning event.")
events.event[_][k] = nil events.event[_][k] = nil
config.events.event[_][k] = nil config.events.event[_][k] = nil
create_event(message,v.comm) create_event(message,v.comm)
end end
else else
log("ERROR","No message with id "..v.id) log("ERROR","No message with id "..v.id)
log("ERROR","Event "..k..".\nEvent description: ")
config.events.event[_][k] = nil
end end
else else
log("ERROR","No channel with id "..v.channel) log("ERROR","No channel with id "..v.channel)
log("ERROR","Event "..k..".\nEvent description: ")
config.events.event[_][k] = nil
end end
end end
end end
local event = command("event",{ local event = command("event",{
category = "Automation", help = {embed={
perms = {"administrator"}, title = "Add a cron event",
args = {"string"}, description = "https://github.com/512mb-org/512mb.org-bot/wiki/Events-and-cronjobs",
fields = {
{name = "Usage:",value = "event ..."},
{name = "Perms:",value = "administrator"},
}
}},
perms = {
"administrator"
},
args = {
"string"
},
exec = function(msg,args,opts) exec = function(msg,args,opts)
return create_event(msg,table.concat(args," ")) return create_event(msg,table.concat(args," "))
end end
@ -218,7 +216,14 @@ local event = command("event",{
plugin:add_command(event) plugin:add_command(event)
local delay = command("delay",{ local delay = command("delay",{
category = "Automation", help = {embed={
title = "Delay a command",
description = "Delay fromat is <number><unit>, where unit is one of the follwing:\n\"h\" - hour,\n\"m\" - minute,\n\"d\" - day,\n\"w\" - week,\n\"y\" - year",
fields = {
{name = "Usage:",value = "delay <delayformat> <command>"},
{name = "Perms:",value = "any"},
}
}},
args = { args = {
"string", "string",
"string" "string"
@ -233,7 +238,14 @@ local delay = command("delay",{
plugin:add_command(delay) plugin:add_command(delay)
local events_comm = command("events",{ local events_comm = command("events",{
category = "Automation", help = {embed={
title = "View your running events",
description = "nuff said.",
fields = {
{name = "Usage:",value = "events <page>"},
{name = "Perms:",value = "any"},
}
}},
exec = function(msg,args,opts) exec = function(msg,args,opts)
args[1] = tonumber(args[1]) or 1 args[1] = tonumber(args[1]) or 1
local upto = args[1]*5 local upto = args[1]*5
@ -242,7 +254,7 @@ local events_comm = command("events",{
title = "Your events: ", title = "Your events: ",
description = "", description = "",
footer = { footer = {
text = "Events "..tostring(upto-4).." - "..tostring(upto).." | Total: "..tostring(#uevents) text = "Events "..tostring(upto-4).." - "..tostring(upto)
} }
}} }}
for I = upto-4,upto do for I = upto-4,upto do
@ -257,9 +269,20 @@ local events_comm = command("events",{
plugin:add_command(events_comm) plugin:add_command(events_comm)
local user_events_comm = command("user-events",{ local user_events_comm = command("user-events",{
category = "Automation", help = {embed={
args = {"member"}, title = "View running events of a certain user",
perms = {"administrator"}, description = "nuff said.",
fields = {
{name = "Usage:",value = "user-events <user> <page>"},
{name = "Perms:",value = "administrator"},
}
}},
args = {
"member"
},
perms = {
"administrator"
},
exec = function(msg,args,opts) exec = function(msg,args,opts)
args[2] = tonumber(args[2]) or 1 args[2] = tonumber(args[2]) or 1
local upto = args[2]*5 local upto = args[2]*5
@ -283,8 +306,17 @@ local user_events_comm = command("user-events",{
plugin:add_command(user_events_comm) plugin:add_command(user_events_comm)
local remove_event= command("remove-event",{ local remove_event= command("remove-event",{
category = "Automation", help = {embed={
args = {"string"}, title = "Remove an event",
description = "nuff said.",
fields = {
{name = "Usage:",value = "remove-event <id>"},
{name = "Perms:",value = "any"},
}
}},
args = {
"string"
},
exec = function(msg,args,opts) exec = function(msg,args,opts)
return remove_user_event(msg.author.id,args[1]) return remove_user_event(msg.author.id,args[1])
end end
@ -292,6 +324,14 @@ local remove_event= command("remove-event",{
plugin:add_command(remove_event) plugin:add_command(remove_event)
local remove_user_event_c = command("remove-user-event",{ local remove_user_event_c = command("remove-user-event",{
help = {embed={
title = "Remove an event from a user",
description = "nuff said.",
fields = {
{name = "Usage:",value = "remove-user-event <user> <id>"},
{name = "Perms:",value = "administrator"},
}
}},
args = { args = {
"member", "member",
"string" "string"
@ -299,21 +339,12 @@ local remove_user_event_c = command("remove-user-event",{
perms = { perms = {
"administrator" "administrator"
}, },
category = "Automation",
exec = function(msg,args,opts) exec = function(msg,args,opts)
return remove_user_event(args[1].id,args[2]) return remove_user_event(args[1].id,args[2])
end end
}) })
plugin:add_command(remove_user_event_c) plugin:add_command(remove_user_event_c)
local date_c = command("date",{
category = "Utilities",
exec = function(msg,args,opts)
msg:reply(os.date("%d.%m.%Y %H:%M"))
end
})
plugin:add_command(date_c)
local timer = discordia.Clock() local timer = discordia.Clock()
timer:on("min",function() timer:on("min",function()
for k,v in pairs(events.timer) do for k,v in pairs(events.timer) do
@ -329,19 +360,17 @@ timer:on("min",function()
end) end)
--load events file --load events file
local fhandler = io.open(plugin_path.."/events.lua","r") local fhandler = io.open("./plugins/cron/events.lua","r")
local data = fhandler:read("*a") local data = fhandler:read("*a")
fhandler:close() fhandler:close()
local eventfunc = load(data,"event loader: "..plugin_path.."/events.lua",nil,setmetatable({ local eventfunc = load(data,"event loader: ./plugins/cron/events.lua",nil,setmetatable({
id = id, id = id,
event_emitter = event_emitter, client = client,
exec = exec, exec = exec,
events = events, events = events,
config = config, config = config
discordia = discordia
},{__index = _G})) },{__index = _G}))
eventfunc() eventfunc()
timer:start(true)
plugin:load_helpdb(plugin_path.."help.lua") timer:start(true)
return plugin return plugin

View File

@ -1,6 +0,0 @@
return {
["save"] = "Force-save config data",
["error"] = "Force error",
["permerror"] = "Force permission error",
["return_error"] = "Force a return value error",
}

View File

@ -1,32 +1,10 @@
local plugin = import("classes.plugin")("debug") local plugin = import("classes.plugin")("debug")
local command = import("classes.command") local command = import("classes.command")
local save = command("save",{ local save = command("save",{
help = "Force-save config data",
exec = function() exec = function()
server:save_config() server:save_config()
end end
}) })
plugin:add_command(save) plugin:add_command(save)
local err = command("error",{
exec = function()
error("Errored successfully!")
end
})
plugin:add_command(err)
local perm_error = command("permerror",{
users = {
["245973168257368076"] = -1
},
exec = function(msg)
msg:reply([[o no he's hot]])
end
})
plugin:add_command(perm_error)
local return_error = command("return_error",{
exec = function(msg)
msg:reply("nono :)")
return false
end
})
plugin:add_command(return_error)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -4,110 +4,112 @@ local plugin_class = import("classes.plugin")
local command = import("classes.command") local command = import("classes.command")
local plugin = plugin_class() local plugin = plugin_class()
local settings = { local settings = {
tapesize = 30000, tapesize = 30000,
cellsize = 1, cellsize = 1,
debug = true, debug = true,
limit = 500000 limit = 500000
} }
c_brainfuck = command("brainfuck",{ c_brainfuck = command("brainfuck",{
category = "Miscellaneous", args = {
args = { "string"
"string" },
}, exec = function(msg,args,opts)
exec = function(msg,args,opts) settings.load_extensions = {}
settings.load_extensions = {} settings.path = "./lib/brainfuck/"
settings.path = "./lib/brainfuck/" local instance = brainfuck.new(args[1],settings)
local instance = brainfuck.new(args[1],settings) local result,opcount,err = instance:run(args[2] or "")
local result,opcount,err = instance:run(args[2] or "") if result == "" then
if result == "" then result = ""
result = "" end
end if not err then
if not err then if opts["o"] or opts["output-only"] then
if opts["o"] or opts["output-only"] then msg:reply(tostring(result):gsub("@","\\@"))
msg:reply(tostring(result):gsub("@","\\@")) else
else msg:reply({ embed = {
msg:reply({ embed = { title = "Result:",
title = "Result:", color = discordia.Color.fromHex("#32cd32").value,
color = discordia.Color.fromHex("#32cd32").value, description = "```"..tostring(result):gsub("`","\\`").." ```",
description = "```"..tostring(result):gsub("`","\\`").." ```", footer = {
footer = { text = "Finished in "..opcount.." operations"
text = "Finished in "..opcount.." operations" }
} }})
}})
end
else
msg:reply({
embed = {
title = "Error:",
description = "```"..tostring(err).." ```",
color = discordia.Color.fromHex("#32cd32").value,
}
})
end
end end
else
msg:reply({
embed = {
title = "Error:",
description = "```"..tostring(err).." ```",
color = discordia.Color.fromHex("#32cd32").value,
}
})
end
end
}) })
plugin:add_command(c_brainfuck) plugin:add_command(c_brainfuck)
c_befunge = command("befunge",{ c_befunge = command("befunge",{
category = "Miscellaneous", args = {
args = { "string"
"string" },
}, exec = function(msg,args,opts)
exec = function(msg,args,opts) local code = msg.content:match("```(.+)```")
local code = msg.content:match("```(.+)```") if not code then
if not code then msg:reply("Invalid syntax")
msg:reply("Invalid syntax") return
return end
end local input = msg.content:match("```.+``` ?(.+)") or ""
local input = msg.content:match("```.+``` ?(.+)") or "" local stdout = ""
local stdout = "" local stderr = ""
local stderr = "" befunge:init(code,{
befunge:init(code,{ opcount = 10000,
opcount = 10000, handle_int_input = function()
handle_int_input = function() local int = input:match("^%d+")
local int = input:match("^%d+") if not int then
if not int then return
return end
end input = input:gsub("^%d+","",1)
input = input:gsub("^%d+","",1) return tonumber(int)
return tonumber(int) end,
end, handle_input = function()
handle_input = function() local char = input:sub(1,1)
local char = input:sub(1,1) if not char then
if not char then return
return end
end input = input:sub(2,-1)
input = input:sub(2,-1) return string.byte(char)
return string.byte(char) end,
end, handle_output = function(char)
handle_output = function(char) stdout = stdout..char
stdout = stdout..char end,
end, handle_warning = function(warn)
handle_warning = function(warn) stderr = stderr.."[warning] "..warn.."\n"
stderr = stderr.."[warning] "..warn.."\n" end,
end, handle_error = function(error)
handle_error = function(error) stderr = stderr.."[error] "..error.."\n"
stderr = stderr.."[error] "..error.."\n" befunge.interpreter_state = false
befunge.interpreter_state = false
end
})
local opcount = befunge:run()
if opts["o"] or opts["output-only"] then
msg:reply(tostring(stdout):gsub("@","\\@"))
else
msg:reply({embed = {
title = "Result: ",
color = discordia.Color.fromHex("#32cd32").value,
fields = {
{name = "out",value = "```"..stdout:gsub("`","\\`").." ```"},
{name = "err",value = "```"..stderr.." ```"}
},
footer = {
text = "Finished in "..opcount.." operations"
}
}})
end
end end
})
local opcount = befunge:run()
if opts["o"] or opts["output-only"] then
msg:reply(tostring(stdout):gsub("@","\\@"))
else
msg:reply({embed = {
title = "Result: ",
color = discordia.Color.fromHex("#32cd32").value,
fields = {
{name = "out",value = "```"..stdout:gsub("`","\\`").." ```"},
{name = "err",value = "```"..stderr.." ```"}
},
footer = {
text = "Finished in "..opcount.." operations"
}
}})
end
end
}) })
plugin:add_command(c_befunge) plugin:add_command(c_befunge)
plugin:load_helpdb(plugin_path.."help.lua") local helpdb = import(plugin_path:sub(3,-1).."help")
plugin:for_all_commands(function(command)
command:set_help(helpdb[command.name])
end)
return plugin return plugin

View File

@ -1,11 +0,0 @@
return {
["help"] = {embed={
title = "View help embeds for commands and plugins",
description = "To specify if it's a plugin or a command, simply add the option accordingly",
fields = {
{name = "Usage:",value = "help [<command> or --plugin <plugin>]"},
{name = "Perms:",value = "any"},
{name = "Options:",value = "--plugin"}
}
}}
}

View File

@ -1,49 +1,97 @@
local pluginc = import("classes.plugin") local pluginc = import("classes.plugin")
local command = import("classes.command") local command = import("classes.command")
local plugin = pluginc("help") local plugin = pluginc("help")
local color = discordia.Color.fromHex math.randomseed(os.time()+os.clock())
local help_message
local function randomize_stuff()
local chance = math.random(1,100)
if chance < 10 then
help_message = [[
This button here, builds Teleporters. This button, builds Dispensers.
And this little button makes them enemy sum-bitches wish they'd never been born!
--the inspiration behind this bot's design ]]
elseif chance >= 10 and chance < 90 then
help_message = [[
This plugin provides the help command, which can view help messages for plugins and commands
]]
else
help_message = [[
see the invisible
do the impossible
row row
fight da powah
]]
end
end
local function count(tab)
local count = 0
for k,v in pairs(tab) do
count = count+1
end
return count
end
local function concatenate_keys(tab)
local key_list = {}
for k,v in pairs(tab) do
table.insert(key_list,k)
end
return "``"..table.concat(key_list,"``,\n``").."``"
end
local help_command = command("help",{ local help_command = command("help",{
category = "Utilities", help = {embed={
title = "View help embeds for commands and plugins",
description = "To specify if it's a plugin or a command, simply add the option accordingly",
fields = {
{name = "Usage:",value = "help [<command> or --plugin <plugin>]"},
{name = "Perms:",value = "any"},
{name = "Options:",value = "--plugin"}
}
}},
exec = function(msg,args,opts) exec = function(msg,args,opts)
local embed = { randomize_stuff()
color = color("32b3bc").value local embed = {
} color = discordia.Color.fromHex("32b3bc").value
if args[1] then }
if not opts["plugin"] then if args[1] then
if command_handler:get_command(args[1]) then if count(opts) < 1 then
local command = command_handler:get_command(args[1]) if command_handler:get_command(args[1]) then
embed = command:get_help().embed local command = command_handler:get_command(args[1])
else embed = command:get_help().embed
embed.description = "No such command: "..args[1] else
embed.color = color("990000").value embed.description = "No such command"
end end
else elseif (opts["plugin"]) then
local meta = command_handler:get_metadata() --[[ if plugin_data["plugins"] [args[1] ] then
local comms = meta.plugins[args[1]] embed.title = "Plugin ``"..args[1].."``:"
if not comms then embed.description = plugin_data["plugins"] [args[1] ]["_help"]
embed.description = "Unable to find plugin: "..args[1] embed.fields = {{
embed.color = color("990000").value name = "Commands:",
else value ="``"..table.concat(plugin_data["plugins"] [args[1] ],"``,\n``").."``"
embed.title = "Plugin ``"..args[1].."``" }}
embed.description = "``"..table.concat(comms,"``, ``").."``" else
end embed.description = "No such plugin"
end end
else --]]
local meta = command_handler:get_metadata() embed.title = "Not yet implemented"
embed.title = "512mb.org commands:" embed.description = "Check again later"
embed.description = "use ``help <command>`` to view help messages. (type ``help help`` for more info)"
embed.fields = {}
for name,category in pairs(meta.categories) do
table.insert(embed.fields,{
name = name,
value = "``"..table.concat(category,"``, ``").."``"
})
end
end end
msg:reply({embed = embed}) else
embed.title = "512mb.org commands:"
embed.description = "use ``help <command>`` to view help messages. (type ``help help`` for more info)"
embed.fields = {}
for k,v in pairs(command_handler:get_commands_metadata().plugins) do
table.insert(embed.fields,{
name = k,
value = "``"..table.concat(v,"``, ``").."``"
})
end
end
msg:reply({embed = embed})
end, end,
}) })
plugin:add_command(help_command) plugin:add_command(help_command)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -1,9 +1,9 @@
return { return {
["prefix"] = {embed={ ["prefix"] = {embed={
title = "Add/delete/list prefixes", title = "Set or view current prefix for this bot",
description = "Multiple prefixes are possible", description = "If you're not sure what's the current prefix, just ping the bot",
fields = { fields = {
{name = "Usage:",value = "prefix [(add | remove | list (default)) [<new prefix>]]"}, {name = "Usage:",value = "prefix [<new prefix> or \"<new prefix>\"]"},
{name = "Perms:",value = "Administrator"}, {name = "Perms:",value = "Administrator"},
} }
}}, }},
@ -16,10 +16,9 @@ return {
``alias !hi "!speak Hello!"`` - reply to !hi with "Hello!" using speak command ``alias !hi "!speak Hello!"`` - reply to !hi with "Hello!" using speak command
``alias !say "!speak ..."`` - reply to !hi with everything typed after !hi ``alias !say "!speak ..."`` - reply to !hi with everything typed after !hi
``alias !say "!speak $1"`` - reply to !hi with the first argument sent along with !hi ``alias !say "!speak $1"`` - reply to !hi with the first argument sent along with !hi
More at https://github.com/512mb-xyz/512mb.org-bot/wiki/Aliases]] More at https://github.com/yessiest/SuppaBot/wiki/Tasks]]
}, },
{name = "Perms: ",value = "Administrator (doesn't apply to created aliases)"}, {name = "Perms: ",value = "Administrator (doesn't apply to created aliases)"}
{name = "Opts: ",value = "`-p` - bind the command to not use a prefix\n`--description=\"your description here\"` - add a description to alias"}
} }
}}, }},
["unalias"] = {embed = { ["unalias"] = {embed = {

View File

@ -4,14 +4,13 @@ local last_message_arrived = discordia.Stopwatch()
local unixToString = import("unixToString") local unixToString = import("unixToString")
local command = import("classes.command") local command = import("classes.command")
local plugin = import("classes.plugin")("meta") local plugin = import("classes.plugin")("meta")
local purify = import("purify")
if not config.aliases then if not config.aliases then
config.aliases = {} config.aliases = {}
end end
client:on("messageCreate",function(msg) client:on("messageCreate",function(msg)
last_message_arrived:reset() last_message_arrived:reset()
last_message_arrived:start() last_message_arrived:start()
end) end)
local prefix local prefix
@ -22,257 +21,268 @@ for k,v in pairs(command_handler:get_prefixes()) do
end end
local function add_alias(name,comm,prefix,description) local function add_alias(name,comm,prefix,description)
if (not aliases[name]) then if (not aliases[name]) then
log("ALIAS","Adding alias \""..name.."\" for \""..comm.."\"") print("[ALIAS] Adding alias \""..name.."\" for \""..comm.."\"")
config.aliases[name] = {comm = comm,prefix = (prefix == nil)} config.aliases[name] = {comm = comm,prefix = prefix}
aliases[name] = command(name,{ aliases[name] = command(name,{
help = "Alias for ``"..comm.."``", help = "Alias for ``"..comm.."``",
usage = ((prefix and globals.prefix) or "")..name, usage = ((prefix and globals.prefix) or "")..name,
category = "Aliases", exec = function(msg,args2,opts)
exec = function(msg,args2,opts) print("[ALIAS] Triggerting alias "..tostring(comm).." with args \""..tostring(msg.content).."\"")
local str = msg.content:gsub("^%S+ ?","") local str = msg.content:gsub("^%S+ ?","")
aftersub = comm:gsub("%.%.%.",str or "") aftersub = comm:gsub("%.%.%.",str or "")
local status,args = require("air").parse(str) aftersub = aftersub:gsub("%$prefix",prefix or "&")
for k,v in pairs(args) do local status,args = require("air").parse(str)
aftersub = aftersub:gsub("([^\\])%$"..k,"%1"..v) for k,v in pairs(args) do
end aftersub = aftersub:gsub("([^\\])%$"..k,"%1"..v)
log("ALIAS", "Triggering alias "..name.." with args \""..aftersub.."\"") end
command_handler:handle(fake_message(msg,{ command_handler:handle(fake_message(msg,{
content = aftersub content = aftersub
})) }))
end, end,
options = { options = {
prefix = (prefix == nil), prefix = prefix,
custom = true custom = true
} }
}) })
plugin:add_command(aliases[name]) plugin:add_command(aliases[name])
return true return true
else else
return false return false
end end
end end
local function remove_alias(name) local function remove_alias(name)
if config.aliases[name] then if config.aliases[name] then
config.aliases[name] = nil config.aliases[name] = nil
plugin:remove_command(aliases[name]) plugin:remove_command(aliases[name])
aliases[name] = nil aliases[name] = nil
return true return true
else else
return false return false
end
end
local function purify_strings(msg,input)
local text = input
while text:match("<@(%D*)(%d*)>") do
local obj,id = text:match("<@(%D*)(%d*)>")
local substitution = ""
if obj:match("!") then
local member = msg.guild:getMember(id)
if member then
substitution = "@"..member.name
end
elseif obj:match("&") then
local role = msg.guild:getRole(id)
if role then
substitution = "@"..role.name
end
end end
if substitution == "" then
substitution = "<\\@"..obj..id..">"
end
text = text:gsub("<@(%D*)"..id..">",substitution)
end
text = text:gsub("@everyone","")
text = text:gsub("@here","")
return text
end end
for k,v in pairs(config.aliases) do for k,v in pairs(config.aliases) do
commdata = v commdata = v
if type(v) == "string" then --legacy format conversion if type(v) == "string" then --legacy format conversion
commdata = {comm = v, prefix = false} commdata = {comm = v, prefix = false}
end end
add_alias(k,commdata.comm,commdata.prefix) add_alias(k,commdata.comm,commdata.prefix)
end end
local prefix = command("prefix",{ local prefix = command("prefix",{
perms = { help = "Set prefix",
"administrator" usage = "prefix [(add | remove | list (default)) [<new prefix>]]",
}, users = {
category = "Utilities", [client.owner.id] = 1
exec = function(msg,args,opts) },
local function list_prefixes(msg) roles = {
local prefixes = "" ["747042157185073182"] = 1
for k,v in pairs(command_handler:get_prefixes()) do },
prefixes = prefixes.."``"..v:gsub("`","\\`").."``".."\n" perms = {
end "administrator"
msg:reply({embed = { },
title = "Prefixes for this server", exec = function(msg,args,opts)
description = prefixes local function list_prefixes(msg)
}}) local prefixes = ""
end for k,v in pairs(command_handler:get_prefixes()) do
if args[1] then prefixes = prefixes..v.."\n"
if args[1] == "add" and args[2] then end
command_handler:add_prefix(args[2]) msg:reply({embed = {
msg:reply("Added ``"..args[2]:gsub("`","\\`").."`` as a prefix") title = "Prefixes for this server",
elseif args[1] == "remove" and args[2] then description = prefixes
local status,err = command_handler:remove_prefix(args[2]) }})
if status then
msg:reply("Removed the ``"..args[2]:gsub("`","\\`").."`` prefix")
else
msg:reply(err)
end
elseif args[1] == "list" then
list_prefixes(msg)
else
msg:reply("Syntax error")
end
else
list_prefixes(msg)
end
end end
if args[1] then
if args[1] == "add" and args[2] then
command_handler:add_prefix(args[2])
msg:reply("Added "..args[2].." as a prefix")
elseif args[1] == "remove" and args[2] then
local status,err = command_handler:remove_prefix(args[2])
if status then
msg:reply("Removed the "..args[2].." prefix")
else
msg:reply(err)
end
elseif args[1] == "list" then
list_prefixes(msg)
else
msg:reply("Syntax error")
end
else
list_prefixes(msg)
end
end
}) })
plugin:add_command(prefix) plugin:add_command(prefix)
local c_alias = command("alias", { local c_alias = command("alias", {
args = { args = {
"string","string" "string","string"
}, },
perms = { perms = {
"administrator" "administrator"
}, },
category = "Automation", exec = function(msg,args,opts)
exec = function(msg,args,opts) if add_alias(args[1],args[2],(opts["prefix"] or opts["p"]),opts["description"]) then
if add_alias(args[1],args[2],(opts["prefix"] or opts["p"]),opts["description"]) then msg:reply("Bound ``"..args[1].."`` as an alias to ``"..args[2].."``")
msg:reply("Bound ``"..args[1].."`` as an alias to ``"..args[2].."``") else
else msg:reply("``"..args[1].."`` is already bound")
msg:reply("``"..args[1].."`` is already bound")
end
end end
end
}) })
plugin:add_command(c_alias) plugin:add_command(c_alias)
local c_unalias = command("unalias", { local c_unalias = command("unalias", {
args = { args = {
"string" "string"
}, },
perms = { perms = {
"administrator" "administrator"
}, },
category = "Automation", exec = function(msg,args,opts)
exec = function(msg,args,opts) if remove_alias(args[1]) then
if remove_alias(args[1]) then msg:reply("Removed the ``"..args[1].."`` alias")
msg:reply("Removed the ``"..args[1].."`` alias") else
else msg:reply("No such alias")
msg:reply("No such alias")
end
end end
end
}) })
plugin:add_command(c_unalias) plugin:add_command(c_unalias)
local c_aliases = command("aliases", { local c_aliases = command("aliases", {
category = "Automation", exec = function(msg,args,opts)
exec = function(msg,args,opts) msg:reply({embed = {
local page = (tonumber(args[1]) or 1)*5 title = "Aliases for this server",
local events = (function() fields = (function()
local fields = {} local fields = {}
for k,v in pairs(config.aliases) do for k,v in pairs(config.aliases) do
table.insert(fields,{k,v}) table.insert(fields,{name = ((v["prefix"] and prefix) or "")..k,value = v["comm"]})
end end
return fields return fields
end)() end)()
msg:reply({embed = { }})
title = "Aliases for this server", end
fields = (function()
local fields = {}
for i = page-4,page do
if not events[i] then break end
table.insert(fields,{
name = events[i][1],
value = events[i][2].comm
})
end
return fields
end)(),
footer = {
text = "Aliases "..tostring(page-4).." - "..tostring(page).." | Total: "..tostring(#events)
}
}})
end
}) })
plugin:add_command(c_aliases) plugin:add_command(c_aliases)
local c_ping = command("ping", { local c_ping = command("ping", {
category = "Utilities", exec = function(msg,args,opts)
exec = function(msg,args,opts) local before = msg:getDate()
local before = msg:getDate() local reply = msg:reply("Pong!")
local reply = msg:reply("Pong!") if not reply then
if not reply then log("ERROR","Couldn't send the ping reply for some reason")
log("ERROR","Couldn't send the ping reply for some reason") return
return
end
local after = reply:getDate()
local latency = (after:toMilliseconds() - before:toMilliseconds())
last_message_arrived:stop()
local uptime = discordia.Date():toSeconds() - server.uptime:toSeconds()
local processing = (last_message_arrived:getTime():toMilliseconds())
msg:reply({embed = {
title = "Stats:",
fields = {
{name = "Latency",value = tostring(math.floor(latency)).."ms"},
{name = "Processing time",value = tostring(math.floor(processing)).."ms"},
{name = "Uptime",value = tostring(unixToString(uptime))}
}
}})
end end
local after = reply:getDate()
local latency = (after:toMilliseconds() - before:toMilliseconds())
last_message_arrived:stop()
local uptime = discordia.Date():toSeconds() - server.uptime:toSeconds()
local processing = (last_message_arrived:getTime():toMilliseconds())
msg:reply({embed = {
title = "Stats:",
fields = {
{name = "Latency",value = tostring(math.floor(latency)).."ms"},
{name = "Processing time",value = tostring(math.floor(processing)).."ms"},
{name = "Uptime",value = tostring(unixToString(uptime))}
}
}})
end
}) })
plugin:add_command(c_ping) plugin:add_command(c_ping)
local c_about = command("about", { local c_about = command("about", {
category = "Miscellaneous", exec = function(msg,args,opts)
exec = function(msg,args,opts)
local rand = math.random local rand = math.random
local author = client:getUser("245973168257368076") local author = client:getUser("245973168257368076")
msg:reply({embed = { msg:reply({embed = {
title = "About 512mb.org bot", title = "About 512mb.org bot",
thumbnail = { thumbnail = {
url = client.user:getAvatarURL() url = client.user:getAvatarURL()
}, },
color = discordia.Color.fromRGB(rand(50,200),rand(50,200),rand(50,200)).value, color = discordia.Color.fromRGB(rand(50,200),rand(50,200),rand(50,200)).value,
description = "512mb.org is an open-source bot written in Lua. It is based on a beta rewrite version of the Suppa-Bot.", description = "512mb.org is an open-source bot written in Lua. It is based on a beta rewrite version of the Suppa-Bot.",
fields = { fields = {
{name = "Source Code: ",value = "https://github.com/512mb-xyz/512mb.org-bot"}, {name = "Source Code: ",value = "https://github.com/512mb-xyz/512mb.org-bot"},
{name = "Author: ",value = author.tag}, {name = "Author: ",value = author.tag},
{name = "Invite: ",value = "Not available yet"} {name = "Invite: ",value = "Not available yet"}
}, },
footer = { footer = {
text = "For any information regarding the bot, contact yessiest on 512mb.org discord." text = "For any information regarding the bot, contact yessiest on 512mb.org discord."
} }
}}) }})
end end
}) })
plugin:add_command(c_about) plugin:add_command(c_about)
local c_server = command("server", { local c_server = command("server", {
category = "Utilities", exec = function(msg,args,opts)
exec = function(msg,args,opts)
msg:reply({embed = { msg:reply({embed = {
thumbnail = { thumbnail = {
url = msg.guild.iconURL url = msg.guild.iconURL
}, },
title = msg.guild.name, title = msg.guild.name,
description = msg.guild.description, description = msg.guild.description,
fields = { fields = {
{name = "Members",value = msg.guild.totalMemberCount}, {name = "Members",value = msg.guild.totalMemberCount},
{name = "Owner",value = (msg.guild.owner and msg.guild.owner.user.tag..":"..msg.guild.owner.user.id) or msg.guild.ownerId}, {name = "Owner",value = (msg.guild.owner and msg.guild.owner.user.tag..":"..msg.guild.owner.user.id) or msg.guild.ownerId},
{name = "Created At",value = os.date("!%c",msg.guild.createdAt).." (UTC+0)"}, {name = "Created At",value = os.date("!%c",msg.guild.createdAt).." (UTC+0)"},
{name = "Text Channels",value = msg.guild.textChannels:count()}, {name = "Text Channels",value = msg.guild.textChannels:count()},
{name = "Voice Channels",value = msg.guild.voiceChannels:count()} {name = "Voice Channels",value = msg.guild.voiceChannels:count()}
} }
}}) }})
end, end,
}) })
plugin:add_command(c_server) plugin:add_command(c_server)
local c_user = command("user", { local c_user = command("user", {
category = "Utilities", exec = function(msg,args,opts)
exec = function(msg,args,opts)
local member = msg.guild:getMember((args[1] or ""):match("%d+")) or msg.guild:getMember(msg.author.id) local member = msg.guild:getMember((args[1] or ""):match("%d+")) or msg.guild:getMember(msg.author.id)
local roles = "" local roles = ""
for k,v in pairs(member.roles) do for k,v in pairs(member.roles) do
roles = roles..v.mentionString.."\n" roles = roles..v.mentionString.."\n"
end end
msg:reply({embed = { msg:reply({embed = {
title = member.user.tag..":"..member.user.id, title = member.user.tag..":"..member.user.id,
thumbnail = { thumbnail = {
url = member.user:getAvatarURL() url = member.user:getAvatarURL()
}, },
fields = { fields = {
{name = "Profile Created At",value = os.date("!%c",member.user.createdAt).." (UTC+0)"}, {name = "Profile Created At",value = os.date("!%c",member.user.createdAt).." (UTC+0)"},
{name = "Joined At",value = os.date("!%c",discordia.Date.fromISO(member.joinedAt):toSeconds()).." (UTC+0)",inline = true}, {name = "Joined At",value = os.date("!%c",discordia.Date.fromISO(member.joinedAt):toSeconds()).." (UTC+0)",inline = true},
{name = "Boosting",value = ((member.premiumSince and "Since "..member.premiumSince) or "No"),inline = true}, {name = "Boosting",value = ((member.premiumSince and "Since "..member.premiumSince) or "No"),inline = true},
{name = "Highest Role",value = member.highestRole.mentionString,inline = true}, {name = "Highest Role",value = member.highestRole.mentionString,inline = true},
{name = "Roles",value = roles,inline = true} {name = "Roles",value = roles,inline = true}
} }
}}) }})
end, end,
}) })
plugin:add_command(c_user) plugin:add_command(c_user)
@ -280,9 +290,11 @@ local c_speak = command("speak", {
args = { args = {
"string" "string"
}, },
category = "Utilities",
exec = function(msg,args,opts) exec = function(msg,args,opts)
local text = purify.purify_pings(msg, table.concat(args," ")) local text = purify_strings(msg, table.concat(args," "))
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
msg:reply(text) msg:reply(text)
msg:delete() msg:delete()
end, end,
@ -290,83 +302,62 @@ local c_speak = command("speak", {
plugin:add_command(c_speak) plugin:add_command(c_speak)
local c_adminSpeak = command("adminSpeak", { local c_adminSpeak = command("adminSpeak", {
category = "Utilities", args = {
args = { "string"
"string" },
}, exec = function(msg,args,opts)
exec = function(msg,args,opts) local text = table.concat(args," ")
local text = table.concat(args," ") if opts["unescape"] or opts["u"] then
msg:reply(text) text = text:gsub("\\","")
msg:delete() end
end, msg:reply(text)
perms = { msg:delete()
"mentionEveryone" end,
} perms = {
"mentionEveryone"
}
}) })
plugin:add_command(c_adminSpeak) plugin:add_command(c_adminSpeak)
local c_adminSendToChannel = command("adminSendToChannel", {
category = "Utilities",
args = {
"textChannel",
"string"
},
exec = function(msg,args,opts)
local channel = args[1]
table.remove(args,1)
local text = table.concat(args," ")
channel:send(text)
end,
perms = {
"mentionEveryone"
}
})
plugin:add_command(c_adminSendToChannel)
local c_echo = command("echo",{ local c_echo = command("echo",{
category = "Utilities",
args = { args = {
"string" "string"
}, },
exec = function(msg,args,opts) exec = function(msg,args,opts)
local text = purify.purify_pings(msg, table.concat(args," ")) local text = purify_strings(msg, table.concat(args," "))
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
msg:reply(text) msg:reply(text)
end, end,
}) })
plugin:add_command(c_echo) plugin:add_command(c_echo)
local c_sendToChannel = command("sendToChannel",{
category = "Utilities",
args = {
"textChannel",
"string"
},
exec = function(msg,args,opts)
local channel = args[1]
table.remove(args,1)
local text = purify.purify_pings(msg, table.concat(args," "))
channel:send(text)
end,
})
plugin:add_command(c_sendToChannel)
local c_pingself = command("pingself",{ local c_pingself = command("pingself",{
category = "Utilities",
args = { args = {
"string" "string"
}, },
exec = function(msg,args,opts) exec = function(msg,args,opts)
local text = purify.purify_pings(msg, table.concat(args," ")) local text = purify_strings(msg, table.concat(args," "))
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
msg:reply("<@"..tostring(msg.member.id).."> "..text) msg:reply("<@"..tostring(msg.member.id).."> "..text)
end, end,
}) })
plugin:add_command(c_pingself) plugin:add_command(c_pingself)
plugin.removal_callback = function() plugin.removal_callback = function()
for k,v in pairs(config.aliases) do for k,v in pairs(config.aliases) do
remove_alias(k) remove_alias(k)
end end
end end
plugin:load_helpdb(plugin_path.."help.lua") local helpdb = import(plugin_path:sub(3,-1).."help")
plugin:for_all_commands(function(command)
if helpdb[command.name] then
command:set_help(helpdb[command.name])
end
end)
return plugin return plugin

View File

@ -1,32 +0,0 @@
local discordia = require('discordia')
return {
["enable"] = {embed = {
title = "Enable plugin",
description = [[This command loads a plugin,
addng its commands to the command pool]],
fields = {
{name = "Usage:",value = "load <plugin-name>"},
{name = "Perms:",value = "Administrator, other (via ``rules --allow``)"}
},
color = discordia.Color.fromHex("ff5100").value
}},
["disable"] = {embed = {
title = "Disable a loaded plugin",
description = [[This commands unloads a previously loaded plugin,
removing its commands from the command pool]],
fields = {
{name = "Usage:",value = "unload <plugin-name>"},
{name = "Perms:",value = "Administrator, other (via ``rules --allow``)"}
},
color = discordia.Color.fromHex("ff5100").value
}},
["plugins"] = {embed = {
title = "View all known plugins",
description = [[This commmand prints info on loaded and unloaded plugins]],
fields = {
{name = "Usage:",value = "plugins"},
{name = "Perms:",value = "Administrator, other (via ``rules --allow``)"}
},
color = discordia.Color.fromHex("ff5100").value
}},
}

View File

@ -4,83 +4,107 @@ local plugin = plugin_c("pluginmanager")
local utilities = require("table-utils") local utilities = require("table-utils")
local generic_admin_template = { local generic_admin_template = {
args = {"string"}, args = {"string"},
perms = { perms = {
"administrator" "administrator"
}, },
} }
local enable = command("enable",utilities.overwrite(generic_admin_template,{ local enable = command("enable",utilities.overwrite(generic_admin_template,{
category = "Utilities", help = {embed = {
title = "Enable plugin",
description = [[This command loads a plugin,
adding its commands to the command pool]],
fields = {
{name = "Usage:",value = "load <plugin-name>"},
{name = "Perms:",value = "Administrator, other (via ``rules --allow``)"}
},
color = discordia.Color.fromHex("ff5100").value
}},
exec = function(msg,args,opts) exec = function(msg,args,opts)
local status,message = plugin_handler:load(args[1]) local status,message = plugin_handler:load(args[1])
local plugin_data = command_handler:get_metadata().plugins local plugin_data = command_handler:get_commands_metadata().plugins
local embed = { local embed = {
description = message, description = message,
color = discordia.Color.fromHex("ff5100").value, color = discordia.Color.fromHex("ff5100").value,
}
if status then
embed.fields = {
{name = "New commands:",value =
table.concat(plugin_data[args[1]] or {},", ").." "
}
} }
if status then end
embed.fields = { msg:reply({embed = embed})
{name = "New commands:",value =
table.concat(plugin_data[args[1]] or {},", ").." "
}
}
end
msg:reply({embed = embed})
end end
})) }))
plugin:add_command(enable) plugin:add_command(enable)
local disable = command("disable",utilities.overwrite(generic_admin_template,{ local disable = command("disable",utilities.overwrite(generic_admin_template,{
category = "Utilities", help = {embed = {
title = "Disable a loaded plugin",
description = [[This commands unloads a previously loaded plugin,
removing its commands from the command pool]],
fields = {
{name = "Usage:",value = "unload <plugin-name>"},
{name = "Perms:",value = "Administrator, other (via ``rules --allow``)"}
},
color = discordia.Color.fromHex("ff5100").value
}},
exec = function(msg,args,opts) exec = function(msg,args,opts)
local plugin_data = command_handler:get_metadata().plugins local plugin_data = command_handler:get_commands_metadata().plugins
if not (args[1] == "plugins") then if not (args[1] == "plugins") then
local status,message = plugin_handler:unload(args[1]) local status,message = plugin_handler:unload(args[1])
local embed = { local embed = {
description = message, description = message,
color = discordia.Color.fromHex("ff5100").value, color = discordia.Color.fromHex("ff5100").value,
}
if status then
embed.fields = {
{name = "Removed commands:",value =
table.concat(plugin_data[args[1]] or {},", ").." "
} }
if status then }
embed.fields = {
{name = "Removed commands:",value =
table.concat(plugin_data[args[1]] or {},", ").." "
}
}
end
msg:reply({embed = embed})
else
msg:reply("TIME PARADOX")
end end
msg:reply({embed = embed})
else
msg:reply("TIME PARADOX")
end
end end
})) }))
plugin:add_command(disable) plugin:add_command(disable)
local plugins = command("plugins",utilities.overwrite(generic_admin_template,{ local plugins = command("plugins",utilities.overwrite(generic_admin_template,{
category = "Utilities", help = {embed = {
args = {}, title = "View all known plugins",
exec = function(msg,args,opts) description = [[This commmand prints info on loaded and unloaded plugins]],
local all_plugins = plugin_handler:list_loadable() fields = {
local unloaded_plugins = {} {name = "Usage:",value = "plugins"},
local loaded_plugins = {} {name = "Perms:",value = "Administrator, other (via ``rules --allow``)"}
for k,v in pairs(all_plugins) do }
if not v.loaded then }},
table.insert(unloaded_plugins,k) args = {},
else exec = function(msg,args,opts)
table.insert(loaded_plugins,k) local all_plugins = plugin_handler:list_loadable()
end local unloaded_plugins = {}
end local loaded_plugins = {}
if #unloaded_plugins == 0 then for k,v in pairs(all_plugins) do
table.insert(unloaded_plugins," ") if not v.loaded then
end table.insert(unloaded_plugins,k)
msg:reply({embed={ else
color = discordia.Color.fromHex("ff5100").value, table.insert(loaded_plugins,k)
fields = {
{name = "Loaded plugins",value = "``"..table.concat(loaded_plugins,"``,\n``").."``"},
{name = "Unloaded plugins",value = "``"..table.concat(unloaded_plugins,"``,\n``").."``"}
}
}})
end end
end
if #unloaded_plugins == 0 then
table.insert(unloaded_plugins," ")
end
msg:reply({embed={
color = discordia.Color.fromHex("ff5100").value,
fields = {
{name = "Loaded plugins",value = "``"..table.concat(loaded_plugins,"``,\n``").."``"},
{name = "Unloaded plugins",value = "``"..table.concat(unloaded_plugins,"``,\n``").."``"}
}
}})
end
})) }))
plugin:add_command(plugins) plugin:add_command(plugins)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -1,42 +0,0 @@
return {
["pivot"] = {embed={
title = "Select a pivot message to manipulate",
description = "Pivot is like a message selector which allows easy reaction manipulations",
fields = {
{name = "Usage: ",value = "pivot <message link>"},
{name = "Perms: ",valeu = "Administartor"}
}
}},
["role-toggle"] = {embed={
title = "Add a simple role switch to the pivot",
description = "Note: you cannot assign more than one role to a single reaction",
fields = {
{name = "Usage: ",value = "role-toggle <emoji> <role ping or role id>"},
{name = "Perms: ",value = "administrator"}
}
}},
["remove-reaction"] = {embed={
title = "Remove a reaction from a pivot",
description = "If you don't specify a reaction to remove, the entire pivot for the message is removed automatically",
fields = {
{name = "Usage: ",value = "remove-reaction <emoji>"},
{name = "Perms: ",value = "Administrator"}
}
}},
["toggle"] = {embed={
title = "Add a toggle that runs specific commands",
description = "Note: you cannot assign more than one action to a single reaction \n``$user`` gets replaced with the id of the user that interacted with the reaction.",
fields = {
{name = "Usage: ",value = "toggle <emoji> <command-on> <command-off>"},
{name = "Perms: ",value = "administrator"}
}
}},
["button"] = {embed={
title = "Add a button that runs specific command when pressed",
description = "Note: you cannot assign more than one action to a single reaction \n``$user`` gets replaced with the id of the user that interacted with the reaction.",
fields = {
{name = "Usage: ",value = "button <emoji> <command>"},
{name = "Perms: ",value = "administrator"}
}
}},
}

View File

@ -7,300 +7,334 @@ local segment = {}
segment.pivots = config segment.pivots = config
local getEmoji = function(id) local getEmoji = function(id)
local emoji = guild:getEmoji(id:match("(%d+)[^%d]*$")) local emoji = guild:getEmoji(id:match("(%d+)[^%d]*$"))
if emoji then if emoji then
return emoji return emoji
else else
return id return id
end end
end end
local function count(tab) local function count(tab)
local n = 0 local n = 0
for k,v in pairs(tab) do for k,v in pairs(tab) do
n = n + 1 n = n + 1
end end
return n return n
end end
local pivot = command("pivot",{ local pivot = command("pivot",{
category = "Automation", help = {embed={
args = { title = "Select a pivot message to manipulate",
"messageLink" description = "Pivot is like a message selector which allows easy reaction manipulations",
}, fields = {
perms = { {name = "Usage: ",value = "pivot <message link>"},
"administrator" {name = "Perms: ",valeu = "Administartor"}
}, }
exec = function(msg,args,opts) }},
if segment.pivot and count(segment.pivot.buttons) == 0 then args = {
log("REACTIONS","Deleting pivot: "..tostring(segment.pivot.message)) "messageLink"
segment.pivots[segment.pivot.message] = nil },
end perms = {
local message = args[1] "administrator"
if not message then },
msg:reply("Couldn't find message with id "..args[2]) exec = function(msg,args,opts)
return false if segment.pivot and count(segment.pivot.buttons) == 0 then
end print("[REACTIONS] Deleting pivot: "..tostring(segment.pivot.message))
if not segment.pivots[message.id] then segment.pivots[segment.pivot.message] = nil
log("REACTIONS","Creating pivot: "..tostring(message.id)) end
segment.pivots[message.id] = {} local message = args[1]
segment.pivots[message.id].message = message.id if not message then
segment.pivots[message.id].channel = message.channel.id msg:reply("Couldn't find message with id "..args[2])
segment.pivots[message.id].buttons = {} return false
end end
segment.pivot = segment.pivots[message.id] if not segment.pivots[message.id] then
return true print("[REACTIONS] Creating pivot: "..tostring(message.id))
end segment.pivots[message.id] = {}
}) segment.pivots[message.id].message = message.id
segment.pivots[message.id].channel = message.channel.id
segment.pivots[message.id].buttons = {}
end
segment.pivot = segment.pivots[message.id]
return true
end
})
plugin:add_command(pivot) plugin:add_command(pivot)
local role_toggle = command("role-toggle",{ local role_toggle = command("role-toggle",{
category = "Automation", help = {embed={
args = { title = "Add a simple role switch to the pivot",
"string", description = "Note: you cannot assign more than one role to a single reaction",
"role", fields = {
}, {name = "Usage: ",value = "role-toggle <emoji> <role ping or role id>"},
perms = { {name = "Perms: ",value = "administrator"}
"administrator" }
}, }},
exec = function(msg,args,opts) args = {
if not segment.pivot then "string",
msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again") "role",
return false },
end perms = {
local emoji = getEmoji(args[1]) "administrator"
local channel = guild:getChannel(segment.pivot.channel) },
if not channel then exec = function(msg,args,opts)
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") if not segment.pivot then
return false msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again")
end return false
local message = channel:getMessage(segment.pivot.message) end
if not message then local emoji = getEmoji(args[1])
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") local channel = guild:getChannel(segment.pivot.channel)
return false if not channel then
end msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
log("REACTIONS","Adding role-toggle listener") return false
local grabEmoji = function(reaction) end
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = { local message = channel:getMessage(segment.pivot.message)
type = "role-toggler", if not message then
role = tostring(args[2].id) msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
} return false
end end
message:removeReaction(emoji,client.user.id) print("[REACTIONS] Adding role-toggle listener")
client:once("reactionAdd",grabEmoji) local grabEmoji = function(reaction)
if not message:addReaction(emoji) then segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
client:removeListener("reactionAdd",grabEmoji) type = "role-toggler",
msg:reply("Couldn't add reaction - emoji might be invalid") role = tostring(args[2].id)
return false }
else end
return true message:removeReaction(emoji,client.user.id)
end client:once("reactionAdd",grabEmoji)
end if not message:addReaction(emoji) then
}) client:removeListener("reactionAdd",grabEmoji)
msg:reply("Couldn't add reaction - emoji might be invalid")
return false
else
return true
end
end
})
plugin:add_command(role_toggle) plugin:add_command(role_toggle)
local remove_reaction = command("remove-reaction",{ local remove_reaction = command("remove-reaction",{
category = "Automation", help = {embed={
perms = { title = "Remove a reaction from a pivot",
"administrator" description = "If you don't specify a reaction to remove, the entire pivot for the message is removed automatically",
}, fields = {
exec = function(msg,args,opts) {name = "Usage: ",value = "remove-reaction <emoji>"},
local channel = guild:getChannel(segment.pivot.channel) {name = "Perms: ",value = "Administrator"}
if not channel then }
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") }},
return false perms = {
end "administrator"
local message = channel:getMessage(segment.pivot.message) },
if not message then exec = function(msg,args,opts)
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") local channel = guild:getChannel(segment.pivot.channel)
return false if not channel then
end msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
log("REACTIONS","Removing reaction listener") return false
if args[1] then end
local emoji = getEmoji(args[1]) local message = channel:getMessage(segment.pivot.message)
message:removeReaction(emoji,client.user.id) if not message then
segment.pivot.buttons[((type(emoji) == "table") and emoji.id) or emoji] = nil msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return true return false
else end
message:clearReactions() print("[REACTIONS] Removing reaction listener")
segment.pivots[tostring(message.id)] = nil if args[1] then
segment.pivot = nil local emoji = getEmoji(args[1])
return true message:removeReaction(emoji,client.user.id)
end segment.pivot.buttons[((type(emoji) == "table") and emoji.id) or emoji] = nil
end return true
}) else
message:clearReactions()
segment.pivots[tostring(message.id)] = nil
segment.pivot = nil
return true
end
end
})
plugin:add_command(remove_reaction) plugin:add_command(remove_reaction)
local toggle = command("toggle",{ local toggle = command("toggle",{
category = "Automation", help = {embed={
args = { title = "Add a toggle that runs specific commands",
"string", description = "Note: you cannot assign more than one action to a single reaction \n``$user`` gets replaced with the id of the user that interacted with the reaction.",
"string", fields = {
"string", {name = "Usage: ",value = "toggle <emoji> <command-on> <command-off>"},
}, {name = "Perms: ",value = "administrator"}
perms = { }
"administrator" }},
}, args = {
exec = function(msg,args,opts) "string",
if not segment.pivot then "string",
msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again") "string",
return false },
end perms = {
local emoji = getEmoji(args[1]) "administrator"
local channel = guild:getChannel(segment.pivot.channel) },
if not channel then exec = function(msg,args,opts)
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") if not segment.pivot then
return false msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again")
end return false
local message = channel:getMessage(segment.pivot.message) end
if not message then local emoji = getEmoji(args[1])
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") local channel = guild:getChannel(segment.pivot.channel)
return false if not channel then
end msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
log("REACTIONS","Adding toggle listener") return false
local grabEmoji = function(reaction) end
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = { local message = channel:getMessage(segment.pivot.message)
type = "toggler", if not message then
on = args[2], msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
off = args[3], return false
} end
end print("[REACTIONS] Adding toggle listener")
message:removeReaction(emoji,client.user.id) local grabEmoji = function(reaction)
client:once("reactionAdd",grabEmoji) segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
if not message:addReaction(emoji) then type = "toggler",
client:removeListener("reactionAdd",grabEmoji) on = args[2],
msg:reply("Couldn't add reaction - emoji might be invalid") off = args[3],
return false }
else end
return true message:removeReaction(emoji,client.user.id)
end client:once("reactionAdd",grabEmoji)
end if not message:addReaction(emoji) then
}) client:removeListener("reactionAdd",grabEmoji)
msg:reply("Couldn't add reaction - emoji might be invalid")
return false
else
return true
end
end
})
plugin:add_command(toggle) plugin:add_command(toggle)
local button = command("button",{ local button = command("button",{
category = "Automation", help = {embed={
args = { title = "Add a button that runs specific command when pressed",
"string", description = "Note: you cannot assign more than one action to a single reaction \n``$user`` gets replaced with the id of the user that interacted with the reaction.",
"string", fields = {
}, {name = "Usage: ",value = "button <emoji> <command>"},
perms = { {name = "Perms: ",value = "administrator"}
"administrator" }
}, }},
exec = function(msg,args,opts) args = {
if not segment.pivot then "string",
msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again") "string",
return false },
end perms = {
local emoji = getEmoji(args[1]) "administrator"
local channel = guild:getChannel(segment.pivot.channel) },
if not channel then exec = function(msg,args,opts)
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") if not segment.pivot then
return false msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again")
end return false
local message = channel:getMessage(segment.pivot.message) end
if not message then local emoji = getEmoji(args[1])
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported") local channel = guild:getChannel(segment.pivot.channel)
return false if not channel then
end msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
log("REACTIONS","Adding button listener") return false
local grabEmoji = function(reaction) end
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = { local message = channel:getMessage(segment.pivot.message)
type = "button", if not message then
on = args[2], msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
} return false
end end
message:removeReaction(emoji,client.user.id) print("[REACTIONS] Adding button listener")
client:once("reactionAdd",grabEmoji) local grabEmoji = function(reaction)
if not message:addReaction(emoji) then segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
client:removeListener("reactionAdd",grabEmoji) type = "button",
msg:reply("Couldn't add reaction - emoji might be invalid") on = args[2],
return false }
else end
return true message:removeReaction(emoji,client.user.id)
end client:once("reactionAdd",grabEmoji)
end if not message:addReaction(emoji) then
}) client:removeListener("reactionAdd",grabEmoji)
msg:reply("Couldn't add reaction - emoji might be invalid")
return false
else
return true
end
end
})
plugin:add_command(button) plugin:add_command(button)
local buttonOn = function(message,hash,userID) local buttonOn = function(message,hash,userID)
if not message then if not message then
log("ERROR","Attempted to find a deleted message") log("ERROR","Attempted to find a deleted message")
return return
end end
if segment.pivots[tostring(message.id)] and userID ~= client.user.id then if segment.pivots[tostring(message.id)] and userID ~= client.user.id then
local current_pivot = segment.pivots[tostring(message.id)] local current_pivot = segment.pivots[tostring(message.id)]
if current_pivot.buttons[tostring(hash)] then if current_pivot.buttons[tostring(hash)] then
local current_button = current_pivot.buttons[tostring(hash)] local current_button = current_pivot.buttons[tostring(hash)]
local new_content local new_content
if current_button.on then if current_button.on then
new_content = current_button.on:gsub("%$user",userID) new_content = current_button.on:gsub("%$user",userID)
end end
if current_button.type == "role-toggler" then if current_button.type == "role-toggler" then
guild:getMember(userID):addRole(current_button.role) guild:getMember(userID):addRole(current_button.role)
end end
if current_button.type == "toggler" then if current_button.type == "toggler" then
command_handler:handle(fake_message(message,{ command_handler:handle(fake_message(message,{
delete = function() end, delete = function() end,
content = new_content content = new_content
})) }))
end end
if current_button.type == "button" then if current_button.type == "button" then
command_handler:handle(fake_message(message,{ command_handler:handle(fake_message(message,{
delete = function() end, delete = function() end,
content = new_content content = new_content
})) }))
end end
end
end end
end
end end
local buttonOff = function(message,hash,userID) local buttonOff = function(message,hash,userID)
if not message then if not message then
log("ERROR","Attempted to find a deleted message") log("ERROR","Attempted to find a deleted message")
return return
end end
if segment.pivots[tostring(message.id)] and userID ~= client.user.id then if segment.pivots[tostring(message.id)] and userID ~= client.user.id then
local current_pivot = segment.pivots[tostring(message.id)] local current_pivot = segment.pivots[tostring(message.id)]
if current_pivot.buttons[tostring(hash)] then if current_pivot.buttons[tostring(hash)] then
local current_button = current_pivot.buttons[tostring(hash)] local current_button = current_pivot.buttons[tostring(hash)]
local new_content local new_content
if current_button.off then if current_button.off then
new_content = current_button.off:gsub("%$user",userID) new_content = current_button.off:gsub("%$user",userID)
end end
if current_button.type == "role-toggler" then if current_button.type == "role-toggler" then
guild:getMember(userID):removeRole(current_button.role) guild:getMember(userID):removeRole(current_button.role)
end end
if current_button.type == "toggler" then if current_button.type == "toggler" then
command_handler:handle(fake_message(message,{ command_handler:handle(fake_message(message,{
delete = function() end, delete = function() end,
content = new_content content = new_content
})) }))
end end
end
end end
end
end end
events:on("reactionAdd",function(reaction,userID) events:on("reactionAdd",function(reaction,userID)
local message = reaction.message local message = reaction.message
local hash = tostring(reaction.emojiId or reaction.emojiName) local hash = tostring(reaction.emojiId or reaction.emojiName)
buttonOn(message,hash,userID) buttonOn(message,hash,userID)
end) end)
events:on("reactionRemove",function(reaction,userID) events:on("reactionRemove",function(reaction,userID)
local message = reaction.message local message = reaction.message
local hash = tostring(reaction.emojiId or reaction.emojiName) local hash = tostring(reaction.emojiId or reaction.emojiName)
buttonOff(message,hash,userID) buttonOff(message,hash,userID)
end) end)
events:on("reactionAddUncached",function(channelId,messageId,hash,userId) events:on("reactionAddUncached",function(channelId,messageId,hash,userId)
local message = client:getChannel(channelId):getMessage(messageId) local message = client:getChannel(channelId):getMessage(messageId)
local hash = tostring(hash) local hash = tostring(hash)
buttonOn(message,hash,userId) buttonOn(message,hash,userId)
end) end)
events:on("reactionRemoveUncached",function(channelId,messageId,hash,userId) events:on("reactionRemoveUncached",function(channelId,messageId,hash,userId)
local message = client:getChannel(channelId):getMessage(messageId) local message = client:getChannel(channelId):getMessage(messageId)
local hash = tostring(hash) local hash = tostring(hash)
buttonOff(message,hash,userId) buttonOff(message,hash,userId)
end) end)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -1,5 +0,0 @@
return {
["droleadd"] = "Add a default role to assign for new users",
["droledel"] = "Remove a role from the list of default roles",
["drolelist"] = "List all default roles",
}

View File

@ -9,19 +9,19 @@ client:on("memberJoin",function(member)
end) end)
local droleadd = command("droleadd",{ local droleadd = command("droleadd",{
category = "Automation", help = "Add a default role to assign for new users",
usage = "droleadd <role>", usage = "droleadd <role>",
perms = {"administrator"}, perms = {"administrator"},
args = { args = {
"role" "role"
}, },
exec = function(msg,args,opts) exec = function(msg,args,opts)
table.insert(config.default_roles,args[1].id) table.insert(config.default_roles,args[1].id)
msg:reply("Added role "..args[1].name.." to default roles list") msg:reply("Added role "..args[1].name.." to default roles list")
end, end,
}) })
local droledel = command("droledel",{ local droledel = command("droledel",{
category = "Automation", help = "Remove a role from the list of default roles",
usage = "droledel <role>", usage = "droledel <role>",
perms = {"administrator"}, perms = {"administrator"},
args = { args = {
@ -37,7 +37,7 @@ local droledel = command("droledel",{
end end
}) })
local drolelist = command("drolelist", { local drolelist = command("drolelist", {
category = "Automation", help = "List all default roles",
usage = "drolelist", usage = "drolelist",
perms = {"administrator"}, perms = {"administrator"},
exec = function(msg,args,opts) exec = function(msg,args,opts)
@ -56,6 +56,5 @@ local drolelist = command("drolelist", {
plugin:add_command(droleadd) plugin:add_command(droleadd)
plugin:add_command(droledel) plugin:add_command(droledel)
plugin:add_command(drolelist) plugin:add_command(drolelist)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -1,64 +0,0 @@
return {
["grant-role"] = {embed={
title = "Grant a role to the user",
description = "If <user> is not provided, the caller is assumed as the <user> argument.",
fields = {
{name = "Usage:",value = "grant-role <role id> [<user>]"},
{name = "Perms:",value = "administrator"},
{name = "Options:",value = "-q - quiet (don't print the result)"}
}
}},
["revoke-role"] = {embed={
title = "Revoke a role from the user",
description = "If <user> is not provided, the caller is assumed as the <user> argument.",
fields = {
{name = "Usage:",value = "revoke-role <role id> [<user>]"},
{name = "Perms:",value = "administrator"},
{name = "Options:",value = "-q - quiet (don't print the result)"}
}
}},
["warn"] = {embed={
title = "Warn a user",
description = "nuff said.",
fields = {
{name = "Usage:",value = "warn <user> <reason>"},
{name = "Perms:",value = "kickMembers"},
}
}},
["infractions"] = { embed = {
title = "List user infractions",
description = "Infractions include kicks, bans, mutes and warnings.",
fields = {
{name = "Usage: ", value = "infractions <user> [<startfrom>]"},
{name = "Perms: ", value = "kickMembers"},
{name = "Options: ", value = "--type=(warn default,ban,kick)"}
}
}},
["purge"] = { embed = {
title = "Purge a number of messages",
description = "nuff said.",
fields = {
{name = "Usage: ", value = "purge <number>"},
{name = "Perms: ", value = "manageMessages"},
{name = "Options: ", value = "`--regex (regex)` - match content against regex; \n`--user (user)` - match user against id/name; \n`-w` - match webhook messages"}
}
}},
["ban"] = { embed = {
title = "Ban members",
description = "nuff said.",
fields = {
{name = "Usage: ", value = "ban <member>"},
{name = "Perms: ", value = "banMembers"},
{name = "Options: ", value = "--reason=\"<reason>\""},
},
}},
["kick"] = { embed = {
title = "Ban members",
description = "nuff said.",
fields = {
{name = "Usage: ", value = "kick <member>"},
{name = "Perms: ", value = "kickMembers"},
{name = "Options: ", value = "--reason=\"<reason>\""},
},
}},
}

View File

@ -17,7 +17,15 @@ CREATE TABLE infractions(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, desc T
end end
local grantrole = command("grant-role",{ local grantrole = command("grant-role",{
category = "Security", help = {embed={
title = "Grant a role to the user",
description = "If <user> is not provided, the caller is assumed as the <user> argument.",
fields = {
{name = "Usage:",value = "grant-role <role id> [<user>]"},
{name = "Perms:",value = "administrator"},
{name = "Options:",value = "-q - quiet (don't print the result)"}
}
}},
perms = { perms = {
"administrator" "administrator"
}, },
@ -33,7 +41,15 @@ local grantrole = command("grant-role",{
plugin:add_command(grantrole) plugin:add_command(grantrole)
local revokerole = command("revoke-role",{ local revokerole = command("revoke-role",{
category = "Security", help = {embed={
title = "Revoke a role from the user",
description = "If <user> is not provided, the caller is assumed as the <user> argument.",
fields = {
{name = "Usage:",value = "revoke-role <role id> [<user>]"},
{name = "Perms:",value = "administrator"},
{name = "Options:",value = "-q - quiet (don't print the result)"}
}
}},
perms = { perms = {
"administrator" "administrator"
}, },
@ -48,36 +64,15 @@ local revokerole = command("revoke-role",{
}) })
plugin:add_command(revokerole) plugin:add_command(revokerole)
local ban = command("ban",{
category = "Security",
perms = {
"banMembers"
},
args = {
"member"
},
exec = function(msg,args,opts)
return args[1]:ban(opts["reason"])
end
})
plugin:add_command(ban)
local kick = command("kick", {
category = "Security",
perms = {
"kickMembers",
},
args = {
"member"
},
exec = function(msg,args,opts)
return args[1]:ban(opts["reason"])
end
})
plugin:add_command(kick)
local warn = command("warn",{ local warn = command("warn",{
category = "Security", help = {embed={
title = "Warn a user",
description = "nuff said.",
fields = {
{name = "Usage:",value = "warn <user> <reason>"},
{name = "Perms:",value = "kickMembers"},
}
}},
perms = { perms = {
"kickMembers" "kickMembers"
}, },
@ -104,7 +99,15 @@ local warn = command("warn",{
plugin:add_command(warn) plugin:add_command(warn)
local infractions = command("infractions", { local infractions = command("infractions", {
category = "Security", help = { embed = {
title = "List user infractions",
description = "Infractions include kicks, bans, mutes and warnings.",
fields = {
{name = "Usage: ", value = "infractions <user> [<startfrom>]"},
{name = "Perms: ", value = "kickMembers"},
{name = "Options: ", value = "--type=(warn default,ban,kick)"}
}
}},
perms = { perms = {
"kickMembers" "kickMembers"
}, },
@ -147,7 +150,15 @@ local infractions = command("infractions", {
plugin:add_command(infractions) plugin:add_command(infractions)
local purge = command("purge",{ local purge = command("purge",{
category = "Security", help = { embed = {
title = "Purge a number of messages",
description = "nuff said.",
fields = {
{name = "Usage: ", value = "purge <number>"},
{name = "Perms: ", value = "manageMessages"},
{name = "Options: ", value = "`--regex (regex)` - match content against regex; \n`--user (user)` - match user against id/name; \n`-w` - match webhook messages"}
}
}},
perms = { perms = {
"manageMessages" "manageMessages"
}, },
@ -199,5 +210,5 @@ local purge = command("purge",{
end end
}) })
plugin:add_command(purge) plugin:add_command(purge)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -1,37 +0,0 @@
return {
["dice"] = "Simulates a dice throw, prints the value of each die",
["cards"] = "Draw a specific amount of playing cards and display them",
["calculate"] = {embed={
title = "Calculate an expression",
description = "Calculates maths using libqalculate. https://qalculate.github.io/ for more info",
fields = {
{name = "Usage",value = [[calculate "<expression>"]]},
{name = "Perms: ",value = "All"},
{name = "Options",value = "`-e` - exact mode"}
}
}},
["pfp"] = "Show the profile picture of a user, or if none is specified, of yourself",
["markpov"] = { embed = {
title = "Generate some text using markov chains",
description = "Generates text using the markov chain rule applied to a predefined set of words",
fields = {
{name = "Usage: ", value = "markov <text to start with>"},
{name = "Options: ", value = [[
--preseteset> - Select a text preset. Currently available:
``defaul- Generated from a wikipedia page on markov chains
``freud`The largest one, generated from a page on Sigmund Freud
``reddit Generated from reddit comments
``travist`` - Generated from transcript of a video by PlasticPills on travis scott burger
]] },
{name = "Perms: ", value = "any"}
}
}},
["embed"] = {embed={
title = "Convert JSON objects into embeds",
description = "If you've worked with discord.js before, this might be simple. If you haven't, then check out https://github.com/512mb-org/512mb.org-bot/wiki/Embeds",
fields = {
{name = "Usage",value = [[embed {code}]]},
{name = "Perms: ",value = "All"},
}
}},
}

View File

@ -28,134 +28,152 @@ function to_bit_string(num)
end end
local flip = command("flip",{ local flip = command("flip",{
category = "Miscellaneous", help = "Flips a coin, obv.",
help = "Flips a coin, obv.", usage = "flip",
usage = "flip", exec = function(msg,args,opts)
exec = function(msg,args,opts) local coin = math.random(1,100)%2
local coin = math.random(1,100)%2 if coin > 0 then
if coin > 0 then msg:reply("Heads")
msg:reply("Heads") else
else msg:reply("Tails")
msg:reply("Tails") end
end end,
end,
}) })
plugin:add_command(flip) plugin:add_command(flip)
local dice = command("dice",{ local dice = command("dice",{
category = "Miscellaneous", help = "Simulates a dice throw, prints the value of each die",
usage = "dice <2d6,d30,d20+4,etc>", usage = "dice <2d6,d30,d20+4,etc>",
exec = function(msg,args,opts) exec = function(msg,args,opts)
local out = {embed = { local out = {embed = {
fields = {}, fields = {},
footer = { footer = {
text = 0 text = 0
} }
}} }}
for I = 1,#args do for I = 1,#args do
local v = args[I] local v = args[I]
for J = 1,(v:match("(%d+)d%d+") or 1) do for J = 1,(v:match("(%d+)d%d+") or 1) do
local value = math.random(1,tonumber(v:match("d(%d+)"))) local value = math.random(1,tonumber(v:match("d(%d+)")))
if v:find("d%d+[%+%-]%d+") then if v:find("d%d+[%+%-]%d+") then
if v:match("d%d+([%+%-])") == "+" then if v:match("d%d+([%+%-])") == "+" then
value = value + tonumber(v:match("d%d+[%+%-](%d+)")) value = value + tonumber(v:match("d%d+[%+%-](%d+)"))
else else
value = value - tonumber(v:match("d%d+[%+%-](%d+)")) value = value - tonumber(v:match("d%d+[%+%-](%d+)"))
end end
end end
out.embed.fields[#out.embed.fields+1] = {name = "d"..v:match("d(%d+)"),value = value, inline = true} out.embed.fields[#out.embed.fields+1] = {name = "d"..v:match("d(%d+)"),value = value, inline = true}
out.embed.footer.text = out.embed.footer.text+value out.embed.footer.text = out.embed.footer.text+value
if #out.embed.fields >= 25 then if #out.embed.fields >= 25 then
break break
end
end end
end if #out.embed.fields >= 25 then
if #out.embed.fields >= 25 then break
break end
end end
end out.embed.footer.text = "Total: "..out.embed.footer.text
out.embed.footer.text = "Total: "..out.embed.footer.text msg:reply(out)
msg:reply(out) end,
end,
}) })
plugin:add_command(dice) plugin:add_command(dice)
local cards = command("cards",{ local cards = command("cards",{
category = "Miscellaneous", help = "Draw a specific amount of playing cards and display them",
usage = "cards <amount>", usage = "cards <amount>",
args = {"number"}, args = {"number"},
exec = function(msg,args,opts) exec = function(msg,args,opts)
local out = {embed = { local out = {embed = {
fields = {} fields = {}
}} }}
local random = math.random local random = math.random
for I = 1,(args[1] < 25 and args[1]) or 25 do for I = 1,(args[1] < 25 and args[1]) or 25 do
local suits = {"spades","clubs","diamonds","hearts"} local suits = {"spades","clubs","diamonds","hearts"}
local values = { local values = {
"A","1","2","3","4","5", "A","1","2","3","4","5",
"6","7","8","9","J","Q","K" "6","7","8","9","J","Q","K"
} }
out.embed.fields[I] = {name = "card", value = " :"..suits[random(1,4)]..":"..values[random(1,11)].." ",inline = true} out.embed.fields[I] = {name = "card", value = " :"..suits[random(1,4)]..":"..values[random(1,11)].." ",inline = true}
end end
msg:reply(out) msg:reply(out)
end, end,
}) })
plugin:add_command(cards) plugin:add_command(cards)
local calculate = command("calculate",{ local calculate = command("calculate",{
category = "Miscellaneous", help = "Calculates maths using libqalculate. https://qalculate.github.io/ for more info",
args = { usage = [[
"string" calculate "<expression>"
}, ``-e`` - exact mode
exec = function(msg,args,opts) ]],
msg:reply(qalculator.qalc(table.concat(args," "),opts["e"])) args = {
end, "string"
},
exec = function(msg,args,opts)
msg:reply(qalculator.qalc(args[1],opts["e"]))
end,
}) })
plugin:add_command(calculate) plugin:add_command(calculate)
local pfp = command("pfp",{ local pfp = command("pfp",{
category = "Miscellaneous", help = "Show the profile picture of a user, or if none is specified, of yourself",
exec = function(msg,args,opts) usage = "pfp <user or none>",
local user = client:getUser((args[1] or ""):match("%d+")) exec = function(msg,args,opts)
if user then local user = client:getUser((args[1] or ""):match("%d+"))
msg:reply(user:getAvatarURL().."?size=2048") if user then
else msg:reply(user:getAvatarURL().."?size=2048")
msg:reply(msg.author:getAvatarURL().."?size=2048") else
end msg:reply(msg.author:getAvatarURL().."?size=2048")
end, end
end,
}) })
plugin:add_command(pfp) plugin:add_command(pfp)
local markov = command("markov",{ local markov = command("markov",{
category = "Miscellaneous", help = { embed = {
exec = function(msg,args,opts) title = "Generate some text using markov chains",
local preset,code,err = import("file").readJSON("./resources/"..(opts["preset"] or "default"):match("%w+")..".json",{system_failed = true}) description = "Generates text using the markov chain rule applied to a predefined set of words",
if preset.system_failed then fields = {
msg:reply("No such preset") {name = "Usage: ", value = "markov <text to start with>"},
return {name = "Options: ", value = [[
--preset=<preset> - Select a text preset. Currently available:
``default`` - Generated from a wikipedia page on markov chains
``freud`` - The largest one, generated from a page on Sigmund Freud
``reddit`` - Generated from reddit comments
``travisscott`` - Generated from transcript of a video by PlasticPills on travis scott burger
]] },
{name = "Perms: ", value = "any"}
}
}},
exec = function(msg,args,opts)
local preset,code,err = import("file").readJSON("./resources/"..(opts["preset"] or "default"):match("%w+")..".json",{system_failed = true})
if preset.system_failed then
msg:reply("No such preset")
return
end
markov_instance:load_state(preset)
local output = markov_instance:run("The",100)
msg:reply(output)
end end
markov_instance:load_state(preset)
local output = markov_instance:run("The",100)
msg:reply(output)
end
}) })
plugin:add_command(markov) plugin:add_command(markov)
local embed = command("embed",{ local embed = command("embed",{
category = "Miscellaneous", help = "Convert JSON objects into embeds",
args = { usage = "If you've worked with discord.js before, this might be simple. If you haven't, then check out https://github.com/yessiest/SuppaBot/wiki/Embeds",
"string" args = {
}, "string"
exec = function(msg,args,opts) },
local embed = msg.content:match("{.+}") exec = function(msg,args,opts)
if not embed then local embed = msg.content:match("{.+}")
msg:reply("Invalid embed object") if not embed then
return msg:reply("Invalid embed object")
return
end
local embed_obj,code,err = import("json").decode(embed)
if not embed_obj then
msg:reply("Error while decoding JSON object: "..tostring(err))
return
end
if pcall(discordia.Color.fromHex,embed_obj.color) then
embed_obj.color = discordia.Color.fromHex(embed_obj.color).value
end
msg:reply({embed = embed_obj})
end end
local embed_obj,code,err = import("json").decode(embed)
if not embed_obj then
msg:reply("Error while decoding JSON object: "..tostring(err))
return
end
if pcall(discordia.Color.fromHex,embed_obj.color) then
embed_obj.color = discordia.Color.fromHex(embed_obj.color).value
end
msg:reply({embed = embed_obj})
end
}) })
plugin:add_command(embed) plugin:add_command(embed)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin return plugin

View File

@ -0,0 +1,478 @@
--TODO: Add domain-specific manuals, Document
local air = require("air")
local json = require("json")
local file = require("file")
file.activate_json(json)
local segment = {}
segment.setnames = {}
segment.name = "enforcer"
segment.settings = {
automod = {
list = {
},
status = false,
warn_limit = 3
}
}
if globals.enofrcer then
if globals.enforcer.setnames then
segment.setnames = globals.enforcer.setnames
end
if globals.enforcer.settings then
segment.settings = global.enforcer.settings
end
end
segment.warns = file.readJSON("./servers/"..id.."/warns.json",{})
events:on("serverSaveConfig",function()
if not globals.enforcer then
globals.enforcer = {}
end
globals.enforcer.setnames = segment.setnames
globals.enforcer.settings = segment.settings
file.writeJSON("./servers/"..id.."/warns.json",segment.warns)
end)
local warn = function(ID,reason)
local guild = client:getGuild(id)
local member = guild:getMember(tostring(ID))
if not segment.warns[tostring(ID)] then segment.warns[tostring(ID)] = {} end
table.insert(segment.warns[tostring(ID)],1,reason)
if segment.settings.warn_limit and (#segment.warns[tostring(ID)] >= segment.settings.warn_limit) and guild:getMember(tostring(ID)) then
if segment.settings.warn_punishment == "kick" then
member:kick("Warning quota exceeded.")
elseif segment.settings.warn_punishment == "ban" then
member:ban("Warning quota exceeded.",segment.settings.ban_days)
end
end
_ = (client:getUser(tostring(ID)) and client:getUser(tostring(ID)):send("__You have been warned.__\nReason: "..reason))
signals:emit("warn",function(args)
if args[1] and member.name:find(args[1],1,true) then
return true
elseif not args[1] then
return true
else
return false
end
end,{
user = member.id,
name = member.name
})
end
segment.commands = {
["change-name"] = {
help = {embed = {
title = "Enforce a name upon a specific user",
description = "Whenever the user attempts to change their name, it will be changed back",
fields = {
{name = "Usage: ",value = "change-name <user> <name>"},
{name = "Perms: ",value = "manageNicknames"}
}
}},
perms = {
perms = {
"manageNicknames"
}
},
args = {
"member",
"string"
},
exec = function(msg,args,opts)
name = args[2]
args[1]:setNickname(name)
segment.setnames[tostring(args[1].id)] = name
msg:reply("Now assigning an enforced name upon "..args[1].name)
end
},
["reset-name"] = {
help = {embed = {
title = "Stop enforcing a name upon a user",
description = "Reverses the effect of ``change-name``",
fields = {
{name = "Usage: ",value = "reset-name"},
{name = "Perms: ",value = "manageNicknames"}
}
}},
perms = {
perms = {
"manageNicknames"
}
},
args = {
"member"
},
exec = function(msg,args,opts)
if segment.setnames[tostring(args[1].id)] then
segment.setnames[tostring(args[1].id)] = nil
args[1]:setNickname(nil)
msg:reply("No longer tracking "..args[1].name)
else
msg:reply("This user haven't been assigned an enforced name")
end
end
},
["wipe"] = {
help = {embed={
title = "Wipe user messages",
description = "Searches and deletes all messages of a specific user in a specified range",
fields = {
{name = "Usage: ",value = "wipe-user <range> <user mention or id>"},
{name = "Perms: ",value = "manageMessages"}
}
}},
perms = {
perms = {
"manageMessages"
}
},
args = {
"number",
},
exec = function(msg,args,opts)
if tonumber(args[1]) and tonumber(args[1]) > 101 then
msg:reply("Search limit is too high")
return
end
local messages = {}
msg.channel:getMessages(args[1]):forEach(function(v) messages[#messages+1] = v.id end)
msg.channel:bulkDelete(messages)
end
},
["wipe-user"] = {
help = {embed={
title = "Wipe user messages",
description = "Searches and deletes all messages of a specific user in a specified range",
fields = {
{name = "Usage: ",value = "wipe-user <range> <user mention or id>"},
{name = "Perms: ",value = "manageMessages"}
}
}},
perms = {
perms = {
"manageMessages"
}
},
args = {
"number",
"member"
},
exec = function(msg,args,opts)
if tonumber(args[1]) and tonumber(args[1]) > 101 then
msg:reply("Search limit is too high")
return
end
local messages = {}
local target = args[2].user
msg.channel:getMessages(args[1]):forEach(function(v)
if v.author.id == target.id then
messages[#messages+1] = v.id
end
end)
msg.channel:bulkDelete(messages)
end
},
["wipe-pattern"] = {
help = {embed={
title = "Wipe by pattern",
description = "Searches for a specific pattern in a range of messages, and wipes if certain conditions are met",
fields = {
{name = "Usage: ",value = "wipe-pattern <range> <pattern>"},
{name = "Perms: ",value = "manageMessages"}
}
}},
perms = {
perms = {
"manageMessages"
}
},
args = {
"number",
"string"
},
exec = function(msg,args,opts)
if tonumber(args[1]) and tonumber(args[1]) > 101 then
msg:reply("Search limit is too high")
return
end
local messages = {}
msg.channel:getMessages(args[1]):forEach(function(v)
if v.content:find(args[2],1,true) then
messages[#messages+1] = v.id
end
end)
msg.channel:bulkDelete(messages)
end
},
["kick"] = {
help = {embed={
title = "Kick a member",
description = "Self-descriptive",
fields = {
{name = "Usage: ",value = "kick <member> [<reason>]"},
{name = "Perms: ",value= "kickMembers"}
}
}},
perms = {
perms = {
"kickMembers"
}
},
args = {
"member"
},
exec = function(msg,args,opts)
local member = args[1]
signals:emit("kick",function(args)
if args[1] and member.name:find(args[1],1,true) then
return true
elseif not args[1] then
return true
else
return false
end
end,{
user = member.id,
name = member.name,
reason = args[2]
})
member:kick(args[2])
end
},
["ban"] = {
help = {embed={
title = "Ban a member",
description = "Self-descriptive",
fields = {
{name = "Usage: ",value = "ban <member> [<reason> [<days>]]"},
{name = "Perms: ",value= "banMembers"}
}
}},
perms = {
perms = {
"banMembers"
}
},
args = {
"member"
},
exec = function(msg,args,opts)
local member = args[1]
signals:emit("kick",function(args)
if args[1] and member.name:find(args[1],1,true) then
return true
elseif not args[1] then
return true
else
return false
end
end,{
user = member.id,
name = member.name,
reason = args[2],
days = args[3]
})
member:ban(args[2],tonumber(args[3]))
end
},
["purge"] = {
help = {embed={
title = "Purge bot messages",
description = "If a number is provided, the bot will search through that amount of messages, or through 100 of them by default",
fields = {
{name = "Usage: ",value = "ban <member> [<reason> [<days>]]"},
{name = "Perms: ",value= "manageMessages"}
}
}},
perms = {
perms = {
"manageMessages"
}
},
exec = function(msg,args,opts)
local messages = {}
if tonumber(args[1]) and tonumber(args[1]) > 101 then
msg:reply("Search limit is too high")
return
end
msg.channel:getMessages(tonumber(args[1]) or 100):forEach(function(v)
if (v.author.id == client.user.id) or (v.content:find(globals.prefix)==1) then
messages[#messages+1] = v.id
end
end)
msg.channel:bulkDelete(messages)
end
},
["warn"] = {
help = {embed={
title = "Warn a user",
descriptions = "Warnings by themselves don't do any punishment to the user, but they allow managing users",
fields = {
{name = "Usage: ",value = "warn <user> <reason>"},
{name = "Perms: ",value = "kickMembers"}
}
}},
perms = {
perms = {
"kickMembers"
}
},
args = {
"member",
"string"
},
exec = function(msg,args,opts)
local reason = table.concat(args," ",2)
warn(args[1].id,reason)
msg:reply({embed = {
title = "User "..args[1].name.." warned",
description = "Reason: ```"..reason.."```"
}})
end
},
["warns"] = {
help = {embed={
title = "List warnings",
descriptions = "self-descriptive",
fields = {
{name = "Usage: ",value = "warns <member> [<page>]"},
{name = "Perms: ",value = "kickMembers"}
}
}},
perms = {
perms = {
"kickMembers"
}
},
args = {
"member",
},
exec = function(msg,args,opts)
local page = (tonumber(args[2]) or 1)-1
local new_embed = {
title = "Warnings for "..args[1].name,
fields = {}
}
if page < 0 then
new_embed.description = "Page "..page.." not found, reverting to first page"
page = 0
end
if segment.warns[tostring(args[1].id)] and #segment.warns[tostring(args[1].id)] > 0 then
for I = 1+(page*5),5+(page*5) do
local warn = segment.warns[tostring(args[1].id)][I]
if warn then
table.insert(new_embed.fields,{name = "ID: "..tostring(I),value = warn})
end
end
msg:reply({embed = new_embed})
else
msg:reply("This user has no warnings")
end
end
},
["unwarn"] = {
help = {embed={
title = "Revoke a warning issued to a user",
descriptions = "self-descriptive",
fields = {
{name = "Usage: ",value = "unwarn <user> [<warn id>]"},
{name = "Perms: ",value = "kickMembers"}
}
}},
perms = {
perms = {
"kickMembers"
}
},
args = {
"member",
},
exec = function(msg,args,opts)
local warn_id = (tonumber(args[2]) or 1)
if segment.warns[tostring(args[1].id)][warn_id] then
table.remove(segment.warns[tostring(args[1].id)],warn_id)
msg:reply("Revoked warning #"..warn_id)
else
msg:reply("No warning with id "..warn_id)
end
end
},
["add-role"] = {
help = {embed={
title = "Give some specific user a role",
descriptions = "self-descriptive",
fields = {
{name = "Usage: ",value = "unwarn <member> <role>"},
{name = "Perms: ",value = "manageRoles"}
}
}},
perms = {
perms = {
"manageRoles"
}
},
args = {
"member",
"role"
},
exec = function(msg,args,opts)
args[1]:addRole(tostring(args[2].id))
end
},
["remove-role"] = {
help = {embed={
title = "Revoke a role from a user",
descriptions = "self-descriptive",
fields = {
{name = "Usage: ",value = "remove-role <member> <role>"},
{name = "Perms: ",value = "manageRoles"}
}
}},
perms = {
perms = {
"manageRoles"
}
},
args = {
"member",
"role"
},
exec = function(msg,args,opts)
args[1]:removeRole(tostring(args[2].id))
end
},
}
events:on("memberUpdate",function(member)
if segment.setnames[tostring(member.id)] and member.nickname ~= segment.setnames[tostring(member.id)] then
member:setNickname(segment.setnames[tostring(member.id)])
end
end)
--old automod code
--[[
events:on("messageCreate",function(msg)
if segment.settings.automod.status then
local trigger = ""
for k,v in pairs(segment.settings.automod.list) do
if msg.content:find(v) and msg.author ~= client.user then
trigger = trigger..v..","
end
end
if trigger ~= "" then
full_text,author = msg.content.."",msg.author
msg:delete()
msg.author:send("The words \""..trigger.."\" are banned on this server.\nThis is the text that these words were found in: ```"..full_text.."```")
if segment.settings.automod.punishment == "kick" then
msg.author:kick("Usage of banned words")
elseif segment.settings.automod.punishment == "warn" then
warn(msg.author.id,"Usage of banned words",msg.guild)
end
end
end
end)
]]
return segment

View File

@ -0,0 +1,340 @@
local segment = {}
local emulate = require("emulate")({
client = client,
discordia = discordia,
})
local file = require("file")
file.activate_json(require("json"))
local guild = client:getGuild(id)
segment.pivots = file.readJSON("./servers/"..id.."/reactions.json",{})
local getEmoji = function(id)
local emoji = guild:getEmoji(id:match("(%d+)[^%d]*$"))
if emoji then
return emoji
else
return id
end
end
local function count(tab)
local n = 0
for k,v in pairs(tab) do
n = n + 1
end
return n
end
segment.commands = {
["pivot"] = {
help = {
title = "Select a pivot message to manipulate",
description = "Pivot is like a message selector which allows easy reaction manipulations",
fields = {
{name = "Usage: ",value = "pivot <message link>"},
{name = "Perms: ",valeu = "Administartor"}
}
},
args = {
"messageLink"
},
perms = {
perms = {
"administrator"
}
},
exec = function(msg,args,opts)
if segment.pivot and count(segment.pivot.buttons) == 0 then
segment.pivots[segment.pivot.message] = nil
end
local message = args[1]
if not message then
msg:reply("Couldn't find message with id "..args[2])
return nil
end
if not segment.pivots[message.id] then
segment.pivots[message.id] = {}
segment.pivots[message.id].message = message.id
segment.pivots[message.id].channel = message.channel.id
segment.pivots[message.id].buttons = {}
end
segment.pivot = segment.pivots[message.id]
msg:reply("Pivot message set to "..message.link)
end
},
["role-toggle"] = {
help = {
title = "Add a simple role switch to the pivot",
description = "Note: you cannot assign more than one role to a single reaction",
fields = {
{name = "Usage: ",value = "role-toggle <emoji> <role ping or role id>"},
{name = "Perms: ",value = "administrator"}
}
},
args = {
"string",
"role",
},
perms = {
perms = {
"administrator"
}
},
exec = function(msg,args,opts)
if not segment.pivot then
msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again")
return nil
end
local emoji = getEmoji(args[1])
local channel = guild:getChannel(segment.pivot.channel)
if not channel then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local message = channel:getMessage(segment.pivot.message)
if not message then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local grabEmoji = function(reaction)
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
type = "role-toggler",
role = tostring(args[2].id)
}
msg:reply("Role toggler added successfully")
end
message:removeReaction(emoji,client.user.id)
client:once("reactionAdd",grabEmoji)
if not message:addReaction(emoji) then
client:removeListener("reactionAdd",grabEmoji)
msg:reply("Couldn't add reaction - emoji might be invalid")
end
end
},
["remove-reaction"] = {
help = {
title = "Remove a reaction from a pivot",
description = "If you don't specify a reaction to remove, the entire pivot for the message is removed automatically",
fields = {
{name = "Usage: ",value = "remove-reaction <emoji>"},
{name = "Perms: ",value = "Administrator"}
}
},
perms = {
perms = {
"administrator"
}
},
exec = function(msg,args,opts)
local channel = guild:getChannel(segment.pivot.channel)
if not channel then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local message = channel:getMessage(segment.pivot.message)
if not message then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
if args[1] then
local emoji = getEmoji(args[1])
message:removeReaction(emoji,client.user.id)
segment.pivot.buttons[((type(emoji) == "table") and emoji.id) or emoji] = nil
msg:reply("Action successfully removed")
else
message:clearReactions()
segment.pivots[tostring(message.id)] = nil
segment.pivot = nil
msg:reply("Pivot successfully removed")
end
end
},
["toggle"] = {
help = {
title = "Add a toggle that runs specific commands",
description = "Note: you cannot assign more than one action to a single reaction \n``$user`` gets replaced with the id of the user that interacted with the reaction.",
fields = {
{name = "Usage: ",value = "toggle <emoji> <command-on> <command-off>"},
{name = "Perms: ",value = "administrator"}
}
},
args = {
"string",
"string",
"string",
},
perms = {
perms = {
"administrator"
}
},
exec = function(msg,args,opts)
if not segment.pivot then
msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again")
return nil
end
local emoji = getEmoji(args[1])
local channel = guild:getChannel(segment.pivot.channel)
if not channel then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local message = channel:getMessage(segment.pivot.message)
if not message then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local grabEmoji = function(reaction)
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
type = "toggler",
on = args[2],
off = args[3],
}
msg:reply("Toggler added successfully")
end
message:removeReaction(emoji,client.user.id)
client:once("reactionAdd",grabEmoji)
if not message:addReaction(emoji) then
client:removeListener("reactionAdd",grabEmoji)
msg:reply("Couldn't add reaction - emoji might be invalid")
end
end
},
["button"] = {
help = {
title = "Add a button that runs specific command when pressed",
description = "Note: you cannot assign more than one action to a single reaction \n``$user`` gets replaced with the id of the user that interacted with the reaction.",
fields = {
{name = "Usage: ",value = "button <emoji> <command>"},
{name = "Perms: ",value = "administrator"}
}
},
args = {
"string",
"string",
},
perms = {
perms = {
"administrator"
}
},
exec = function(msg,args,opts)
if not segment.pivot then
msg:reply("Pivot not selected. Use "..globals.prefix.."pivot to select it and then try again")
return nil
end
local emoji = getEmoji(args[1])
local channel = guild:getChannel(segment.pivot.channel)
if not channel then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local message = channel:getMessage(segment.pivot.message)
if not message then
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return nil
end
local grabEmoji = function(reaction)
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
type = "button",
on = args[2],
}
msg:reply("Button added successfully")
end
message:removeReaction(emoji,client.user.id)
client:once("reactionAdd",grabEmoji)
if not message:addReaction(emoji) then
client:removeListener("reactionAdd",grabEmoji)
msg:reply("Couldn't add reaction - emoji might be invalid")
end
end
},
}
local buttonOn = function(message,hash,userID)
if not message then
log("ERROR","Attempted to find a deleted message")
return
end
if segment.pivots[tostring(message.id)] and userID ~= client.user.id then
local current_pivot = segment.pivots[tostring(message.id)]
if current_pivot.buttons[tostring(hash)] then
local current_button = current_pivot.buttons[tostring(hash)]
local new_content
if current_button.on then
new_content = current_button.on:gsub("%$user",userID)
end
if current_button.type == "role-toggler" then
guild:getMember(userID):addRole(current_button.role)
end
if current_button.type == "toggler" then
emulate.send(message,{
delete = function() end,
content = new_content
})
end
if current_button.type == "button" then
emulate.send(message,{
delete = function() end,
content = new_content
})
end
end
end
end
local buttonOff = function(message,hash,userID)
if not message then
log("ERROR","Attempted to find a deleted message")
return
end
if segment.pivots[tostring(message.id)] and userID ~= client.user.id then
local current_pivot = segment.pivots[tostring(message.id)]
if current_pivot.buttons[tostring(hash)] then
local current_button = current_pivot.buttons[tostring(hash)]
local new_content
if current_button.off then
new_content = current_button.off:gsub("%$user",userID)
end
if current_button.type == "role-toggler" then
guild:getMember(userID):removeRole(current_button.role)
end
if current_button.type == "toggler" then
emulate.send(message,{
delete = function() end,
content = new_content
})
end
end
end
end
events:on("reactionAdd",function(reaction,userID)
local message = reaction.message
local hash = tostring(reaction.emojiId or reaction.emojiName)
buttonOn(message,hash,userID)
end)
events:on("reactionRemove",function(reaction,userID)
local message = reaction.message
local hash = tostring(reaction.emojiId or reaction.emojiName)
buttonOff(message,hash,userID)
end)
events:on("reactionAddUncached",function(channelId,messageId,hash,userId)
local message = client:getChannel(channelId):getMessage(messageId)
local hash = tostring(hash)
buttonOn(message,hash,userId)
end)
events:on("reactionRemoveUncached",function(channelId,messageId,hash,userId)
local message = client:getChannel(channelId):getMessage(messageId)
local hash = tostring(hash)
buttonOff(message,hash,userId)
end)
events:on("serverSaveConfig",function()
file.writeJSON("./servers/"..id.."/reactions.json",segment.pivots)
end)
return segment

View File

@ -0,0 +1,120 @@
segment = {}
local check_perms = require("check_perms")
local fake_server = {
id = id
}
local file = require("file")
file.activate_json(require("json"))
local overlay = file.readJSON("./servers/"..id.."/overlay.json",{})
local cached_commands = {}
events:on("commandPoolUpdate",function()
local commands = plugins.get()["commands"]
for k,v in pairs(commands) do
if not cached_commands[k] then
if overlay[k] then
if not v.perms then
v.perms = {}
end
v.perms.users = overlay[k].users or v.perms.users
v.perms.roles = overlay[k].roles or v.perms.roles
end
cached_commands[k] = true
end
end
for k,v in pairs(cached_commands) do
if not commands[k] then
cached_commands[k] = nil
end
end
end)
events:on("serverSaveConfig",function()
file.writeJSON("./servers/"..id.."/overlay.json",overlay)
end)
segment.commands = {
["rules"] = {
args = {
"string",
"string",
},
perms = {
perms = {
"administrator"
}
}
exec = function(msg,args,opts)
local target,command = args[1],args[2]
local commands = plugins.get()["commands"]
if not commands[target] then
msg:reply("Target command not found")
return
end
local name = target
target = commands[target]
if command == "list" then
local roles = "```"
for k,v in pairs(target.perms.roles or {}) do
roles = roles..((v > 0 and "allow ") or (v < 0 and "disallow "))..k.."\n"
end
roles = roles.." ```"
local users = "```"
for k,v in pairs(target.perms.users or {}) do
users = users..((v > 0 and "allow ") or (v < 0 and "disallow "))..k.."\n"
end
users = users.." ```"
msg:reply({embed={
title = "Custom permissions for command ``"..name.."``",
fields = {
{name = "Roles",value = roles},
{name = "Users",value = users}
}
}})
else
if not check_perms(fake_server,target,msg,require("discordia")) then
msg:reply("You don't have a high enough permission to change rules for this command")
return
end
local type,id = args[3],args[4]
if not id then
msg:reply("Type and ID are needed to create a proper rule")
end
if (type ~= "user") and (type ~= "role") then
msg:reply("Type can only be ``user`` or ``role``")
end
id = id:match("%d+")
local state = 0
if command == "allow" then
state = 1
elseif command == "disallow" then
state = -1
elseif command == "reset" then
state = nil
end
if not overlay[name] then
overlay[name] = {}
overlay[name].users = {}
overlay[name].roles = {}
end
if not target.perms then
target.perms = {}
end
if type == "user" then
if not target.perms.users then
target.perms.users = {}
end
target.perms.users[id] = state
overlay[name].users[id] = state
elseif type == "role" then
if not target.perms.roles then
target.perms.roles = {}
end
target.perms.roles[id] = state
overlay[name].roles[id] = state
end
msg:reply("Changes applied.")
end
end
}
}
return segment

362
todo-stuff/tasks/init.lua Normal file
View File

@ -0,0 +1,362 @@
--haha yes time to make cron in lua
local segment = {}
segment.help = "Add tasks to be executed later"
local file = require("file")
file.activate_json(require("json"))
local utils = require("bot_utils")
local emulate = require("emulate")({
client = client,
discordia = discordia
})
segment.name = "task"
local preload_tab = file.readJSON("./servers/"..id.."/crontab.json",{})
segment.tab = {}
globals.utc = globals.utc or 0
globals.utc_minutes = globals.utc_minutes or 0
segment.coroutines = {}
local absolute_fake_generator = require("absolute_fake")
local function cronTime(time)
if type(time) ~= "string" then
return false
end
local mask = {60,24,32,13}
local tokens = {}
local err = false
time:gsub("[%d%*]+",function(c) table.insert(tokens,c) end,4)
for I = 1,4 do --check if date/time format matches
if not ((tokens[I]:match("%d+") and tonumber(tokens[I]) < mask[I]) or ((tokens[I] == "*") and (I > 1))) then
err = true
end
end
if not err then
return tokens
else
return nil
end
end
local function getFakeMessageOf(channel,member,content)
return absolute_fake_generator(client,discordia,member,channel,id,content)
end
local function addEventTask(task)
if task.event and task.channel and task.member then
task.coroutineID = math.random(1000000000,9999999999)
while segment.coroutines[task.coroutineID] do
task.coroutineID = math.random(1000000000,9999999999)
end
local taskID = #segment.tab+1
segment.coroutines[task.coroutineID] = function(check,args)
local check_func = ((type(check) == "function") and check) or (function() return true end)
if (not task.args) or (type(task.args) == "table" and check_func(task.args)) then
local content = task.task
for k,v in pairs(args or {}) do
content = content:gsub("%$"..k,tostring(v))
end
local dupeable = getFakeMessageOf(task.channel,task.member)
if not dupeable then
log("ERROR","Failed to dupe a message properly")
return nil
end
emulate.send(dupeable,{
content = content,
delete = function() end
})
if task.once then
signals:removeListener(task.event,segment.coroutines[task.coroutineID])
table.remove(segment.tab,taskID)
segment.coroutines[task.coroutineID] = nil
end
end
end
signals:on(task.event,segment.coroutines[task.coroutineID])
table.insert(segment.tab,task)
return true
else
return false
end
end
local function addTimeTask(task)
if type(task.time) == "table" then
table.insert(segment.tab,task)
return true
else
return false
end
end
for k,v in pairs(preload_tab) do
if v.type == "event" then
addEventTask(v)
else
addTimeTask(v)
end
end
segment.commands = {
["task"] = {
help = {embed = {
title = "Add tasks",
description = "Tasks are like cron tasks, in a sense that you specify when to execute them and what command to execute",
fields = {
{name = "Usage: ",value = "task (\"minute hour day month\" or @<event name> \"argument1\" \"argument2\") \"command\""},
{name = "Perms: ",value = "Administrator"},
{name = "Opts: ",value = [[
--description=\"description here\" - self-descriptive
--once - remove the task after completion
]]},
{name = "Examples: ",value = [[
``task "5 10 * *" "]]..globals.prefix..[[speak hi"`` -- sends "hi" to the current channel at 10:05 every day every month
``task "5 10 15 *" "]]..globals.prefix..[[speak hi"`` -- sends "hi" to the current channel at 10:05 every 15th day of the month
``task --once "5 10 15 *" "]]..globals.prefix..[[speak hi"`` -- same as before, except the task gets removed after sending the message
additional examples can be found at https://github.com/yessiest/SuppaBot/wiki/Tasks
]]}
}
}},
perms = {
perms = {
"administrator"
}
},
args = {
"string",
"string"
},
exec = function(msg,args,opts)
local command = args[#args]
if args[1]:match("^@%w+") then
local event = args[1]:match("^@(%w+)")
local conditions = utils.slice(args,2,#args-1)
local status = addEventTask({
type = "event",
channel = tostring(msg.channel.id),
member = tostring(msg.member.id),
event = event,
args = conditions,
task = command,
description = opts["description"] or "",
once = opts["once"]
})
if status then
msg:reply("Task "..(#segment.tab).." added")
else
msg:reply("Failed to add task")
end
elseif args[1]:match("^ ?%d+ [%d*]+ [%d*]+ [%d*]+ ?$") then
local status = addTimeTask({
type = "time",
time = cronTime(args[1]:match("^ ?%d+ [%d*]+ [%d*]+ [%d*]+ ?$")),
channel = tostring(msg.channel.id),
member = tostring(msg.member.id),
task = command,
description = opts["description"] or "",
once = opts["once"]
})
if status then
msg:reply("Task "..(#segment.tab).." added")
else
msg:reply("Failed to add task")
end
else
msg:reply("Syntax error")
end
end
},
["tasks"] = {
help = {embed = {
title = "List all tasks",
description = "Bold white text is conditions for the task, code block is the command to be executed",
fields = {
{name = "Usage: ",value = "tasks"},
{name = "Perms: ",value = "Administrator"},
}
}},
perms = {
perms = {
"administrator"
}
},
exec = function(msg,args,opts)
msg:reply({embed = {
title = "Tasks: ",
fields = (function()
local output = {}
for k,v in pairs(segment.tab) do
if v.type == "event" then
table.insert(output,{
name = tostring(k)..": @"..v.event.." "..table.concat(v.args," "),
value = "```"..v.task.."```\n"..tostring(v.description),
inline = true
})
elseif v.type == "time" then
table.insert(output,{
name = tostring(k)..": "..table.concat(v.time," "),
value = "```"..v.task.."```\n"..tostring(v.description),
inline = true
})
end
end
return output
end)()
}})
end
},
["task-remove"] = {
help = {embed = {
title = "Remove a task",
description = "That one is self-descriptive",
fields = {
{name = "Usage: ",value = "remove-task <task id>"},
{name = "Perms: ",value = "Administrator"},
}
}},
perms = {
perms = {
"administrator"
}
},
args = {
"number"
},
exec = function(msg,args,opts)
if segment.tab[args[1]] then
local task = segment.tab[args[1]]
if task.type == "event" then
signals:removeListener(task.event,segment.coroutines[task.coroutineID])
segment.coroutines[task.coroutineID] = nil
end
table.remove(segment.tab,args[2])
msg:reply("Task "..args[1].." removed")
else
msg:reply("Task "..args[1].." not found")
end
end
},
["utc"] = {
help = {embed = {
title = "Set the UTC time offset",
description = "If your UTC time offset is x:45 or x:30 simply add \":30\" or \"45\" to the number accordingly",
fields = {
{name = "Usage: ",value = "utc <hour>[:<minute>]"},
{name = "Perms: ",value = "Administrator"},
}
}},
perms = {
perms = {
"administrator"
}
},
args = {
"string"
},
exec = function(msg,args,opts)
if args[1]:match("^%d+$") then
globals.utc = tonumber(args[1]:match("^(%d+)$"))
msg:reply("UTC offset set")
elseif args[1]:match("^%d+:%d+$") then
globals.utc = tonumber(args[1]:match("^(%d+):%d+$"))
globals.utc_minutes = tonumber(args[1]:match("^%d+:(%d+)$"))
msg:reply("UTC offset set")
else
msg:reply("Invalid syntax")
end
end
},
["time"] = {
help = {embed = {
title = "View the internal bot time",
description = "If you've set a time offset previously, it will get accounted",
fields = {
{name = "Usage: ",value = "time"},
{name = "Perms: ",value = "all"},
}
}},
exec = function(msg,args,opts)
local utc_time = os.date("%c",os.time()+(3600)*(globals.utc-4)+(60)*(globals.utc_minutes))
msg:reply(utc_time)
end
}
}
segment.unload = function()
for k,v in pairs(segment.tab) do
if v.type == "event" then
signals:removeListener(v.event,segment.coroutines[v.coroutineID])
segment.coroutines[v.coroutineID] = nil
segment.tab[k] = nil
end
end
end
local function check_time(date,crondate)
local mask = {"min","hour","day","month"}
local output = true
for I = 1,4 do
if not (tonumber(crondate[I]) == tonumber(date[mask[I]]) or crondate[I] == "*") then
output = false
end
end
return output
end
events:on("messageCreate",function(msg)
signals:emit("message",function(args)
local output = true
if not args[1] then
output = true
elseif msg.content:find(args[1],1,true) then
output = true
else
output = false
end
if output and (tostring(msg.author.id) == tostring(client.user.id)) then
output = false
end
if output and (msg.emulated) then
output = false
end
if output and (not args[2]) then
output = true
elseif output and (msg.author.name:find(args[2],1,true)) then
output = true
else
output = false
end
return output
end,{
user = msg.author.id,
content = msg.content,
name = msg.author.name,
ctxid = msg.id
})
return
end)
events:on("serverSaveConfig",function()
file.writeJSON("./servers/"..id.."/crontab.json",segment.tab)
end)
events:on("clock",function()
if tonumber(os.time())%60 == 30 then
local utc_time = os.date("*t",os.time()+(3600)*(globals.utc-4)+(60)*(globals.utc_minutes))
for k,v in pairs(segment.tab) do
if (v.type == "time") and check_time(utc_time,v.time) then
emulate_message = getFakeMessageOf(v.channel,v.member)
if emulate_message then
emulate.send(emulate_message,{
content = v.task,
delete = function() end
})
else
log("ERROR","There are no messages to dupe a new one from")
end
if v.once then
table.remove(segment.tab,k)
end
end
end
end
end)
return segment