|
@@ -11,19 +11,19 @@ import (
|
|
)
|
|
)
|
|
|
|
|
|
const sleepDuration time.Duration = 1 * time.Second
|
|
const sleepDuration time.Duration = 1 * time.Second
|
|
-const callbacksLocation string = "chatclbks"
|
|
|
|
|
|
+const callbacksLocation string = "clbks"
|
|
|
|
|
|
-// An utility struct for incoming messages.
|
|
|
|
-type IncomingMessage struct {
|
|
|
|
- Sender string
|
|
|
|
- Body string
|
|
|
|
|
|
+// An utility struct for incoming events.
|
|
|
|
+type IncomingEvent struct {
|
|
|
|
+ Type string
|
|
|
|
+ Data map[string]string
|
|
}
|
|
}
|
|
|
|
|
|
// Executor executes Lua scripts in a shared Lua VM.
|
|
// Executor executes Lua scripts in a shared Lua VM.
|
|
type Executor struct {
|
|
type Executor struct {
|
|
incomingScripts chan string
|
|
incomingScripts chan string
|
|
outgoingMsgs chan string
|
|
outgoingMsgs chan string
|
|
- incomingMsgs chan IncomingMessage
|
|
|
|
|
|
+ incomingEvents chan IncomingEvent
|
|
stateMutex sync.Mutex
|
|
stateMutex sync.Mutex
|
|
state *lua.State
|
|
state *lua.State
|
|
xmppStream stream.Stream
|
|
xmppStream stream.Stream
|
|
@@ -33,7 +33,7 @@ func NewExecutor(s stream.Stream) *Executor {
|
|
e := &Executor{
|
|
e := &Executor{
|
|
incomingScripts: make(chan string),
|
|
incomingScripts: make(chan string),
|
|
outgoingMsgs: make(chan string),
|
|
outgoingMsgs: make(chan string),
|
|
- incomingMsgs: make(chan IncomingMessage),
|
|
|
|
|
|
+ incomingEvents: make(chan IncomingEvent),
|
|
}
|
|
}
|
|
e.xmppStream = s
|
|
e.xmppStream = s
|
|
e.state = lua.NewState()
|
|
e.state = lua.NewState()
|
|
@@ -46,12 +46,27 @@ func NewExecutor(s stream.Stream) *Executor {
|
|
}
|
|
}
|
|
|
|
|
|
registerClbk := func(l *lua.State) int {
|
|
registerClbk := func(l *lua.State) int {
|
|
- // get callbacks table
|
|
|
|
|
|
+ // get events table
|
|
l.PushString(callbacksLocation)
|
|
l.PushString(callbacksLocation)
|
|
l.Table(lua.RegistryIndex)
|
|
l.Table(lua.RegistryIndex)
|
|
|
|
+ // get callbacks table
|
|
|
|
+ evtName := lua.CheckString(l, 1)
|
|
|
|
+ l.PushString(evtName)
|
|
|
|
+ l.Table(-2)
|
|
|
|
+ // create new table if one doesn't exist
|
|
|
|
+ if l.IsNil(-1) {
|
|
|
|
+ l.Pop(1)
|
|
|
|
+ // create and add table to the events table
|
|
|
|
+ l.PushString(evtName)
|
|
|
|
+ l.NewTable()
|
|
|
|
+ l.SetTable(-3)
|
|
|
|
+ // and get it back
|
|
|
|
+ l.PushString(evtName)
|
|
|
|
+ l.Table(-2)
|
|
|
|
+ }
|
|
// set callback
|
|
// set callback
|
|
- l.PushValue(1)
|
|
|
|
l.PushValue(2)
|
|
l.PushValue(2)
|
|
|
|
+ l.PushValue(3)
|
|
l.SetTable(-3)
|
|
l.SetTable(-3)
|
|
l.SetTop(0)
|
|
l.SetTop(0)
|
|
return 0
|
|
return 0
|
|
@@ -59,34 +74,43 @@ func NewExecutor(s stream.Stream) *Executor {
|
|
|
|
|
|
listClbks := func(l *lua.State) int {
|
|
listClbks := func(l *lua.State) int {
|
|
clbkNames := []string{}
|
|
clbkNames := []string{}
|
|
- // get callbacks table
|
|
|
|
|
|
+ // get events table
|
|
l.PushString(callbacksLocation)
|
|
l.PushString(callbacksLocation)
|
|
l.Table(lua.RegistryIndex)
|
|
l.Table(lua.RegistryIndex)
|
|
- // loop
|
|
|
|
- l.PushNil()
|
|
|
|
- for l.Next(-2) {
|
|
|
|
- key, ok := l.ToString(-2)
|
|
|
|
- // ignore non-string shit
|
|
|
|
- if ok {
|
|
|
|
- clbkNames = append(clbkNames, key)
|
|
|
|
|
|
+ // get callbacks for the event
|
|
|
|
+ evtName := lua.CheckString(l, 1)
|
|
|
|
+ l.PushString(evtName)
|
|
|
|
+ l.Table(-2)
|
|
|
|
+ if !l.IsNil(-1) {
|
|
|
|
+ // loop
|
|
|
|
+ l.PushNil()
|
|
|
|
+ for l.Next(-2) {
|
|
|
|
+ key, ok := l.ToString(-2)
|
|
|
|
+ // ignore non-string shit
|
|
|
|
+ if ok {
|
|
|
|
+ clbkNames = append(clbkNames, key)
|
|
|
|
+ }
|
|
|
|
+ l.Pop(1)
|
|
}
|
|
}
|
|
- l.Pop(1)
|
|
|
|
- }
|
|
|
|
- l.SetTop(0)
|
|
|
|
- l.NewTable()
|
|
|
|
- // build a list from callback names
|
|
|
|
- for i, key := range clbkNames {
|
|
|
|
- l.PushInteger(i + 1)
|
|
|
|
- l.PushString(key)
|
|
|
|
- l.SetTable(-3)
|
|
|
|
|
|
+ l.SetTop(0)
|
|
|
|
+ l.NewTable()
|
|
|
|
+ // build a list from callback names
|
|
|
|
+ for i, key := range clbkNames {
|
|
|
|
+ l.PushInteger(i + 1)
|
|
|
|
+ l.PushString(key)
|
|
|
|
+ l.SetTable(-3)
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ l.SetTop(0)
|
|
|
|
+ l.PushNil()
|
|
}
|
|
}
|
|
return 1
|
|
return 1
|
|
}
|
|
}
|
|
|
|
|
|
var chatLibrary = []lua.RegistryFunction{
|
|
var chatLibrary = []lua.RegistryFunction{
|
|
lua.RegistryFunction{"send", send},
|
|
lua.RegistryFunction{"send", send},
|
|
- lua.RegistryFunction{"onmessage", registerClbk},
|
|
|
|
- lua.RegistryFunction{"listmsghandlers", listClbks},
|
|
|
|
|
|
+ lua.RegistryFunction{"addEventHandler", registerClbk},
|
|
|
|
+ lua.RegistryFunction{"listEventHandlers", listClbks},
|
|
}
|
|
}
|
|
|
|
|
|
lua.NewLibrary(e.state, chatLibrary)
|
|
lua.NewLibrary(e.state, chatLibrary)
|
|
@@ -127,31 +151,42 @@ func (e *Executor) sendingRoutine() {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func (e *Executor) processIncomingMsgs() {
|
|
|
|
- for msg := range e.incomingMsgs {
|
|
|
|
|
|
+func (e *Executor) processIncomingEvents() {
|
|
|
|
+ for evt := range e.incomingEvents {
|
|
e.stateMutex.Lock()
|
|
e.stateMutex.Lock()
|
|
- // get callbacks table
|
|
|
|
|
|
+ // get events table
|
|
e.state.PushString(callbacksLocation)
|
|
e.state.PushString(callbacksLocation)
|
|
e.state.Table(lua.RegistryIndex)
|
|
e.state.Table(lua.RegistryIndex)
|
|
|
|
+ // get callbacks table for the specific event
|
|
|
|
+ e.state.PushString(evt.Type)
|
|
|
|
+ e.state.Table(-2)
|
|
// loop over callbacks
|
|
// loop over callbacks
|
|
- e.state.PushNil()
|
|
|
|
- for e.state.Next(-2) {
|
|
|
|
- if e.state.IsFunction(-1) {
|
|
|
|
- e.state.PushString(msg.Sender)
|
|
|
|
- e.state.PushString(msg.Body)
|
|
|
|
- err := e.state.ProtectedCall(2, 0, 0)
|
|
|
|
- if err != nil {
|
|
|
|
- m := entity.MSG(entity.GROUPCHAT)
|
|
|
|
- m.To = "golang@conference.jabber.ru"
|
|
|
|
- m.Body, _ = e.state.ToString(-1)
|
|
|
|
- e.xmppStream.Write(entity.ProduceStatic(m))
|
|
|
|
|
|
+ if !e.state.IsNil(-1) {
|
|
|
|
+ e.state.PushNil()
|
|
|
|
+ for e.state.Next(-2) {
|
|
|
|
+ if e.state.IsFunction(-1) {
|
|
|
|
+ // create the table which will be passed to the handler
|
|
|
|
+ e.state.NewTable()
|
|
|
|
+ // loop over the event data, populating the table
|
|
|
|
+ for k, v := range evt.Data {
|
|
|
|
+ e.state.PushString(k)
|
|
|
|
+ e.state.PushString(v)
|
|
|
|
+ e.state.SetTable(-3)
|
|
|
|
+ }
|
|
|
|
+ err := e.state.ProtectedCall(1, 0, 0)
|
|
|
|
+ if err != nil {
|
|
|
|
+ m := entity.MSG(entity.GROUPCHAT)
|
|
|
|
+ m.To = "golang@conference.jabber.ru"
|
|
|
|
+ m.Body, _ = e.state.ToString(-1)
|
|
|
|
+ e.xmppStream.Write(entity.ProduceStatic(m))
|
|
|
|
+ e.state.Pop(1)
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
e.state.Pop(1)
|
|
e.state.Pop(1)
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- e.state.Pop(1)
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- // pop callbacks table
|
|
|
|
|
|
+ // pop callbacks table or nil value
|
|
e.state.Pop(1)
|
|
e.state.Pop(1)
|
|
e.stateMutex.Unlock()
|
|
e.stateMutex.Unlock()
|
|
}
|
|
}
|
|
@@ -160,12 +195,12 @@ func (e *Executor) processIncomingMsgs() {
|
|
func (e *Executor) Start() {
|
|
func (e *Executor) Start() {
|
|
go e.sendingRoutine()
|
|
go e.sendingRoutine()
|
|
go e.execute()
|
|
go e.execute()
|
|
- go e.processIncomingMsgs()
|
|
|
|
|
|
+ go e.processIncomingEvents()
|
|
}
|
|
}
|
|
|
|
|
|
func (e *Executor) Stop() {
|
|
func (e *Executor) Stop() {
|
|
close(e.incomingScripts)
|
|
close(e.incomingScripts)
|
|
- close(e.incomingMsgs)
|
|
|
|
|
|
+ close(e.incomingEvents)
|
|
close(e.outgoingMsgs)
|
|
close(e.outgoingMsgs)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -173,8 +208,7 @@ func (e *Executor) Run(script string) {
|
|
e.incomingScripts <- script
|
|
e.incomingScripts <- script
|
|
}
|
|
}
|
|
|
|
|
|
-// Call this on every incoming message - it's required for
|
|
|
|
-// chat.onmessage to work.
|
|
|
|
-func (e *Executor) NewMessage(msg IncomingMessage) {
|
|
|
|
- e.incomingMsgs <- msg
|
|
|
|
|
|
+// Call this on every event - it's required for event handlers to work
|
|
|
|
+func (e *Executor) NewEvent(evt IncomingEvent) {
|
|
|
|
+ e.incomingEvents <- evt
|
|
}
|
|
}
|