Compare commits

...

6 Commits

Author SHA1 Message Date
Yessiest 07501e7fa0 Fixed a bug in AIR 2022-05-21 21:31:14 +04:00
Yessiest b4d0f9ea54 Updated guide on self-hosting the bot 2022-05-21 20:46:02 +04:00
Yessiest e87056a2c2 Freaky ghost bed 2022-05-21 19:19:02 +04:00
Yessiest f9bb6824f3 Ain't afraid of no bed 2022-05-21 01:21:23 +04:00
Yessiest ca17b716ba ain't afraid of no sleep, 2022-05-20 22:20:49 +04:00
Yessiest 38c9e580b9 bustin 2022-05-20 21:52:22 +04:00
53 changed files with 2370 additions and 3694 deletions

View File

@ -1,11 +1,13 @@
[![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
yet another 512mb bot. it's running on a discord library for the luvit framework called [Discordia](https://github.com/SinisterRectus/discordia)
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).
# Self-hosting
place the bot token in the "./token" file, change server id in bot.lua to the id of your server.
compile [luvit](https://luvit.io) and run bot.lua.
1. Get a bot token and place it in ./token
2. Install libqalculate and compile luaqalc
3. Place the ``libqalculator.so`` in ./libraries/
4. Get [luvit](https://luvit.io) and run ``bot.lua`` using the ``luvit`` binary
# License
```

View File

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

Binary file not shown.

View File

@ -1,34 +1,9 @@
--rewrite this lib (P.S: done)
--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.match_strings = function(string)
local strings = {}
string = string:gsub("\"(.-[^\\])\"",function(capt)
string_id = string_id + 1
strings["%str"..string_id] = capt:gsub("\\\"","\"")
return " %str"..string_id
end)
return string,strings
end
local function parse_strings(thing,strings)
--find all strings and replace them with string ids with no spaces
local strings = strings or {}
local string_count = 0
local function get_string(text)
string_count = string_count + 1
local id = "&;"..tostring(string_count)..";&"
strings[string_count] = text
return id
end
thing = thing:gsub("(\")\"",get_string)
thing = thing:gsub("(\".-[^\\])\"",get_string)
thing = thing:gsub("(\')\'",get_string)
thing = thing:gsub("(\'.-[^\\])\'",get_string)
return thing,strings
end
--this table will look up special types
special_case = {
object_types = {
["voiceChannel"] = function(id,client,guild_id)
local guild = client:getGuild(guild_id)
local channel = guild:getChannel(id:match("(%d+)[^%d]*$"))
@ -116,88 +91,63 @@ special_case = {
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
}
air.parse = function(string,argmatch,client,guild_id)
local args,opts = {},{}
local string_id = 0
local strings = {}
string = string:gsub("[%s\n]+\"\"",function(capt)
string_id = string_id + 1
strings["%str"..string_id] = ""
return " %str"..string_id
end)
string = string:gsub("[%s\n]+\"(.-[^\\])\"",function(capt)
string_id = string_id + 1
strings["%str"..string_id] = capt:gsub("\\\"","\"")
return " %str"..string_id
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
if argmatch and #argmatch > 0 then
local match,err = false
local new_args = {}
for k,v in pairs(argmatch) do
if not args[k] then
match = false
err = "Missing arguments: "..table.concat(argmatch,", ",k)
break
end
if v == "number" and tonumber(args[k]) then
match = true
new_args[k] = tonumber(args[k])
elseif v == "string" then
match = true
new_args[k] = args[k]
elseif special_case[v] then
match,new_args[k] = special_case[v](args[k],client,guild_id)
local strings = parse_string(string,"[\"']")
local argmatch = table_utils.shallowcopy(argmatch or {})
local tokens,args,opts = {},{},{}
-- Tokenize
for k,v in pairs(strings) do
local padded_string = v:match("^%s*(.+)%s*$")
if padded_string:match("^[\"'].*[\"']$") then
table.insert(tokens,padded_string)
else
match = false
end
if match == false then
err = "Type mismatch for argument "..k..": "..argmatch[k].." expected."
break
v:gsub("%S+",function(text)
table.insert(tokens,text)
end)
end
end
for k,v in pairs(args) do
if not new_args[k] then
new_args[k] = v
end
end
return match,new_args,opts,err
-- Remove opts and match arguments
for k,v in pairs(tokens) do
if v:match("^%-%-%w+=$") then
local optname = table.remove(tokens,k):match("^%-%-(%w+)=$")
local value = tokens[k]
opts[optname] = value
elseif v:match("^%-%-%w+$") then
local optname = v:match("^%-%-(%w+)$")
opts[optname] = true
elseif v:match("^%-%w+$") then
local opts = v:gsub("%w",function(c)
opts[c] = true
end)
else
return true,args,opts
local arg = table.remove(argmatch,1)
if arg then
local status,obj = object_types[arg](v,client,guild_id)
if not status then
return false, args, opts, "Mismatched argument "..tostring(#arg)..": "..arg.." expected."
end
table.insert(args,obj)
else
table.insert(args,select(2,object_types["string"](v)))
end
end
end
if #argmatch > 0 then
return false, args, opts, "Missing arguments: "..table.concat(argmatch,", ")
end
return true, args, opts
end
return air

View File

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

View File

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

View File

@ -6,24 +6,8 @@ local discordia = import("discordia")
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
end
end
local allow = highest_role_status
function command_acl:check_group(highest_role)
local allow = self.group_rules[tostring(highest_role.id)]
return found,(allow and allow == 1)
end
--The following methods extend the ACL class to add the "perm" permissions

View File

@ -10,106 +10,93 @@ 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.pool = {}
self.prefixes = {}
self.command_meta = {
self.meta = {
plugins = {},
categories = {}
}
end
function command_handler:add_prefix(prefix)
local purified_prefix = purify.purify_escapes(prefix)
self.prefixes[purified_prefix] = purified_prefix
self.prefixes[prefix] = purify.purify_escapes(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
if self.prefixes[prefix] and table_utils.count(self.prefixes) > 1 then
self.prefixes[prefix] = nil
return true
else
return false, (
(self.prefixes[purified_prefix] and "No such prefix") or
"Cannot remove the last remaining prefix"
)
end
if not self.prefixes[prefix] then
return false, "Prefix not found"
end
return false, "Cannot remove last remaining prefix!"
end
function command_handler:get_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] = {}
if self.pool[command.name] then
return false, "Already have a command with the same name"
end
if not self.command_meta.categories[command.options.category] then
self.command_meta.categories[command.options.category] = {}
self.pool[command.name] = command
if not self.meta.plugins[command.parent.name] then
self.meta.plugins[command.parent.name] = {}
end
table.insert(self.command_meta.categories[command.options.category],command.name)
table.insert(self.command_meta.plugins[command.parent.name],command.name)
self.meta.plugins[command.parent.name][command.name] = command.name
if not self.meta.categories[command.category] then
self.meta.categories[command.category] = {}
end
self.meta.categories[command.category][command.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
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
if not self.pool[command.name] then
return false
end
self.pool[command.name] = nil
self.meta.categories[command.category][command.name] = nil
self.meta.plugins[command.parent.name][command.name] = nil
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
return self.pool[name]
end
function command_handler:get_commands(name)
local list = {}
for k,v in pairs(self.command_pool) do
for k,v in pairs(self.pool) do
table.insert(list,k)
end
return list
end
function command_handler:get_commands_metadata()
return table_utils.deepcopy(self.command_meta)
function command_handler:get_metadata()
local plugins,categories = {},{}
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
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
end
else
if message.content:match("^"..name.."$") or message.content:match("^"..name.."%s") then
command:exec(message)
return
function command_handler:handle(message,ignore_flag)
local content = message.content
local prefix = ""
local command
for k,v in pairs(self.prefixes) do
if content:match("^"..v) then
prefix = k
end
end
command = content:sub(prefix:len()+1,-1):match("^[%-_%w]+")
if self.pool[command] then
if (prefix == "") and self.pool[command].options.prefix == false then
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

View File

@ -7,16 +7,15 @@ local command = class("Command")
local acl = import("classes.command-acl")
local discordia = import("discordia")
function command:__init(name,callback)
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.category = "None"
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
prefix = true, --if true check for prefix at the start. if not, don't check for prefix
timeout = 1000, --set the timeout for a command
}
if type(callback) == "table" then
@ -24,6 +23,9 @@ function command:__init(name,callback)
self.options[k] = v
end
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
@ -35,12 +37,8 @@ function command:__init(name,callback)
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
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
@ -61,10 +59,7 @@ function command:generate_help(description,usage)
else
backup_usage_str = "not defined"
end
local permissions = table.concat(self.rules:export_snapshot()["perms"] or {},"\n")
if permissions == "" then
permissions = "All"
end
local permissions = table.concat(self.rules:export_snapshot()["perms"] or {"All"},"\n")
self.help = {embed = {
title = "Help for ``"..self.name.."``",
description = description,
@ -77,12 +72,12 @@ function command:generate_help(description,usage)
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
if type(obj) == "table" then
self.help = obj
else
error("Type "..type(obj).." cannot be set as a help message")
self:generate_help(obj,
(type(usage) == "string" and usage)
or "No description provided.")
end
return self
end
@ -93,6 +88,7 @@ function command:get_help()
end
return self.help
end
function command:set_timeout_callback(fn)
assert(type(fn) == "function","function expected, got "..type(fn))
self.timeout_callback = fn
@ -100,30 +96,36 @@ function command:set_timeout_callback(fn)
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
function command:check_permissions(message,special_flag)
local ctime = discordia.Date():toMilliseconds()
if (ctime-self.options.timeout < self.timer) and (not ignore_flag) then
if self.timeout_callback then
self.timeout_callback(fn)
return false
self.timeout_callback(message)
end
return false
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
-- user rules first, group second, permission rules last
if ignore_flag == 2 then
return true
end
if self.rules:check_group(message.member.roles) then
local found,allow = self.rules:check_group(message.member.roles)
return allow
local User, allowUser = self.rules:check_user(tostring(message.author.id))
local Group, allowGroup = self.rules:check_group(message.member.highestRole)
if User then
return allowUser
end
if Group then
return allowGroup
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)
function command:exec(message,ignore_flag)
if message.author.bot and (not self.options.allow_bots) then
return false
end
if self:check_permissions(message,ignore_flag) then
local exec = self.callback
if not self.callback then
error("Callback not set for command "..self.name)
@ -131,14 +133,8 @@ function command:exec(message,args,opts)
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
@ -147,23 +143,19 @@ function command:exec(message,args,opts)
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("")
message:addReaction((status and "") or "")
end
return
end
else
message:addReaction("⚠️")
message:reply("An internal error occured: "..status)
return
end
else
message:addReaction("")
message:reply(err)
return
end
else
message:addReaction("")
end
end
--add decorators for the callback
function command:set_decorator(fn)

View File

@ -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

View File

@ -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)")

View File

@ -11,6 +11,8 @@ local json = import("json")
local core = import("core")
local emitter_proxy = import("classes.emitter-proxy")
local table_utils = import("table-utils")
local log = import("logging")
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
@ -18,7 +20,7 @@ function plugin_handler:__init(parent_server)
self.plugin_info = {}
self.plugin_paths = {}
self.server_handler.event_emitter:on("serverSaveConfig",function()
print("[SERVER] Saving plugins configs")
log("SERVER", "Saving plugins configs")
for name,plugin in pairs(self.plugins) do
self:save_plugin_config(name)
end
@ -98,7 +100,7 @@ function plugin_handler:load(name)
server = self.server_handler,
command_handler = self.server_handler.command_handler,
plugin_handler = self.server_handler.plugin_handler,
log = function() end,
log = log,
config = self:load_plugin_config(name),
import = import,
},{__index = _G})

View File

@ -56,4 +56,27 @@ function plugin:remove_command(command_object)
self.command_handler:remove_command(command_object)
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

View File

@ -6,6 +6,7 @@ local command_handler = import("classes.command-handler")
local file = import("file")
local eventlist = import("eventlist")
local discordia = import("discordia")
local log = import("logging")
local function check_partitioning(id,...)
args = {...}
@ -38,8 +39,8 @@ function server_handler:__init(client,guild,options)
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.default_plugins = options.default_plugins or {"plugins"}
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()
@ -74,7 +75,7 @@ function server_handler:__init(client,guild,options)
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))
log("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)
@ -82,7 +83,7 @@ function server_handler:__init(client,guild,options)
end
function server_handler:load_config(path)
print("[SERVER] Loading config")
log("SERVER", "Loading config")
if path then
self.config = file.readJSON(path,{})
else
@ -92,7 +93,7 @@ function server_handler:load_config(path)
end
function server_handler:save_config(path)
print("[SERVER] Saving config")
log("SERVER", "Saving config")
if path then
file.writeJSON(path,self.config)
else

View File

@ -8,6 +8,7 @@ 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.
]]
local find_strings = require("string_parse")
local safe_regex = function(str,pattern)
local status,ret = pcall(string.match,str,pattern)
@ -18,6 +19,7 @@ end
if _VERSION=="Lua 5.1" then
table.unpack = unpack
end
local cron = {
directive_handler = nil
}
@ -200,50 +202,6 @@ local syntax = {
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 newtable = {}
for i = pos,#t do
@ -264,7 +222,6 @@ cron._split = function(text)
return tokens
end
cron._split_with_strings = function(text)
-- Parse strings
local nt = find_strings(text)
@ -317,6 +274,9 @@ cron.parse_directive = function(tokens)
break
end
end
if stop then
break
end
end
-- We use a delimiter so that command start wouldn't be ambiguous
-- Rather than defining an amount of arguments to directives, we

View File

@ -2,7 +2,7 @@
--That is, unability to load core modules from the files that were required
return function(reqfunc)
local function import(path)
local paths = { }
local paths = {}
package.path:gsub("[^;]+",function(path)
table.insert(paths,path)
end)
@ -23,14 +23,14 @@ return function(reqfunc)
return reqfunc(path)
else
content = file:read("*a")
local func,err = load(content,"import: "..filename,nil,setmetatable({
local f,err = load(content,"import: "..filename,nil,setmetatable({
require = reqfunc,
import = import,
},{__index = _G}))
if err then
error(err)
error("[import: "..filename.."] "..tostring(err))
end
return func()
return f()
end
end
return import

26
libraries/logging.lua Normal file
View File

@ -0,0 +1,26 @@
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 9e6f09050f9ebecc4ffcea1234b920468254a79b
Subproject commit 23fcea17c2eb380b6a6b1ba848d5bc7ebdc38b05

View File

@ -51,7 +51,7 @@ end
function markov.walk(self,start)
if not self.init then
error("Attempted to use an instance method on an uninitialized instance")
error("Attempted to use method on uninitialized instances")
end
local random = math.random(0,1e7)/1e7
local words = {}
@ -76,7 +76,7 @@ end
function markov.expand_vocabulary(self,source)
if not self.init then
error("Attempted to use an instance method on an uninitialized instance")
error("Attempted to use method on uninitialized instances")
end
self.net = register_words(source,self.net)
end

View File

@ -21,6 +21,8 @@ purify.purify_pings = function(msg,input)
end
text = text:gsub("<@(%D*)"..id..">",substitution)
end
text = text:gsub("@everyone","")
text = text:gsub("@here","")
return text
end

View File

@ -0,0 +1,44 @@
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

@ -28,6 +28,14 @@ utilities.shallowcopy = function(orig)
end
return copy
end
utilities.listcopy = function(orig)
local list = {}
for k,v in pairs(orig) do
table.insert(list,v)
end
return list
end
--overwrite the original table's properties with new properties
utilities.overwrite = function(original,overwrite)
local new = utilities.shallowcopy(original)

View File

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

51
plugins/cron/help.lua Normal file
View File

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

6
plugins/debug/help.lua Normal file
View File

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

View File

@ -1,10 +1,32 @@
local plugin = import("classes.plugin")("debug")
local command = import("classes.command")
local save = command("save",{
help = "Force-save config data",
exec = function()
server:save_config()
end
})
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

View File

@ -10,6 +10,7 @@ local settings = {
limit = 500000
}
c_brainfuck = command("brainfuck",{
category = "Miscellaneous",
args = {
"string"
},
@ -47,6 +48,7 @@ c_brainfuck = command("brainfuck",{
})
plugin:add_command(c_brainfuck)
c_befunge = command("befunge",{
category = "Miscellaneous",
args = {
"string"
},
@ -107,9 +109,5 @@ c_befunge = command("befunge",{
end
})
plugin:add_command(c_befunge)
local helpdb = import(plugin_path:sub(3,-1).."help")
plugin:for_all_commands(function(command)
command:set_help(helpdb[command.name])
end)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

11
plugins/help/help.lua Normal file
View File

@ -0,0 +1,11 @@
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,97 +1,49 @@
local pluginc = import("classes.plugin")
local command = import("classes.command")
local plugin = pluginc("help")
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 color = discordia.Color.fromHex
local help_command = command("help",{
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"}
}
}},
category = "Utilities",
exec = function(msg,args,opts)
randomize_stuff()
local embed = {
color = discordia.Color.fromHex("32b3bc").value
color = color("32b3bc").value
}
if args[1] then
if count(opts) < 1 then
if not opts["plugin"] then
if command_handler:get_command(args[1]) then
local command = command_handler:get_command(args[1])
embed = command:get_help().embed
else
embed.description = "No such command"
end
elseif (opts["plugin"]) then
--[[ if plugin_data["plugins"] [args[1] ] then
embed.title = "Plugin ``"..args[1].."``:"
embed.description = plugin_data["plugins"] [args[1] ]["_help"]
embed.fields = {{
name = "Commands:",
value ="``"..table.concat(plugin_data["plugins"] [args[1] ],"``,\n``").."``"
}}
else
embed.description = "No such plugin"
end
--]]
embed.title = "Not yet implemented"
embed.description = "Check again later"
embed.description = "No such command: "..args[1]
embed.color = color("990000").value
end
else
local meta = command_handler:get_metadata()
local comms = meta.plugins[args[1]]
if not comms then
embed.description = "Unable to find plugin: "..args[1]
embed.color = color("990000").value
else
embed.title = "Plugin ``"..args[1].."``"
embed.description = "``"..table.concat(comms,"``, ``").."``"
end
end
else
local meta = command_handler:get_metadata()
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
for name,category in pairs(meta.categories) do
table.insert(embed.fields,{
name = k,
value = "``"..table.concat(v,"``, ``").."``"
name = name,
value = "``"..table.concat(category,"``, ``").."``"
})
end
end
msg:reply({embed = embed})
end,
})
})
plugin:add_command(help_command)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

View File

@ -1,9 +1,9 @@
return {
["prefix"] = {embed={
title = "Set or view current prefix for this bot",
description = "If you're not sure what's the current prefix, just ping the bot",
title = "Add/delete/list prefixes",
description = "Multiple prefixes are possible",
fields = {
{name = "Usage:",value = "prefix [<new prefix> or \"<new prefix>\"]"},
{name = "Usage:",value = "prefix [(add | remove | list (default)) [<new prefix>]]"},
{name = "Perms:",value = "Administrator"},
}
}},
@ -16,9 +16,10 @@ return {
``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 $1"`` - reply to !hi with the first argument sent along with !hi
More at https://github.com/yessiest/SuppaBot/wiki/Tasks]]
More at https://github.com/512mb-xyz/512mb.org-bot/wiki/Aliases]]
},
{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 = {

View File

@ -4,6 +4,7 @@ local last_message_arrived = discordia.Stopwatch()
local unixToString = import("unixToString")
local command = import("classes.command")
local plugin = import("classes.plugin")("meta")
local purify = import("purify")
if not config.aliases then
config.aliases = {}
end
@ -22,26 +23,26 @@ end
local function add_alias(name,comm,prefix,description)
if (not aliases[name]) then
print("[ALIAS] Adding alias \""..name.."\" for \""..comm.."\"")
config.aliases[name] = {comm = comm,prefix = prefix}
log("ALIAS","Adding alias \""..name.."\" for \""..comm.."\"")
config.aliases[name] = {comm = comm,prefix = (prefix == nil)}
aliases[name] = command(name,{
help = "Alias for ``"..comm.."``",
usage = ((prefix and globals.prefix) or "")..name,
category = "Aliases",
exec = function(msg,args2,opts)
print("[ALIAS] Triggerting alias "..tostring(comm).." with args \""..tostring(msg.content).."\"")
local str = msg.content:gsub("^%S+ ?","")
aftersub = comm:gsub("%.%.%.",str or "")
aftersub = aftersub:gsub("%$prefix",prefix or "&")
local status,args = require("air").parse(str)
for k,v in pairs(args) do
aftersub = aftersub:gsub("([^\\])%$"..k,"%1"..v)
end
log("ALIAS", "Triggering alias "..name.." with args \""..aftersub.."\"")
command_handler:handle(fake_message(msg,{
content = aftersub
}))
end,
options = {
prefix = prefix,
prefix = (prefix == nil),
custom = true
}
})
@ -63,32 +64,6 @@ local function remove_alias(name)
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
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
for k,v in pairs(config.aliases) do
commdata = v
if type(v) == "string" then --legacy format conversion
@ -98,22 +73,15 @@ for k,v in pairs(config.aliases) do
end
local prefix = command("prefix",{
help = "Set prefix",
usage = "prefix [(add | remove | list (default)) [<new prefix>]]",
users = {
[client.owner.id] = 1
},
roles = {
["747042157185073182"] = 1
},
perms = {
"administrator"
},
category = "Utilities",
exec = function(msg,args,opts)
local function list_prefixes(msg)
local prefixes = ""
for k,v in pairs(command_handler:get_prefixes()) do
prefixes = prefixes..v.."\n"
prefixes = prefixes.."``"..v:gsub("`","\\`").."``".."\n"
end
msg:reply({embed = {
title = "Prefixes for this server",
@ -123,11 +91,11 @@ local prefix = command("prefix",{
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")
msg:reply("Added ``"..args[2]:gsub("`","\\`").."`` 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")
msg:reply("Removed the ``"..args[2]:gsub("`","\\`").."`` prefix")
else
msg:reply(err)
end
@ -150,6 +118,7 @@ local c_alias = command("alias", {
perms = {
"administrator"
},
category = "Automation",
exec = function(msg,args,opts)
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].."``")
@ -167,6 +136,7 @@ local c_unalias = command("unalias", {
perms = {
"administrator"
},
category = "Automation",
exec = function(msg,args,opts)
if remove_alias(args[1]) then
msg:reply("Removed the ``"..args[1].."`` alias")
@ -178,22 +148,39 @@ local c_unalias = command("unalias", {
plugin:add_command(c_unalias)
local c_aliases = command("aliases", {
category = "Automation",
exec = function(msg,args,opts)
local page = (tonumber(args[1]) or 1)*5
local events = (function()
local fields = {}
for k,v in pairs(config.aliases) do
table.insert(fields,{k,v})
end
return fields
end)()
msg:reply({embed = {
title = "Aliases for this server",
fields = (function()
local fields = {}
for k,v in pairs(config.aliases) do
table.insert(fields,{name = ((v["prefix"] and prefix) or "")..k,value = v["comm"]})
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)()
end)(),
footer = {
text = "Aliases "..tostring(page-4).." - "..tostring(page).." | Total: "..tostring(#events)
}
}})
end
})
plugin:add_command(c_aliases)
local c_ping = command("ping", {
category = "Utilities",
exec = function(msg,args,opts)
local before = msg:getDate()
local reply = msg:reply("Pong!")
@ -219,6 +206,7 @@ local c_ping = command("ping", {
plugin:add_command(c_ping)
local c_about = command("about", {
category = "Miscellaneous",
exec = function(msg,args,opts)
local rand = math.random
local author = client:getUser("245973168257368076")
@ -243,6 +231,7 @@ local c_about = command("about", {
plugin:add_command(c_about)
local c_server = command("server", {
category = "Utilities",
exec = function(msg,args,opts)
msg:reply({embed = {
thumbnail = {
@ -263,6 +252,7 @@ local c_server = command("server", {
plugin:add_command(c_server)
local c_user = command("user", {
category = "Utilities",
exec = function(msg,args,opts)
local member = msg.guild:getMember((args[1] or ""):match("%d+")) or msg.guild:getMember(msg.author.id)
local roles = ""
@ -290,11 +280,9 @@ local c_speak = command("speak", {
args = {
"string"
},
category = "Utilities",
exec = function(msg,args,opts)
local text = purify_strings(msg, table.concat(args," "))
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
local text = purify.purify_pings(msg, table.concat(args," "))
msg:reply(text)
msg:delete()
end,
@ -302,14 +290,12 @@ local c_speak = command("speak", {
plugin:add_command(c_speak)
local c_adminSpeak = command("adminSpeak", {
category = "Utilities",
args = {
"string"
},
exec = function(msg,args,opts)
local text = table.concat(args," ")
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
msg:reply(text)
msg:delete()
end,
@ -319,29 +305,58 @@ local c_adminSpeak = command("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",{
category = "Utilities",
args = {
"string"
},
exec = function(msg,args,opts)
local text = purify_strings(msg, table.concat(args," "))
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
local text = purify.purify_pings(msg, table.concat(args," "))
msg:reply(text)
end,
})
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",{
category = "Utilities",
args = {
"string"
},
exec = function(msg,args,opts)
local text = purify_strings(msg, table.concat(args," "))
if opts["unescape"] or opts["u"] then
text = text:gsub("\\","")
end
local text = purify.purify_pings(msg, table.concat(args," "))
msg:reply("<@"..tostring(msg.member.id).."> "..text)
end,
})
@ -353,11 +368,5 @@ plugin.removal_callback = function()
end
end
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)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

32
plugins/plugins/help.lua Normal file
View File

@ -0,0 +1,32 @@
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

@ -11,19 +11,10 @@ local generic_admin_template = {
}
local enable = command("enable",utilities.overwrite(generic_admin_template,{
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
}},
category = "Utilities",
exec = function(msg,args,opts)
local status,message = plugin_handler:load(args[1])
local plugin_data = command_handler:get_commands_metadata().plugins
local plugin_data = command_handler:get_metadata().plugins
local embed = {
description = message,
color = discordia.Color.fromHex("ff5100").value,
@ -40,18 +31,9 @@ adding its commands to the command pool]],
}))
plugin:add_command(enable)
local disable = command("disable",utilities.overwrite(generic_admin_template,{
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
}},
category = "Utilities",
exec = function(msg,args,opts)
local plugin_data = command_handler:get_commands_metadata().plugins
local plugin_data = command_handler:get_metadata().plugins
if not (args[1] == "plugins") then
local status,message = plugin_handler:unload(args[1])
local embed = {
@ -73,14 +55,7 @@ removing its commands from the command pool]],
}))
plugin:add_command(disable)
local plugins = command("plugins",utilities.overwrite(generic_admin_template,{
help = {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``)"}
}
}},
category = "Utilities",
args = {},
exec = function(msg,args,opts)
local all_plugins = plugin_handler:list_loadable()
@ -106,5 +81,6 @@ local plugins = command("plugins",utilities.overwrite(generic_admin_template,{
end
}))
plugin:add_command(plugins)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

View File

@ -0,0 +1,42 @@
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

@ -24,14 +24,7 @@ local function count(tab)
end
local pivot = command("pivot",{
help = {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"}
}
}},
category = "Automation",
args = {
"messageLink"
},
@ -40,7 +33,7 @@ local pivot = command("pivot",{
},
exec = function(msg,args,opts)
if segment.pivot and count(segment.pivot.buttons) == 0 then
print("[REACTIONS] Deleting pivot: "..tostring(segment.pivot.message))
log("REACTIONS","Deleting pivot: "..tostring(segment.pivot.message))
segment.pivots[segment.pivot.message] = nil
end
local message = args[1]
@ -49,7 +42,7 @@ local pivot = command("pivot",{
return false
end
if not segment.pivots[message.id] then
print("[REACTIONS] Creating pivot: "..tostring(message.id))
log("REACTIONS","Creating pivot: "..tostring(message.id))
segment.pivots[message.id] = {}
segment.pivots[message.id].message = message.id
segment.pivots[message.id].channel = message.channel.id
@ -62,14 +55,7 @@ local pivot = command("pivot",{
plugin:add_command(pivot)
local role_toggle = command("role-toggle",{
help = {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"}
}
}},
category = "Automation",
args = {
"string",
"role",
@ -93,7 +79,7 @@ local role_toggle = command("role-toggle",{
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return false
end
print("[REACTIONS] Adding role-toggle listener")
log("REACTIONS","Adding role-toggle listener")
local grabEmoji = function(reaction)
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
type = "role-toggler",
@ -113,14 +99,7 @@ local role_toggle = command("role-toggle",{
})
plugin:add_command(role_toggle)
local remove_reaction = command("remove-reaction",{
help = {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"}
}
}},
category = "Automation",
perms = {
"administrator"
},
@ -135,7 +114,7 @@ local remove_reaction = command("remove-reaction",{
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return false
end
print("[REACTIONS] Removing reaction listener")
log("REACTIONS","Removing reaction listener")
if args[1] then
local emoji = getEmoji(args[1])
message:removeReaction(emoji,client.user.id)
@ -151,14 +130,7 @@ local remove_reaction = command("remove-reaction",{
})
plugin:add_command(remove_reaction)
local toggle = command("toggle",{
help = {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"}
}
}},
category = "Automation",
args = {
"string",
"string",
@ -183,7 +155,7 @@ local toggle = command("toggle",{
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return false
end
print("[REACTIONS] Adding toggle listener")
log("REACTIONS","Adding toggle listener")
local grabEmoji = function(reaction)
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
type = "toggler",
@ -204,14 +176,7 @@ local toggle = command("toggle",{
})
plugin:add_command(toggle)
local button = command("button",{
help = {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"}
}
}},
category = "Automation",
args = {
"string",
"string",
@ -235,7 +200,7 @@ local button = command("button",{
msg:reply("Something went horribly wrong, but it's not your fault. This incident has been (hopefully) reported")
return false
end
print("[REACTIONS] Adding button listener")
log("REACTIONS","Adding button listener")
local grabEmoji = function(reaction)
segment.pivot.buttons[tostring(reaction.emojiId or reaction.emojiName)] = {
type = "button",
@ -337,4 +302,5 @@ events:on("reactionRemoveUncached",function(channelId,messageId,hash,userId)
buttonOff(message,hash,userId)
end)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

View File

@ -0,0 +1,5 @@
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,7 +9,7 @@ client:on("memberJoin",function(member)
end)
local droleadd = command("droleadd",{
help = "Add a default role to assign for new users",
category = "Automation",
usage = "droleadd <role>",
perms = {"administrator"},
args = {
@ -21,7 +21,7 @@ local droleadd = command("droleadd",{
end,
})
local droledel = command("droledel",{
help = "Remove a role from the list of default roles",
category = "Automation",
usage = "droledel <role>",
perms = {"administrator"},
args = {
@ -37,7 +37,7 @@ local droledel = command("droledel",{
end
})
local drolelist = command("drolelist", {
help = "List all default roles",
category = "Automation",
usage = "drolelist",
perms = {"administrator"},
exec = function(msg,args,opts)
@ -56,5 +56,6 @@ local drolelist = command("drolelist", {
plugin:add_command(droleadd)
plugin:add_command(droledel)
plugin:add_command(drolelist)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

64
plugins/security/help.lua Normal file
View File

@ -0,0 +1,64 @@
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,15 +17,7 @@ CREATE TABLE infractions(id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, desc T
end
local grantrole = command("grant-role",{
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)"}
}
}},
category = "Security",
perms = {
"administrator"
},
@ -41,15 +33,7 @@ local grantrole = command("grant-role",{
plugin:add_command(grantrole)
local revokerole = command("revoke-role",{
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)"}
}
}},
category = "Security",
perms = {
"administrator"
},
@ -64,15 +48,36 @@ local revokerole = command("revoke-role",{
})
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",{
help = {embed={
title = "Warn a user",
description = "nuff said.",
fields = {
{name = "Usage:",value = "warn <user> <reason>"},
{name = "Perms:",value = "kickMembers"},
}
}},
category = "Security",
perms = {
"kickMembers"
},
@ -99,15 +104,7 @@ local warn = command("warn",{
plugin:add_command(warn)
local infractions = command("infractions", {
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)"}
}
}},
category = "Security",
perms = {
"kickMembers"
},
@ -150,15 +147,7 @@ local infractions = command("infractions", {
plugin:add_command(infractions)
local purge = command("purge",{
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"}
}
}},
category = "Security",
perms = {
"manageMessages"
},
@ -210,5 +199,5 @@ local purge = command("purge",{
end
})
plugin:add_command(purge)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

37
plugins/tools/help.lua Normal file
View File

@ -0,0 +1,37 @@
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,6 +28,7 @@ function to_bit_string(num)
end
local flip = command("flip",{
category = "Miscellaneous",
help = "Flips a coin, obv.",
usage = "flip",
exec = function(msg,args,opts)
@ -41,7 +42,7 @@ local flip = command("flip",{
})
plugin:add_command(flip)
local dice = command("dice",{
help = "Simulates a dice throw, prints the value of each die",
category = "Miscellaneous",
usage = "dice <2d6,d30,d20+4,etc>",
exec = function(msg,args,opts)
local out = {embed = {
@ -77,7 +78,7 @@ local dice = command("dice",{
})
plugin:add_command(dice)
local cards = command("cards",{
help = "Draw a specific amount of playing cards and display them",
category = "Miscellaneous",
usage = "cards <amount>",
args = {"number"},
exec = function(msg,args,opts)
@ -98,22 +99,17 @@ local cards = command("cards",{
})
plugin:add_command(cards)
local calculate = command("calculate",{
help = "Calculates maths using libqalculate. https://qalculate.github.io/ for more info",
usage = [[
calculate "<expression>"
``-e`` - exact mode
]],
category = "Miscellaneous",
args = {
"string"
},
exec = function(msg,args,opts)
msg:reply(qalculator.qalc(args[1],opts["e"]))
msg:reply(qalculator.qalc(table.concat(args," "),opts["e"]))
end,
})
plugin:add_command(calculate)
local pfp = command("pfp",{
help = "Show the profile picture of a user, or if none is specified, of yourself",
usage = "pfp <user or none>",
category = "Miscellaneous",
exec = function(msg,args,opts)
local user = client:getUser((args[1] or ""):match("%d+"))
if user then
@ -125,21 +121,7 @@ local pfp = command("pfp",{
})
plugin:add_command(pfp)
local markov = command("markov",{
help = { 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 = [[
--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"}
}
}},
category = "Miscellaneous",
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
@ -153,8 +135,7 @@ local markov = command("markov",{
})
plugin:add_command(markov)
local embed = command("embed",{
help = "Convert JSON objects into embeds",
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",
category = "Miscellaneous",
args = {
"string"
},
@ -176,4 +157,5 @@ local embed = command("embed",{
end
})
plugin:add_command(embed)
plugin:load_helpdb(plugin_path.."help.lua")
return plugin

View File

@ -1,478 +0,0 @@
--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

@ -1,340 +0,0 @@
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

@ -1,120 +0,0 @@
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

View File

@ -1,362 +0,0 @@
--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