Пример #1
0
func TestHMain(t *testing.T) {
	reset(t)
	user, fid, fidT := seedDBUser(t)
	req := dt.Request{CMD: "Hi", UserID: user.ID}

	// Test via a UserID
	byt, err := json.Marshal(req)
	if err != nil {
		t.Fatal(err)
	}
	c, b := request("POST", "/", byt)
	if c != http.StatusOK {
		log.Info(b)
		t.Fatal("expected", http.StatusOK, "got", c)
	}
	if b == "Something went wrong with my wiring... I'll get that fixed up soon." {
		t.Fatal(`expected "Hi there :)" but got "Something went wrong..."`)
	}

	// Test via a FlexID
	req.UserID = 0
	req.FlexID = fid
	req.FlexIDType = fidT
	byt, err = json.Marshal(req)
	if err != nil {
		t.Fatal(err)
	}
	c, b = request("POST", "/", byt)
	if c != http.StatusOK {
		log.Info(b)
		t.Fatal("expected", http.StatusOK, "got", c)
	}
}
Пример #2
0
func cleanup() {
	q := `DELETE FROM messages`
	_, err := p.DB.Exec(q)
	if err != nil {
		log.Info("failed to delete messages.", err)
	}
	q = `DELETE FROM states`
	_, err = p.DB.Exec(q)
	if err != nil {
		log.Info("failed to delete messages.", err)
	}
}
Пример #3
0
func writeError(w http.ResponseWriter, err error) {
	tmp := strings.Replace(err.Error(), `"`, "'", -1)
	errS := struct{ Msg string }{Msg: tmp}
	byt, err := json.Marshal(errS)
	if err != nil {
		log.Info("failed to marshal error", err)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	if _, err = w.Write(byt); err != nil {
		log.Info("failed to write error", err)
	}
}
Пример #4
0
func TestPluginSearch(t *testing.T) {
	query := "weather"
	var byt []byte
	var err error
	if testing.Short() {
		log.Info("stubbing plugin search results in short mode.")
		byt = []byte(`[{"Name":"Weather","Valid":true}]`)
	} else {
		byt, err = searchItsAbot(query)
		if err != nil {
			t.Fatal(err)
		}
	}
	var b []byte
	buf := bytes.NewBuffer(b)
	if err = outputPluginResults(buf, byt); err != nil {
		t.Fatal(err)
	}
	tmp := buf.String()
	if !strings.Contains(tmp, "NAME") {
		t.Fatal(err)
	}
	if !strings.Contains(tmp, "Weather") {
		t.Fatal(err)
	}
}
Пример #5
0
// fetchTrainingSentences retrieves training sentences from a remote source
// (via ITSABOT_URL, which defaults to itsabot.org).
func fetchTrainingSentences(pluginID uint64, name string) ([]tSentence, error) {
	c := &http.Client{Timeout: 10 * time.Second}
	pid := strconv.FormatUint(pluginID, 10)
	u := os.Getenv("ITSABOT_URL") + "/api/plugins/train/" + pid
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err = resp.Body.Close(); err != nil {
			log.Info("failed to close response body.", err)
		}
	}()
	ss := []tSentence{}

	// This occurs when the plugin has not been published, which we should
	// ignore on boot.
	if resp.StatusCode == http.StatusBadRequest {
		log.Infof("warn: plugin %s has not been published", name)
		return ss, nil
	}
	err = json.NewDecoder(resp.Body).Decode(&ss)
	return ss, err
}
Пример #6
0
// hapiAdmins returns a list of all admins with the training and manage team
// permissions.
func hapiAdmins(w http.ResponseWriter, r *http.Request) {
	if os.Getenv("ABOT_ENV") != "test" {
		if !isAdmin(w, r) {
			return
		}
		if !isLoggedIn(w, r) {
			return
		}
	}
	var admins []struct {
		ID    uint64
		Name  string
		Email string
	}
	q := `SELECT id, name, email FROM users WHERE admin=TRUE`
	err := db.Select(&admins, q)
	if err != nil && err != sql.ErrNoRows {
		writeErrorInternal(w, err)
		return
	}
	b, err := json.Marshal(admins)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	_, err = w.Write(b)
	if err != nil {
		log.Info("failed to write response.", err)
	}
}
Пример #7
0
// hapiRemoteTokens returns the final six bytes of each auth token used to
// authenticate to the remote service and when.
func hapiRemoteTokens(w http.ResponseWriter, r *http.Request) {
	if os.Getenv("ABOT_ENV") != "test" {
		if !isAdmin(w, r) {
			return
		}
		if !isLoggedIn(w, r) {
			return
		}
	}

	// We initialize the variable here because we want empty slices to
	// marshal to [], not null
	auths := []struct {
		Token     string
		Email     string
		CreatedAt time.Time
		PluginIDs dt.Uint64Slice
	}{}
	q := `SELECT token, email, pluginids, createdat FROM remotetokens`
	err := db.Select(&auths, q)
	if err != nil && err != sql.ErrNoRows {
		writeErrorInternal(w, err)
		return
	}
	byt, err := json.Marshal(auths)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	_, err = w.Write(byt)
	if err != nil {
		log.Info("failed to write response.", err)
	}
}
Пример #8
0
func TestHIndex(t *testing.T) {
	c, b := request("GET", "/", nil)
	if c != http.StatusOK {
		log.Info(b)
		t.Fatal("expected", http.StatusOK, "got", c)
	}
}
Пример #9
0
func loadLocation(l string) *time.Location {
	loc, err := time.LoadLocation(l)
	if err != nil {
		log.Info("failed to load location.", l)
	}
	return loc
}
Пример #10
0
func generatePlugin(l *log.Logger, name string) error {
	// Log in to get the maintainer email
	if os.Getenv("ABOT_ENV") != "test" {
		p := filepath.Join(os.Getenv("HOME"), ".abot.conf")
		if _, err := os.Stat(p); os.IsNotExist(err) {
			login()
		}
	}

	// Ensure the name and path are unique globally
	var words []string
	var lastIdx int
	name = strings.Replace(name, " ", "_", -1)
	dirName := name
	for i, letter := range name {
		if i == 0 {
			continue
		}
		if unicode.IsUpper(letter) {
			words = append(words, name[lastIdx:i])
			lastIdx = i
		}
	}
	words = append(words, name[lastIdx:])
	dirName = strings.Join(words, "_")
	dirName = strings.ToLower(dirName)
	name = strings.ToLower(name)

	// Create the directory
	if err := os.Mkdir(dirName, 0744); err != nil {
		return err
	}

	// Generate a plugin.json file
	if err := buildPluginJSON(dirName, name); err != nil {
		log.Info("failed to create plugin.json")
		return err
	}

	// Generate name.go and name_test.go files with starter keywords and
	// state machines
	if err := buildPluginScaffoldFile(dirName, name); err != nil {
		log.Info("failed to create plugin scaffold file")
		return err
	}
	return nil
}
Пример #11
0
// ExtractCities efficiently from a user's message.
func ExtractCities(db *sqlx.DB, in *dt.Msg) ([]dt.City, error) {
	// Interface type is used to expand the args in db.Select below.
	// Although we're only storing strings, []string{} doesn't work.
	var args []interface{}

	// Look for "at", "in", "on" prepositions to signal that locations
	// follow, skipping everything before
	var start int
	for i := range in.Stems {
		switch in.Stems[i] {
		case "at", "in", "on":
			start = i
			break
		}
	}

	// Prepare sentence for iteration
	tmp := regexNonWords.ReplaceAllString(in.Sentence, "")
	words := strings.Fields(strings.Title(tmp))

	// Iterate through words and bigrams to assemble a DB query
	for i := start; i < len(words); i++ {
		args = append(args, words[i])
	}
	bgs := bigrams(words, start)
	for i := 0; i < len(bgs); i++ {
		args = append(args, bgs[i])
	}

	cities := []dt.City{}
	q := `SELECT name, countrycode FROM cities
	      WHERE countrycode='US' AND name IN (?)
	      ORDER BY LENGTH(name) DESC`
	query, arguments, err := sqlx.In(q, args)
	query = db.Rebind(query)
	rows, err := db.Query(query, arguments...)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err = rows.Close(); err != nil {
			log.Info("failed to close db rows.", err)
		}
	}()
	for rows.Next() {
		city := dt.City{}
		if err = rows.Scan(&city.Name, &city.CountryCode); err != nil {
			return nil, err
		}
		cities = append(cities, city)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	if len(cities) == 0 {
		return nil, ErrNotFound
	}
	return cities, nil
}
Пример #12
0
// hapiAdminsUpdate adds or removes admin permission from a given user.
func hapiAdminsUpdate(w http.ResponseWriter, r *http.Request) {
	if os.Getenv("ABOT_ENV") != "test" {
		if !isAdmin(w, r) {
			return
		}
		if !isLoggedIn(w, r) {
			return
		}
		if !isValidCSRF(w, r) {
			return
		}
	}
	var req struct {
		ID    uint64
		Email string
		Admin bool
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeErrorBadRequest(w, err)
		return
	}
	// This is a clever way to update the user using EITHER email or ID
	// (whatever the client had available). Then we return the ID of the
	// updated entry to send back to the client for faster future requests.
	if req.ID > 0 && len(req.Email) > 0 {
		writeErrorBadRequest(w, errors.New("only one value allowed: ID or Email"))
		return
	}
	q := `UPDATE users SET admin=$1 WHERE id=$2 OR email=$3 RETURNING id`
	err := db.QueryRow(q, req.Admin, req.ID, req.Email).Scan(&req.ID)
	if err == sql.ErrNoRows {
		// This error is frequently user-facing.
		writeErrorBadRequest(w, errors.New("User not found."))
		return
	}
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	var user struct {
		ID    uint64
		Email string
		Name  string
	}
	q = `SELECT id, email, name FROM users WHERE id=$1`
	if err = db.Get(&user, q, req.ID); err != nil {
		writeErrorInternal(w, err)
		return
	}
	byt, err := json.Marshal(user)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	_, err = w.Write(byt)
	if err != nil {
		log.Info("failed to write response.", err)
	}
}
Пример #13
0
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())
}
Пример #14
0
func TestPluginGenerate(t *testing.T) {
	defer func() {
		if err := os.RemoveAll("__test"); err != nil {
			log.Info("failed to clean up __test dir.", err)
		}
		if err := os.RemoveAll("test_here"); err != nil {
			log.Info("failed to clean up __test dir.", err)
		}
	}()

	// generate plugin
	l := log.New("")
	l.SetFlags(0)
	if err := generatePlugin(l, "__test"); err != nil {
		t.Fatal(err)
	}
	if err := generatePlugin(l, "TestHere"); err != nil {
		t.Fatal(err)
	}
}
Пример #15
0
func sendEventsTick(evtChan chan *dt.ScheduledEvent, t time.Time) {
	// Listen for events that need to be sent.
	go func(chan *dt.ScheduledEvent) {
		q := `UPDATE scheduledevents SET sent=TRUE WHERE id=$1`
		select {
		case evt := <-evtChan:
			log.Debug("received event")
			if smsConn == nil {
				log.Info("failed to send scheduled event (missing SMS driver). will retry.")
				return
			}
			// Send event. On error, event will be retried next
			// minute.
			if err := evt.Send(smsConn); err != nil {
				log.Info("failed to send scheduled event", err)
				return
			}
			// Update event as sent
			if _, err := db.Exec(q, evt.ID); err != nil {
				log.Info("failed to update scheduled event as sent",
					err)
				return
			}
		}
	}(evtChan)

	q := `SELECT id, content, flexid, flexidtype
		      FROM scheduledevents
		      WHERE sent=false AND sendat<=$1`
	evts := []*dt.ScheduledEvent{}
	if err := db.Select(&evts, q, time.Now()); err != nil {
		log.Info("failed to queue scheduled event", err)
		return
	}
	for _, evt := range evts {
		// Queue the event for sending
		evtChan <- evt
	}
}
Пример #16
0
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())
}
Пример #17
0
func testPlugin() (int, error) {
	if err := core.LoadPluginsGo(); err != nil {
		return 0, err
	}
	r := plugin.TestPrepare()
	plugin.TestCleanup()
	var count int
	for _, plg := range core.PluginsGo {
		log.Info("loading", plg.Name)
		for _, test := range plg.Tests {
			log.Info("running", test)
			count++
			for in, exps := range test {
				err := plugin.TestReq(r, in, exps)
				if err != nil {
					return count, err
				}
			}
		}
	}
	plugin.TestCleanup()
	return count, nil
}
Пример #18
0
func TestHAPIProfile(t *testing.T) {
	reset(t)
	user, _, _ := seedDBUser(t)
	seedDBUserSession(t, user)

	c, b := userRequest("GET", "/api/user/profile.json", nil, user)
	if c != http.StatusOK {
		log.Info(b)
		t.Fatal("expected", http.StatusOK, "got", c)
	}
	if !strings.Contains(b, "Name") {
		t.Fatal(`expected "Name" but got`, b)
	}
}
Пример #19
0
func TestHSignupSubmit(t *testing.T) {
	reset(t)
	u := "http://*****:*****@example.com",
		"Password": "******",
		"FID": "+13105555555"
	}`)
	c, b := request("POST", u, data)
	if c != http.StatusOK {
		log.Info(b)
		t.Fatal("expected", http.StatusOK, "got", c)
	}
}
Пример #20
0
// preprocess converts a user input into a Msg that's been persisted to the
// database
func preprocess(r *http.Request) (*dt.Msg, error) {
	req := &dt.Request{}
	err := json.NewDecoder(r.Body).Decode(req)
	if err != nil {
		log.Info("could not parse empty body", err)
		return nil, err
	}
	sendPostReceiveEvent(&req.CMD)
	u, err := dt.GetUser(db, req)
	if err != nil {
		return nil, err
	}
	sendPreProcessingEvent(&req.CMD, u)
	// TODO trigger training if needed (see buildInput)
	return NewMsg(u, req.CMD)
}
Пример #21
0
// hMain is the endpoint to hit when you want a direct response via JSON.
// The Abot console uses this endpoint.
func hMain(w http.ResponseWriter, r *http.Request) {
	errMsg := "Something went wrong with my wiring... I'll get that fixed up soon."
	ret, err := ProcessText(r)
	if err != nil {
		if len(ret) > 0 {
			ret = errMsg
		}
		log.Info("failed to process text.", err)
		// TODO notify plugins listening for errors
	}
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Access-Control-Allow-Origin")
	_, err = fmt.Fprint(w, ret)
	if err != nil {
		writeErrorInternal(w, err)
	}
}
Пример #22
0
// hapiAdminExists checks if an admin exists in the database.
func hapiAdminExists(w http.ResponseWriter, r *http.Request) {
	var count int
	q := `SELECT COUNT(*) FROM users WHERE admin=TRUE LIMIT 1`
	if err := db.Get(&count, q); err != nil {
		writeErrorInternal(w, err)
		return
	}
	byt, err := json.Marshal(count > 0)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	_, err = w.Write(byt)
	if err != nil {
		log.Info("failed writing response header.", err)
	}
}
Пример #23
0
func TestHAPILoginSubmit(t *testing.T) {
	reset(t)
	user, _, _ := seedDBUser(t)
	data := struct {
		Email    string
		Password string
	}{
		Email:    user.Email,
		Password: user.Password,
	}
	byt, err := json.Marshal(data)
	if err != nil {
		t.Fatal(err)
	}
	u := "http://localhost:" + os.Getenv("PORT") + "/api/login.json"
	c, b := request("POST", u, byt)
	if c != http.StatusOK {
		log.Info(b)
		t.Fatal("expected", http.StatusOK, "got", c)
	}
}
Пример #24
0
// hapiConversationsNeedTraining returns a list of all sentences that require a
// human response.
func hapiConversationsNeedTraining(w http.ResponseWriter, r *http.Request) {
	if os.Getenv("ABOT_ENV") != "test" {
		if !isAdmin(w, r) {
			return
		}
		if !isLoggedIn(w, r) {
			return
		}
	}
	msgs := []struct {
		Sentence   string
		FlexID     *string
		CreatedAt  time.Time
		UserID     uint64
		FlexIDType *int
	}{}
	q := `SELECT * FROM (
		SELECT DISTINCT ON (flexid)
			userid, flexid, flexidtype, sentence, createdat
		FROM messages
		WHERE needstraining=TRUE AND trained=FALSE AND abotsent=FALSE AND sentence<>''
	) t ORDER BY createdat DESC`
	err := db.Select(&msgs, q)
	if err == sql.ErrNoRows {
		w.WriteHeader(http.StatusOK)
	}
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	byt, err := json.Marshal(msgs)
	if err != nil {
		writeErrorInternal(w, err)
		return
	}
	_, err = w.Write(byt)
	if err != nil {
		log.Info("failed to write response.", err)
	}
}
Пример #25
0
func buildPluginScaffoldFile(dirName, name string) error {
	fi, err := os.Create(filepath.Join(dirName, dirName+".go"))
	if err != nil {
		return err
	}
	defer func() {
		err = fi.Close()
		if err != nil {
			log.Info("failed to close plugin.json.", err)
		}
	}()
	dir, err := os.Getwd()
	if err != nil {
		return err
	}
	dir = filepath.Join(dir, name)
	_, err = fi.WriteString(pluginScaffoldFile(dir, name))
	if err != nil {
		return err
	}
	return nil
}
Пример #26
0
func buildPluginJSON(dirName, name string) error {
	var maintainer string
	if os.Getenv("ABOT_ENV") == "test" {
		maintainer = "*****@*****.**"
	} else {
		fi, err := os.Open(filepath.Join(os.Getenv("HOME"), ".abot.conf"))
		if err != nil {
			return err
		}
		defer func() {
			if err = fi.Close(); err != nil {
				log.Info("failed to close plugin.json file.", err)
			}
		}()
		scn := bufio.NewScanner(fi)
		var lineNum int
		for scn.Scan() {
			if lineNum < 1 {
				lineNum++
				continue
			}
			maintainer = scn.Text()
			break
		}
		if scn.Err() != nil {
			return err
		}
	}
	b := []byte(`{
	"Name": "` + name + `",
	"Maintainer": "` + maintainer + `",
	"Usage": ["Show me a demo"],
	"Tests": [
		{"Show me a demo": ["demo"]}
	]
}`)
	return ioutil.WriteFile(filepath.Join(dirName, "plugin.json"), b, 0744)
}
Пример #27
0
// 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
}
Пример #28
0
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)
	}
}
Пример #29
0
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!")
}
Пример #30
0
func updateAnalyticsTick(t time.Time) {
	if os.Getenv("ABOT_ENV") == "test" {
		return
	}
	log.Info("updating analytics")
	createdAt := t.Round(24 * time.Hour)

	// User count
	var count int
	q := `SELECT COUNT(*) FROM (
		SELECT DISTINCT (flexid, flexidtype) FROM userflexids
	      ) AS t`
	if err := db.Get(&count, q); err != nil {
		log.Info("failed to retrieve user count.", err)
		return
	}
	aq := `INSERT INTO analytics (label, value, createdat)
	       VALUES ($1, $2, $3)
	       ON CONFLICT (label, createdat) DO UPDATE SET value=$2`
	_, err := db.Exec(aq, keyUserCount, count, createdAt)
	if err != nil {
		log.Info("failed to update analytics (user count).", err)
		return
	}

	// Message count
	q = `SELECT COUNT(*) FROM messages`
	if err = db.Get(&count, q); err != nil {
		log.Info("failed to retrieve message count.", err)
		return
	}
	_, err = db.Exec(aq, keyMsgCount, count, createdAt)
	if err != nil {
		log.Info("failed to update analytics (msg count).", err)
		return
	}

	// Messages needing training
	q = `SELECT COUNT(*) FROM messages
	     WHERE needstraining=TRUE AND abotsent=FALSE`
	if err = db.Get(&count, q); err != nil {
		log.Info("failed to retrieve user count.", err)
		return
	}
	_, err = db.Exec(aq, keyTrainCount, count, createdAt)
	if err != nil {
		log.Info("failed to update analytics (msg count).", err)
		return
	}

	// Version number
	client := &http.Client{Timeout: 15 * time.Minute}
	u := "https://raw.githubusercontent.com/itsabot/abot/master/base/plugins.json"
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		log.Info("failed to retrieve version number.", err)
		return
	}
	reqResp, err := client.Do(req)
	if err != nil {
		log.Info("failed to retrieve version number.", err)
		return
	}
	defer func() {
		if err = reqResp.Body.Close(); err != nil {
			log.Info("failed to close body.", err)
		}
	}()
	var remoteConf PluginJSON
	if err = json.NewDecoder(reqResp.Body).Decode(&remoteConf); err != nil {
		log.Info("failed to retrieve version number.", err)
		return
	}
	_, err = db.Exec(aq, keyVersion, remoteConf.Version, createdAt)
	if err != nil {
		log.Info("failed to update analytics (version number).", err)
		return
	}
}