bustin
This commit is contained in:
parent
3dd57d3bdc
commit
38c9e580b9
|
@ -6,87 +6,87 @@ local class = import("classes.baseclass")
|
|||
local table_utils = import("table-utils")
|
||||
local acl = class("ACL")
|
||||
function acl:__init()
|
||||
self.user_rules = {}
|
||||
self.group_rules = {}
|
||||
self.user_rules = {}
|
||||
self.group_rules = {}
|
||||
end
|
||||
function acl:set_user_rule(user_id,status)
|
||||
assert(
|
||||
(status == nil) or (status == 0) or (status == -1) or (status == 1),
|
||||
"invalid status setting"
|
||||
)
|
||||
self.user_rules[user_id] = status
|
||||
assert(
|
||||
(status == nil) or (status == 0) or (status == -1) or (status == 1),
|
||||
"invalid status setting"
|
||||
)
|
||||
self.user_rules[user_id] = status
|
||||
end
|
||||
function acl:set_group_rule(group_id,status)
|
||||
assert(
|
||||
(status == nil) or (status == 0) or (status == -1) or (status == 1),
|
||||
"invalid status setting"
|
||||
)
|
||||
self.group_rules[group_id] = status
|
||||
assert(
|
||||
(status == nil) or (status == 0) or (status == -1) or (status == 1),
|
||||
"invalid status setting"
|
||||
)
|
||||
self.group_rules[group_id] = status
|
||||
end
|
||||
function acl:check_user(user_id)
|
||||
if self.user_rules[user_id] and self.user_rules[user_id] ~= 0 then
|
||||
return true,(self.user_rules[user_id] == 1)
|
||||
else
|
||||
return false
|
||||
end
|
||||
if self.user_rules[user_id] and self.user_rules[user_id] ~= 0 then
|
||||
return true,(self.user_rules[user_id] == 1)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
function acl:check_group(groups)
|
||||
local allow = false
|
||||
local found = false
|
||||
for k,v in pairs(groups) do
|
||||
if self.group_rules[v] then
|
||||
found = true
|
||||
allow = self.group_rules[v]
|
||||
local allow = false
|
||||
local found = false
|
||||
for k,v in pairs(groups) do
|
||||
if self.group_rules[v] then
|
||||
found = true
|
||||
allow = self.group_rules[v]
|
||||
end
|
||||
end
|
||||
end
|
||||
return found,(allow and allow == 1)
|
||||
return found,(allow and allow == 1)
|
||||
end
|
||||
function acl:export_all_lists()
|
||||
local lists = {
|
||||
users = "",
|
||||
groups = ""
|
||||
}
|
||||
for k,v in pairs(self.user_rules) do
|
||||
lists.users = lists.users..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
for k,v in pairs(self.group_rules) do
|
||||
lists.groups = lists.groups..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return lists
|
||||
local lists = {
|
||||
users = "",
|
||||
groups = ""
|
||||
}
|
||||
for k,v in pairs(self.user_rules) do
|
||||
lists.users = lists.users..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
for k,v in pairs(self.group_rules) do
|
||||
lists.groups = lists.groups..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return lists
|
||||
end
|
||||
function acl:export_user_list()
|
||||
local list = ""
|
||||
for k,v in pairs(self.user_rules) do
|
||||
list = list..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return list
|
||||
local list = ""
|
||||
for k,v in pairs(self.user_rules) do
|
||||
list = list..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return list
|
||||
end
|
||||
function acl:export_group_list()
|
||||
local list = ""
|
||||
for k,v in pairs(self.group_rules) do
|
||||
list = list..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return list
|
||||
local list = ""
|
||||
for k,v in pairs(self.group_rules) do
|
||||
list = list..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return list
|
||||
end
|
||||
function acl:export_snapshot()
|
||||
return {
|
||||
user_rules = bot_utils.deepcopy(self.user_rules),
|
||||
group_rules = bot_utils.deepcopy(self.group_rules)
|
||||
}
|
||||
return {
|
||||
user_rules = bot_utils.deepcopy(self.user_rules),
|
||||
group_rules = bot_utils.deepcopy(self.group_rules)
|
||||
}
|
||||
end
|
||||
function acl:import_snapshot(t)
|
||||
self.user_rules = t.user_rules
|
||||
self.group_rules = t.group_rules
|
||||
self.user_rules = t.user_rules
|
||||
self.group_rules = t.group_rules
|
||||
end
|
||||
function acl:import_user_list(list)
|
||||
list:gsub("(%w+):(%d+)",function(id,status)
|
||||
self.user_rules[id] = status
|
||||
end)
|
||||
list:gsub("(%w+):(%d+)",function(id,status)
|
||||
self.user_rules[id] = status
|
||||
end)
|
||||
end
|
||||
function acl:import_group_list(list)
|
||||
list:gsub("(%w+):(%d+)",function(id,status)
|
||||
self.group_rules[id] = status
|
||||
end)
|
||||
list:gsub("(%w+):(%d+)",function(id,status)
|
||||
self.group_rules[id] = status
|
||||
end)
|
||||
end
|
||||
|
||||
return acl
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
--class generator (for the purpose of creating classes)
|
||||
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 = {}
|
||||
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)"})
|
||||
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 = {}
|
||||
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
|
||||
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
|
||||
|
|
|
@ -7,84 +7,84 @@ local enum_perms = discordia.enums.permission
|
|||
--The following method extends the ACL class to work with rule-specific features,
|
||||
--such as the role position
|
||||
function command_acl:check_group(roles)
|
||||
local found = false
|
||||
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
|
||||
local found = false
|
||||
local highest_role = nil
|
||||
local highest_role_status = nil
|
||||
for k,v in pairs(roles) do
|
||||
if self.group_rules[tostring(v.id)] then
|
||||
found = true
|
||||
if not highest_role then
|
||||
highest_role = v
|
||||
highest_role_status = self.group_rules[tostring(v.id)]
|
||||
end
|
||||
if v.position > highest_role.position then
|
||||
highest_role = v
|
||||
highest_role_status = self.group_rules[tostring(v.id)]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local allow = highest_role_status
|
||||
return found,(allow and allow == 1)
|
||||
local allow = highest_role_status
|
||||
return found,(allow and allow == 1)
|
||||
end
|
||||
--The following methods extend the ACL class to add the "perm" permissions
|
||||
--(the fallback when no rule/user permissions are found)
|
||||
function command_acl:__init()
|
||||
self.user_rules = {}
|
||||
self.group_rules = {}
|
||||
self.perm_rules = {}
|
||||
self.user_rules = {}
|
||||
self.group_rules = {}
|
||||
self.perm_rules = {}
|
||||
end
|
||||
function command_acl:check_perm(perms)
|
||||
local output = true
|
||||
for k,v in pairs(self.perm_rules) do
|
||||
if (bit.band(perms[1],enum_perms[v]) == 0) then
|
||||
output = false
|
||||
end
|
||||
end
|
||||
return output
|
||||
local output = true
|
||||
for k,v in pairs(self.perm_rules) do
|
||||
if (bit.band(perms[1],enum_perms[v]) == 0) then
|
||||
output = false
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
function command_acl:set_perm_rules(list)
|
||||
assert(type(list)=="table","table expected, got "..type(list))
|
||||
self.perm_rules = list
|
||||
assert(type(list)=="table","table expected, got "..type(list))
|
||||
self.perm_rules = list
|
||||
end
|
||||
function command_acl:export_all_lists()
|
||||
local lists = {
|
||||
users = "",
|
||||
groups = "",
|
||||
perm = ""
|
||||
}
|
||||
for k,v in pairs(self.user_rules) do
|
||||
lists.users = lists.users..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
for k,v in pairs(self.group_rules) do
|
||||
lists.groups = lists.groups..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
for k,v in pairs(self.perm_rules) do
|
||||
lists.perm = lists.perm..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return lists
|
||||
local lists = {
|
||||
users = "",
|
||||
groups = "",
|
||||
perm = ""
|
||||
}
|
||||
for k,v in pairs(self.user_rules) do
|
||||
lists.users = lists.users..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
for k,v in pairs(self.group_rules) do
|
||||
lists.groups = lists.groups..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
for k,v in pairs(self.perm_rules) do
|
||||
lists.perm = lists.perm..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return lists
|
||||
end
|
||||
function command_acl:export_perm_list()
|
||||
local list = ""
|
||||
for k,v in pairs(self.perm_rules) do
|
||||
list = list..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return list
|
||||
local list = ""
|
||||
for k,v in pairs(self.perm_rules) do
|
||||
list = list..k..":"..tostring(v)..";\n"
|
||||
end
|
||||
return list
|
||||
end
|
||||
function command_acl:export_snapshot()
|
||||
return {
|
||||
user_rules = table_utils.deepcopy(self.user_rules),
|
||||
group_rules = table_utils.deepcopy(self.group_rules),
|
||||
perm_rules = table_utils.deepcopy(self.perm_rules)
|
||||
}
|
||||
return {
|
||||
user_rules = table_utils.deepcopy(self.user_rules),
|
||||
group_rules = table_utils.deepcopy(self.group_rules),
|
||||
perm_rules = table_utils.deepcopy(self.perm_rules)
|
||||
}
|
||||
end
|
||||
function command_acl:import_snapshot(t)
|
||||
self.user_rules = t.user_rules
|
||||
self.group_rules = t.group_rules
|
||||
self.perm_rules = t.perm_rules
|
||||
self.user_rules = t.user_rules
|
||||
self.group_rules = t.group_rules
|
||||
self.perm_rules = t.perm_rules
|
||||
end
|
||||
function command_acl:import_perm_list()
|
||||
list:gsub("(%w+):(%d+)",function(id,status)
|
||||
self.perm_rules[id] = status
|
||||
end)
|
||||
list:gsub("(%w+):(%d+)",function(id,status)
|
||||
self.perm_rules[id] = status
|
||||
end)
|
||||
end
|
||||
return command_acl
|
||||
|
|
|
@ -9,108 +9,108 @@ local command_handler = class("Command-handler")
|
|||
local table_utils = import("table-utils")
|
||||
local purify = import("purify")
|
||||
function command_handler:__init(parent_server)
|
||||
self.server_handler = assert(parent_server,"parent server handler not provided")
|
||||
self.command_pool = {}
|
||||
self.prefixes = {}
|
||||
self.command_meta = {
|
||||
plugins = {},
|
||||
categories = {}
|
||||
}
|
||||
self.server_handler = assert(parent_server,"parent server handler not provided")
|
||||
self.command_pool = {}
|
||||
self.prefixes = {}
|
||||
self.command_meta = {
|
||||
plugins = {},
|
||||
categories = {}
|
||||
}
|
||||
end
|
||||
function command_handler:add_prefix(prefix)
|
||||
local purified_prefix = purify.purify_escapes(prefix)
|
||||
self.prefixes[purified_prefix] = purified_prefix
|
||||
return true
|
||||
local purified_prefix = purify.purify_escapes(prefix)
|
||||
self.prefixes[purified_prefix] = purified_prefix
|
||||
return true
|
||||
end
|
||||
function command_handler:remove_prefix(prefix)
|
||||
local purified_prefix = purify.purify_escapes(prefix)
|
||||
if self.prefixes[purified_prefix] or table_utils.count(self.prefixes) <= 1 then
|
||||
self.prefix[purified_prefix] = nil
|
||||
return true
|
||||
else
|
||||
return false, (
|
||||
(self.prefixes[purified_prefix] and "No such prefix") or
|
||||
"Cannot remove the last remaining prefix"
|
||||
)
|
||||
end
|
||||
local purified_prefix = purify.purify_escapes(prefix)
|
||||
if self.prefixes[purified_prefix] or table_utils.count(self.prefixes) <= 1 then
|
||||
self.prefix[purified_prefix] = nil
|
||||
return true
|
||||
else
|
||||
return false, (
|
||||
(self.prefixes[purified_prefix] and "No such prefix") or
|
||||
"Cannot remove the last remaining prefix"
|
||||
)
|
||||
end
|
||||
end
|
||||
function command_handler:get_prefixes()
|
||||
return table_utils.deepcopy(self.prefixes)
|
||||
return table_utils.deepcopy(self.prefixes)
|
||||
end
|
||||
function command_handler:add_command(command)
|
||||
assert(type(command) == "table","command object expected")
|
||||
local purified_name = purify.purify_escapes(command.name)
|
||||
self.command_pool[purified_name] = command
|
||||
if not self.command_meta.plugins[command.parent.name] then
|
||||
self.command_meta.plugins[command.parent.name] = {}
|
||||
end
|
||||
if not self.command_meta.categories[command.options.category] then
|
||||
self.command_meta.categories[command.options.category] = {}
|
||||
end
|
||||
table.insert(self.command_meta.categories[command.options.category],command.name)
|
||||
table.insert(self.command_meta.plugins[command.parent.name],command.name)
|
||||
return command
|
||||
assert(type(command) == "table","command object expected")
|
||||
local purified_name = purify.purify_escapes(command.name)
|
||||
self.command_pool[purified_name] = command
|
||||
if not self.command_meta.plugins[command.parent.name] then
|
||||
self.command_meta.plugins[command.parent.name] = {}
|
||||
end
|
||||
if not self.command_meta.categories[command.options.category] then
|
||||
self.command_meta.categories[command.options.category] = {}
|
||||
end
|
||||
table.insert(self.command_meta.categories[command.options.category],command.name)
|
||||
table.insert(self.command_meta.plugins[command.parent.name],command.name)
|
||||
return command
|
||||
end
|
||||
function command_handler:remove_command(command)
|
||||
assert(type(command) == "table","command object expected")
|
||||
local purified_name = purify.purify_escapes(command.name)
|
||||
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
|
||||
assert(type(command) == "table","command object expected")
|
||||
local purified_name = purify.purify_escapes(command.name)
|
||||
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
|
||||
table_utils.remove_value(self.command_meta.categories[command.options.category],command.name)
|
||||
if #self.command_meta.categories[command.options.category] == 0 then
|
||||
self.command_meta.categories[command.options.category] = nil
|
||||
end
|
||||
self.command_pool[purified_name] = nil
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
table_utils.remove_value(self.command_meta.categories[command.options.category],command.name)
|
||||
if #self.command_meta.categories[command.options.category] == 0 then
|
||||
self.command_meta.categories[command.options.category] = nil
|
||||
end
|
||||
self.command_pool[purified_name] = nil
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
function command_handler:get_command(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
|
||||
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
|
||||
function command_handler:get_commands(name)
|
||||
local list = {}
|
||||
for k,v in pairs(self.command_pool) do
|
||||
table.insert(list,k)
|
||||
end
|
||||
return list
|
||||
local list = {}
|
||||
for k,v in pairs(self.command_pool) do
|
||||
table.insert(list,k)
|
||||
end
|
||||
return list
|
||||
end
|
||||
function command_handler:get_commands_metadata()
|
||||
return table_utils.deepcopy(self.command_meta)
|
||||
return table_utils.deepcopy(self.command_meta)
|
||||
end
|
||||
function command_handler:handle(message)
|
||||
for name,command in pairs(self.command_pool) do
|
||||
if command.options.regex then
|
||||
if message.content:match(command.options.regex) then
|
||||
command:exec(message)
|
||||
return
|
||||
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
|
||||
for name,command in pairs(self.command_pool) do
|
||||
if command.options.regex then
|
||||
if message.content:match(command.options.regex) then
|
||||
command:exec(message)
|
||||
return
|
||||
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
|
||||
else
|
||||
if message.content:match("^"..name.."$") or message.content:match("^"..name.."%s") then
|
||||
command:exec(message)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if message.content:match("^"..name.."$") or message.content:match("^"..name.."%s") then
|
||||
command:exec(message)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return command_handler
|
||||
|
|
|
@ -7,178 +7,162 @@ local command = class("Command")
|
|||
local acl = import("classes.command-acl")
|
||||
local discordia = import("discordia")
|
||||
function command:__init(name,callback)
|
||||
self.rules = acl()
|
||||
self.name = name
|
||||
self.timer = discordia.Date():toMilliseconds()
|
||||
self.options = {
|
||||
allow_bots = false, --allow bots to execute the command
|
||||
typing_decorator = false, --set if the bot should be "typing" while the command executes
|
||||
category = "None", --set category for the command
|
||||
prefix = true, --if true and if regex isn't enabled, 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)
|
||||
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
|
||||
self.options[k] = v
|
||||
assert(name:match("^[-_%w]+$"),"Name can only contain alphanumeric characters, underscores or dashes")
|
||||
self.rules = acl()
|
||||
self.name = name
|
||||
self.timer = discordia.Date():toMilliseconds()
|
||||
self.options = {
|
||||
allow_bots = false, --allow bots to execute the command
|
||||
typing_decorator = false, --set if the bot should be "typing" while the command executes
|
||||
category = "None", --set category for the command
|
||||
prefix = true, --if true check for prefix at the start. if not, don't check for prefix
|
||||
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
|
||||
self.options[k] = v
|
||||
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
|
||||
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
|
||||
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
|
||||
--set the callback to be called on comm:exec(msg)
|
||||
function command:set_callback(fn)
|
||||
assert(type(fn) == "function","function expected, got "..type(fn))
|
||||
self.callback = fn
|
||||
return self
|
||||
assert(type(fn) == "function","function expected, got "..type(fn))
|
||||
self.callback = fn
|
||||
return self
|
||||
end
|
||||
--generate help using only description and usage, or nothing at all
|
||||
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 usage or (type(usage) == "string"),"Usage should be either string or nil, got "..type(usage))
|
||||
local backup_usage_str
|
||||
if self.args then
|
||||
backup_usage_str = self.name.." <"..table.concat(self.args,"> <")..">"
|
||||
else
|
||||
backup_usage_str = "not defined"
|
||||
end
|
||||
local permissions = table.concat(self.rules:export_snapshot()["perms"] or {},"\n")
|
||||
if permissions == "" then
|
||||
permissions = "All"
|
||||
end
|
||||
self.help = {embed = {
|
||||
title = "Help for ``"..self.name.."``",
|
||||
description = description,
|
||||
fields = {
|
||||
{name = "Usage: ",value = usage or backup_usage_str},
|
||||
{name = "Perms: ",value = permissions}
|
||||
}
|
||||
}}
|
||||
return self
|
||||
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))
|
||||
local backup_usage_str
|
||||
if self.args then
|
||||
backup_usage_str = self.name.." <"..table.concat(self.args,"> <")..">"
|
||||
else
|
||||
backup_usage_str = "not defined"
|
||||
end
|
||||
local permissions = table.concat(self.rules:export_snapshot()["perms"] or {"All"},"\n")
|
||||
self.help = {embed = {
|
||||
title = "Help for ``"..self.name.."``",
|
||||
description = description,
|
||||
fields = {
|
||||
{name = "Usage: ",value = usage or backup_usage_str},
|
||||
{name = "Perms: ",value = permissions}
|
||||
}
|
||||
}}
|
||||
return self
|
||||
end
|
||||
--set the help message to be sent
|
||||
function command:set_help(obj,usage)
|
||||
if type(obj) == "string" then
|
||||
self:generate_help(obj,usage)
|
||||
elseif type(obj) == "table" then
|
||||
self.help = obj
|
||||
else
|
||||
error("Type "..type(obj).." cannot be set as a help message")
|
||||
end
|
||||
return self
|
||||
if type(obj) == "table" then
|
||||
self.help = obj
|
||||
else
|
||||
self:generate_help(obj,
|
||||
(type(usage) == "string" and usage)
|
||||
or "No description provided.")
|
||||
end
|
||||
return self
|
||||
end
|
||||
--print the help message, or generate it if there is none
|
||||
function command:get_help()
|
||||
if not self.help then
|
||||
self:generate_help("Description not defined")
|
||||
end
|
||||
return self.help
|
||||
if not self.help then
|
||||
self:generate_help("Description not defined")
|
||||
end
|
||||
return self.help
|
||||
end
|
||||
function command:set_timeout_callback(fn)
|
||||
assert(type(fn) == "function","function expected, got "..type(fn))
|
||||
self.timeout_callback = fn
|
||||
return self
|
||||
assert(type(fn) == "function","function expected, got "..type(fn))
|
||||
self.timeout_callback = fn
|
||||
return self
|
||||
end
|
||||
|
||||
--check the permissions for command
|
||||
function command:check_permissions(message)
|
||||
if message.author.bot and (not self.options.allow_bots) then
|
||||
return false
|
||||
end
|
||||
if discordia.Date():toMilliseconds()-self.options.timeout < self.timer then
|
||||
if self.timeout_callback then
|
||||
self.timeout_callback(fn)
|
||||
return false
|
||||
if message.author.bot and (not self.options.allow_bots) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
self.timer = discordia.Date():toMilliseconds()
|
||||
if self.rules:check_user(message.author.id) then
|
||||
local found,allow = self.rules:check_user(message.author.id)
|
||||
return allow
|
||||
end
|
||||
if self.rules:check_group(message.member.roles) then
|
||||
local found,allow = self.rules:check_group(message.member.roles)
|
||||
return allow
|
||||
end
|
||||
return self.rules:check_perm(message.member:getPermissions(message.channel))
|
||||
if discordia.Date():toMilliseconds()-self.options.timeout < self.timer then
|
||||
if self.timeout_callback then
|
||||
self.timeout_callback(message)
|
||||
return false
|
||||
end
|
||||
end
|
||||
self.timer = discordia.Date():toMilliseconds()
|
||||
if self.rules:check_user(tostring(message.author.id)) then
|
||||
local found,allow = self.rules:check_user(tostring(message.author.id))
|
||||
return allow
|
||||
end
|
||||
if self.rules:check_group(message.member.roles) then
|
||||
local found,allow = self.rules:check_group(message.member.roles)
|
||||
return allow
|
||||
end
|
||||
return self.rules:check_perm(message.member:getPermissions(message.channel))
|
||||
end
|
||||
--the main entry point for the command - execute the callback within after
|
||||
--multiple checks
|
||||
function command:exec(message,args,opts)
|
||||
local exec = self.callback
|
||||
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 exec = self.callback
|
||||
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 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
|
||||
local status,args,opts,err = import("air").parse(content,self.args,message.client,message.guild.id)
|
||||
if status then
|
||||
local callst,status,response = pcall(self.callback,message,args,opts)
|
||||
if callst then
|
||||
if type(status) == "boolean" then
|
||||
if status then
|
||||
message:addReaction("✅")
|
||||
else
|
||||
message:addReaction("❌")
|
||||
end
|
||||
if self:check_permissions(message) then
|
||||
if self.options.typing_decorator then
|
||||
message.channel:broadcastTyping()
|
||||
end
|
||||
else
|
||||
message:addReaction("⚠️")
|
||||
message:reply("An internal error occured: "..status)
|
||||
end
|
||||
else
|
||||
message:addReaction("❌")
|
||||
message:reply(err)
|
||||
local status,args,opts,err = import("air").parse(content,self.args,message.client,message.guild.id)
|
||||
if status then
|
||||
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
|
||||
else
|
||||
message:addReaction("❌")
|
||||
end
|
||||
end
|
||||
--add decorators for the callback
|
||||
function command:set_decorator(fn)
|
||||
assert(type(fn) == "function","a decorator function expected, got "..type(fn))
|
||||
self.decorator = fn
|
||||
return self
|
||||
assert(type(fn) == "function","a decorator function expected, got "..type(fn))
|
||||
self.decorator = fn
|
||||
return self
|
||||
end
|
||||
--get a list of all properties of the command
|
||||
function command:get_properties()
|
||||
return {
|
||||
name = self.name,
|
||||
category = self.options.category,
|
||||
args = table_utils.deepcopy(self.args),
|
||||
help = table_utils.deepcopy(self.help),
|
||||
prefix = self.prefix
|
||||
}
|
||||
return {
|
||||
name = self.name,
|
||||
category = self.options.category,
|
||||
args = table_utils.deepcopy(self.args),
|
||||
help = table_utils.deepcopy(self.help),
|
||||
prefix = self.prefix
|
||||
}
|
||||
end
|
||||
return command
|
||||
|
|
|
@ -2,94 +2,94 @@ local class = import("classes.baseclass")
|
|||
local emitter_proxy = class("EmitterProxy")
|
||||
|
||||
function emitter_proxy:__init(emitter)
|
||||
self.original = emitter
|
||||
self.callback_pool = {}
|
||||
self.original = emitter
|
||||
self.callback_pool = {}
|
||||
end
|
||||
|
||||
function emitter_proxy:on(event,callback)
|
||||
if not self.callback_pool[event] then
|
||||
self.callback_pool[event] = {}
|
||||
end
|
||||
self.callback_pool[event][callback] = callback
|
||||
self.original:on(event,callback)
|
||||
return callback
|
||||
if not self.callback_pool[event] then
|
||||
self.callback_pool[event] = {}
|
||||
end
|
||||
self.callback_pool[event][callback] = callback
|
||||
self.original:on(event,callback)
|
||||
return callback
|
||||
end
|
||||
|
||||
function emitter_proxy:once(event,callback)
|
||||
if not self.callback_pool[event] then
|
||||
self.callback_pool[event] = {}
|
||||
end
|
||||
local wrapper = function(...)
|
||||
callback(...)
|
||||
self.callback_pool[event][callback] = nil
|
||||
end
|
||||
self.callback_pool[event][callback] = wrapper
|
||||
self.callback_pool[event][wrapper] = wrapper
|
||||
self.original:once(event,wrapper)
|
||||
return callback
|
||||
if not self.callback_pool[event] then
|
||||
self.callback_pool[event] = {}
|
||||
end
|
||||
local wrapper = function(...)
|
||||
callback(...)
|
||||
self.callback_pool[event][callback] = nil
|
||||
end
|
||||
self.callback_pool[event][callback] = wrapper
|
||||
self.callback_pool[event][wrapper] = wrapper
|
||||
self.original:once(event,wrapper)
|
||||
return callback
|
||||
end
|
||||
|
||||
function emitter_proxy:removeListener(event,callback)
|
||||
if self.callback_pool[event] and self.callback_pool[event][callback] then
|
||||
self.callback_pool[event][callback] = nil
|
||||
self.original:removeListener(event,callback)
|
||||
end
|
||||
if self.callback_pool[event] and self.callback_pool[event][callback] then
|
||||
self.callback_pool[event][callback] = nil
|
||||
self.original:removeListener(event,callback)
|
||||
end
|
||||
end
|
||||
|
||||
function emitter_proxy:removeAllListeners(event,callback)
|
||||
if self.callback_pool[event] then
|
||||
for k,v in pairs(self.callback_pool[event]) do
|
||||
self.original:removeListener(event,v)
|
||||
if self.callback_pool[event] then
|
||||
for k,v in pairs(self.callback_pool[event]) do
|
||||
self.original:removeListener(event,v)
|
||||
end
|
||||
self.callback_pool[event] = nil
|
||||
end
|
||||
self.callback_pool[event] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function emitter_proxy:listeners(event)
|
||||
local copy = {}
|
||||
if self.callback_pool[event] then
|
||||
for k,v in pairs(self.callback_pool[event]) do
|
||||
table.insert(copy,v)
|
||||
local copy = {}
|
||||
if self.callback_pool[event] then
|
||||
for k,v in pairs(self.callback_pool[event]) do
|
||||
table.insert(copy,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
return copy
|
||||
return copy
|
||||
end
|
||||
|
||||
function emitter_proxy:listenerCount(event)
|
||||
local count = 0
|
||||
if event then
|
||||
if self.callback_pool[event] then
|
||||
for k,v in pairs(self.callback_pool[event]) do
|
||||
count = count + 1
|
||||
end
|
||||
local count = 0
|
||||
if event then
|
||||
if self.callback_pool[event] then
|
||||
for k,v in pairs(self.callback_pool[event]) do
|
||||
count = count + 1
|
||||
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
|
||||
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
|
||||
return count
|
||||
end
|
||||
|
||||
function emitter_proxy:propogate(event,emitter)
|
||||
if not self.callback_pool[event] then
|
||||
self.callback_pool[event] = {}
|
||||
end
|
||||
local emitter_propogate_handler = function(...)
|
||||
emitter:emit(event,...)
|
||||
end
|
||||
self.callback_pool[event][emitter_propogate_handler] = emitter_propogate_handler
|
||||
self.original:on(event,emitter_propogate_handler)
|
||||
return emitter_propogate_handler
|
||||
if not self.callback_pool[event] then
|
||||
self.callback_pool[event] = {}
|
||||
end
|
||||
local emitter_propogate_handler = function(...)
|
||||
emitter:emit(event,...)
|
||||
end
|
||||
self.callback_pool[event][emitter_propogate_handler] = emitter_propogate_handler
|
||||
self.original:on(event,emitter_propogate_handler)
|
||||
return emitter_propogate_handler
|
||||
end
|
||||
|
||||
function emitter_proxy:destroy()
|
||||
for k,v in pairs(self.callback_pool) do
|
||||
for k2,v2 in pairs(v) do
|
||||
self.original:removeListener(k,v2)
|
||||
for k,v in pairs(self.callback_pool) do
|
||||
for k2,v2 in pairs(v) do
|
||||
self.original:removeListener(k,v2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return emitter_proxy
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
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
|
|
@ -1,90 +0,0 @@
|
|||
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)")
|
||||
|
||||
|
|
@ -12,130 +12,130 @@ local core = import("core")
|
|||
local emitter_proxy = import("classes.emitter-proxy")
|
||||
local table_utils = import("table-utils")
|
||||
function plugin_handler:__init(parent_server)
|
||||
assert(parent_server,"server handler to assign the plugin handler to has not been provided")
|
||||
self.server_handler = parent_server
|
||||
self.plugins = {}
|
||||
self.plugin_info = {}
|
||||
self.plugin_paths = {}
|
||||
self.server_handler.event_emitter:on("serverSaveConfig",function()
|
||||
print("[SERVER] Saving plugins configs")
|
||||
for name,plugin in pairs(self.plugins) do
|
||||
self:save_plugin_config(name)
|
||||
end
|
||||
end)
|
||||
assert(parent_server,"server handler to assign the plugin handler to has not been provided")
|
||||
self.server_handler = parent_server
|
||||
self.plugins = {}
|
||||
self.plugin_info = {}
|
||||
self.plugin_paths = {}
|
||||
self.server_handler.event_emitter:on("serverSaveConfig",function()
|
||||
print("[SERVER] Saving plugins configs")
|
||||
for name,plugin in pairs(self.plugins) do
|
||||
self:save_plugin_config(name)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function plugin_handler:save_plugin_config(name)
|
||||
if self.plugins[name] then
|
||||
file.writeJSON(self.server_handler.config_path..name..".json",self.plugins[name].__env.config)
|
||||
end
|
||||
if self.plugins[name] then
|
||||
file.writeJSON(self.server_handler.config_path..name..".json",self.plugins[name].__env.config)
|
||||
end
|
||||
end
|
||||
|
||||
function plugin_handler:add_plugin_folder(path)
|
||||
assert(type(path) == "string","path should be a string, got "..type(path))
|
||||
table.insert(self.plugin_paths,path)
|
||||
assert(type(path) == "string","path should be a string, got "..type(path))
|
||||
table.insert(self.plugin_paths,path)
|
||||
end
|
||||
|
||||
function plugin_handler:scan_folder(path)
|
||||
local file = io.open(path.."/meta.json","r")
|
||||
if file then
|
||||
local metadata,code,err = json.decode(file:read("*a"))
|
||||
if metadata and metadata.name then
|
||||
self.plugin_info[metadata.name] = metadata
|
||||
self.plugin_info[metadata.name].path = path.."/"
|
||||
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
|
||||
local file = io.open(path.."/meta.json","r")
|
||||
if file then
|
||||
local metadata,code,err = json.decode(file:read("*a"))
|
||||
if metadata and metadata.name then
|
||||
self.plugin_info[metadata.name] = metadata
|
||||
self.plugin_info[metadata.name].path = path.."/"
|
||||
self.plugin_info[metadata.name].loaded = false
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
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()
|
||||
for k,v in pairs(self.plugin_paths) do
|
||||
if file.existsDir(v) then
|
||||
file.ls(v):gsub("[^\n]+",function(c)
|
||||
self:scan_folder(v..c)
|
||||
end)
|
||||
for k,v in pairs(self.plugin_paths) do
|
||||
if file.existsDir(v) then
|
||||
file.ls(v):gsub("[^\n]+",function(c)
|
||||
self:scan_folder(v..c)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function plugin_handler:list_loadable()
|
||||
return table_utils.deepcopy(self.plugin_info)
|
||||
return table_utils.deepcopy(self.plugin_info)
|
||||
end
|
||||
|
||||
function plugin_handler:load(name)
|
||||
if not self.plugin_info[name] then
|
||||
return false, "No such plugin"
|
||||
end
|
||||
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"
|
||||
end
|
||||
if self.plugin_info[name].loaded then
|
||||
return false, "Plugin is already loaded"
|
||||
end
|
||||
local environment = setmetatable({
|
||||
id = self.server_handler.id,
|
||||
globals = self.server_handler.config,
|
||||
signals = emitter_proxy(self.server_handler.signal_emitter),
|
||||
client = self.server_handler.client,
|
||||
events = emitter_proxy(self.server_handler.event_emitter),
|
||||
discordia = import("discordia"),
|
||||
server = self.server_handler,
|
||||
command_handler = self.server_handler.command_handler,
|
||||
plugin_handler = self.server_handler.plugin_handler,
|
||||
log = function() end,
|
||||
config = self:load_plugin_config(name),
|
||||
import = import,
|
||||
},{__index = _G})
|
||||
local plugin_meta = self.plugin_info[name]
|
||||
if file.exists(plugin_meta.path..plugin_meta.main) then
|
||||
environment["plugin_path"] = plugin_meta.path
|
||||
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)
|
||||
if plugin_loader then
|
||||
local plugin_object = plugin_loader()
|
||||
if plugin_object then
|
||||
plugin_object.name = name
|
||||
plugin_object:load(environment)
|
||||
self.plugins[name] = plugin_object
|
||||
self.plugins[name].__env = environment
|
||||
self.plugin_info[name].loaded = true
|
||||
return true
|
||||
else
|
||||
return false, "Plugin object missing"
|
||||
end
|
||||
else
|
||||
return false, err
|
||||
if not self.plugin_info[name] then
|
||||
return false, "No such plugin"
|
||||
end
|
||||
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"
|
||||
end
|
||||
if self.plugin_info[name].loaded then
|
||||
return false, "Plugin is already loaded"
|
||||
end
|
||||
local environment = setmetatable({
|
||||
id = self.server_handler.id,
|
||||
globals = self.server_handler.config,
|
||||
signals = emitter_proxy(self.server_handler.signal_emitter),
|
||||
client = self.server_handler.client,
|
||||
events = emitter_proxy(self.server_handler.event_emitter),
|
||||
discordia = import("discordia"),
|
||||
server = self.server_handler,
|
||||
command_handler = self.server_handler.command_handler,
|
||||
plugin_handler = self.server_handler.plugin_handler,
|
||||
log = function() end,
|
||||
config = self:load_plugin_config(name),
|
||||
import = import,
|
||||
},{__index = _G})
|
||||
local plugin_meta = self.plugin_info[name]
|
||||
if file.exists(plugin_meta.path..plugin_meta.main) then
|
||||
environment["plugin_path"] = plugin_meta.path
|
||||
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)
|
||||
if plugin_loader then
|
||||
local plugin_object = plugin_loader()
|
||||
if plugin_object then
|
||||
plugin_object.name = name
|
||||
plugin_object:load(environment)
|
||||
self.plugins[name] = plugin_object
|
||||
self.plugins[name].__env = environment
|
||||
self.plugin_info[name].loaded = true
|
||||
return true
|
||||
else
|
||||
return false, "Plugin object missing"
|
||||
end
|
||||
else
|
||||
return false, err
|
||||
end
|
||||
else
|
||||
return false, "File specified as the main file is inaccessible"
|
||||
end
|
||||
else
|
||||
return false, "File specified as the main file is inaccessible"
|
||||
end
|
||||
end
|
||||
|
||||
function plugin_handler:unload(name)
|
||||
if self.plugins[name] then
|
||||
self.plugins[name].__env.signals:destroy()
|
||||
self.plugins[name].__env.events:destroy()
|
||||
self.plugins[name]:unload()
|
||||
self.plugin_info[name].loaded = false
|
||||
return true
|
||||
else
|
||||
return false,"Plugin is not loaded"
|
||||
end
|
||||
if self.plugins[name] then
|
||||
self.plugins[name].__env.signals:destroy()
|
||||
self.plugins[name].__env.events:destroy()
|
||||
self.plugins[name]:unload()
|
||||
self.plugin_info[name].loaded = false
|
||||
return true
|
||||
else
|
||||
return false,"Plugin is not loaded"
|
||||
end
|
||||
end
|
||||
return plugin_handler
|
||||
|
|
|
@ -2,58 +2,58 @@ local class = import("classes.baseclass")
|
|||
local plugin = class("Plugin")
|
||||
|
||||
function plugin:__init()
|
||||
self.command_pool = {}
|
||||
self.config = {}
|
||||
self.command_pool = {}
|
||||
self.config = {}
|
||||
end
|
||||
|
||||
function plugin:load(environment)
|
||||
self.command_handler = environment.server.command_handler
|
||||
for k,v in pairs(self.command_pool) do
|
||||
self.command_handler:add_command(v)
|
||||
end
|
||||
self.command_handler = environment.server.command_handler
|
||||
for k,v in pairs(self.command_pool) do
|
||||
self.command_handler:add_command(v)
|
||||
end
|
||||
end
|
||||
|
||||
function plugin:unload()
|
||||
if self.removal_callback then
|
||||
self.removal_callback()
|
||||
end
|
||||
for k,v in pairs(self.command_pool) do
|
||||
self.command_handler:remove_command(v)
|
||||
end
|
||||
if self.removal_callback then
|
||||
self.removal_callback()
|
||||
end
|
||||
for k,v in pairs(self.command_pool) do
|
||||
self.command_handler:remove_command(v)
|
||||
end
|
||||
end
|
||||
|
||||
function plugin:for_all_commands(fn)
|
||||
assert(type(fn)=="function","function expected, got "..type(fn))
|
||||
for k,v in pairs(self.command_pool) do
|
||||
fn(v)
|
||||
end
|
||||
assert(type(fn)=="function","function expected, got "..type(fn))
|
||||
for k,v in pairs(self.command_pool) do
|
||||
fn(v)
|
||||
end
|
||||
end
|
||||
|
||||
function plugin:for_every_new_command(fn)
|
||||
assert(type(fn)=="function","function expected, got "..type(fn))
|
||||
self.decorator = fn
|
||||
assert(type(fn)=="function","function expected, got "..type(fn))
|
||||
self.decorator = fn
|
||||
end
|
||||
|
||||
function plugin:add_command(command_object)
|
||||
if self.decorator then
|
||||
self.fn(command_object)
|
||||
end
|
||||
command_object.parent = self
|
||||
self.command_pool[command_object] = command_object
|
||||
--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
|
||||
if self.command_handler then
|
||||
self.command_handler:add_command(command_object)
|
||||
end
|
||||
if self.decorator then
|
||||
self.fn(command_object)
|
||||
end
|
||||
command_object.parent = self
|
||||
self.command_pool[command_object] = command_object
|
||||
--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
|
||||
if self.command_handler then
|
||||
self.command_handler:add_command(command_object)
|
||||
end
|
||||
end
|
||||
|
||||
function plugin:remove_command(command_object)
|
||||
if self.command_pool[command_object] then
|
||||
self.command_pool[command_object] = nil
|
||||
end
|
||||
--remove command after post-init state
|
||||
if self.command_handler then
|
||||
self.command_handler:remove_command(command_object)
|
||||
end
|
||||
if self.command_pool[command_object] then
|
||||
self.command_pool[command_object] = nil
|
||||
end
|
||||
--remove command after post-init state
|
||||
if self.command_handler then
|
||||
self.command_handler:remove_command(command_object)
|
||||
end
|
||||
end
|
||||
return plugin
|
||||
|
|
|
@ -8,96 +8,96 @@ local eventlist = import("eventlist")
|
|||
local discordia = import("discordia")
|
||||
|
||||
local function check_partitioning(id,...)
|
||||
args = {...}
|
||||
v = args[1]
|
||||
if type(v) == "table" and v.guild and v.guild.id == id then
|
||||
return true
|
||||
elseif not (type(v) == "table") then
|
||||
return true
|
||||
elseif type(v) == "table" and (not v.guild) and (tostring(v):find("Guild: ")) and v.id == id then
|
||||
return true
|
||||
elseif type(v) == "table" and (not v.guild) and (v.message) and (v.message.guild.id == id) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
args = {...}
|
||||
v = args[1]
|
||||
if type(v) == "table" and v.guild and v.guild.id == id then
|
||||
return true
|
||||
elseif not (type(v) == "table") then
|
||||
return true
|
||||
elseif type(v) == "table" and (not v.guild) and (tostring(v):find("Guild: ")) and v.id == id then
|
||||
return true
|
||||
elseif type(v) == "table" and (not v.guild) and (v.message) and (v.message.guild.id == id) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function server_handler:__init(client,guild,options)
|
||||
assert(type(client) == "table","discordia client expected, got "..type(client))
|
||||
self.client = client
|
||||
self.uptime = discordia.Date()
|
||||
self.event_emitter = core.Emitter:new()
|
||||
self.signal_emitter = core.Emitter:new()
|
||||
self.plugin_handler = plugin_handler(self)
|
||||
self.command_handler = command_handler(self)
|
||||
self.id = guild.id
|
||||
--conifgurable properties
|
||||
self.config_path = options.path or "./servers/%id/"
|
||||
self.autosave = options.path or true
|
||||
self.autosave_frequency = options.autosave_frequency or 10
|
||||
self.plugin_search_paths = options.plugin_search_paths or {"./plugins/"}
|
||||
self.default_plugins = options.default_plugins or {"test"}
|
||||
self.default_prefixes = options.default_prefixes or {"&","<@!"..self.client.user.id..">"}
|
||||
self.config = {}
|
||||
self.config_path = self.config_path:gsub("%%id",self.id)
|
||||
self:load_config()
|
||||
self.config["prefix"] = self.config["prefix"] or self.default_prefixes[1] or "(missing prefix)"
|
||||
self.message_counter = 0
|
||||
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)
|
||||
if msg.guild and msg.guild.id == self.id then
|
||||
self.command_handler:handle(msg)
|
||||
assert(type(client) == "table","discordia client expected, got "..type(client))
|
||||
self.client = client
|
||||
self.uptime = discordia.Date()
|
||||
self.event_emitter = core.Emitter:new()
|
||||
self.signal_emitter = core.Emitter:new()
|
||||
self.plugin_handler = plugin_handler(self)
|
||||
self.command_handler = command_handler(self)
|
||||
self.id = guild.id
|
||||
--conifgurable properties
|
||||
self.config_path = options.path or "./servers/%id/"
|
||||
self.autosave = options.path or true
|
||||
self.autosave_frequency = options.autosave_frequency or 10
|
||||
self.plugin_search_paths = options.plugin_search_paths or {"./plugins/"}
|
||||
self.default_plugins = options.default_plugins or {"test"}
|
||||
self.default_prefixes = options.default_prefixes or {"&","<@!"..self.client.user.id..">"}
|
||||
self.config = {}
|
||||
self.config_path = self.config_path:gsub("%%id",self.id)
|
||||
self:load_config()
|
||||
self.config["prefix"] = self.config["prefix"] or self.default_prefixes[1] or "(missing prefix)"
|
||||
self.message_counter = 0
|
||||
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)
|
||||
if msg.guild and msg.guild.id == self.id then
|
||||
self.command_handler:handle(msg)
|
||||
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)
|
||||
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
|
||||
|
||||
function server_handler:load_config(path)
|
||||
print("[SERVER] Loading config")
|
||||
if path then
|
||||
self.config = file.readJSON(path,{})
|
||||
else
|
||||
self.config = file.readJSON(self.config_path.."config.json")
|
||||
end
|
||||
self.event_emitter:emit("serverLoadConfig",self.config)
|
||||
print("[SERVER] Loading config")
|
||||
if path then
|
||||
self.config = file.readJSON(path,{})
|
||||
else
|
||||
self.config = file.readJSON(self.config_path.."config.json")
|
||||
end
|
||||
self.event_emitter:emit("serverLoadConfig",self.config)
|
||||
end
|
||||
|
||||
function server_handler:save_config(path)
|
||||
print("[SERVER] Saving config")
|
||||
if path then
|
||||
file.writeJSON(path,self.config)
|
||||
else
|
||||
file.writeJSON(self.config_path.."config.json",self.config)
|
||||
end
|
||||
self.event_emitter:emit("serverSaveConfig",self.config)
|
||||
print("[SERVER] Saving config")
|
||||
if path then
|
||||
file.writeJSON(path,self.config)
|
||||
else
|
||||
file.writeJSON(self.config_path.."config.json",self.config)
|
||||
end
|
||||
self.event_emitter:emit("serverSaveConfig",self.config)
|
||||
end
|
||||
return server_handler
|
||||
|
|
|
@ -7,4 +7,29 @@ local save = command("save",{
|
|||
end
|
||||
})
|
||||
plugin:add_command(save)
|
||||
local err = command("error",{
|
||||
help = "Force error",
|
||||
exec = function()
|
||||
error("Errored successfully!")
|
||||
end
|
||||
})
|
||||
plugin:add_command(err)
|
||||
local perm_error = command("permerror",{
|
||||
help = "Force permission error",
|
||||
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",{
|
||||
help = "Force a return value error",
|
||||
exec = function(msg)
|
||||
msg:reply("nono :)")
|
||||
return false
|
||||
end
|
||||
})
|
||||
plugin:add_command(return_error)
|
||||
return plugin
|
||||
|
|
Loading…
Reference in New Issue