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) {
|
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) {
|
func (ca CustomAction) Act(c *Context) {
|
||||||
ca(c)
|
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 (
|
import (
|
||||||
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
"log"
|
//"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The wrapper around Telegram API.
|
// The wrapper around Telegram API.
|
||||||
|
@ -41,36 +41,40 @@ func (bot *Bot) Run() error {
|
||||||
|
|
||||||
updates := bot.GetUpdatesChan(uc)
|
updates := bot.GetUpdatesChan(uc)
|
||||||
|
|
||||||
|
chans := make(map[SessionId] chan *Update)
|
||||||
for u := range updates {
|
for u := range updates {
|
||||||
|
if u.Message != nil {
|
||||||
// Create new session if the one does not exist
|
// Create new session if the one does not exist
|
||||||
// for this user.
|
// for this user.
|
||||||
sid := SessionId(u.Message.Chat.ID)
|
sid := SessionId(u.Message.Chat.ID)
|
||||||
if _, ok := bot.sessions[sid] ; !ok {
|
if _, ok := bot.sessions[sid] ; !ok {
|
||||||
bot.sessions.Add(sid)
|
bot.sessions.Add(sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
session := bot.sessions[sid]
|
|
||||||
ctx := &Context{
|
// The "start" command resets the bot
|
||||||
B: bot,
|
// by executing the Start Action.
|
||||||
S: session,
|
if u.Message.IsCommand() {
|
||||||
}
|
cmd := u.Message.Command()
|
||||||
|
if cmd == "start" {
|
||||||
// The "start" command resets the bot
|
// Getting current session and context.
|
||||||
// by executing the Start Action.
|
session := bot.sessions[sid]
|
||||||
if u.Message.IsCommand() {
|
ctx := &Context{
|
||||||
cmd := u.Message.Command()
|
B: bot,
|
||||||
if cmd == "start" {
|
Session: session,
|
||||||
bot.Start.Act(ctx)
|
}
|
||||||
|
|
||||||
|
chn := make(chan *Update)
|
||||||
|
chans[sid] = chn
|
||||||
|
// Starting the goroutine for the user.
|
||||||
|
go ctx.handleUpdateChan(chn)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
chn := chans[sid]
|
||||||
|
chn <- &u
|
||||||
screen := bot.Screens[session.CurrentScreenId]
|
}
|
||||||
err := screen.Render(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("screen rendering:", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -58,6 +58,15 @@ func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton {
|
||||||
return apix.NewInlineKeyboardButtonData(btn.Text, btn.Text)
|
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 {
|
func NewButtonRow(btns ...*Button) ButtonRow {
|
||||||
return btns
|
return btns
|
||||||
|
|
|
@ -1,27 +1,60 @@
|
||||||
package behx
|
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
|
// The type represents way to interact with user in
|
||||||
// handling functions. Is provided to Act() function always.
|
// handling functions. Is provided to Act() function always.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
S *Session
|
*Session
|
||||||
B *Bot
|
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.
|
// Changes screen of user to the Id one.
|
||||||
func (c *Context) ChangeScreen(screenId ScreenId) error {
|
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) {
|
if !c.B.ScreenExists(screenId) {
|
||||||
return ScreenNotExistErr
|
return ScreenNotExistErr
|
||||||
}
|
}
|
||||||
|
|
||||||
c.S.PreviousScreenId = c.S.CurrentScreenId
|
screen := c.B.Screens[screenId]
|
||||||
c.S.CurrentScreenId = screenId
|
screen.Render(c)
|
||||||
|
|
||||||
|
c.Session.ChangeScreen(screenId)
|
||||||
|
c.KeyboardId = screen.KeyboardId
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (kbd *Keyboard) buttonMap() ButtonMap {
|
||||||
ret := make(ButtonMap)
|
ret := make(ButtonMap)
|
||||||
for _, vi := range kbd.Rows {
|
for _, vi := range kbd.Rows {
|
||||||
for _, vj := range vi {
|
for _, vj := range vi {
|
||||||
ret[vj.Text] = vj
|
ret[vj.Key()] = vj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,9 @@ func (st ScreenText) String() string {
|
||||||
return string(st)
|
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 {
|
func (s *Screen) Render(c *Context) error {
|
||||||
id := c.S.Id.ToTelegram()
|
id := c.Id.ToTelegram()
|
||||||
|
|
||||||
msg := apix.NewMessage(id, s.Text.String())
|
msg := apix.NewMessage(id, s.Text.String())
|
||||||
|
|
||||||
|
@ -60,21 +60,27 @@ func (s *Screen) Render(c *Context) error {
|
||||||
return err
|
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]
|
// Replace keyboard with the new one.
|
||||||
if !ok {
|
if s.KeyboardId != "" {
|
||||||
return KeyboardNotExistErr
|
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 {
|
if _, err := c.B.Send(msg) ; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
package behx
|
package behx
|
||||||
|
|
||||||
|
import (
|
||||||
|
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Update = apix.Update
|
||||||
|
|
||||||
// Represents unique value to identify chats.
|
// Represents unique value to identify chats.
|
||||||
// In fact is simply ID of the chat.
|
// In fact is simply ID of the chat.
|
||||||
type SessionId int64
|
type SessionId int64
|
||||||
|
@ -7,9 +13,13 @@ type SessionId int64
|
||||||
// The type represents current state of
|
// The type represents current state of
|
||||||
// user interaction per each of them.
|
// user interaction per each of them.
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
// Unique identifier for the session, Telegram chat's ID.
|
||||||
Id SessionId
|
Id SessionId
|
||||||
|
// Current screen identifier.
|
||||||
CurrentScreenId ScreenId
|
CurrentScreenId ScreenId
|
||||||
|
// ID of the previous screen.
|
||||||
PreviousScreenId ScreenId
|
PreviousScreenId ScreenId
|
||||||
|
// The currently showed on display keyboard.
|
||||||
KeyboardId KeyboardId
|
KeyboardId KeyboardId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,13 +27,25 @@ type Session struct {
|
||||||
// as key.
|
// as key.
|
||||||
type SessionMap map[SessionId] *Session
|
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 {
|
func (si SessionId) ToTelegram() int64 {
|
||||||
return int64(si)
|
return int64(si)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add new empty session by it's ID.
|
||||||
func (sm SessionMap) Add(sid SessionId) {
|
func (sm SessionMap) Add(sid SessionId) {
|
||||||
sm[sid] = &Session{
|
sm[sid] = NewSession(sid)
|
||||||
Id: sid,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
var rootKbd = behx.NewKeyboard(
|
var rootKbd = behx.NewKeyboard(
|
||||||
behx.NewButtonRow(
|
behx.NewButtonRow(
|
||||||
behx.NewButton(
|
behx.NewButton(
|
||||||
"PRESS ME",
|
"1",
|
||||||
behx.NewCustomAction(func(c *behx.Context){
|
behx.NewCustomAction(func(c *behx.Context){
|
||||||
log.Println("pressed the button!")
|
log.Println("pressed the button!")
|
||||||
}),
|
}),
|
||||||
|
@ -21,9 +21,18 @@ var rootKbd = behx.NewKeyboard(
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
behx.NewButtonRow(
|
behx.NewButtonRow(
|
||||||
behx.NewButton("PRESS ME 3", behx.NewCustomAction(func(c *behx.Context){
|
behx.NewButton("To second screen", behx.NewScreenChange("second")),
|
||||||
log.Println("pressed third button!")
|
),
|
||||||
})),
|
)
|
||||||
|
|
||||||
|
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",
|
"root",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var secondScreen = behx.NewScreen(
|
||||||
|
"Second screen!",
|
||||||
|
"",
|
||||||
|
"second",
|
||||||
|
)
|
||||||
|
|
||||||
var behaviour = behx.NewBehaviour(
|
var behaviour = behx.NewBehaviour(
|
||||||
behx.NewScreenChange("start"),
|
behx.NewScreenChange("start"),
|
||||||
behx.ScreenMap{
|
behx.ScreenMap{
|
||||||
"start": startScreen,
|
"start": startScreen,
|
||||||
|
"second": secondScreen,
|
||||||
},
|
},
|
||||||
behx.KeyboardMap{
|
behx.KeyboardMap{
|
||||||
"root": rootKbd,
|
"root": rootKbd,
|
||||||
"inline": inlineKbd,
|
"inline": inlineKbd,
|
||||||
|
"second": secondKbd,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue