...
This commit is contained in:
parent
d7db586637
commit
564f1e8dc4
12 changed files with 383 additions and 2 deletions
56
src/behx/action.go
Normal file
56
src/behx/action.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
// Implementing the intereface lets you
|
||||||
|
// provide behaviour for the buttons etc.
|
||||||
|
type Action interface {
|
||||||
|
Act(*Bot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customized action for the bot.
|
||||||
|
type CustomAction func(*Bot)
|
||||||
|
|
||||||
|
// The type implements changing screen to the underlying ScreenId
|
||||||
|
type ScreenChange ScreenId
|
||||||
|
|
||||||
|
// Returns new ScreenChange.
|
||||||
|
func NewScreenChange(screen string) ScreenChange {
|
||||||
|
return ScreenChange(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns new CustomAction.
|
||||||
|
func NewCustomAction(fn func(*Bot)) CustomAction {
|
||||||
|
return CustomAction(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc ScreenChange) Act(c *Context) {
|
||||||
|
c.ChangeScreen(ScreenId(sc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ca CustomAction) Act(c *Context) {
|
||||||
|
ca(bot)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
29
src/behx/beh.go
Normal file
29
src/behx/beh.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
// The package implements
|
||||||
|
// behaviour for the Telegram bots.
|
||||||
|
|
||||||
|
|
||||||
|
// The type describes behaviour for the bot.
|
||||||
|
type Behaviour struct {
|
||||||
|
Start Action
|
||||||
|
Screens ScreenMap
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check whether the screen exists in the behaviour.
|
||||||
|
func (beh *Behaviour) ScreenExists(id ScreenId) bool {
|
||||||
|
_, ok := bot.behaviour.Screens[id]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the screen by it's ID.
|
||||||
|
func (beh *Behaviour) GetScreen(id ScreenId) *Screen {
|
||||||
|
if !beh.ScreenExists(id) {
|
||||||
|
panic(ScreenNotExistErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
screen := beh.Screens[id]
|
||||||
|
return screen
|
||||||
|
}
|
||||||
|
|
69
src/behx/bot.go
Normal file
69
src/behx/bot.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
import (
|
||||||
|
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The wrapper around Telegram API.
|
||||||
|
type Bot struct {
|
||||||
|
*apix.BotAPI
|
||||||
|
*Behaviour
|
||||||
|
sessions SessionMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the new bot for running the Behaviour.
|
||||||
|
func NewBot(token string, beh *Behaviour, sessions SessionMap) (*Bot, error) {
|
||||||
|
bot, err := tgbotapi.NewBotAPI(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make new sessions if no current are provided.
|
||||||
|
if sessions == nil {
|
||||||
|
sessions = make(SessionMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Bot{
|
||||||
|
BotAPI: bot,
|
||||||
|
Behaviour: beh,
|
||||||
|
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the bot with the Behaviour.
|
||||||
|
func (bot *Bot) Run() error {
|
||||||
|
bot.Debug = true
|
||||||
|
|
||||||
|
uc := tgbotapi.NewUpdate(0)
|
||||||
|
uc.Timeout = 60
|
||||||
|
|
||||||
|
updates := bot.GetUpdatesChan(uc)
|
||||||
|
|
||||||
|
for u := range updates {
|
||||||
|
// Create new session if the one does not exist
|
||||||
|
// for this user.
|
||||||
|
sid := SessionId(u.Message.Chat.ID)
|
||||||
|
if !bot.sessions.Exist(sid) {
|
||||||
|
bot.sessions.Add(sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
session := bot.sessions.Get(sid)
|
||||||
|
ctx := &beh.Context{
|
||||||
|
B: bot,
|
||||||
|
S: session,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "start" command resets the bot
|
||||||
|
// by executing the Start Action.
|
||||||
|
if u.MessageIsCommand() {
|
||||||
|
cmd := u.Message.Command()
|
||||||
|
if cmd == "start" {
|
||||||
|
bot.Start.Act(ctx)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
29
src/behx/button.go
Normal file
29
src/behx/button.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
import (
|
||||||
|
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The type wraps Telegram API's button to provide Action functionality.
|
||||||
|
type Button struct {
|
||||||
|
apix.KeyboardButton
|
||||||
|
Action Acter
|
||||||
|
}
|
||||||
|
|
||||||
|
type ButtonMap map[string] *Button
|
||||||
|
|
||||||
|
// Represents the reply button row.
|
||||||
|
type ButtonRow []*Button
|
||||||
|
|
||||||
|
// Returns new button with specified text and action.
|
||||||
|
func NewButton(text string, action Action) *Button {
|
||||||
|
return &Button{
|
||||||
|
KeyboardButton: apix.NewKeyboardButton(text)
|
||||||
|
Action: action,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewButtonRow(btns ...*Button) ButtonRow {
|
||||||
|
return btns
|
||||||
|
}
|
||||||
|
|
22
src/behx/context.go
Normal file
22
src/behx/context.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
// The type represents way to interact with user in
|
||||||
|
// handling functions. Is provided to Act() function always.
|
||||||
|
type Context struct {
|
||||||
|
S *Session
|
||||||
|
B *Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) ChangeScreen(screen ScreenId) error {
|
||||||
|
if !bot.ScreenExists(screenId) {
|
||||||
|
return ScreenNotExistErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Send(text string) error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
11
src/behx/errors.go
Normal file
11
src/behx/errors.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ScreenNotExistErr = errors.New("screen does not exist")
|
||||||
|
SessionNotExistErr = errors.New("session does not exist")
|
||||||
|
)
|
||||||
|
|
48
src/behx/keyboard.go
Normal file
48
src/behx/keyboard.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
import (
|
||||||
|
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
var otherKeyboard = tgbotapi.NewReplyKeyboard(
|
||||||
|
tgbotapi.NewKeyboardButtonRow(
|
||||||
|
tgbotapi.NewKeyboardButton("a"),
|
||||||
|
tgbotapi.NewKeyboardButton("b"),
|
||||||
|
tgbotapi.NewKeyboardButton("c"),
|
||||||
|
),
|
||||||
|
tgbotapi.NewKeyboardButtonRow(
|
||||||
|
tgbotapi.NewKeyboardButton("d"),
|
||||||
|
tgbotapi.NewKeyboardButton("e"),
|
||||||
|
tgbotapi.NewKeyboardButton("f"),
|
||||||
|
),
|
||||||
|
)*/
|
||||||
|
|
||||||
|
// The type represents reply keyboard which
|
||||||
|
// is supposed to be showed on a Screen.
|
||||||
|
type Keyboard struct {
|
||||||
|
rows []ButtonRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the new reply keyboard with rows as specified.
|
||||||
|
func NewKeyboard(rows ...ButtonRow) *Keyboard {
|
||||||
|
return &Keyboard{
|
||||||
|
rows: rows,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the Keyboard to the Telegram API type.
|
||||||
|
func (kbd *Keyboard) ToTelegram() ReplyKeyboardMarkup {
|
||||||
|
rows := [][]apix.KeyboardButton{}
|
||||||
|
for _, row := range kbd.rows {
|
||||||
|
buttons := []apix.KeyboardButton{}
|
||||||
|
for _, button := range row {
|
||||||
|
buttons = append(buttons, apix.NewKeyboardButton(button.text))
|
||||||
|
}
|
||||||
|
rows = append(rows, buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apix.NewReplyKeyboard(rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kbd *Keyboard) ButtonMap() ButtonMap
|
||||||
|
|
5
src/behx/main.go
Normal file
5
src/behx/main.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
// The package implements behaviourial
|
||||||
|
// definition for the Telegram bots through the API.
|
||||||
|
|
20
src/behx/map.go
Normal file
20
src/behx/map.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
type Map[K comparable, V any] map[K] V
|
||||||
|
|
||||||
|
func (m Map[K, V]) Exist(k K) bool {
|
||||||
|
_, ok := sm[sid]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Map[K, V]) Get(k K, v V) V {
|
||||||
|
ret := m[sid]
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (sm Map[K, V]) Set(v V) {
|
||||||
|
sm[sid] = &Session{
|
||||||
|
Id: sid,
|
||||||
|
}
|
||||||
|
}
|
31
src/behx/screen.go
Normal file
31
src/behx/screen.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
// Unique identifier for the screen.
|
||||||
|
type ScreenId string
|
||||||
|
|
||||||
|
// Should be replaced with something that can be
|
||||||
|
// dinamicaly rendered. (WIP)
|
||||||
|
type ScreenText string
|
||||||
|
|
||||||
|
// Screen statement of the bot.
|
||||||
|
// Mostly what buttons to show.
|
||||||
|
type Screen struct {
|
||||||
|
// Text to be sent to the user when changing to the screen.
|
||||||
|
Text ScreenText
|
||||||
|
// Keyboard to be displayed on the screen.
|
||||||
|
Keyboard *Keyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map structure for the screens.
|
||||||
|
type ScreenMap Map[ScreenId, *Screen]
|
||||||
|
map[ScreenId] *Screen
|
||||||
|
|
||||||
|
// Returns the new screen with specified Text and Keyboard.
|
||||||
|
func NewScreen(text ScreenText, kbd *Keyboard) *Screen {
|
||||||
|
return &Screen {
|
||||||
|
Text: text,
|
||||||
|
Keyboard: kbd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
19
src/behx/session.go
Normal file
19
src/behx/session.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package behx
|
||||||
|
|
||||||
|
// Represents unique value to identify chats.
|
||||||
|
// In fact is simply ID of the chat.
|
||||||
|
type SessionId int64
|
||||||
|
|
||||||
|
// The type represents current state of
|
||||||
|
// user interaction per each of them.
|
||||||
|
type Session struct {
|
||||||
|
Id SessionId
|
||||||
|
CurrentScreenId ScreenId
|
||||||
|
PreviousScreenId ScreenId
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type represents map of sessions using
|
||||||
|
// as key.
|
||||||
|
type SessionMap map[SessionId] *Session
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,9 +5,37 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
"boteval/src/cmd/behx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var startScreen = behx.NewScreen(
|
||||||
|
behx.NewKeyboard(
|
||||||
|
behx.NewButtonRow(
|
||||||
|
behx.NewButton(
|
||||||
|
"PRESS ME",
|
||||||
|
behx.NewCustomAction(func(c *behx.Context){
|
||||||
|
log.Println("pressed the button!")
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
behx.NewButton("PRESS ME 2", behx.NewCustomAction(func(c *behx.Context){
|
||||||
|
log.Println("pressed another button!")
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
behx.NewButtonRow(
|
||||||
|
behx.NewButton("PRESS ME 3", behx.NewCustomAction(func(c *behx.Context){
|
||||||
|
log.Println("pressed third button!")
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
var behaviour = behx.Behaviour{
|
||||||
|
StartAction: behx.NewScreenChange("start"),
|
||||||
|
Screens: behx.ScreenMap{
|
||||||
|
"start": startScreen,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
var numericKeyboard = tgbotapi.NewReplyKeyboard(
|
var numericKeyboard = tgbotapi.NewReplyKeyboard(
|
||||||
tgbotapi.NewKeyboardButtonRow(
|
tgbotapi.NewKeyboardButtonRow(
|
||||||
tgbotapi.NewKeyboardButton("1"),
|
tgbotapi.NewKeyboardButton("1"),
|
||||||
|
@ -22,6 +49,19 @@ var numericKeyboard = tgbotapi.NewReplyKeyboard(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var otherKeyboard = tgbotapi.NewReplyKeyboard(
|
||||||
|
tgbotapi.NewKeyboardButtonRow(
|
||||||
|
tgbotapi.NewKeyboardButton("a"),
|
||||||
|
tgbotapi.NewKeyboardButton("b"),
|
||||||
|
tgbotapi.NewKeyboardButton("c"),
|
||||||
|
),
|
||||||
|
tgbotapi.NewKeyboardButtonRow(
|
||||||
|
tgbotapi.NewKeyboardButton("d"),
|
||||||
|
tgbotapi.NewKeyboardButton("e"),
|
||||||
|
tgbotapi.NewKeyboardButton("f"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
token := os.Getenv("BOT_TOKEN")
|
token := os.Getenv("BOT_TOKEN")
|
||||||
|
|
||||||
|
@ -49,6 +89,8 @@ func main() {
|
||||||
switch update.Message.Text {
|
switch update.Message.Text {
|
||||||
case "open":
|
case "open":
|
||||||
msg.ReplyMarkup = numericKeyboard
|
msg.ReplyMarkup = numericKeyboard
|
||||||
|
case "letters" :
|
||||||
|
msg.ReplyMarkup = otherKeyboard
|
||||||
case "close":
|
case "close":
|
||||||
msg.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true)
|
msg.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue