Concurrent menu/bot. Disappearing option in menu. User groups.

Signed-off-by: Ettore Dreucci <ettore.dreucci@gmail.com>
This commit is contained in:
2018-06-27 12:34:57 +02:00
parent 2960ea217a
commit 6f4653ce34
7 changed files with 198 additions and 58 deletions

View File

@@ -12,13 +12,19 @@ func main() {
} }
err = redisInit(cmdFlags.redisAddr, cmdFlags.redisPwd, cmdFlags.redisDB) err = redisInit(cmdFlags.redisAddr, cmdFlags.redisPwd, cmdFlags.redisDB)
defer redisClient.Close()
if err != nil { if err != nil {
log.Fatalf("Error in initializing redis instance: %v", err) log.Fatalf("Error in initializing redis instance: %v", err)
} }
defer redisClient.Close()
err = botInit()
if err != nil {
log.Fatalf("Error initializing bot: %v", err)
}
defer bot.Stop()
if cmdFlags.interactive { if cmdFlags.interactive {
mainMenu() mainMenuLoop()
} else if cmdFlags.token != "" { } else if cmdFlags.token != "" {
err = setBotToken(cmdFlags.token) err = setBotToken(cmdFlags.token)
if err == ErrAddToken { if err == ErrAddToken {

View File

@@ -45,7 +45,7 @@ func removeBotAdmins() error {
if redisClient == nil { if redisClient == nil {
return ErrNilPointer return ErrNilPointer
} }
botAdmins, err := redisClient.SMembers(adminSet).Result() botAdmins, err := redisClient.SMembers(adminUsers).Result()
if err != nil { if err != nil {
log.Printf("Couldn't retrieve admins: %v", err) log.Printf("Couldn't retrieve admins: %v", err)
return ErrRedisRetrieveSet return ErrRedisRetrieveSet
@@ -120,13 +120,13 @@ func addBotAdmin(newAdminID string) error {
return ErrAddAdmin return ErrAddAdmin
} }
if !isUser { if !isUser {
err = addUser(&tb.User{int(chat.ID), chat.FirstName, chat.LastName, chat.Username}) err = addUser(&tb.User{ID: int(chat.ID), FirstName: chat.FirstName, LastName: chat.LastName, Username: chat.Username})
if err != nil { if err != nil {
log.Printf("Error adding user: %v", err) log.Printf("Error adding user: %v", err)
return ErrAddUser return ErrAddUser
} }
} }
err = redisClient.SAdd(adminSet, adminID).Err() err = redisClient.SAdd(adminUsers, adminID).Err()
if err != nil { if err != nil {
log.Printf("Error in adding new admin ID: %v", err) log.Printf("Error in adding new admin ID: %v", err)
return ErrRedisAddSet return ErrRedisAddSet
@@ -142,7 +142,7 @@ func addBotAdmin(newAdminID string) error {
log.Printf("Error getting user info: %v", err) log.Printf("Error getting user info: %v", err)
return ErrGetUser return ErrGetUser
} }
err = sendMessage(user, "Sei stato aggiunto come amministratore del BarandaBot") err = sendMessage(user, newAdminMsg)
if err != nil { if err != nil {
log.Printf("Error sending message to new admin: %v", err) log.Printf("Error sending message to new admin: %v", err)
return ErrSendMsg return ErrSendMsg
@@ -155,10 +155,21 @@ func removeBotAdmin(adminID int) error {
if redisClient == nil { if redisClient == nil {
return ErrNilPointer return ErrNilPointer
} }
err := redisClient.SRem(adminSet, strconv.Itoa(adminID)).Err() err := redisClient.SRem(adminUsers, strconv.Itoa(adminID)).Err()
if err != nil { if err != nil {
log.Printf("Error removing admin from set: %v", err) log.Printf("Error removing admin from set: %v", err)
return ErrRedisRemSet return ErrRedisRemSet
} }
user, err := getUserInfo(adminID)
if err != nil {
log.Printf("Error getting user info: %v", err)
return ErrGetUser
}
err = sendMessage(user, delAdminMsg)
if err != nil {
log.Printf("Error sending message to removed admin: %v", err)
return ErrSendMsg
}
return nil return nil
} }

View File

@@ -2,14 +2,11 @@ package main
import ( import (
"bufio" "bufio"
"encoding/json"
"fmt" "fmt"
"log" "log"
"os" "os"
"regexp" "regexp"
"strings" "strings"
tb "gopkg.in/tucnak/telebot.v2"
) )
func setBotToken(newToken string) error { func setBotToken(newToken string) error {
@@ -49,12 +46,12 @@ func getBotToken() (string, error) {
if redisClient == nil { if redisClient == nil {
return "", ErrNilPointer return "", ErrNilPointer
} }
token, err := redisClient.Get(botToken).Result() tokenExists, err := redisClient.Exists(botToken).Result()
if err != nil { if err != nil {
log.Printf("Couldn't retrieve bot token: %v", err) log.Printf("Error checking if token exists in db: %v", err)
return "", ErrRedisRetrieveSet return "", ErrRedisCheckSet
} }
if token == "" { if tokenExists == 0 {
fmt.Println("No bot token found.") fmt.Println("No bot token found.")
err := setBotToken("") err := setBotToken("")
if err != nil { if err != nil {
@@ -62,20 +59,19 @@ func getBotToken() (string, error) {
return "", ErrAddToken return "", ErrAddToken
} }
} }
token, err := redisClient.Get(botToken).Result()
if err != nil {
log.Printf("Couldn't retrieve bot token: %v", err)
return "", ErrRedisRetrieveSet
}
return token, nil return token, nil
} }
func addBotInfo(botToken string, bot *tb.Bot) error { func addBotInfo(botToken string, botUser string) error {
if redisClient == nil { if redisClient == nil {
return ErrNilPointer return ErrNilPointer
} }
jsonBot, err := json.Marshal(&bot) err := redisClient.HSet(botInfo, botToken, botUser).Err()
if err != nil {
log.Printf("Error marshalling bot info: %v", err)
return ErrJSONMarshall
}
err = redisClient.HSet(botHash, botToken, string(jsonBot)).Err()
if err != nil { if err != nil {
log.Printf("Error in adding bot info: %v", err) log.Printf("Error in adding bot info: %v", err)
return ErrRedisAddHash return ErrRedisAddHash
@@ -88,7 +84,7 @@ func removeBotInfo(botToken string) error {
if redisClient == nil { if redisClient == nil {
return ErrNilPointer return ErrNilPointer
} }
err := redisClient.HDel(botHash, botToken).Err() err := redisClient.HDel(botInfo, botToken).Err()
if err != nil { if err != nil {
log.Printf("Error in removing bot info: %v", err) log.Printf("Error in removing bot info: %v", err)
return ErrRedisDelHash return ErrRedisDelHash

View File

@@ -4,15 +4,28 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"strconv" "strconv"
"strings"
tb "gopkg.in/tucnak/telebot.v2" tb "gopkg.in/tucnak/telebot.v2"
) )
type userGroup int
const (
ugSoprano userGroup = iota
ugContralto
ugTenore
ugBasso
ugCommissario
ugReferente
ugPreparatore
)
func addUser(user *tb.User) error { func addUser(user *tb.User) error {
if redisClient == nil { if redisClient == nil {
return ErrNilPointer return ErrNilPointer
} }
err := redisClient.SAdd(userSet, user.ID).Err() err := redisClient.SAdd(usersID, user.ID).Err()
if err != nil { if err != nil {
log.Printf("Error in adding user ID: %v", err) log.Printf("Error in adding user ID: %v", err)
return ErrRedisAddSet return ErrRedisAddSet
@@ -22,7 +35,7 @@ func addUser(user *tb.User) error {
log.Printf("Error in marshalling user to json: %v", err) log.Printf("Error in marshalling user to json: %v", err)
return ErrJSONMarshall return ErrJSONMarshall
} }
err = redisClient.HSet(userHash, strconv.Itoa(user.ID), jsonUser).Err() err = redisClient.HSet(usersInfo, strconv.Itoa(user.ID), jsonUser).Err()
if err != nil { if err != nil {
log.Printf("Error adding user info in hash: %v", err) log.Printf("Error adding user info in hash: %v", err)
return ErrRedisAddHash return ErrRedisAddHash
@@ -35,7 +48,7 @@ func isUser(userID int) (bool, error) {
if redisClient == nil { if redisClient == nil {
return false, ErrNilPointer return false, ErrNilPointer
} }
user, err := redisClient.SIsMember(userSet, strconv.Itoa(userID)).Result() user, err := redisClient.SIsMember(usersID, strconv.Itoa(userID)).Result()
if err != nil { if err != nil {
log.Printf("Error checking if ID is bot user: %v", err) log.Printf("Error checking if ID is bot user: %v", err)
return false, ErrRedisCheckSet return false, ErrRedisCheckSet
@@ -47,7 +60,7 @@ func getUserInfo(userID int) (*tb.User, error) {
if redisClient == nil { if redisClient == nil {
return nil, ErrNilPointer return nil, ErrNilPointer
} }
user, err := redisClient.HGet(userHash, strconv.Itoa(userID)).Result() user, err := redisClient.HGet(usersInfo, strconv.Itoa(userID)).Result()
if err != nil { if err != nil {
log.Printf("Error retriving user info from hash: %v", err) log.Printf("Error retriving user info from hash: %v", err)
return nil, ErrRedisRetrieveHash return nil, ErrRedisRetrieveHash
@@ -65,7 +78,7 @@ func isAuthrizedUser(userID int) (bool, error) {
if redisClient == nil { if redisClient == nil {
return false, ErrNilPointer return false, ErrNilPointer
} }
auth, err := redisClient.SIsMember(authUserSet, strconv.Itoa(userID)).Result() auth, err := redisClient.SIsMember(authUsers, strconv.Itoa(userID)).Result()
if err != nil { if err != nil {
log.Printf("Error checking if user is authorized: %v", err) log.Printf("Error checking if user is authorized: %v", err)
return false, ErrRedisCheckSet return false, ErrRedisCheckSet
@@ -78,13 +91,13 @@ func authorizeUser(userID int, authorized bool) error {
return ErrNilPointer return ErrNilPointer
} }
if authorized { if authorized {
err := redisClient.SAdd(authUserSet, strconv.Itoa(userID)).Err() err := redisClient.SAdd(authUsers, strconv.Itoa(userID)).Err()
if err != nil { if err != nil {
log.Printf("Error adding token to set: %v", err) log.Printf("Error adding token to set: %v", err)
return ErrRedisAddSet return ErrRedisAddSet
} }
} else { } else {
err := redisClient.SRem(authUserSet, strconv.Itoa(userID)).Err() err := redisClient.SRem(authUsers, strconv.Itoa(userID)).Err()
if err != nil { if err != nil {
log.Printf("Error removing token from set: %v", err) log.Printf("Error removing token from set: %v", err)
return ErrRedisRemSet return ErrRedisRemSet
@@ -92,3 +105,43 @@ func authorizeUser(userID int, authorized bool) error {
} }
return nil return nil
} }
func setUserGroups(userID int, groups ...userGroup) error {
if redisClient == nil {
return ErrNilPointer
}
var csvGroups string
for _, group := range groups {
csvGroups += strconv.Itoa(int(group)) + ","
}
err := redisClient.HSet(usersGroups, strconv.Itoa(userID), csvGroups).Err()
if err != nil {
log.Printf("Error adding user groups to hash: %v", err)
return ErrRedisAddHash
}
return nil
}
func getUserGroups(userID int) ([]userGroup, error) {
if redisClient == nil {
return nil, ErrNilPointer
}
csvGroups, err := redisClient.HGet(usersGroups, strconv.Itoa(userID)).Result()
if err != nil {
log.Printf("Error retrieving user groups: %v", err)
return nil, ErrRedisRetrieveHash
}
var retGroups []userGroup
groups := strings.Split(csvGroups, ",")
for _, group := range groups {
intGroup, err := strconv.Atoi(group)
if err != nil {
log.Printf("Error converting user group: %v", err)
return nil, ErrAtoiConv
}
retGroups = append(retGroups, userGroup(intGroup))
}
return retGroups, nil
}

View File

@@ -9,11 +9,12 @@ import (
const ( const (
botToken = "botToken" botToken = "botToken"
botHash = "botInfo" botInfo = "botInfo"
userSet = "userID" usersID = "usersID"
userHash = "userInfo" usersInfo = "usersInfo"
authUserSet = "authUser" usersGroups = "usersGroups"
adminSet = "adminID" authUsers = "authUsers"
adminUsers = "adminUsers"
) )
var redisClient *redis.Client var redisClient *redis.Client

View File

@@ -5,6 +5,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"os"
"strings" "strings"
"github.com/dixonwille/wmenu" "github.com/dixonwille/wmenu"
@@ -23,7 +24,7 @@ type flags struct {
var cmdFlags flags var cmdFlags flags
var ( var (
welcomeMessage = "Welcome in barandaBot! Here you can control the bot(s) options and configurations." welcomeMessage = "Welcome! Here you can control the bot options and configurations."
//ErrStdRead is thrown when it's not possible to read from the standard input //ErrStdRead is thrown when it's not possible to read from the standard input
ErrStdRead = errors.New("stdin: couldn't read string from stdin") ErrStdRead = errors.New("stdin: couldn't read string from stdin")
//ErrMainMenu is thrown when a menu couldn't be started //ErrMainMenu is thrown when a menu couldn't be started
@@ -80,31 +81,62 @@ func getFlags() error {
return nil return nil
} }
func mainMenu() error { func exit() error {
fmt.Println(welcomeMessage) if isStartedBot {
log.Printf("Stopping %s", bot.Me.Username)
bot.Stop()
log.Println("Bot stopped")
}
log.Println("Closing redis instance")
redisClient.Close()
log.Println("Redis instance closed")
log.Println("Exiting")
os.Exit(0)
return nil
}
func mainMenu() *wmenu.Menu {
menu := wmenu.NewMenu("What do you want to do?") menu := wmenu.NewMenu("What do you want to do?")
menu.LoopOnInvalid() menu.LoopOnInvalid()
menu.Option("Start Bot", nil, true, func(opt wmenu.Opt) error { if !isStartedBot {
return botStart() menu.Option("Start bot", nil, true, func(opt wmenu.Opt) error {
}) return botStart()
menu.Option("Set bot token", nil, false, func(opt wmenu.Opt) error { })
return setBotToken("") menu.Option("Set bot token", nil, false, func(opt wmenu.Opt) error {
}) return setBotToken("")
})
}
if isStartedBot {
menu.Option("Stop bot", nil, true, func(opt wmenu.Opt) error {
return botStop()
})
}
menu.Option("Add bot admin(s)", nil, false, func(opt wmenu.Opt) error { menu.Option("Add bot admin(s)", nil, false, func(opt wmenu.Opt) error {
return addBotAdmins(nil) return addBotAdmins(nil)
}) })
menu.Option("Remove bot admin(s)", nil, false, func(opt wmenu.Opt) error { menu.Option("Remove bot admin(s)", nil, false, func(opt wmenu.Opt) error {
return removeBotAdmins() return removeBotAdmins()
}) })
menu.Option("Exit", nil, false, func(opt wmenu.Opt) error {
return exit()
})
var returnErr error return menu
}
func mainMenuLoop() error {
menu := mainMenu()
botStatus := isStartedBot
fmt.Println(welcomeMessage)
for { for {
err := menu.Run() err := menu.Run()
if err != nil { if err != nil {
log.Printf("Error in main menu: %v", err) log.Printf("Error in main menu: %v", err)
returnErr = ErrMainMenu }
if botStatus != isStartedBot {
botStatus = isStartedBot
menu = mainMenu()
} }
} }
return returnErr
} }

View File

@@ -9,7 +9,13 @@ import (
tb "gopkg.in/tucnak/telebot.v2" tb "gopkg.in/tucnak/telebot.v2"
) )
const (
newAdminMsg string = "Sei stato aggiunto come amministratore. Adesso hai a disposizione una serie aggiuntiva di comandi e controlli per il bot."
delAdminMsg string = "Sei stato rimosso da amministratore."
)
var bot *tb.Bot var bot *tb.Bot
var isStartedBot bool
var ( var (
//ErrNilPointer is thrown when a pointer is nil //ErrNilPointer is thrown when a pointer is nil
@@ -18,15 +24,21 @@ var (
ErrIDFromMsg = errors.New("telegram: couldn't retrieve user ID from message") ErrIDFromMsg = errors.New("telegram: couldn't retrieve user ID from message")
//ErrSendMsg is thrown when the message couldn't be send //ErrSendMsg is thrown when the message couldn't be send
ErrSendMsg = errors.New("telegram: cannot send message") ErrSendMsg = errors.New("telegram: cannot send message")
//ErrChatRetrive is thrown when the chat cannot be retrieved //ErrChatRetrieve is thrown when the chat cannot be retrieved
ErrChatRetrieve = errors.New("telegram: cannot retrieve chat") ErrChatRetrieve = errors.New("telegram: cannot retrieve chat")
//ErrTokenMissing is thrown when neither a token is in the db nor one is passed with -t on program start
ErrTokenMissing = errors.New("telegram: cannot start bot without a token")
//ErrBotInit is thrown when a bot couldn't be initialized
ErrBotInit = errors.New("telegram: error in bot initialization")
//ErrBotConn is thrown when there is a connection problem
ErrBotConn = errors.New("telegram: cannot connect to bot")
) )
func botInit() error { func botInit() error {
token, err := getBotToken() token, err := getBotToken()
if err != nil { if err != nil {
log.Printf("Error in retriving bot token: %v. Cannot start telebot without token.", err) log.Printf("Error in retriving bot token: %v. Cannot start telebot without token.", err)
return err return ErrTokenMissing
} }
poller := &tb.LongPoller{Timeout: 15 * time.Second} poller := &tb.LongPoller{Timeout: 15 * time.Second}
@@ -61,15 +73,22 @@ func botInit() error {
Token: token, Token: token,
Poller: middlePoller, Poller: middlePoller,
}) })
if err != nil { if err != nil {
log.Printf("Error in enstablishing connection for bot %s: %v", bot.Me.Username, err) log.Printf("Error in enstablishing connection for bot %s: %v", bot.Me.Username, err)
} else { return ErrBotConn
err = addBotInfo(token, bot)
if err != nil {
log.Printf("Error: bot %s info couldn't be added: %v", bot.Me.Username, err)
}
} }
err = setBotHandlers()
if err != nil {
log.Printf("Error setting bot handlers: %v", err)
return ErrBotInit
}
err = addBotInfo(token, bot.Me.Username)
if err != nil {
log.Printf("Error: bot %s info couldn't be added: %v", bot.Me.Username, err)
}
return nil return nil
} }
@@ -82,11 +101,11 @@ func sendMessage(user *tb.User, msg string) error {
return nil return nil
} }
func botStart() error { func setBotHandlers() error {
if bot == nil { if bot == nil {
return ErrNilPointer return ErrNilPointer
} }
log.Printf("Started %s", bot.Me.Username)
bot.Handle("/hello", func(m *tb.Message) { bot.Handle("/hello", func(m *tb.Message) {
bot.Send(m.Sender, "hello world") bot.Send(m.Sender, "hello world")
}) })
@@ -94,7 +113,29 @@ func botStart() error {
bot.Send(m.Sender, strconv.Itoa(m.Sender.ID)) bot.Send(m.Sender, strconv.Itoa(m.Sender.ID))
}) })
bot.Start() return nil
}
func botStart() error {
if bot == nil {
return ErrNilPointer
}
go bot.Start()
isStartedBot = true
log.Printf("Started %s", bot.Me.Username)
return nil
}
func botStop() error {
if bot == nil {
return ErrNilPointer
}
log.Printf("Stopping %s", bot.Me.Username)
bot.Stop()
isStartedBot = false
log.Println("Bot stopped")
return nil return nil
} }