mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-25 09:36:24 -07:00
Add a simple event system and the ability for lua plugins to register and trigger events
This commit is contained in:
parent
6af9f7155f
commit
05e01c6366
4 changed files with 180 additions and 3 deletions
51
pkg/events/events.go
Normal file
51
pkg/events/events.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
)
|
||||
|
||||
var (
|
||||
registeredEvents map[string][]EventHandler
|
||||
testingMode bool
|
||||
)
|
||||
|
||||
type EventHandler func(string, ...interface{})
|
||||
|
||||
// RegisterEvent registers a new event handler to be called when any of the elements of triggers are passed
|
||||
// to TriggerEvent
|
||||
func RegisterEvent(triggers []string, handler func(trigger string, i ...interface{})) {
|
||||
for _, t := range triggers {
|
||||
registeredEvents[t] = append(registeredEvents[t], handler)
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerEvent triggers the event handler registered to trigger
|
||||
func TriggerEvent(trigger string, data ...interface{}) (handled bool, recovered bool) {
|
||||
errEv := gcutil.LogError(nil).Caller(1)
|
||||
defer func() {
|
||||
if a := recover(); a != nil {
|
||||
if !testingMode {
|
||||
errEv.Err(fmt.Errorf("%s", a)).
|
||||
Str("event", trigger).
|
||||
Msg("Recovered from panic while handling event")
|
||||
}
|
||||
handled = true
|
||||
recovered = true
|
||||
}
|
||||
}()
|
||||
for _, handler := range registeredEvents[trigger] {
|
||||
handler(trigger, data...)
|
||||
handled = true
|
||||
}
|
||||
errEv.Discard()
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
registeredEvents = map[string][]EventHandler{}
|
||||
testingMode = strings.HasSuffix(os.Args[0], ".test")
|
||||
}
|
49
pkg/events/events_test.go
Normal file
49
pkg/events/events_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package events
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPanicRecover(t *testing.T) {
|
||||
RegisterEvent([]string{"TestPanicRecoverEvt"}, func(tr string, i ...interface{}) {
|
||||
t.Log("Testing panic recover")
|
||||
t.Log(i[0])
|
||||
})
|
||||
handled, recovered := TriggerEvent("TestPanicRecoverEvt") // should panic
|
||||
if !handled {
|
||||
t.Fatal("TriggerEvent for TestPanicRecoverEvt wasn't handled")
|
||||
}
|
||||
t.Log("TestPanicRecoverEvt recovered: ", recovered)
|
||||
if !recovered {
|
||||
t.Fatal("TestPanicRecoverEvt should have caused a panic and recovered from it")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventEditValue(t *testing.T) {
|
||||
RegisterEvent([]string{"TestEventEditValue"}, func(tr string, i ...interface{}) {
|
||||
p := i[0].(*int)
|
||||
*p += 1
|
||||
})
|
||||
var a int
|
||||
t.Logf("a before TestEventEditValue triggered: %d", a)
|
||||
TriggerEvent("TestEventEditValue", &a)
|
||||
if a == 0 {
|
||||
t.Fatal("TestEventEditValue event didn't properly increment the pointer to a passed to it when triggered")
|
||||
}
|
||||
t.Logf("a after TestEventEditValue triggered: %d", a)
|
||||
}
|
||||
|
||||
func TestMultipleEventTriggers(t *testing.T) {
|
||||
triggered := map[string]bool{}
|
||||
RegisterEvent([]string{"a", "b"}, func(tr string, i ...interface{}) {
|
||||
triggered[tr] = true
|
||||
})
|
||||
TriggerEvent("a")
|
||||
TriggerEvent("b")
|
||||
aTriggered := triggered["a"]
|
||||
bTriggered := triggered["b"]
|
||||
if !aTriggered {
|
||||
t.Fatal("a event not triggered")
|
||||
}
|
||||
if !bTriggered {
|
||||
t.Fatal("b event not triggered")
|
||||
}
|
||||
}
|
|
@ -2,8 +2,10 @@ package gcplugin
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/events"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
|
@ -28,6 +30,26 @@ func ClosePlugins() {
|
|||
}
|
||||
}
|
||||
|
||||
func lvalueToInterface(v lua.LValue) interface{} {
|
||||
lt := v.Type()
|
||||
switch lt {
|
||||
case lua.LTNil:
|
||||
return nil
|
||||
case lua.LTBool:
|
||||
return lua.LVAsBool(v)
|
||||
case lua.LTNumber:
|
||||
return lua.LVAsNumber(v)
|
||||
case lua.LTString:
|
||||
return v.String()
|
||||
default:
|
||||
gcutil.LogError(nil).Caller(1).
|
||||
Interface("lvalue", v).
|
||||
Str("type", lt.String()).
|
||||
Msg("Unrecognized or unsupported Lua type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLuaLogFunc(which string) lua.LGFunction {
|
||||
return func(l *lua.LState) int {
|
||||
switch which {
|
||||
|
@ -47,10 +69,48 @@ func createLuaLogFunc(which string) lua.LGFunction {
|
|||
}
|
||||
}
|
||||
|
||||
func luaEventRegisterHandlerAdapter(l *lua.LState, fn *lua.LFunction) events.EventHandler {
|
||||
return func(trigger string, data ...interface{}) {
|
||||
args := []lua.LValue{
|
||||
luar.New(l, trigger),
|
||||
}
|
||||
for _, i := range data {
|
||||
args = append(args, luar.New(l, i))
|
||||
}
|
||||
l.CallByParam(lua.P{
|
||||
Fn: fn,
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func registerLuaFunctions() {
|
||||
lState.Register("info_log", createLuaLogFunc("info"))
|
||||
lState.Register("warn_log", createLuaLogFunc("warn"))
|
||||
lState.Register("error_log", createLuaLogFunc("error"))
|
||||
lState.Register("event_register", func(l *lua.LState) int {
|
||||
table := l.CheckTable(-2)
|
||||
var triggers []string
|
||||
table.ForEach(func(i, val lua.LValue) {
|
||||
triggers = append(triggers, val.String())
|
||||
})
|
||||
fn := l.CheckFunction(-1)
|
||||
events.RegisterEvent(triggers, luaEventRegisterHandlerAdapter(l, fn))
|
||||
return 0
|
||||
})
|
||||
lState.Register("event_trigger", func(l *lua.LState) int {
|
||||
trigger := l.CheckString(1)
|
||||
numArgs := l.GetTop()
|
||||
var data []interface{}
|
||||
for i := 2; i <= numArgs; i++ {
|
||||
v := l.CheckAny(i)
|
||||
data = append(data, lvalueToInterface(v))
|
||||
}
|
||||
fmt.Println("triggering", trigger)
|
||||
events.TriggerEvent(trigger, data...)
|
||||
return 0
|
||||
})
|
||||
lState.SetGlobal("_GOCHAN_VERSION", lua.LString(config.GetVersion().String()))
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,19 @@ print(string.format("Message before changing: %q", post.MessageRaw))
|
|||
post.MessageRaw = "Message modified by a plugin\n"
|
||||
post.Message = "Message modified by a plugin<br />"
|
||||
print(string.format("Modified message text: %q", post.MessageText))`
|
||||
|
||||
eventsTestingStr = `event_register({"newPost"}, function(tr, ...)
|
||||
print("newPost triggered :D")
|
||||
for i, v in ipairs(arg) do
|
||||
print(i .. ": " .. tostring(v))
|
||||
end
|
||||
end)
|
||||
|
||||
event_trigger("newPost", "blah", 16, 3.14, true, nil)`
|
||||
)
|
||||
|
||||
func initPluginTests() {
|
||||
config.SetVersion("3.1")
|
||||
config.SetVersion("3.4.1")
|
||||
initLua()
|
||||
}
|
||||
|
||||
|
@ -31,8 +40,8 @@ func TestVersionFunction(t *testing.T) {
|
|||
t.Fatal(err.Error())
|
||||
}
|
||||
testingVersionStr := lState.Get(-1).(lua.LString)
|
||||
if testingVersionStr != "3.1" {
|
||||
t.Fatalf("%q != \"3.1\"", testingVersionStr)
|
||||
if testingVersionStr != "3.4.1" {
|
||||
t.Fatalf(`%q != "3.4.1"`, testingVersionStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,3 +63,11 @@ func TestStructPassing(t *testing.T) {
|
|||
t.Fatal("message was not properly modified by plugin")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventPlugins(t *testing.T) {
|
||||
initPluginTests()
|
||||
err := lState.DoString(eventsTestingStr)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue