Implemented goroutining for each user.
This commit is contained in:
parent
6dd954e8ad
commit
36f529df58
8 changed files with 154 additions and 84 deletions
|
@ -23,34 +23,13 @@ func NewCustomAction(fn func(*Context)) CustomAction {
|
|||
}
|
||||
|
||||
func (sc ScreenChange) Act(c *Context) {
|
||||
c.ChangeScreen(ScreenId(sc))
|
||||
err := c.ChangeScreen(ScreenId(sc))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ca CustomAction) Act(c *Context) {
|
||||
ca(c)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// The type describes interface
|
||||
// defining what should be done.
|
||||
type Actioner interface {
|
||||
Act(*bot)
|
||||
}
|
||||
|
||||
// Simple way to define that the button should just change
|
||||
// screen to the Id.
|
||||
type ScreenChanger ScreenId
|
||||
|
||||
// Custom function to be executed on button press.
|
||||
type ActionFunc func(*Bot)
|
||||
|
||||
func (a ActionFunc) Act(bot *Bot) {
|
||||
a(bot)
|
||||
}
|
||||
|
||||
func (sc ScreenChanger) Act(bot *Bot) {
|
||||
bot.ChangeScreenTo(sc)
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package behx
|
|||
|
||||
import (
|
||||
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"log"
|
||||
//"log"
|
||||
)
|
||||
|
||||
// The wrapper around Telegram API.
|
||||
|
@ -41,36 +41,40 @@ func (bot *Bot) Run() error {
|
|||
|
||||
updates := bot.GetUpdatesChan(uc)
|
||||
|
||||
chans := make(map[SessionId] chan *Update)
|
||||
for u := range updates {
|
||||
|
||||
// Create new session if the one does not exist
|
||||
// for this user.
|
||||
sid := SessionId(u.Message.Chat.ID)
|
||||
if _, ok := bot.sessions[sid] ; !ok {
|
||||
bot.sessions.Add(sid)
|
||||
}
|
||||
|
||||
session := bot.sessions[sid]
|
||||
ctx := &Context{
|
||||
B: bot,
|
||||
S: session,
|
||||
}
|
||||
|
||||
// The "start" command resets the bot
|
||||
// by executing the Start Action.
|
||||
if u.Message.IsCommand() {
|
||||
cmd := u.Message.Command()
|
||||
if cmd == "start" {
|
||||
bot.Start.Act(ctx)
|
||||
if u.Message != nil {
|
||||
// Create new session if the one does not exist
|
||||
// for this user.
|
||||
sid := SessionId(u.Message.Chat.ID)
|
||||
if _, ok := bot.sessions[sid] ; !ok {
|
||||
bot.sessions.Add(sid)
|
||||
}
|
||||
|
||||
|
||||
// The "start" command resets the bot
|
||||
// by executing the Start Action.
|
||||
if u.Message.IsCommand() {
|
||||
cmd := u.Message.Command()
|
||||
if cmd == "start" {
|
||||
// Getting current session and context.
|
||||
session := bot.sessions[sid]
|
||||
ctx := &Context{
|
||||
B: bot,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
chn := make(chan *Update)
|
||||
chans[sid] = chn
|
||||
// Starting the goroutine for the user.
|
||||
go ctx.handleUpdateChan(chn)
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
screen := bot.Screens[session.CurrentScreenId]
|
||||
err := screen.Render(ctx)
|
||||
if err != nil {
|
||||
log.Println("screen rendering:", err)
|
||||
}
|
||||
|
||||
chn := chans[sid]
|
||||
chn <- &u
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -58,6 +58,15 @@ func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton {
|
|||
return apix.NewInlineKeyboardButtonData(btn.Text, btn.Text)
|
||||
}
|
||||
|
||||
// Return the key of the button to identify it by messages and callbacks.
|
||||
func (btn *Button) Key() string {
|
||||
if btn.Data != "" {
|
||||
return btn.Data
|
||||
}
|
||||
|
||||
// If no match then return the data one with data the same as the text.
|
||||
return btn.Text
|
||||
}
|
||||
|
||||
func NewButtonRow(btns ...*Button) ButtonRow {
|
||||
return btns
|
||||
|
|
|
@ -1,27 +1,60 @@
|
|||
package behx
|
||||
|
||||
import (
|
||||
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type Update = apix.Update
|
||||
|
||||
// The type represents way to interact with user in
|
||||
// handling functions. Is provided to Act() function always.
|
||||
type Context struct {
|
||||
S *Session
|
||||
*Session
|
||||
B *Bot
|
||||
U *Update
|
||||
}
|
||||
|
||||
// Goroutie function to handle each user.
|
||||
func (ctx *Context) handleUpdateChan(updates chan *Update) {
|
||||
bot := ctx.B
|
||||
session := ctx.Session
|
||||
bot.Start.Act(ctx)
|
||||
for u := range updates {
|
||||
if u.Message != nil {
|
||||
screen := bot.Screens[session.CurrentScreenId]
|
||||
|
||||
kbd := bot.Keyboards[screen.KeyboardId]
|
||||
btns := kbd.buttonMap()
|
||||
|
||||
text := u.Message.Text
|
||||
btn, ok := btns[text]
|
||||
|
||||
// Skipping wrong text messages.
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
btn.Action.Act(ctx)
|
||||
} else if u.CallbackQuery != nil {
|
||||
cb := apix.NewCallback(u.CallbackQuery.ID, u.CallbackQuery.Data)
|
||||
|
||||
_, err := bot.Request(cb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Changes screen of user to the Id one.
|
||||
func (c *Context) ChangeScreen(screenId ScreenId) error {
|
||||
// Return if it will not change anything.
|
||||
if c.CurrentScreenId == screenId {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !c.B.ScreenExists(screenId) {
|
||||
return ScreenNotExistErr
|
||||
}
|
||||
|
||||
c.S.PreviousScreenId = c.S.CurrentScreenId
|
||||
c.S.CurrentScreenId = screenId
|
||||
screen := c.B.Screens[screenId]
|
||||
screen.Render(c)
|
||||
|
||||
c.Session.ChangeScreen(screenId)
|
||||
c.KeyboardId = screen.KeyboardId
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func (kbd *Keyboard) buttonMap() ButtonMap {
|
|||
ret := make(ButtonMap)
|
||||
for _, vi := range kbd.Rows {
|
||||
for _, vj := range vi {
|
||||
ret[vj.Text] = vj
|
||||
ret[vj.Key()] = vj
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ func (st ScreenText) String() string {
|
|||
return string(st)
|
||||
}
|
||||
|
||||
// Renders output of the screen to the side of the user.
|
||||
// Renders output of the screen only to the side of the user.
|
||||
func (s *Screen) Render(c *Context) error {
|
||||
id := c.S.Id.ToTelegram()
|
||||
id := c.Id.ToTelegram()
|
||||
|
||||
msg := apix.NewMessage(id, s.Text.String())
|
||||
|
||||
|
@ -60,21 +60,27 @@ func (s *Screen) Render(c *Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if s.KeyboardId != "" {
|
||||
msg = apix.NewMessage(id, ">")
|
||||
msg = apix.NewMessage(id, ">")
|
||||
// Checking if we need to resend the keyboard.
|
||||
if s.KeyboardId != c.KeyboardId {
|
||||
// Remove keyboard by default.
|
||||
var tkbd any
|
||||
tkbd = apix.NewRemoveKeyboard(true)
|
||||
|
||||
kbd, ok := c.B.Keyboards[s.KeyboardId]
|
||||
if !ok {
|
||||
return KeyboardNotExistErr
|
||||
// Replace keyboard with the new one.
|
||||
if s.KeyboardId != "" {
|
||||
kbd, ok := c.B.Keyboards[s.KeyboardId]
|
||||
if !ok {
|
||||
return KeyboardNotExistErr
|
||||
}
|
||||
tkbd = kbd.ToTelegram()
|
||||
}
|
||||
|
||||
msg.ReplyMarkup = kbd.ToTelegram()
|
||||
msg.ReplyMarkup = tkbd
|
||||
if _, err := c.B.Send(msg) ; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package behx
|
||||
|
||||
import (
|
||||
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type Update = apix.Update
|
||||
|
||||
// Represents unique value to identify chats.
|
||||
// In fact is simply ID of the chat.
|
||||
type SessionId int64
|
||||
|
@ -7,9 +13,13 @@ type SessionId int64
|
|||
// The type represents current state of
|
||||
// user interaction per each of them.
|
||||
type Session struct {
|
||||
// Unique identifier for the session, Telegram chat's ID.
|
||||
Id SessionId
|
||||
// Current screen identifier.
|
||||
CurrentScreenId ScreenId
|
||||
// ID of the previous screen.
|
||||
PreviousScreenId ScreenId
|
||||
// The currently showed on display keyboard.
|
||||
KeyboardId KeyboardId
|
||||
}
|
||||
|
||||
|
@ -17,13 +27,25 @@ type Session struct {
|
|||
// as key.
|
||||
type SessionMap map[SessionId] *Session
|
||||
|
||||
// Return new empty session with
|
||||
func NewSession(id SessionId) *Session {
|
||||
return &Session{
|
||||
Id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Changes screen of user to the Id one for the session.
|
||||
func (c *Session) ChangeScreen(screenId ScreenId) {
|
||||
c.PreviousScreenId = c.CurrentScreenId
|
||||
c.CurrentScreenId = screenId
|
||||
}
|
||||
|
||||
// Convert the SessionId to Telegram API's type.
|
||||
func (si SessionId) ToTelegram() int64 {
|
||||
return int64(si)
|
||||
}
|
||||
|
||||
// Add new empty session by it's ID.
|
||||
func (sm SessionMap) Add(sid SessionId) {
|
||||
sm[sid] = &Session{
|
||||
Id: sid,
|
||||
}
|
||||
sm[sid] = NewSession(sid)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
var rootKbd = behx.NewKeyboard(
|
||||
behx.NewButtonRow(
|
||||
behx.NewButton(
|
||||
"PRESS ME",
|
||||
"1",
|
||||
behx.NewCustomAction(func(c *behx.Context){
|
||||
log.Println("pressed the button!")
|
||||
}),
|
||||
|
@ -21,9 +21,18 @@ var rootKbd = behx.NewKeyboard(
|
|||
})),
|
||||
),
|
||||
behx.NewButtonRow(
|
||||
behx.NewButton("PRESS ME 3", behx.NewCustomAction(func(c *behx.Context){
|
||||
log.Println("pressed third button!")
|
||||
})),
|
||||
behx.NewButton("To second screen", behx.NewScreenChange("second")),
|
||||
),
|
||||
)
|
||||
|
||||
var secondKbd = behx.NewKeyboard(
|
||||
behx.NewButtonRow(
|
||||
behx.NewButton(
|
||||
"❤",
|
||||
behx.NewCustomAction(func(c *behx.Context){
|
||||
log.Println("pressed the button!")
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -53,14 +62,22 @@ var startScreen = behx.NewScreen(
|
|||
"root",
|
||||
)
|
||||
|
||||
var secondScreen = behx.NewScreen(
|
||||
"Second screen!",
|
||||
"",
|
||||
"second",
|
||||
)
|
||||
|
||||
var behaviour = behx.NewBehaviour(
|
||||
behx.NewScreenChange("start"),
|
||||
behx.ScreenMap{
|
||||
"start": startScreen,
|
||||
"second": secondScreen,
|
||||
},
|
||||
behx.KeyboardMap{
|
||||
"root": rootKbd,
|
||||
"inline": inlineKbd,
|
||||
"second": secondKbd,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue