postman0 9 éve
szülő
commit
fa743101a8
2 módosított fájl, 172 hozzáadás és 0 törlés
  1. 155 0
      jsexecutor/executor.go
  2. 17 0
      main.go

+ 155 - 0
jsexecutor/executor.go

@@ -0,0 +1,155 @@
+package jsexecutor
+
+import (
+	"fmt"
+	"github.com/kpmy/xippo/c2s/stream"
+	"github.com/kpmy/xippo/entity"
+	"github.com/robertkrimen/otto"
+	"sync"
+	"time"
+)
+
+const sleepDuration time.Duration = 1 * time.Second
+
+// An utility struct for incoming events.
+type IncomingEvent struct {
+	Type string
+	Data map[string]string
+}
+
+// Executor executes JS scripts in a shared JS VM.
+type Executor struct {
+	incomingScripts chan string
+	outgoingMsgs    chan string
+	incomingEvents  chan IncomingEvent
+	stateMutex      sync.Mutex
+	eventHandlers   map[string]map[string]otto.Value
+	vm              *otto.Otto
+	xmppStream      stream.Stream
+}
+
+func NewExecutor(s stream.Stream) *Executor {
+	e := &Executor{
+		incomingScripts: make(chan string),
+		outgoingMsgs:    make(chan string),
+		incomingEvents:  make(chan IncomingEvent),
+		eventHandlers:   make(map[string]map[string]otto.Value),
+	}
+	e.xmppStream = s
+	e.vm = otto.New()
+
+	send := func(call otto.FunctionCall) otto.Value {
+		str, _ := call.Argument(0).ToString()
+		e.outgoingMsgs <- str
+		return otto.UndefinedValue()
+	}
+
+	addHandler := func(call otto.FunctionCall) otto.Value {
+		evtName, err := call.Argument(0).ToString()
+		handlerName, err := call.Argument(1).ToString()
+		if err != nil {
+			return otto.FalseValue()
+		}
+		val := call.Argument(2)
+		handlers, ok := e.eventHandlers[evtName]
+		if !ok {
+			e.eventHandlers[evtName] = map[string]otto.Value{handlerName: val}
+		} else {
+			handlers[handlerName] = val
+		}
+		return otto.TrueValue()
+	}
+
+	listHandlers := func(call otto.FunctionCall) otto.Value {
+		evtName, err := call.Argument(0).ToString()
+		if err != nil {
+			return otto.UndefinedValue()
+		}
+		list := []string{}
+		for handlerName := range e.eventHandlers[evtName] {
+			list = append(list, handlerName)
+		}
+		val, err := e.vm.ToValue(list)
+		if err != nil {
+			return otto.UndefinedValue()
+		} else {
+			return val
+		}
+	}
+
+	chatLibrary, _ := e.vm.Object("Chat = {};")
+	chatLibrary.Set("send", send)
+	chatLibrary.Set("addEventHandler", addHandler)
+	chatLibrary.Set("listEventHandlers", listHandlers)
+	return e
+}
+
+func (e *Executor) execute() {
+	for script := range e.incomingScripts {
+		e.stateMutex.Lock()
+		_, err := e.vm.Run(script)
+		if err != nil {
+			fmt.Printf("js fucking shit error: %s\n", err)
+			m := entity.MSG(entity.GROUPCHAT)
+			m.To = "golang@conference.jabber.ru"
+			m.Body = err.Error()
+			e.xmppStream.Write(entity.ProduceStatic(m))
+		}
+		e.stateMutex.Unlock()
+	}
+}
+
+func (e *Executor) sendingRoutine() {
+	for msg := range e.outgoingMsgs {
+		m := entity.MSG(entity.GROUPCHAT)
+		m.To = "golang@conference.jabber.ru"
+		m.Body = msg
+		err := e.xmppStream.Write(entity.ProduceStatic(m))
+		if err != nil {
+			fmt.Printf("send error: %s", err)
+		}
+		time.Sleep(sleepDuration)
+	}
+}
+
+func (e *Executor) processIncomingEvents() {
+	for evt := range e.incomingEvents {
+		e.stateMutex.Lock()
+		obj, _ := e.vm.Object("({})")
+		for key, value := range evt.Data {
+			obj.Set(key, value)
+		}
+		for _, handler := range e.eventHandlers[evt.Type] {
+			_, err := handler.Call(obj.Value(), obj.Value())
+			if err != nil {
+				fmt.Printf("js fucking shit error: %s\n", err)
+				m := entity.MSG(entity.GROUPCHAT)
+				m.To = "golang@conference.jabber.ru"
+				m.Body = err.Error()
+				e.xmppStream.Write(entity.ProduceStatic(m))
+			}
+		}
+		e.stateMutex.Unlock()
+	}
+}
+
+func (e *Executor) Start() {
+	go e.sendingRoutine()
+	go e.execute()
+	go e.processIncomingEvents()
+}
+
+func (e *Executor) Stop() {
+	close(e.incomingScripts)
+	close(e.incomingEvents)
+	close(e.outgoingMsgs)
+}
+
+func (e *Executor) Run(script string) {
+	e.incomingScripts <- script
+}
+
+// Call this on every event - it's required for event handlers to work
+func (e *Executor) NewEvent(evt IncomingEvent) {
+	e.incomingEvents <- evt
+}

+ 17 - 0
main.go

@@ -6,6 +6,7 @@ import (
 	"github.com/ivpusic/golog"
 	"reflect"
 	//	"github.com/skratchdot/open-golang/open"
+	"github.com/kpmy/xep/jsexecutor"
 	"github.com/kpmy/xep/luaexecutor"
 	"github.com/kpmy/xep/muc"
 	"github.com/kpmy/xippo/c2s/actors"
@@ -65,6 +66,7 @@ type (
 var posts *Posts
 
 var executor *luaexecutor.Executor
+var jsexec *jsexecutor.Executor
 
 func init() {
 	flag.StringVar(&user, "u", "goxep", "-u=user")
@@ -101,6 +103,13 @@ func doLua(script string) func(stream.Stream) error {
 	}
 }
 
+func doJS(script string) func(stream.Stream) error {
+	return func(s stream.Stream) error {
+		jsexec.Run(script)
+		return nil
+	}
+}
+
 func doLuaAndPrint(script string) func(stream.Stream) error {
 	return doLua(fmt.Sprintf(`chat.send(%s)`, script))
 }
@@ -117,6 +126,8 @@ func bot(st stream.Stream) error {
 	actors.With().Do(actors.C(steps.PresenceTo(units.Bare2Full(ROOM, ME), entity.CHAT, "ПЩ сюды: https://github.com/kpmy/xep"))).Run(st)
 	executor = luaexecutor.NewExecutor(st)
 	executor.Start()
+	jsexec = jsexecutor.NewExecutor(st)
+	jsexec.Start()
 	for {
 		st.Ring(conv(func(_e entity.Entity) {
 			switch e := _e.(type) {
@@ -137,11 +148,17 @@ func bot(st stream.Stream) error {
 					if sender != ME {
 						executor.NewEvent(luaexecutor.IncomingEvent{"message",
 							map[string]string{"sender": sender, "body": e.Body}})
+						jsexec.NewEvent(jsexecutor.IncomingEvent{"message",
+							map[string]string{"sender": sender, "body": e.Body}})
 						switch {
 						case strings.HasPrefix(e.Body, "lua>"):
 							go func(script string) {
 								actors.With().Do(actors.C(doLua(script))).Run(st)
 							}(strings.TrimPrefix(e.Body, "lua>"))
+						case strings.HasPrefix(e.Body, "js>"):
+							go func(script string) {
+								actors.With().Do(actors.C(doJS(script))).Run(st)
+							}(strings.TrimPrefix(e.Body, "js>"))
 						case strings.HasPrefix(e.Body, "say"):
 							go func(script string) {
 								actors.With().Do(actors.C(doLuaAndPrint(script))).Run(st)