func TestMain(m *testing.M) { err := os.Setenv("ABOT_ENV", "test") if err != nil { log.Fatal("failed to set ABOT_ENV.", err) } r, err = core.NewServer() if err != nil { log.Fatal("failed to start abot server.", err) } p, err = plugin.New("testplugin") if err != nil { log.Fatal("failed to build test plugin.", err) } p.Config.Name = "testplugin" p.Trigger = &dt.StructuredInput{ Commands: []string{"get"}, Objects: []string{"result"}, } plugin.SetStates(p, [][]dt.State{ []dt.State{ { OnEntry: func(in *dt.Msg) string { return "entered first" }, OnInput: func(in *dt.Msg) {}, Complete: func(in *dt.Msg) (bool, string) { return true, "" }, }, }, Iterate(p, "", OptsIterate{ IterableMemKey: keyMem, ResultMemKeyIdx: keyRes, }), []dt.State{ { OnEntry: func(in *dt.Msg) string { return "Great!" }, OnInput: func(in *dt.Msg) {}, Complete: func(in *dt.Msg) (bool, string) { return true, "" }, }, }, }) p.SM.SetOnReset(func(in *dt.Msg) { p.DeleteMemory(in, keyMem) p.DeleteMemory(in, keyRes) ResetIterate(p, in) }) if err = plugin.Register(p); err != nil { log.Fatal("failed to register test plugin.", err) } cleanup() exitVal := m.Run() cleanup() os.Exit(exitVal) }
func TestMain(m *testing.M) { if err := LoadEnvVars(); err != nil { log.Info("failed to load env vars", err) } if err := os.Setenv("ABOT_ENV", "test"); err != nil { log.Fatal("failed to set ABOT_ENV", err) } var err error router, err = NewServer() if err != nil { log.Fatal("failed to start server", err) } os.Exit(m.Run()) }
func TestMain(m *testing.M) { log.SetDebug(true) if err := os.Setenv("ABOT_DEBUG", "true"); err != nil { log.Fatal(err) } os.Exit(m.Run()) }
func TestMain(m *testing.M) { if err := core.LoadEnvVars(); err != nil { log.Info("failed to load env vars", err) } if err := os.Setenv("ABOT_ENV", "test"); err != nil { log.Fatal(err) } os.Exit(m.Run()) }
// GetSetting retrieves a specific setting's value. It throws a fatal error if // the setting has not been declared in the plugin's plugin.json file. func (p *Plugin) GetSetting(name string) string { if p.Config.Settings[name] == nil { pluginName := p.Config.Name if len(pluginName) == 0 { pluginName = "plugin" } m := fmt.Sprintf( "missing setting %s. please declare it in the %s's plugin.json", name, pluginName) log.Fatal(m) } var val string q := `SELECT value FROM settings WHERE name=$1 AND pluginname=$2` err := p.DB.Get(&val, q, name, p.Config.Name) if err == sql.ErrNoRows { return p.Config.Settings[name].Default } if err != nil { log.Info("failed to get plugin setting.", err) return "" } return val }
func publishPlugin(c *cli.Context) error { p := filepath.Join(os.Getenv("HOME"), ".abot.conf") fi, err := os.Open(p) if err != nil { if err.Error() == fmt.Sprintf("open %s: no such file or directory", p) { login() publishPlugin(c) return nil } log.Fatal(err) } defer func() { if err = fi.Close(); err != nil { log.Fatal(err) } }() // Prepare request if len(c.Args().First()) == 0 { log.Fatal("missing plugin's `go get` path") } reqData := struct { Path string Secret string }{ Path: c.Args().First(), Secret: core.RandSeq(24), } byt, err := json.Marshal(reqData) if err != nil { log.Fatal(err) } u := os.Getenv("ITSABOT_URL") + "/api/plugins.json" req, err := http.NewRequest("POST", u, bytes.NewBuffer(byt)) if err != nil { log.Fatal(err) } // Populate req with login credentials from ~/.abot.conf scn := bufio.NewScanner(fi) var lineNum int for scn.Scan() { line := scn.Text() cookie := &http.Cookie{} switch lineNum { case 0: cookie.Name = "iaID" case 1: cookie.Name = "iaEmail" case 2: req.Header.Set("Authorization", "Bearer "+line) case 3: cookie.Name = "iaIssuedAt" default: log.Fatal("unknown line in abot.conf") } if lineNum != 2 { cookie.Value = url.QueryEscape(line) req.AddCookie(cookie) } lineNum++ } if err = scn.Err(); err != nil { log.Fatal(err) } cookie := &http.Cookie{} cookie.Name = "iaScopes" req.AddCookie(cookie) client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer func() { if err = resp.Body.Close(); err != nil { log.Fatal(err) } }() if resp.StatusCode == 401 { login() publishPlugin(c) } else if resp.StatusCode != 202 { log.Info("something went wrong. status code", resp.StatusCode) var msg string if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil { log.Fatal(err) } log.Fatal(msg) } // Make a websocket request to get updates about the publishing process uri, err := url.Parse(os.Getenv("ITSABOT_URL")) if err != nil { log.Fatal(err) } ws, err := websocket.Dial("ws://"+uri.Host+"/api/ws", "", os.Getenv("ABOT_URL")) if err != nil { log.Fatal(err) } if err = websocket.Message.Send(ws, reqData.Secret); err != nil { log.Fatal(err) } var msg socket.Msg l := log.New("") l.SetFlags(0) l.Info("> Establishing connection with server...") var established bool var lastMsg string for { websocket.JSON.Receive(ws, &msg) if !established { l.Info("OK") established = true } if msg.Content == lastMsg { log.Info("server hung up. please try again") if err = ws.Close(); err != nil { log.Info("failed to close socket.", err) } return nil } lastMsg = msg.Content if msg.Type == socket.MsgTypeFinishedSuccess || msg.Type == socket.MsgTypeFinishedFailed { if err = ws.Close(); err != nil { log.Info("failed to close socket.", err) } return nil } if len(msg.Content) < 2 { l.Info(msg.Content) continue } tmp := msg.Content[0:2] if tmp == "> " || tmp == "==" { l.Info("") } l.Info(msg.Content) } }
func login() { reader := bufio.NewReader(os.Stdin) fmt.Print("Email: ") email, err := reader.ReadString('\n') if err != nil { log.Fatal(err) } fmt.Print("Password: "******"ITSABOT_URL") + "/api/users/login.json" resp, err := http.Post(u, "application/json", bytes.NewBuffer(byt)) if err != nil { log.Fatal(err) } defer func() { if err = resp.Body.Close(); err != nil { log.Fatal(err) } }() var data struct { ID uint64 Email string Scopes []string AuthToken string IssuedAt uint64 } if err = json.NewDecoder(resp.Body).Decode(&data); err != nil { log.Fatal(err) } if resp.StatusCode == 401 { log.Fatal(errors.New("invalid email/password combination")) } // Create abot.conf file, truncate if exists fi, err := os.Create(filepath.Join(os.Getenv("HOME"), ".abot.conf")) if err != nil { log.Fatal(err) } defer func() { if err = fi.Close(); err != nil { log.Fatal(err) } }() // Insert auth data s := fmt.Sprintf("%d\n%s\n%s\n%d", data.ID, data.Email, data.AuthToken, data.IssuedAt) _, err = fi.WriteString(s) if err != nil { log.Fatal(err) } log.Info("Success!") }
func main() { rand.Seed(time.Now().UnixNano()) log.SetDebug(os.Getenv("ABOT_DEBUG") == "true") if err := core.LoadEnvVars(); err != nil { log.Fatal(err) } app := cli.NewApp() app.Commands = []cli.Command{ { Name: "new", Usage: "generate a new abot", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) if len(c.Args()) != 1 { l.Fatal("usage: abot new {name}") } if strings.Contains(c.Args().First(), " ") { l.Fatal("name cannot include a space. use camelCase") } err := newAbot(l, c.Args().First(), c.Args().Get(1)) if err != nil { l.Fatalf("could not build new abot. %s", err) } l.Info("Success. Created " + c.Args().First()) return nil }, }, { Name: "server", Aliases: []string{"s"}, Usage: "run server", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) cmd := exec.Command("/bin/sh", "-c", "go build") out, err := cmd.CombinedOutput() if err != nil { l.Info(string(out)) return err } dir, err := os.Getwd() if err != nil { return err } _, file := filepath.Split(dir) cmd = exec.Command("/bin/sh", "-c", "./"+file) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err = cmd.Start(); err != nil { return err } return cmd.Wait() }, }, { Name: "install", Aliases: []string{"i"}, Usage: "download and install plugins listed in plugins.json", Action: func(c *cli.Context) error { errChan := make(chan errMsg) l := log.New("") l.SetFlags(0) l.SetDebug(os.Getenv("ABOT_DEBUG") == "true") go func() { select { case errC := <-errChan: if errC.err == nil { // Success l.Info(errC.msg) os.Exit(0) } // Plugins install failed, so remove incomplete plugins.go file errR := os.Remove("plugins.go") if errR != nil && !os.IsNotExist(errR) { l.Info("could not remove plugins.go file.", errR) } if len(errC.msg) > 0 { l.Fatalf("could not install plugins.\n%s\n%s", errC.msg, errC.err) } l.Fatalf("could not install plugins.\n%s", errC.err) } }() installPlugins(l, errChan) for { // Keep process running until a message is received } }, }, { Name: "search", Usage: "search plugins indexed on itsabot.org", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) args := c.Args() if len(args) == 0 || len(args) > 2 { l.Fatal(errors.New(`usage: abot plugin search {term}`)) } if err := searchPlugins(args.First()); err != nil { l.Fatalf("could not start console\n%s", err) } return nil }, }, { Name: "update", Aliases: []string{"u", "upgrade"}, Usage: "update and install plugins listed in plugins.json", Action: func(c *cli.Context) error { updatePlugins() return nil }, }, { Name: "publish", Aliases: []string{"p"}, Usage: "publish a plugin to itsabot.org", Action: func(c *cli.Context) error { publishPlugin(c) return nil }, }, { Name: "test", Aliases: []string{"t"}, Usage: "tests plugins", Action: func(c *cli.Context) error { lg.SetFlags(0) count, err := testPlugin() if err != nil { return err } if count == 0 { lg.Println("No tests found. Did you run \"abot install\"?") return nil } lg.Printf("Success (%d tests).\n", count) return nil }, }, { Name: "generate", Aliases: []string{"g"}, Usage: "generate plugin scaffolding", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) args := c.Args() if len(args) != 1 { l.Fatal(errors.New(`usage: abot generate {name}`)) } generatePlugin(l, args.First()) l.Info("Created", args.First(), "in", filepath.Join(os.Getenv("PWD"), args.First())) return nil }, }, { Name: "login", Aliases: []string{"l"}, Usage: "log into itsabot.org to enable publishing plugins", Action: func(c *cli.Context) error { login() return nil }, }, { Name: "console", Aliases: []string{"c"}, Usage: "communicate with a running abot server", Action: func(c *cli.Context) error { if err := startConsole(c); err != nil { l := log.New("") l.SetFlags(0) l.Fatalf("could not start console\n%s", err) } return nil }, }, { Name: "dbconsole", Aliases: []string{"dbc"}, Usage: "communicate with a running abot server", Action: func(c *cli.Context) error { cmd := exec.Command("/bin/sh", "-c", "psql "+os.Getenv("ABOT_DATABASE_URL")) cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin if err := cmd.Start(); err != nil { return err } return cmd.Wait() }, }, } app.Action = func(c *cli.Context) error { cli.ShowAppHelp(c) return nil } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
// New builds a Plugin with its trigger, RPC, and configuration settings from // its plugin.json. func New(url string) (*dt.Plugin, error) { if err := core.LoadEnvVars(); err != nil { log.Fatal(err) } db, err := core.ConnectDB("") if err != nil { return nil, err } // Read plugin.json data from within plugins.go, unmarshal into struct c := dt.PluginConfig{} if len(os.Getenv("ABOT_PATH")) > 0 { p := filepath.Join(os.Getenv("ABOT_PATH"), "plugins.go") var scn *bufio.Scanner fi, err := os.OpenFile(p, os.O_RDONLY, 0666) if os.IsNotExist(err) { goto makePlugin } if err != nil { return nil, err } defer func() { if err = fi.Close(); err != nil { log.Info("failed to close file", fi.Name()) return } }() var found bool var data string scn = bufio.NewScanner(fi) for scn.Scan() { t := scn.Text() if !found && t != url { continue } else if t == url { found = true continue } else if len(t) >= 1 && t[0] == '}' { data += t break } data += t } if err = scn.Err(); err != nil { return nil, err } if len(data) > 0 { if err = json.Unmarshal([]byte(data), &c); err != nil { return nil, err } if len(c.Name) == 0 { return nil, ErrMissingPluginName } } } makePlugin: l := log.New(c.Name) l.SetDebug(os.Getenv("ABOT_DEBUG") == "true") plg := &dt.Plugin{ Trigger: &dt.StructuredInput{}, SetBranches: func(in *dt.Msg) [][]dt.State { return nil }, Events: &dt.PluginEvents{ PostReceive: func(cmd *string) {}, PreProcessing: func(cmd *string, u *dt.User) {}, PostProcessing: func(in *dt.Msg) {}, PreResponse: func(in *dt.Msg, resp *string) {}, }, Config: c, DB: db, Log: l, } plg.SM = dt.NewStateMachine(plg) return plg, nil }