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)
defer redisClient.Close()
if err != nil {
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 {
mainMenu()
mainMenuLoop()
} else if cmdFlags.token != "" {
err = setBotToken(cmdFlags.token)
if err == ErrAddToken {

View File

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

View File

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

View File

@@ -4,15 +4,28 @@ import (
"encoding/json"
"log"
"strconv"
"strings"
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 {
if redisClient == nil {
return ErrNilPointer
}
err := redisClient.SAdd(userSet, user.ID).Err()
err := redisClient.SAdd(usersID, user.ID).Err()
if err != nil {
log.Printf("Error in adding user ID: %v", err)
return ErrRedisAddSet
@@ -22,7 +35,7 @@ func addUser(user *tb.User) error {
log.Printf("Error in marshalling user to json: %v", err)
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 {
log.Printf("Error adding user info in hash: %v", err)
return ErrRedisAddHash
@@ -35,7 +48,7 @@ func isUser(userID int) (bool, error) {
if redisClient == nil {
return false, ErrNilPointer
}
user, err := redisClient.SIsMember(userSet, strconv.Itoa(userID)).Result()
user, err := redisClient.SIsMember(usersID, strconv.Itoa(userID)).Result()
if err != nil {
log.Printf("Error checking if ID is bot user: %v", err)
return false, ErrRedisCheckSet
@@ -47,7 +60,7 @@ func getUserInfo(userID int) (*tb.User, error) {
if redisClient == nil {
return nil, ErrNilPointer
}
user, err := redisClient.HGet(userHash, strconv.Itoa(userID)).Result()
user, err := redisClient.HGet(usersInfo, strconv.Itoa(userID)).Result()
if err != nil {
log.Printf("Error retriving user info from hash: %v", err)
return nil, ErrRedisRetrieveHash
@@ -65,7 +78,7 @@ func isAuthrizedUser(userID int) (bool, error) {
if redisClient == nil {
return false, ErrNilPointer
}
auth, err := redisClient.SIsMember(authUserSet, strconv.Itoa(userID)).Result()
auth, err := redisClient.SIsMember(authUsers, strconv.Itoa(userID)).Result()
if err != nil {
log.Printf("Error checking if user is authorized: %v", err)
return false, ErrRedisCheckSet
@@ -78,13 +91,13 @@ func authorizeUser(userID int, authorized bool) error {
return ErrNilPointer
}
if authorized {
err := redisClient.SAdd(authUserSet, strconv.Itoa(userID)).Err()
err := redisClient.SAdd(authUsers, strconv.Itoa(userID)).Err()
if err != nil {
log.Printf("Error adding token to set: %v", err)
return ErrRedisAddSet
}
} else {
err := redisClient.SRem(authUserSet, strconv.Itoa(userID)).Err()
err := redisClient.SRem(authUsers, strconv.Itoa(userID)).Err()
if err != nil {
log.Printf("Error removing token from set: %v", err)
return ErrRedisRemSet
@@ -92,3 +105,43 @@ func authorizeUser(userID int, authorized bool) error {
}
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 (
botToken = "botToken"
botHash = "botInfo"
userSet = "userID"
userHash = "userInfo"
authUserSet = "authUser"
adminSet = "adminID"
botInfo = "botInfo"
usersID = "usersID"
usersInfo = "usersInfo"
usersGroups = "usersGroups"
authUsers = "authUsers"
adminUsers = "adminUsers"
)
var redisClient *redis.Client

View File

@@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"log"
"os"
"strings"
"github.com/dixonwille/wmenu"
@@ -23,7 +24,7 @@ type flags struct {
var cmdFlags flags
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 = errors.New("stdin: couldn't read string from stdin")
//ErrMainMenu is thrown when a menu couldn't be started
@@ -80,31 +81,62 @@ func getFlags() error {
return nil
}
func mainMenu() error {
fmt.Println(welcomeMessage)
func exit() error {
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.LoopOnInvalid()
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("")
})
if !isStartedBot {
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("")
})
}
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 {
return addBotAdmins(nil)
})
menu.Option("Remove bot admin(s)", nil, false, func(opt wmenu.Opt) error {
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 {
err := menu.Run()
if err != nil {
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"
)
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 isStartedBot bool
var (
//ErrNilPointer is thrown when a pointer is nil
@@ -18,15 +24,21 @@ var (
ErrIDFromMsg = errors.New("telegram: couldn't retrieve user ID from message")
//ErrSendMsg is thrown when the message couldn't be send
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")
//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 {
token, err := getBotToken()
if err != nil {
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}
@@ -61,15 +73,22 @@ func botInit() error {
Token: token,
Poller: middlePoller,
})
if err != nil {
log.Printf("Error in enstablishing connection for bot %s: %v", bot.Me.Username, err)
} else {
err = addBotInfo(token, bot)
if err != nil {
log.Printf("Error: bot %s info couldn't be added: %v", bot.Me.Username, err)
}
return ErrBotConn
}
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
}
@@ -82,11 +101,11 @@ func sendMessage(user *tb.User, msg string) error {
return nil
}
func botStart() error {
func setBotHandlers() error {
if bot == nil {
return ErrNilPointer
}
log.Printf("Started %s", bot.Me.Username)
bot.Handle("/hello", func(m *tb.Message) {
bot.Send(m.Sender, "hello world")
})
@@ -94,7 +113,29 @@ func botStart() error {
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
}