From 02612e9b7507dc845de0b2cabfb88ed89fbd8f61 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 05:43:35 +0700 Subject: [PATCH 01/10] add use of bolt db --- bot.go | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/bot.go b/bot.go index d8992f6..afab6c0 100644 --- a/bot.go +++ b/bot.go @@ -1,13 +1,19 @@ package main import ( + "encoding/json" "fmt" "log" "math/rand" "os" + "strconv" "strings" "time" + "crypto/tls" + "net/http" + + "github.com/boltdb/bolt" tb "gopkg.in/tucnak/telebot.v2" ) @@ -33,11 +39,35 @@ type userInfo struct { const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" var userMap = make(map[int]*userInfo) +var userBk = "userInfo" +var userIdKey = "userId" +var userDisplayNameKey = "displayName" +var userTosAgreedBk = "tosAgreed" +var userSubcriptionBk = "subcription" +var userRegistrationStepBk = "registrationStep" +var userLastSigninRequestBk = "lastSigninRequest" func main() { + //Set env value + botToken := "SIGNIN_BOT_TOKEN" + os.Setenv(botToken, "587498524:AAH5eMVzvxRU9pLy9hD3TY48hiQhi3QCSYs") + //Setup log + log.SetFlags(log.LstdFlags | log.Llongfile) + //db setup + dbStorage := "my.db" + db, err := initDb(dbStorage) + db, err = initDbBucket(db, userBk) + defer db.Close() + + //Create http client + transCfg := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //disable verify + } + client := &http.Client{Transport: transCfg} b, err := tb.NewBot(tb.Settings{ - Token: os.Getenv("SIGNIN_BOT_TOKEN"), + Token: os.Getenv(botToken), Poller: &tb.LongPoller{Timeout: 10 * time.Second}, + Client: client, }) if err != nil { @@ -271,3 +301,94 @@ func doCreateAccount(b *tb.Bot, m *tb.Message) { func informSignin(b *tb.Bot, m *tb.Message) { send(b, m, "To sign-in Kyber Network, please type /signin") } + +func initDb(storage string) (*bolt.DB, error) { + log.Printf("Initialize boltdb!") + db, err := bolt.Open(storage, 0600, nil) + if err != nil { + log.Fatal(err) + } else { + log.Printf("DB is initialize successful to %s.", storage) + } + return db, err +} + +func initDbBucket(db *bolt.DB, bucket string) (*bolt.DB, error) { + err := db.Update(func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte(bucket)) + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", bucket, err) + log.Print(errStr) + return errStr + } + /* + _, err = rootBk.CreateBucketIfNotExists([]byte(userDisplayNameBk)) + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", userDisplayNameBk, err) + log.Print(errStr) + return errStr + } + + _, err = rootBk.CreateBucketIfNotExists([]byte(userTosAgreedBk)) + + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", userTosAgreedBk, err) + log.Print(errStr) + return errStr + } + + _, err = rootBk.CreateBucketIfNotExists([]byte(userSubcriptionBk)) + + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", userSubcriptionBk, err) + log.Print(errStr) + return errStr + } + + _, err = rootBk.CreateBucketIfNotExists([]byte(userRegistrationStepBk)) + + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", userRegistrationStepBk, err) + log.Print(errStr) + return errStr + } + + _, err = rootBk.CreateBucketIfNotExists([]byte(userLastSigninRequestBk)) + + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", userLastSigninRequestBk, err) + log.Print(errStr) + return errStr + } + */ + return nil + }) + return db, err +} +func updateUserInfo(db *bolt.DB, user *userInfo, id int, bucket string) error { + err := db.Update(func(tx *bolt.Tx) error { + bk := tx.Bucket([]byte(bucket)) + userBytes, err := json.Marshal(user) + if err == nil { + err = bk.Put([]byte(strconv.Itoa(id)), []byte(userBytes)) + if err == nil { + log.Printf("Insert user id=%i to db successfully!", id) + } + } + return nil + }) + return err +} +func getUserInfo(db *bolt.DB, id int, bucket string) userInfo { + userBytes := db.View(func(tx *bolt.Tx) (value []byte) { + bk := tx.Bucket([]byte(bucket)) + value = bk.Get([]byte(strconv.Itoa(id))) + return value + }) + var users []userInfo + json.Unmarshal(userBytes, &users) + if len(users) > 1 { + return users[0] + } + return nil +} From 2b370ea3c910bc41435b045de3cc61148a90a4c3 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 05:46:53 +0700 Subject: [PATCH 02/10] add use of bolt db --- bot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.go b/bot.go index afab6c0..c057089 100644 --- a/bot.go +++ b/bot.go @@ -50,7 +50,7 @@ var userLastSigninRequestBk = "lastSigninRequest" func main() { //Set env value botToken := "SIGNIN_BOT_TOKEN" - os.Setenv(botToken, "587498524:AAH5eMVzvxRU9pLy9hD3TY48hiQhi3QCSYs") + os.Setenv(botToken, "xxxxxxxxxxxxx") //Setup log log.SetFlags(log.LstdFlags | log.Llongfile) //db setup From 95d53fbd3898d57d5be0dc8a1a63461809b81326 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 17:50:09 +0700 Subject: [PATCH 03/10] asdfasdf --- bot.go | 314 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 180 insertions(+), 134 deletions(-) diff --git a/bot.go b/bot.go index c057089..267fe56 100644 --- a/bot.go +++ b/bot.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "time" + "errors" "crypto/tls" "net/http" @@ -17,6 +18,10 @@ import ( tb "gopkg.in/tucnak/telebot.v2" ) +const APP_ENV_DB_STORAGE = "BLUE_BOT_DB_STORAGE" +const APP_ENV_BOT_TOKEN = "SIGNIN_BOT_TOKEN" + + type Step int const ( @@ -29,35 +34,152 @@ const ( ) type userInfo struct { - displayName string - tosAgreed bool - subscription bool - registrationStep Step - lastSigninRequest time.Time + DisplayName string + TosAgreed bool + Subscription bool + RegistrationStep Step + LastSigninRequest time.Time } const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" var userMap = make(map[int]*userInfo) -var userBk = "userInfo" -var userIdKey = "userId" -var userDisplayNameKey = "displayName" -var userTosAgreedBk = "tosAgreed" -var userSubcriptionBk = "subcription" -var userRegistrationStepBk = "registrationStep" -var userLastSigninRequestBk = "lastSigninRequest" -func main() { - //Set env value - botToken := "SIGNIN_BOT_TOKEN" - os.Setenv(botToken, "xxxxxxxxxxxxx") +var defaultDbStorage = "my.db" +var defaultDbFileMode os.FileMode = 0600 +var defaultBucket = "userinfo" + + +func test(){ + fmt.Printf("hello, world\n") + var user userInfo + user.DisplayName="test name" + user.TosAgreed=false + user.Subscription=true + user.RegistrationStep=stepToAskTos + + log.Printf("User info step=%d displayName=%s tosAgreed=%s", user.RegistrationStep, user.DisplayName, user.TosAgreed) + + userBytes, err := json.Marshal(user) + if err == nil{ + os.Stdout.Write(userBytes) + } + +} + +func appInit(){ //Setup log log.SetFlags(log.LstdFlags | log.Llongfile) - //db setup - dbStorage := "my.db" - db, err := initDb(dbStorage) - db, err = initDbBucket(db, userBk) + + //Set env value + tempValue := os.Getenv(APP_ENV_DB_STORAGE) + if tempValue == ""{ + os.Setenv(APP_ENV_DB_STORAGE, defaultDbStorage) + } + err := initDb() + if err != nil { + log.Fatal(err) + } + err = initDbBucket(defaultBucket) + if err != nil { + log.Fatal(err) + } + + tempValue = os.Getenv(APP_ENV_BOT_TOKEN) + if tempValue == ""{ + log.Fatal("I dont have the KEY to open the door! :(") + } +} + +func initDb() (error) { + storage := os.Getenv(APP_ENV_DB_STORAGE) + log.Printf("InitDB: Initialize boltdb with storage %s!", storage) + db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) + if err != nil { + log.Fatal(err) + } else { + log.Printf("InitDB: DB is initialize successful to %s.", storage) + } defer db.Close() + return err +} + +func initDbBucket(bucket string) (error) { + storage := os.Getenv(APP_ENV_DB_STORAGE) + db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) + if err != nil { + log.Fatal(err) + } + + err = db.Update(func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte(bucket)) + if err != nil { + errStr := fmt.Errorf("Could not create bucket %s error: %s", bucket, err) + log.Print(errStr) + return errStr + } + return nil + }) + defer db.Close() + return err +} +func updateUserInfo(user *userInfo, id int, bucket string) error { + storage := os.Getenv(APP_ENV_DB_STORAGE) + db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) + if err != nil { + log.Fatal(err) + } + + err = db.Update(func(tx *bolt.Tx) error { + bk := tx.Bucket([]byte(bucket)) + log.Printf("User info step=%d displayName=%s tosAgreed=%s", user.RegistrationStep, user.DisplayName, user.TosAgreed) + userBytes, err := json.Marshal(user) + if err == nil { + err = bk.Put([]byte(strconv.Itoa(id)), []byte(userBytes)) + if err == nil { + log.Printf("updateUserInfo: Insert user id=%d value=%s to db successfully!", id, userBytes) + }else{ + log.Printf("updateUserInfo: Failure insert user id=%d value=%s to db!", id, userBytes) + } + }else{ + log.Printf("updateUserInfo: Failure to jsonize user data with id=%d", id) + } + return nil + }) + + defer db.Close() + return err +} + +func getUserInfo(id int, bucket string) (userInfo, error) { + storage := os.Getenv(APP_ENV_DB_STORAGE) + db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) + if err != nil { + log.Fatal(err) + } + var user userInfo + err = db.View(func(tx *bolt.Tx) error { + bk := tx.Bucket([]byte(bucket)) + userBytes := bk.Get([]byte(strconv.Itoa(id))) + + var users []userInfo + json.Unmarshal(userBytes, &users) + if len(users) > 1{ + user = users[0] + return nil + } + errStr := "Not found info of user id=" + strconv.Itoa(id) + return errors.New(errStr) + }) + return user, err +} + + +func main() { + + appInit() + + test() //Create http client transCfg := &http.Transport{ @@ -65,7 +187,7 @@ func main() { } client := &http.Client{Transport: transCfg} b, err := tb.NewBot(tb.Settings{ - Token: os.Getenv(botToken), + Token: os.Getenv(APP_ENV_BOT_TOKEN), Poller: &tb.LongPoller{Timeout: 10 * time.Second}, Client: client, }) @@ -76,14 +198,17 @@ func main() { } b.Handle(tb.OnText, func(m *tb.Message) { + log.Printf("Handle onText=%s", m.Text) handleReply(b, m) }) b.Handle("/start", func(m *tb.Message) { + log.Printf("Handle /start command=%s", m.Text) next(b, m) }) b.Handle("/signin", func(m *tb.Message) { + log.Printf("Handle /signin command=%s", m.Text) next(b, m) }) @@ -99,9 +224,10 @@ func randString(n int) string { } func getFullName(m *tb.Message) string { + log.Printf("getFullName: from user id=%d", m.Sender.ID) if user, ok := userMap[m.Sender.ID]; ok { - if user.displayName != "" { - return user.displayName + if user.DisplayName != "" { + return user.DisplayName } } return fmt.Sprintf("%s %s", m.Sender.FirstName, m.Sender.LastName) @@ -144,6 +270,8 @@ func sendfAndHideKeyboard(b *tb.Bot, m *tb.Message, text string, a ...interface{ func next(b *tb.Bot, m *tb.Message) { if user, ok := userMap[m.Sender.ID]; ok { + log.Printf("next: registrationStep=%d for User id=%d send msg=%s", user.RegistrationStep, m.Sender.ID, m.Text) + updateUserInfo(user, m.Sender.ID, defaultBucket) funcArray := []func(*tb.Bot, *tb.Message){ confirmDisplayName, askDisplayName, @@ -151,7 +279,7 @@ func next(b *tb.Bot, m *tb.Message) { askSubcription, doCreateAccount, sendSigninLink} - funcArray[user.registrationStep](b, m) + funcArray[user.RegistrationStep](b, m) } else { // registration startRegistration(b, m) @@ -160,7 +288,8 @@ func next(b *tb.Bot, m *tb.Message) { func sendSigninLink(b *tb.Bot, m *tb.Message) { user := userMap[m.Sender.ID] - last := user.lastSigninRequest + last := user.LastSigninRequest + log.Printf("sendSigninLink: send to user id=%d lastSigninRequest=%d registrationStep=%d,", m.Sender.ID, last, user.RegistrationStep) if !last.IsZero() { elapsed := time.Now().Sub(last).Minutes() if elapsed < 1.0 { @@ -177,7 +306,7 @@ func sendSigninLink(b *tb.Bot, m *tb.Message) { m.Sender.ID, ) if err == nil { - user.lastSigninRequest = time.Now() + user.LastSigninRequest = time.Now() } } @@ -205,45 +334,46 @@ func isNo(text string) bool { func handleReply(b *tb.Bot, m *tb.Message) { if user, ok := userMap[m.Sender.ID]; ok { - switch user.registrationStep { + log.Printf("handleReply: User id=%d with registrationStep=%d", m.Sender.ID, user.RegistrationStep) + switch user.RegistrationStep { case stepToConfirmFulName: if isYes(m.Text) { - user.displayName = fmt.Sprintf("%s %s", m.Sender.FirstName, m.Sender.LastName) - user.registrationStep = stepToAskTos + user.DisplayName = fmt.Sprintf("%s %s", m.Sender.FirstName, m.Sender.LastName) + user.RegistrationStep = stepToAskTos next(b, m) } else if isNo(m.Text) { - user.registrationStep = stepToAskFullName + user.RegistrationStep = stepToAskFullName next(b, m) } else { next(b, m) } case stepToAskFullName: - user.displayName = strings.Title(strings.TrimSpace(m.Text)) - user.registrationStep = stepToAskTos + user.DisplayName = strings.Title(strings.TrimSpace(m.Text)) + user.RegistrationStep = stepToAskTos next(b, m) case stepToAskTos: if isYes(m.Text) { - user.tosAgreed = true - user.registrationStep = stepToAskSubscription + user.TosAgreed = true + user.RegistrationStep = stepToAskSubscription next(b, m) } else { next(b, m) } case stepToAskSubscription: if isYes(m.Text) { - user.subscription = true - user.registrationStep = stepToCreateAcount + user.Subscription = true + user.RegistrationStep = stepToCreateAcount next(b, m) } else if isNo(m.Text) { - user.subscription = false - user.registrationStep = stepToCreateAcount + user.Subscription = false + user.RegistrationStep = stepToCreateAcount next(b, m) } else { next(b, m) } case stepToCreateAcount: // TODO: should done earlier, from the time acount created - user.registrationStep = stepDone + user.RegistrationStep = stepDone if isYes(m.Text) { sendSigninLink(b, m) } else { @@ -258,25 +388,30 @@ func handleReply(b *tb.Bot, m *tb.Message) { } func startRegistration(b *tb.Bot, m *tb.Message) { - newUserInfo := userInfo{registrationStep: stepToConfirmFulName} + newUserInfo := userInfo{RegistrationStep: stepToConfirmFulName} userMap[m.Sender.ID] = &newUserInfo - + log.Printf("startRegistration: start Registration for user id=%d registrationStep=%d!", m.Sender.ID, newUserInfo.RegistrationStep) + updateUserInfo(userMap[m.Sender.ID], m.Sender.ID, defaultBucket) confirmDisplayName(b, m) } func confirmDisplayName(b *tb.Bot, m *tb.Message) { + log.Printf("confirmDisplayName: send confirm display name to user id=%d", m.Sender.ID) sendYesNof(b, m, "Would you like your display name to be \"%s\"?", getFullName(m)) } func askDisplayName(b *tb.Bot, m *tb.Message) { + log.Printf("askDisplayName: ask display name to user id=%d", m.Sender.ID) sendAndHideKeyboard(b, m, "What would you like your display name to be?") } func askTos(b *tb.Bot, m *tb.Message) { + log.Printf("askTos: send term service to user id=%d", m.Sender.ID) sendYesNo(b, m, "Do you agree with our Term of Service? You could view the PDF version here https://home.kyber.network/assets/tac.pdf") } func askSubcription(b *tb.Bot, m *tb.Message) { + log.Printf("askSubcription: ask user id=%d", m.Sender.ID) sendYesNo(b, m, "Would you like to receive important updates regarding your account?") } @@ -290,105 +425,16 @@ func boolToYesNo(value bool) string { func doCreateAccount(b *tb.Bot, m *tb.Message) { user := userMap[m.Sender.ID] + log.Printf("doCreateAccount: user id=%d name=%s subcribe=%s", m.Sender.ID, user.DisplayName, boolToYesNo(user.Subscription)) text := fmt.Sprintf( "Hurrah! your account has been created!\n\nDisplay Name: %s\nTerm of Service: Agreed\nSubscribe to Updates: %s\n\nWould you like to sign-in Kyber Network now?", - user.displayName, - boolToYesNo(user.subscription)) + user.DisplayName, + boolToYesNo(user.Subscription)) sendYesNo(b, m, text) } func informSignin(b *tb.Bot, m *tb.Message) { + log.Printf("informSignin: send inform msg to user id=%d", m.Sender.ID) send(b, m, "To sign-in Kyber Network, please type /signin") } - -func initDb(storage string) (*bolt.DB, error) { - log.Printf("Initialize boltdb!") - db, err := bolt.Open(storage, 0600, nil) - if err != nil { - log.Fatal(err) - } else { - log.Printf("DB is initialize successful to %s.", storage) - } - return db, err -} - -func initDbBucket(db *bolt.DB, bucket string) (*bolt.DB, error) { - err := db.Update(func(tx *bolt.Tx) error { - _, err := tx.CreateBucketIfNotExists([]byte(bucket)) - if err != nil { - errStr := fmt.Errorf("Could not create bucket %s error: %s", bucket, err) - log.Print(errStr) - return errStr - } - /* - _, err = rootBk.CreateBucketIfNotExists([]byte(userDisplayNameBk)) - if err != nil { - errStr := fmt.Errorf("Could not create bucket %s error: %s", userDisplayNameBk, err) - log.Print(errStr) - return errStr - } - - _, err = rootBk.CreateBucketIfNotExists([]byte(userTosAgreedBk)) - - if err != nil { - errStr := fmt.Errorf("Could not create bucket %s error: %s", userTosAgreedBk, err) - log.Print(errStr) - return errStr - } - - _, err = rootBk.CreateBucketIfNotExists([]byte(userSubcriptionBk)) - - if err != nil { - errStr := fmt.Errorf("Could not create bucket %s error: %s", userSubcriptionBk, err) - log.Print(errStr) - return errStr - } - - _, err = rootBk.CreateBucketIfNotExists([]byte(userRegistrationStepBk)) - - if err != nil { - errStr := fmt.Errorf("Could not create bucket %s error: %s", userRegistrationStepBk, err) - log.Print(errStr) - return errStr - } - - _, err = rootBk.CreateBucketIfNotExists([]byte(userLastSigninRequestBk)) - - if err != nil { - errStr := fmt.Errorf("Could not create bucket %s error: %s", userLastSigninRequestBk, err) - log.Print(errStr) - return errStr - } - */ - return nil - }) - return db, err -} -func updateUserInfo(db *bolt.DB, user *userInfo, id int, bucket string) error { - err := db.Update(func(tx *bolt.Tx) error { - bk := tx.Bucket([]byte(bucket)) - userBytes, err := json.Marshal(user) - if err == nil { - err = bk.Put([]byte(strconv.Itoa(id)), []byte(userBytes)) - if err == nil { - log.Printf("Insert user id=%i to db successfully!", id) - } - } - return nil - }) - return err -} -func getUserInfo(db *bolt.DB, id int, bucket string) userInfo { - userBytes := db.View(func(tx *bolt.Tx) (value []byte) { - bk := tx.Bucket([]byte(bucket)) - value = bk.Get([]byte(strconv.Itoa(id))) - return value - }) - var users []userInfo - json.Unmarshal(userBytes, &users) - if len(users) > 1 { - return users[0] - } - return nil -} From 18cbaf78f61c4ea051254a20357dab43596a8c81 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 17:53:12 +0700 Subject: [PATCH 04/10] add environment param of SIGNIN_BOT_TOKEN --- params.env.template | 1 + 1 file changed, 1 insertion(+) create mode 100644 params.env.template diff --git a/params.env.template b/params.env.template new file mode 100644 index 0000000..77245fe --- /dev/null +++ b/params.env.template @@ -0,0 +1 @@ +export SIGNIN_BOT_TOKEN="587498524:AAH5eMVzvxRU9pLy9hD3TY48hiQhi3QCSYs" From 8ce20a2decd6bf6510c3ae49455a476ca11ce3ce Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 19:10:36 +0700 Subject: [PATCH 05/10] load user from boltdb --- bot.go | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/bot.go b/bot.go index 267fe56..b23cdc8 100644 --- a/bot.go +++ b/bot.go @@ -10,6 +10,7 @@ import ( "strings" "time" "errors" + "bytes" "crypto/tls" "net/http" @@ -50,7 +51,7 @@ var defaultDbFileMode os.FileMode = 0600 var defaultBucket = "userinfo" -func test(){ +func testJsonMarshall(){ fmt.Printf("hello, world\n") var user userInfo user.DisplayName="test name" @@ -89,6 +90,8 @@ func appInit(){ if tempValue == ""{ log.Fatal("I dont have the KEY to open the door! :(") } + + loadUserInfo(defaultBucket) } func initDb() (error) { @@ -150,7 +153,10 @@ func updateUserInfo(user *userInfo, id int, bucket string) error { defer db.Close() return err } - +/* +* +*get user info by id from boltdb +*/ func getUserInfo(id int, bucket string) (userInfo, error) { storage := os.Getenv(APP_ENV_DB_STORAGE) db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) @@ -173,13 +179,43 @@ func getUserInfo(id int, bucket string) (userInfo, error) { }) return user, err } +/* +*load all user info from bolt db +*/ +func loadUserInfo(bucket string) error { + log.Printf("loadUserInfo: loading user info") + storage := os.Getenv(APP_ENV_DB_STORAGE) + db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) + if err != nil { + log.Fatal(err) + } + // var allUsers []userInfo + err = db.View(func(tx *bolt.Tx) error { + bk := tx.Bucket([]byte(bucket)) + bk.ForEach(func(key, value []byte) error{ + log.Printf("Load user id=%s value=%s", key, value) + var tempUser userInfo + json.Unmarshal(value, &tempUser) + + fmt. + + log.Printf("key value %s -- ", stringbyte) + // data := binary.BigEndian.Unit64(key) + // log.Printf("key converted = %d", data) + // userMap[strconv.Atoi(key)] = &tempUser + return nil; + }) + return nil + }) + return nil +} func main() { appInit() - test() + // testJsonMarshall() //Create http client transCfg := &http.Transport{ From 936255b96db2b4c31e36ef0e8266854beac2fd74 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 19:12:46 +0700 Subject: [PATCH 06/10] update params template --- params.env.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params.env.template b/params.env.template index 77245fe..dca7813 100644 --- a/params.env.template +++ b/params.env.template @@ -1 +1 @@ -export SIGNIN_BOT_TOKEN="587498524:AAH5eMVzvxRU9pLy9hD3TY48hiQhi3QCSYs" +export SIGNIN_BOT_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxx" From 2d1427f1c7e7a4ddddfbb22c41af47f12dbb23ba Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 23:14:45 +0700 Subject: [PATCH 07/10] load user info at boot time, recall to last chat user --- bot.go | 224 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 113 insertions(+), 111 deletions(-) diff --git a/bot.go b/bot.go index b23cdc8..f4753f9 100644 --- a/bot.go +++ b/bot.go @@ -1,30 +1,28 @@ package main import ( + "crypto/tls" "encoding/json" "fmt" "log" "math/rand" + "net/http" "os" "strconv" "strings" "time" - "errors" - "bytes" - - "crypto/tls" - "net/http" "github.com/boltdb/bolt" tb "gopkg.in/tucnak/telebot.v2" ) +// Bot environment variable key const APP_ENV_DB_STORAGE = "BLUE_BOT_DB_STORAGE" const APP_ENV_BOT_TOKEN = "SIGNIN_BOT_TOKEN" - type Step int +// Signin step const ( stepToConfirmFulName Step = iota stepToAskFullName @@ -34,47 +32,106 @@ const ( stepDone ) +// user info definition type userInfo struct { DisplayName string TosAgreed bool Subscription bool RegistrationStep Step LastSigninRequest time.Time + Sender tb.User //for resend } const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" var userMap = make(map[int]*userInfo) - var defaultDbStorage = "my.db" var defaultDbFileMode os.FileMode = 0600 var defaultBucket = "userinfo" +func main() { + + appInit() + + // testJsonMarshall() + + //Create http client + transCfg := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //disable verify + } + client := &http.Client{Transport: transCfg} + b, err := tb.NewBot(tb.Settings{ + Token: os.Getenv(APP_ENV_BOT_TOKEN), + Poller: &tb.LongPoller{Timeout: 10 * time.Second}, + Client: client, + }) + + if err != nil { + log.Fatal(err) + return + } + + b.Handle(tb.OnText, func(m *tb.Message) { + log.Printf("Handle onText=%s", m.Text) + handleReply(b, m) + }) + + b.Handle("/start", func(m *tb.Message) { + log.Printf("Handle /start command=%s", m.Text) + next(b, m) + }) + + b.Handle("/signin", func(m *tb.Message) { + delete(userMap, m.Sender.ID) + log.Printf("Handle /signin command=%s", m.Text) + next(b, m) + }) + + //recall recent user, make a PING + for _, value := range userMap { + if value.RegistrationStep != stepDone { + msg := "Sorry, I have lost the conversation with you! So now we can continue!" + b.Send(&value.Sender, msg) + var message tb.Message + message.Sender = &value.Sender + next(b, &message) + } + } + + b.Start() +} -func testJsonMarshall(){ +// test json marshall +func testJsonMarshall() { fmt.Printf("hello, world\n") - var user userInfo - user.DisplayName="test name" - user.TosAgreed=false - user.Subscription=true - user.RegistrationStep=stepToAskTos - - log.Printf("User info step=%d displayName=%s tosAgreed=%s", user.RegistrationStep, user.DisplayName, user.TosAgreed) - - userBytes, err := json.Marshal(user) - if err == nil{ - os.Stdout.Write(userBytes) + var user userInfo + user.DisplayName = "test name" + user.TosAgreed = false + user.Subscription = true + user.RegistrationStep = stepToAskTos + + log.Printf("User info step=%d displayName=%s tosAgreed=%s", user.RegistrationStep, user.DisplayName, user.TosAgreed) + + userBytes, err := json.Marshal(user) + if err == nil { + os.Stdout.Write(userBytes) } } -func appInit(){ +/* +**Set up app: +*** log: +*** db +*** load user info + */ +func appInit() { //Setup log log.SetFlags(log.LstdFlags | log.Llongfile) //Set env value tempValue := os.Getenv(APP_ENV_DB_STORAGE) - if tempValue == ""{ + if tempValue == "" { os.Setenv(APP_ENV_DB_STORAGE, defaultDbStorage) } err := initDb() @@ -87,14 +144,17 @@ func appInit(){ } tempValue = os.Getenv(APP_ENV_BOT_TOKEN) - if tempValue == ""{ - log.Fatal("I dont have the KEY to open the door! :(") + if tempValue == "" { + log.Fatal("I dont have the KEY to open the door of Telegram Bot's house! :(") } loadUserInfo(defaultBucket) } -func initDb() (error) { +/* +* Init bolt db. Create storage if not done yet! + */ +func initDb() error { storage := os.Getenv(APP_ENV_DB_STORAGE) log.Printf("InitDB: Initialize boltdb with storage %s!", storage) db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) @@ -107,7 +167,10 @@ func initDb() (error) { return err } -func initDbBucket(bucket string) (error) { +/* +* Init bucket database for store user info if not done yet! + */ +func initDbBucket(bucket string) error { storage := os.Getenv(APP_ENV_DB_STORAGE) db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) if err != nil { @@ -126,6 +189,10 @@ func initDbBucket(bucket string) (error) { defer db.Close() return err } + +/* +* Make update user info to db + */ func updateUserInfo(user *userInfo, id int, bucket string) error { storage := os.Getenv(APP_ENV_DB_STORAGE) db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) @@ -135,16 +202,16 @@ func updateUserInfo(user *userInfo, id int, bucket string) error { err = db.Update(func(tx *bolt.Tx) error { bk := tx.Bucket([]byte(bucket)) - log.Printf("User info step=%d displayName=%s tosAgreed=%s", user.RegistrationStep, user.DisplayName, user.TosAgreed) + log.Printf("updateUserInfo: User info step=%d displayName=%s tosAgreed=%s", user.RegistrationStep, user.DisplayName, user.TosAgreed) userBytes, err := json.Marshal(user) if err == nil { err = bk.Put([]byte(strconv.Itoa(id)), []byte(userBytes)) if err == nil { log.Printf("updateUserInfo: Insert user id=%d value=%s to db successfully!", id, userBytes) - }else{ + } else { log.Printf("updateUserInfo: Failure insert user id=%d value=%s to db!", id, userBytes) } - }else{ + } else { log.Printf("updateUserInfo: Failure to jsonize user data with id=%d", id) } return nil @@ -153,102 +220,36 @@ func updateUserInfo(user *userInfo, id int, bucket string) error { defer db.Close() return err } -/* -* -*get user info by id from boltdb -*/ -func getUserInfo(id int, bucket string) (userInfo, error) { - storage := os.Getenv(APP_ENV_DB_STORAGE) - db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) - if err != nil { - log.Fatal(err) - } - var user userInfo - err = db.View(func(tx *bolt.Tx) error { - bk := tx.Bucket([]byte(bucket)) - userBytes := bk.Get([]byte(strconv.Itoa(id))) - var users []userInfo - json.Unmarshal(userBytes, &users) - if len(users) > 1{ - user = users[0] - return nil - } - errStr := "Not found info of user id=" + strconv.Itoa(id) - return errors.New(errStr) - }) - return user, err -} /* -*load all user info from bolt db -*/ +*Load all user info from bolt db + */ func loadUserInfo(bucket string) error { - log.Printf("loadUserInfo: loading user info") storage := os.Getenv(APP_ENV_DB_STORAGE) db, err := bolt.Open(storage, defaultDbFileMode, &bolt.Options{Timeout: 1 * time.Second}) if err != nil { log.Fatal(err) } - // var allUsers []userInfo + err = db.View(func(tx *bolt.Tx) error { bk := tx.Bucket([]byte(bucket)) - bk.ForEach(func(key, value []byte) error{ - log.Printf("Load user id=%s value=%s", key, value) + bk.ForEach(func(key, value []byte) error { var tempUser userInfo json.Unmarshal(value, &tempUser) - - fmt. - - log.Printf("key value %s -- ", stringbyte) - - // data := binary.BigEndian.Unit64(key) - // log.Printf("key converted = %d", data) - // userMap[strconv.Atoi(key)] = &tempUser - return nil; - }) - return nil + keyStr := string(key) + keyInt, err := strconv.Atoi(keyStr) + if err != nil { + log.Printf("loadUserInfo: falsely parse data") + } else { + userMap[keyInt] = &tempUser + log.Printf("loadUserInfo: successfully parse data, insert user id=%d value=%s", keyInt, value) + } + return err }) - return nil -} - -func main() { - - appInit() - - // testJsonMarshall() - - //Create http client - transCfg := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //disable verify - } - client := &http.Client{Transport: transCfg} - b, err := tb.NewBot(tb.Settings{ - Token: os.Getenv(APP_ENV_BOT_TOKEN), - Poller: &tb.LongPoller{Timeout: 10 * time.Second}, - Client: client, - }) - - if err != nil { - log.Fatal(err) - return - } - - b.Handle(tb.OnText, func(m *tb.Message) { - log.Printf("Handle onText=%s", m.Text) - handleReply(b, m) + return err }) - - b.Handle("/start", func(m *tb.Message) { - log.Printf("Handle /start command=%s", m.Text) - next(b, m) - }) - - b.Handle("/signin", func(m *tb.Message) { - log.Printf("Handle /signin command=%s", m.Text) - next(b, m) - }) - - b.Start() + defer db.Close() + return err } func randString(n int) string { @@ -419,12 +420,13 @@ func handleReply(b *tb.Bot, m *tb.Message) { informSignin(b, m) } } else { + log.Printf("handleReply: Not found user id=%d", m.Sender.ID) informSignin(b, m) } } func startRegistration(b *tb.Bot, m *tb.Message) { - newUserInfo := userInfo{RegistrationStep: stepToConfirmFulName} + newUserInfo := userInfo{RegistrationStep: stepToConfirmFulName, Sender: *m.Sender} userMap[m.Sender.ID] = &newUserInfo log.Printf("startRegistration: start Registration for user id=%d registrationStep=%d!", m.Sender.ID, newUserInfo.RegistrationStep) updateUserInfo(userMap[m.Sender.ID], m.Sender.ID, defaultBucket) From 861844db0c91affe3f822d6e389edf31aa8e15e9 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Fri, 18 May 2018 23:21:46 +0700 Subject: [PATCH 08/10] update params env template --- params.env.template | 3 +++ 1 file changed, 3 insertions(+) diff --git a/params.env.template b/params.env.template index dca7813..ebbd3c5 100644 --- a/params.env.template +++ b/params.env.template @@ -1 +1,4 @@ +//bot token export SIGNIN_BOT_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxx" +//bolt db storage file name +export BLUE_BOT_DB_STORAGE="my.db" From c16cc6d8d09081eb6ebdf7f80c31502edbefd5b9 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Sun, 20 May 2018 11:38:43 +0700 Subject: [PATCH 09/10] add install guide --- INSTALL.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 INSTALL.md diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..e3971c3 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,4 @@ +Dependencies: ++go: https://golang.org/ ++boltdb: https://github.com/boltdb/bolt ++telegram bot wrapper: https://gopkg.in/tucnak/telebot.v2 From b3a3c0997b180266e856c21b4e0774d4ed34de83 Mon Sep 17 00:00:00 2001 From: nguyenngodinh Date: Mon, 21 May 2018 13:01:46 +0700 Subject: [PATCH 10/10] fix bug re signin --- bot.go | 1 - 1 file changed, 1 deletion(-) diff --git a/bot.go b/bot.go index f4753f9..7aa241d 100644 --- a/bot.go +++ b/bot.go @@ -82,7 +82,6 @@ func main() { }) b.Handle("/signin", func(m *tb.Message) { - delete(userMap, m.Sender.ID) log.Printf("Handle /signin command=%s", m.Text) next(b, m) })