Basic plugin system.

master
q3k 2013-09-22 14:29:54 +02:00
parent 6703df6f78
commit 839323b009
2 changed files with 102 additions and 2 deletions

95
core/plugin.lua Normal file
View File

@ -0,0 +1,95 @@
-- An isolated API for plugins
-- These plugins are not yet made to be a secure sandbox, as hooking
-- into signal might be destructive or reveal sensitive data.
-- The goal is to be able to unload a plugin and remove all its' hooks
-- and commands.
local Plugins = {}
plugin = {}
local API = {}
-- All these functions start with the 'plugin_id' argument, which
-- will be auto-bound to the plugin name when called from a plugin.
function API.HookAdd(plugin_id, event_name, hook_name, callback)
local HookName = plugin_id .. ':' .. hook_name
local PluginHooks = Plugins[plugin_id].Hooks
PluginHooks[#PluginHooks + 1] = HookName
hook.Add(event_name, HookName, function(...)
local Args = {...}
local Success, Message = pcall(callback(unpack(Args)))
if not Success then
hook.Call("plugin.HookCallFailed", HookName, Message)
return nil
else
return Message
end
end)
end
function API.CommandAdd(plugin_id, command_name, arity, callback, access, help)
bot:AddCommand(command_name, arity, callback, access, help)
local PluginCommands = Plugins[plugin_id].Commands
PluginCommands[#PluginCommands + 1] = command_name
end
function API.Register(plugin_id, plugin_name, version, url, author)
local Plugin = Plugins[plugin_id]
Plugin.Name = plugin_name
Plugin.Version = Version
Plugin.URL = url
Plugin.Author = author
end
function plugin.Create(plugin_id)
local Plugin = {}
Plugin.Hooks = {}
Plugin.Commands = {}
Plugin.ID = plugin_id
Plugin.Name = ""
Plugin.Version = 1.0
Plugin.URL = ""
Plugin.Author = "Nameless Wonder"
Plugins[plugin_id] = Plugin
Plugins[plugin_id].Env = plugin.PrepareEnvironment(plugin_id)
end
function plugin.PrepareEnvironment(plugin_id)
local function BindPluginID(f)
return function(...)
local Args = {...}
return f(plugin_id, unpack(Args))
end
end
local Env = {}
Env.table = require('table')
Env.string = require('string')
Env.json = require('json')
Env.plugin = {}
for K, F in pairs(API) do
Env.plugin[K] = BindPluginID(F)
end
return Env
end
function plugin.RunCode(plugin_id, code)
if not Plugins[plugin_id] then
plugin.Create(plugin_id)
end
if code:byte(1) == 27 then
return nil, "Refused to load bytecode."
end
local Function, Message = loadstring(code)
if not Function then
return nil, Message
end
setfenv(Function, Plugins[plugin_id].Env)
return pcall(Function)
end

View File

@ -2,6 +2,7 @@ require('core.hook')
require('core.reactor')
require('core.irc')
require('core.bot')
require('core.plugin')
require('socket')
local https = require('ssl.https')
@ -22,7 +23,7 @@ end)
reactor:Initialize()
bot:Initialize(irc, ',')
bot:AddCommand('at', 0, function(Username, Channel)
--[[bot:AddCommand('at', 0, function(Username, Channel)
local Body, Code, Headers, Status = https.request('https://at.hackerspace.pl/api')
if Code ~= 200 then
error(string.format("Status code returned: %i", Code))
@ -34,7 +35,11 @@ bot:AddCommand('at', 0, function(Username, Channel)
end
Channel:Say(table.concat(Users, ','))
end, "Show who's at the Warsaw Hackerspace.")
end, "Show who's at the Warsaw Hackerspace.")]]--
plugin.RunCode('test', [[plugin.CommandAdd('test', 0, function(Username, Channel)
Channel:Say('hello!')
end, "Test command.", 0)]])
irc:Connect('irc.freenode.net', 6667, 'moonspeak', 'moonspeak', 'moonspeak')
reactor:Run()