feat: another refactoring. Less code needed. No focus on marshalling now.
This commit is contained in:
parent
c576e891b8
commit
1471d7cdae
26 changed files with 205 additions and 260 deletions
13
beh.go
13
beh.go
|
@ -4,13 +4,12 @@ package tg
|
|||
type Behaviour struct {
|
||||
Root Component
|
||||
Init Action
|
||||
Screens ScreenMap
|
||||
//Screens ScreenMap
|
||||
}
|
||||
|
||||
// Returns new empty behaviour.
|
||||
func NewBehaviour() *Behaviour {
|
||||
return &Behaviour{
|
||||
Screens: make(ScreenMap),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +20,11 @@ func (b *Behaviour) SetInit(a Action) *Behaviour {
|
|||
return b
|
||||
}
|
||||
|
||||
/*func (b *Behaviour) SetScreens(screens ScreenMap) *Behaviour {
|
||||
b.Screens = screens
|
||||
return b
|
||||
}
|
||||
|
||||
// Sets the root node of the Behaviour.
|
||||
// Mostly used for commands and such stuff.
|
||||
func (b *Behaviour) SetRootNode(node *RootNode) *Behaviour {
|
||||
|
@ -28,6 +32,7 @@ func (b *Behaviour) SetRootNode(node *RootNode) *Behaviour {
|
|||
return b
|
||||
}
|
||||
|
||||
*/
|
||||
// The function sets as the standard root widget CommandWidget
|
||||
// and its commands..
|
||||
func (b *Behaviour) SetRootWidget(root Component) *Behaviour {
|
||||
|
@ -35,6 +40,7 @@ func (b *Behaviour) SetRootWidget(root Component) *Behaviour {
|
|||
return b
|
||||
}
|
||||
|
||||
/*
|
||||
// Check whether the screen exists in the behaviour.
|
||||
func (beh *Behaviour) PathExist(pth Path) bool {
|
||||
_, ok := beh.Screens[pth]
|
||||
|
@ -43,7 +49,6 @@ func (beh *Behaviour) PathExist(pth Path) bool {
|
|||
|
||||
// Returns the screen by it's ID.
|
||||
func (beh *Behaviour) GetScreen(pth Path) *Screen {
|
||||
pth = pth.Clean()
|
||||
if !beh.PathExist(pth) {
|
||||
panic(ScreenNotExistErr)
|
||||
}
|
||||
|
@ -51,4 +56,4 @@ func (beh *Behaviour) GetScreen(pth Path) *Screen {
|
|||
screen := beh.Screens[pth]
|
||||
return screen
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
23
bot.go
23
bot.go
|
@ -43,7 +43,7 @@ func (bot *Bot) SetDebug(debug bool) *Bot {
|
|||
return bot
|
||||
}
|
||||
|
||||
func (bot *Bot) Api() *tgbotapi.BotAPI {
|
||||
func (bot *Bot) API() *tgbotapi.BotAPI {
|
||||
return bot.api
|
||||
}
|
||||
|
||||
|
@ -53,16 +53,16 @@ func (bot *Bot) Me() User {
|
|||
|
||||
// Send the Renderable to the specified session client side.
|
||||
// Can be used for both group and private sessions because
|
||||
// SessionId represents both for chat IDs.
|
||||
// SessionID represents both for chat IDs.
|
||||
func (bot *Bot) Send(
|
||||
sid SessionId, v Sendable,
|
||||
sid SessionID, v Sendable,
|
||||
) (*Message, error) {
|
||||
config := v.SendConfig(sid, bot)
|
||||
if config.Error != nil {
|
||||
return nil, config.Error
|
||||
}
|
||||
|
||||
msg, err := bot.api.Send(config.ToApi())
|
||||
msg, err := bot.api.Send(config.ToAPI())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func (bot *Bot) Send(
|
|||
}
|
||||
|
||||
func (bot *Bot) Sendf(
|
||||
sid SessionId, format string, v ...any,
|
||||
sid SessionID, format string, v ...any,
|
||||
) (*Message, error){
|
||||
return bot.Send(
|
||||
sid,
|
||||
|
@ -81,7 +81,7 @@ func (bot *Bot) Sendf(
|
|||
|
||||
// Send to the session specified its ID raw chattable from the tgbotapi.
|
||||
func (bot *Bot) SendRaw(
|
||||
sid SessionId, v tgbotapi.Chattable,
|
||||
sid SessionID, v tgbotapi.Chattable,
|
||||
) (*Message, error) {
|
||||
msg, err := bot.api.Send(v)
|
||||
if err != nil {
|
||||
|
@ -93,7 +93,7 @@ func (bot *Bot) SendRaw(
|
|||
// Get session by its ID. Can be used for any scope
|
||||
// including private, group and channel.
|
||||
func (bot *Bot) GotSession(
|
||||
sid SessionId,
|
||||
sid SessionID,
|
||||
) (*Session, bool) {
|
||||
session, ok := bot.sessions[sid]
|
||||
return session, ok
|
||||
|
@ -158,7 +158,7 @@ func (bot *Bot) SetCommands(
|
|||
|
||||
botCmds := []tgbotapi.BotCommand{}
|
||||
for _, cmd := range cmds {
|
||||
botCmds = append(botCmds, cmd.ToApi())
|
||||
botCmds = append(botCmds, cmd.ToAPI())
|
||||
}
|
||||
|
||||
//tgbotapi.NewBotCommandScopeAllPrivateChats(),
|
||||
|
@ -210,7 +210,7 @@ func (bot *Bot) Run() error {
|
|||
go bot.handleGroup(chn)
|
||||
}*/
|
||||
|
||||
me, _ := bot.Api().GetMe()
|
||||
me, _ := bot.API().GetMe()
|
||||
bot.me = me
|
||||
for up := range updates {
|
||||
u := Update{
|
||||
|
@ -238,11 +238,10 @@ func (bot *Bot) Run() error {
|
|||
// The function handles updates supposed for the private
|
||||
// chat with the bot.
|
||||
func (bot *Bot) handlePrivate(updates chan Update) {
|
||||
var sid SessionId
|
||||
var sid SessionID
|
||||
for u := range updates {
|
||||
sid = SessionId(u.FromChat().ID)
|
||||
sid = SessionID(u.FromChat().ID)
|
||||
session, sessionOk := bot.sessions[sid]
|
||||
|
||||
if u.Message != nil && !sessionOk {
|
||||
// Creating session if we have none
|
||||
// but only on text messages.
|
||||
|
|
|
@ -78,14 +78,14 @@ func (btn Button) WithSendLocation(ok bool) Button {
|
|||
return btn
|
||||
}
|
||||
|
||||
func (btn Button) Go(pth Path) Button {
|
||||
return btn.WithAction(ScreenGo{
|
||||
func (btn Button) Go(pth Widget) Button {
|
||||
return btn.WithAction(WidgetGo{
|
||||
Path: pth,
|
||||
})
|
||||
}
|
||||
|
||||
func (btn Button) GoWithArg(pth Path, arg any) Button {
|
||||
return btn.WithAction(ScreenGo{
|
||||
func (btn Button) GoWithArg(pth Widget, arg any) Button {
|
||||
return btn.WithAction(WidgetGo{
|
||||
Path: pth,
|
||||
Arg: arg,
|
||||
})
|
||||
|
|
|
@ -20,7 +20,7 @@ var BotCommands = []tg.Command{
|
|||
tg.NewCommand(
|
||||
"start",
|
||||
"start or restart the bot or move to the start screen",
|
||||
).Go(StartAbsPath),
|
||||
).Go(StartWidget),
|
||||
tg.NewCommand(
|
||||
"info",
|
||||
"info desc",
|
||||
|
|
|
@ -5,10 +5,9 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
IncDecPath tg.Path = "inc-dec"
|
||||
)
|
||||
|
||||
// A simple example widget to show
|
||||
// how to store and get session data values
|
||||
// and working with dynamic panels.
|
||||
var IncDecWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var (
|
||||
kbd *tg.InlineCompo
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"surdeus.su/core/tg"
|
||||
)
|
||||
|
||||
var HomeButton = tg.Buttonf("Home").Go("/")
|
||||
var BackButton = tg.Buttonf("Back").Go("-")
|
||||
var HomeButton = tg.Buttonf("Home").Go(StartWidget)
|
||||
var BackButton = tg.Buttonf("Back").Go(tg.Back)
|
||||
var BackKeyboard = tg.NewKeyboard().Row(
|
||||
BackButton,
|
||||
)
|
||||
|
|
|
@ -4,10 +4,6 @@ import (
|
|||
"surdeus.su/core/tg"
|
||||
)
|
||||
|
||||
const (
|
||||
LocationPath tg.Path = "location"
|
||||
)
|
||||
|
||||
var LocationWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.Messagef(
|
||||
|
|
|
@ -22,67 +22,36 @@ func ExtractSessionData(c tg.Context) *SessionData {
|
|||
var beh = tg.NewBehaviour().SetInit(tg.Func(func(c tg.Context) {
|
||||
// The session initialization.
|
||||
c.SetSessionData(&SessionData{})
|
||||
})).SetRootNode(tg.NewRootNode(
|
||||
// The "/" widget.
|
||||
StartWidget,
|
||||
|
||||
tg.NewNode(
|
||||
PanelPath,
|
||||
PanelWidget,
|
||||
),
|
||||
|
||||
tg.NewNode(
|
||||
MutateMessagesPath,
|
||||
MutateMessagesWidget,
|
||||
|
||||
tg.NewNode(
|
||||
UpperCasePath,
|
||||
MutateMessagesToUpperCaseWidget,
|
||||
),
|
||||
tg.NewNode(
|
||||
LowerCasePath,
|
||||
MutateMessagesToLowerCaseWidget,
|
||||
),
|
||||
tg.NewNode(
|
||||
EscapePath,
|
||||
MutateMessagesEscapeWidget,
|
||||
),
|
||||
),
|
||||
|
||||
tg.NewNode(
|
||||
IncDecPath,
|
||||
IncDecWidget,
|
||||
),
|
||||
|
||||
tg.NewNode(
|
||||
LocationPath,
|
||||
LocationWidget,
|
||||
),
|
||||
)).SetRootWidget(tg.NewCommandCompo().SetUsage(
|
||||
})).SetRootWidget(
|
||||
// Setting as the most top
|
||||
// widget command handling
|
||||
// so we can call them at any screen.
|
||||
tg.NewCommandCompo().SetUsage(
|
||||
UsageAction,
|
||||
).SetPreStart(
|
||||
).SetPreStart(
|
||||
PreStartAction,
|
||||
).SetCommands(
|
||||
).SetCommands(
|
||||
BotCommands...,
|
||||
))
|
||||
),
|
||||
)
|
||||
|
||||
func main() {
|
||||
token := os.Getenv("BOT_TOKEN")
|
||||
|
||||
bot, err := tg.NewBot(token)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
log.Fatalf("tg.NewBot(...): %s", err)
|
||||
}
|
||||
bot = bot.SetBehaviour(beh)
|
||||
//SetDebug(true)
|
||||
//bot.API().Debug = true
|
||||
|
||||
bot.SetData(&BotData{
|
||||
Name: "Jay",
|
||||
})
|
||||
|
||||
log.Printf("Authorized on account %s", bot.Api().Self.UserName)
|
||||
log.Printf("Authorized on account %s", bot.API().Self.UserName)
|
||||
err = bot.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatalf("bot.Run(...): %s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,22 +43,15 @@ func (w *MutateMessageCompo) Filter(u tg.Update) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
UpperCasePath tg.Path = "upper-case"
|
||||
LowerCasePath = "lower-case"
|
||||
EscapePath = "escape"
|
||||
MutateMessagesPath = "mutate-messages"
|
||||
)
|
||||
|
||||
var MutateMessagesWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var MutateMessagesWidget= tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.Messagef(
|
||||
"Choose widget to mutate strings",
|
||||
).Reply(
|
||||
tg.NewKeyboard().Row(
|
||||
tg.Buttonf("Upper case").Go(UpperCasePath),
|
||||
tg.Buttonf("Lower case").Go(LowerCasePath),
|
||||
tg.Buttonf("Escape chars").Go(EscapePath),
|
||||
tg.Buttonf("Upper case").Go(UpperCaseWidget),
|
||||
tg.Buttonf("Lower case").Go(LowerCaseWidget),
|
||||
tg.Buttonf("Escape chars").Go(EscapeWidget),
|
||||
).Row(
|
||||
BackButton,
|
||||
).Reply(),
|
||||
|
@ -66,7 +59,7 @@ var MutateMessagesWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
|||
}
|
||||
})
|
||||
|
||||
var MutateMessagesToLowerCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var LowerCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.Messagef(
|
||||
"Type a string and the bot will convert it to lower case",
|
||||
|
@ -77,7 +70,7 @@ var MutateMessagesToLowerCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
|||
}
|
||||
})
|
||||
|
||||
var MutateMessagesToUpperCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var UpperCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.Messagef(
|
||||
"Type a string and the bot will convert it to upper case",
|
||||
|
@ -87,7 +80,7 @@ var MutateMessagesToUpperCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
|||
NewMutateMessageCompo(strings.ToUpper),
|
||||
}
|
||||
})
|
||||
var MutateMessagesEscapeWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var EscapeWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.Messagef(
|
||||
"Type a string and the bot will escape characters in it",
|
||||
|
|
|
@ -4,11 +4,7 @@ import (
|
|||
"surdeus.su/core/tg"
|
||||
)
|
||||
|
||||
const (
|
||||
PanelPath tg.Path = "panel"
|
||||
)
|
||||
|
||||
var PanelWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var DynamicPanelWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
var (
|
||||
n = 0
|
||||
ln = 4
|
||||
|
|
|
@ -5,36 +5,30 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
StartAbsPath tg.Path = "/"
|
||||
)
|
||||
|
||||
var StartWidget = tg.RenderFunc(func(c tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.Messagef(
|
||||
fmt.Sprint(
|
||||
"Hello, %s!\n",
|
||||
"The testing bot started!\n",
|
||||
"Hello, %s!",
|
||||
"The testing bot started!",
|
||||
"You can see the basics of usage in the ",
|
||||
"cmd/test/main.go file!",
|
||||
"cmd/test/main.go file and other files in the cmd/test!",
|
||||
),
|
||||
c.CallbackUpdate().SentFrom().UserName,
|
||||
).Inline(
|
||||
tg.NewKeyboard().Row(
|
||||
tg.Buttonf("TeleGopher Vultras page").
|
||||
WithUrl("https://vultras.su/core/tg"),
|
||||
tg.Buttonf("TeleGopher surdeus.su page").
|
||||
WithUrl("https://surdeus.su/core/tg"),
|
||||
).Inline(),
|
||||
),
|
||||
|
||||
tg.Messagef("Choose your interest").Reply(
|
||||
tg.NewKeyboard().Row(
|
||||
tg.Buttonf("Inc/Dec").Go(IncDecPath),
|
||||
).Row(
|
||||
tg.Buttonf("Mutate messages").Go(MutateMessagesPath),
|
||||
).Row(
|
||||
tg.Buttonf("Send location").Go(LocationPath),
|
||||
).Row(
|
||||
tg.Buttonf("Dynamic panel").Go(PanelPath),
|
||||
tg.NewKeyboard().List(
|
||||
tg.Buttonf("Inc/Dec").Go(IncDecWidget),
|
||||
tg.Buttonf("Mutate messages").Go(MutateMessagesWidget),
|
||||
tg.Buttonf("Send location").Go(LocationWidget),
|
||||
tg.Buttonf("Dynamic panel").Go(DynamicPanelWidget),
|
||||
tg.Buttonf("Check panic").Go(nil),
|
||||
).Reply(),
|
||||
),
|
||||
|
||||
|
|
18
command.go
18
command.go
|
@ -4,6 +4,7 @@ import (
|
|||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
|
||||
type CommandType uint8
|
||||
const (
|
||||
PrivateCommandType CommandType = iota
|
||||
|
@ -44,7 +45,7 @@ func (c Command) WithWidget(w Widget) Command {
|
|||
}
|
||||
|
||||
// Convert command into the tgbotapi.BotCommand
|
||||
func (c Command) ToApi() tgbotapi.BotCommand {
|
||||
func (c Command) ToAPI() tgbotapi.BotCommand {
|
||||
ret := tgbotapi.BotCommand{}
|
||||
ret.Command = string(c.Name)
|
||||
ret.Description = c.Description
|
||||
|
@ -52,14 +53,14 @@ func (c Command) ToApi() tgbotapi.BotCommand {
|
|||
}
|
||||
|
||||
// Simple command to go to another screen.
|
||||
func (c Command) Go(pth Path) Command {
|
||||
return c.WithAction(ScreenGo{
|
||||
func (c Command) Go(pth Widget) Command {
|
||||
return c.WithAction(WidgetGo{
|
||||
Path: pth,
|
||||
})
|
||||
}
|
||||
|
||||
func (c Command) GoWithArg(pth Path, arg any) Command {
|
||||
return c.WithAction(ScreenGo{
|
||||
func (c Command) GoWithArg(pth Widget, arg any) Command {
|
||||
return c.WithAction(WidgetGo{
|
||||
Path: pth,
|
||||
Arg: arg,
|
||||
})
|
||||
|
@ -125,7 +126,7 @@ func (compo *CommandCompo) Serve(c Context) {
|
|||
// First should bring the new command into the action.
|
||||
c.Bot().DeleteCommands()
|
||||
err := c.Bot().SetCommands(
|
||||
tgbotapi.NewBotCommandScopeChat(c.SessionId().ToApi()),
|
||||
tgbotapi.NewBotCommandScopeChat(c.SessionID().ToAPI()),
|
||||
compo.Commands,
|
||||
)
|
||||
|
||||
|
@ -135,11 +136,12 @@ func (compo *CommandCompo) Serve(c Context) {
|
|||
|
||||
var cmdUpdates *UpdateChan
|
||||
for u := range c.Input() {
|
||||
if c.Path() == "" && u.Message != nil {
|
||||
if c.Path() == nil && u.Message != nil {
|
||||
// Skipping and executing the preinit action
|
||||
// while we have the empty screen.
|
||||
// E. g. the session did not start.
|
||||
if !(u.Message.IsCommand() && u.Message.Command() == "start") {
|
||||
if !u.Message.IsCommand() ||
|
||||
u.Message.Command() != "start" {
|
||||
c.WithUpdate(u).Run(compo.PreStart)
|
||||
continue
|
||||
}
|
||||
|
|
87
context.go
87
context.go
|
@ -30,7 +30,7 @@ type Context struct {
|
|||
// maybe you will find another usage for this.
|
||||
// Returns users context by specified session ID
|
||||
// or false if the user is not logged in.
|
||||
func (c Context) As(sid SessionId) (Context, bool) {
|
||||
func (c Context) As(sid SessionID) (Context, bool) {
|
||||
s, ok := c.Bot().GotSession(sid)
|
||||
if !ok {
|
||||
return Context{}, false
|
||||
|
@ -58,6 +58,9 @@ func (f Func) Render(_ Context) UI {
|
|||
}
|
||||
}
|
||||
|
||||
// The type represents type
|
||||
// of current context the processing is happening
|
||||
// in.
|
||||
type ContextType uint8
|
||||
const (
|
||||
NoContextType ContextType = iota
|
||||
|
@ -69,7 +72,14 @@ const (
|
|||
func (c Context) serve() {
|
||||
beh := c.Bot().behaviour
|
||||
c.Run(beh.Init)
|
||||
for {
|
||||
defer func(){
|
||||
if err := recover() ; err != nil {
|
||||
// Need to add some handling later.
|
||||
}
|
||||
}()
|
||||
beh.Root.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (c Context) Arg() any {
|
||||
|
@ -85,12 +95,12 @@ func (c Context) Run(a Action) {
|
|||
|
||||
// Sends to the Sendable object to the session user.
|
||||
func (c Context) Send(v Sendable) (*Message, error) {
|
||||
config := v.SendConfig(c.SessionId(), c.Bot())
|
||||
config := v.SendConfig(c.SessionID(), c.Bot())
|
||||
if config.Error != nil {
|
||||
return nil, config.Error
|
||||
}
|
||||
|
||||
msg, err := c.Bot().Api().Send(config.ToApi())
|
||||
msg, err := c.Bot().API().Send(config.ToAPI())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -172,14 +182,16 @@ func (c Context) CallbackUpdate() *Update {
|
|||
}
|
||||
|
||||
// Returns the reader for specified file ID and path.
|
||||
func (c Context) GetFile(fileId FileId) (io.ReadCloser, string, error) {
|
||||
file, err := c.Bot().Api().GetFile(tgbotapi.FileConfig{FileID:string(fileId)})
|
||||
func (c Context) GetFile(fileID FileID) (io.ReadCloser, string, error) {
|
||||
file, err := c.Bot().API().GetFile(tgbotapi.FileConfig{
|
||||
FileID: string(fileID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
r, err := http.Get(fmt.Sprintf(
|
||||
"https://api.telegram.org/file/bot%s/%s",
|
||||
c.Bot().Api().Token,
|
||||
c.Bot().API().Token,
|
||||
file.FilePath,
|
||||
))
|
||||
if err != nil {
|
||||
|
@ -193,8 +205,8 @@ func (c Context) GetFile(fileId FileId) (io.ReadCloser, string, error) {
|
|||
}
|
||||
|
||||
// Reads all the content from the specified file.
|
||||
func (c Context) ReadFile(fileId FileId) ([]byte, string, error) {
|
||||
file, pth, err := c.GetFile(fileId)
|
||||
func (c Context) ReadFile(fileID FileID) ([]byte, string, error) {
|
||||
file, pth, err := c.GetFile(fileID)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
@ -240,10 +252,9 @@ func (c Context) runWidget(widget Widget, arg any) (*UpdateChan, error) {
|
|||
return nil, EmptyWidgetErr
|
||||
}
|
||||
|
||||
pth := c.Path()
|
||||
compos := widget.Render(c.WithArg(arg))
|
||||
// Leave if changed path or components are empty.
|
||||
if compos == nil || pth != c.Path() {
|
||||
if compos == nil {
|
||||
return nil, EmptyCompoErr
|
||||
}
|
||||
chns := make([]*UpdateChan, len(compos))
|
||||
|
@ -289,53 +300,42 @@ func (c Context) runWidget(widget Widget, arg any) (*UpdateChan, error) {
|
|||
}
|
||||
|
||||
// Simple go without an argument for context.
|
||||
func (c Context) Go(pth Path) error {
|
||||
func (c Context) Go(pth Widget) error {
|
||||
return c.GoWithArg(pth, nil)
|
||||
}
|
||||
|
||||
// Changes screen of user to the Id one.
|
||||
// Also gives the arg to the widget calling
|
||||
// contexts.
|
||||
func (c Context) GoWithArg(pth Path, arg any) error {
|
||||
// Go to the specified widget with the
|
||||
// specificed argument.
|
||||
func (c Context) GoWithArg(pth Widget, arg any) error {
|
||||
var err error
|
||||
if pth == "" {
|
||||
c.session.pathHistory = []Path{}
|
||||
if pth == nil {
|
||||
c.session.pathHistory = []Widget{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var back bool
|
||||
if pth == "-" {
|
||||
if pth == Back {
|
||||
if len(c.session.pathHistory) < 2 {
|
||||
return c.GoWithArg("", arg)
|
||||
return c.GoWithArg(nil, arg)
|
||||
}
|
||||
pth = c.session.pathHistory[len(c.session.pathHistory)-2]
|
||||
c.session.pathHistory = c.session.pathHistory[:len(c.session.pathHistory)-1]
|
||||
}
|
||||
// Getting the screen and changing to
|
||||
// then executing its widget.
|
||||
if !pth.IsAbs() {
|
||||
pth = (c.Path() + "/" + pth).Clean()
|
||||
c.session.pathHistory =
|
||||
c.session.pathHistory[:len(c.session.pathHistory)-1]
|
||||
back = true
|
||||
}
|
||||
|
||||
if !c.PathExist(pth) {
|
||||
return ScreenNotExistErr
|
||||
}
|
||||
|
||||
if !back && c.Path() != pth {
|
||||
if !back {
|
||||
c.session.pathHistory = append(c.session.pathHistory, pth)
|
||||
}
|
||||
|
||||
// Stopping the current widget.
|
||||
screen := c.Bot().behaviour.Screens[pth]
|
||||
c.session.skippedUpdates.Close()
|
||||
|
||||
if screen.Widget != nil {
|
||||
c.session.skippedUpdates, err = c.runWidget(screen.Widget, arg)
|
||||
// Running the new one.
|
||||
c.session.skippedUpdates, err = c.runWidget(pth, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return NoWidgetForScreenErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -352,8 +352,8 @@ func (c Context) SessionData() any {
|
|||
return c.session.Data
|
||||
}
|
||||
|
||||
func (c Context) SessionId() SessionId {
|
||||
return c.session.Id
|
||||
func (c Context) SessionID() SessionID {
|
||||
return c.session.ID
|
||||
}
|
||||
|
||||
func (c Context) SessionScope() SessionScope {
|
||||
|
@ -372,20 +372,15 @@ func (c Context) Bot() *Bot {
|
|||
return c.session.bot
|
||||
}
|
||||
|
||||
// Returns true if the path exists and false otherwise.
|
||||
func (c Context) PathExist(pth Path) bool {
|
||||
return c.session.bot.behaviour.PathExist(pth)
|
||||
}
|
||||
|
||||
// Return context's session's path history.
|
||||
func (c Context) PathHistory() []Path {
|
||||
func (c Context) PathHistory() []Widget {
|
||||
return c.session.pathHistory
|
||||
}
|
||||
|
||||
func (c Context) Path() Path {
|
||||
func (c Context) Path() Widget {
|
||||
ln := len(c.session.pathHistory)
|
||||
if ln == 0 {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
return c.session.pathHistory[ln-1]
|
||||
}
|
||||
|
|
4
devel-loop
Executable file
4
devel-loop
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
wgo sh -c './btest && ./exe/test'
|
||||
|
7
file.go
7
file.go
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type FileID string
|
||||
type FileConfig = tgbotapi.FileConfig
|
||||
type PhotoConfig = tgbotapi.PhotoConfig
|
||||
type FileType int
|
||||
|
@ -107,10 +108,10 @@ func (f *File) SendData() string {
|
|||
}
|
||||
|
||||
func (f *File) SendConfig(
|
||||
sid SessionId, bot *Bot,
|
||||
sid SessionID, bot *Bot,
|
||||
) (SendConfig) {
|
||||
var config SendConfig
|
||||
cid := sid.ToApi()
|
||||
cid := sid.ToAPI()
|
||||
|
||||
switch f.Type() {
|
||||
case PhotoFileType:
|
||||
|
@ -119,7 +120,7 @@ func (f *File) SendConfig(
|
|||
|
||||
config.Chattable = photo
|
||||
case DocumentFileType:
|
||||
doc := tgbotapi.NewDocument(sid.ToApi(), f)
|
||||
doc := tgbotapi.NewDocument(sid.ToAPI(), f)
|
||||
doc.Caption = f.caption
|
||||
config.Chattable = doc
|
||||
default:
|
||||
|
|
29
go.go
29
go.go
|
@ -1,22 +1,33 @@
|
|||
package tg
|
||||
|
||||
func Go(pth Path) UI {
|
||||
func Go(pth Widget) UI {
|
||||
return UI{
|
||||
GoWidget(pth),
|
||||
WidgetGo{
|
||||
Path: pth,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type GoWidget string
|
||||
// Implementing the Server interface.
|
||||
func (widget GoWidget) Serve(c Context) {
|
||||
c.input.Close()
|
||||
c.Go(Path(widget))
|
||||
// The type implements changing current path to the widget.
|
||||
type WidgetGo struct {
|
||||
Path Widget
|
||||
Arg any
|
||||
}
|
||||
|
||||
func (widget GoWidget) Render(c Context) UI {
|
||||
func (sc WidgetGo) Act(c Context) {
|
||||
c.GoWithArg(sc.Path, sc.Arg)
|
||||
}
|
||||
|
||||
// Implementing the Server interface.
|
||||
func (widget WidgetGo) Serve(c Context) {
|
||||
c.input.Close()
|
||||
c.Go(widget)
|
||||
}
|
||||
|
||||
func (widget WidgetGo) Render(c Context) UI {
|
||||
return UI{widget}
|
||||
}
|
||||
|
||||
func (widget GoWidget) Filter(u Update) bool {
|
||||
func (widget WidgetGo) Filter(u Update) bool {
|
||||
return true
|
||||
}
|
||||
|
|
16
inline.go
16
inline.go
|
@ -11,7 +11,7 @@ type Inline struct {
|
|||
}
|
||||
|
||||
// Convert the inline keyboard to markup for the tgbotapi.
|
||||
func (kbd Inline) ToApi() tgbotapi.InlineKeyboardMarkup {
|
||||
func (kbd Inline) ToAPI() tgbotapi.InlineKeyboardMarkup {
|
||||
rows := [][]tgbotapi.InlineKeyboardButton{}
|
||||
for _, row := range kbd.Rows {
|
||||
if row == nil {
|
||||
|
@ -38,12 +38,12 @@ type InlineCompo struct {
|
|||
|
||||
// Implementing the Sendable interface.
|
||||
func (compo *InlineCompo) SendConfig(
|
||||
sid SessionId, bot *Bot,
|
||||
sid SessionID, bot *Bot,
|
||||
) (SendConfig) {
|
||||
sendConfig := compo.MessageCompo.SendConfig(sid, bot)
|
||||
msg := sendConfig.Chattable.(tgbotapi.MessageConfig)
|
||||
if len(compo.Inline.Rows) > 0 {
|
||||
msg.ReplyMarkup = compo.Inline.ToApi()
|
||||
msg.ReplyMarkup = compo.Inline.ToAPI()
|
||||
}
|
||||
sendConfig.Chattable = msg
|
||||
|
||||
|
@ -56,23 +56,23 @@ func (compo *InlineCompo) SendConfig(
|
|||
func (compo *InlineCompo) Update(c Context) error {
|
||||
if compo.Message != nil {
|
||||
var edit tgbotapi.Chattable
|
||||
markup := compo.Inline.ToApi()
|
||||
markup := compo.Inline.ToAPI()
|
||||
ln := len(markup.InlineKeyboard)
|
||||
if ln == 0 || compo.Inline.Rows == nil {
|
||||
edit = tgbotapi.NewEditMessageText(
|
||||
c.SessionId().ToApi(),
|
||||
c.SessionID().ToAPI(),
|
||||
compo.Message.MessageID,
|
||||
compo.Text,
|
||||
)
|
||||
} else {
|
||||
edit = tgbotapi.NewEditMessageTextAndMarkup(
|
||||
c.SessionId().ToApi(),
|
||||
c.SessionID().ToAPI(),
|
||||
compo.Message.MessageID,
|
||||
compo.Text,
|
||||
markup,
|
||||
)
|
||||
}
|
||||
msg, err := c.Bot().Api().Send(edit)
|
||||
msg, err := c.Bot().API().Send(edit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func (compo *InlineCompo) OnOneUpdate(c Context, u Update) error {
|
|||
)
|
||||
data := u.CallbackQuery.Data
|
||||
|
||||
_, err := c.Bot().Api().Request(cb)
|
||||
_, err := c.Bot().API().Request(cb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ type InvoiceCompo struct {
|
|||
}
|
||||
|
||||
func (compo *InvoiceCompo) SendConfig(
|
||||
sid SessionId, bot *Bot,
|
||||
sid SessionID, bot *Bot,
|
||||
) (*SendConfig) {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,9 +12,9 @@ type LocationCompo struct {
|
|||
}
|
||||
|
||||
func (compo *LocationCompo) SendConfig(
|
||||
sid SessionId, bot *Bot,
|
||||
sid SessionID, bot *Bot,
|
||||
) (SendConfig) {
|
||||
cid := sid.ToApi()
|
||||
cid := sid.ToAPI()
|
||||
location := tgbotapi.NewLocation(
|
||||
cid,
|
||||
compo.Latitude,
|
||||
|
|
12
message.go
12
message.go
|
@ -34,11 +34,11 @@ func Escape2(str string) string {
|
|||
// Call the function after the message was sent.
|
||||
func (compo *MessageCompo) Update(c Context) error {
|
||||
edit := tgbotapi.NewEditMessageText(
|
||||
c.Session().Id.ToApi(),
|
||||
c.Session().ID.ToAPI(),
|
||||
compo.Message.MessageID,
|
||||
compo.Text,
|
||||
)
|
||||
msg, err := c.Bot().Api().Send(edit)
|
||||
msg, err := c.Bot().API().Send(edit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -49,8 +49,8 @@ func (compo *MessageCompo) Update(c Context) error {
|
|||
// Calling the method removes the message on the client side
|
||||
// and sets the Message in the component to nil.
|
||||
func (compo *MessageCompo) Delete(c Context) error {
|
||||
cfg := tgbotapi.NewDeleteMessage(c.session.Id.ToApi(), compo.Message.MessageID)
|
||||
_, err := c.Bot().Api().Send(cfg)
|
||||
cfg := tgbotapi.NewDeleteMessage(c.session.ID.ToAPI(), compo.Message.MessageID)
|
||||
_, err := c.Bot().API().Send(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func (msg *MessageCompo) Location(
|
|||
|
||||
// Implementing the Sendable interface.
|
||||
func (compo *MessageCompo) SendConfig(
|
||||
sid SessionId, bot *Bot,
|
||||
sid SessionID, bot *Bot,
|
||||
) (SendConfig) {
|
||||
var (
|
||||
ret SendConfig
|
||||
|
@ -136,7 +136,7 @@ func (compo *MessageCompo) SendConfig(
|
|||
text = compo.Text
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(sid.ToApi(), text)
|
||||
msg := tgbotapi.NewMessage(sid.ToAPI(), text)
|
||||
msg.ParseMode = compo.ParseMode
|
||||
ret.Chattable = msg
|
||||
|
||||
|
|
2
reply.go
2
reply.go
|
@ -64,7 +64,7 @@ type ReplyCompo struct {
|
|||
|
||||
// Implementing the sendable interface.
|
||||
func (compo *ReplyCompo) SendConfig(
|
||||
sid SessionId, bot *Bot,
|
||||
sid SessionID, bot *Bot,
|
||||
) (SendConfig) {
|
||||
sendConfig := compo.MessageCompo.SendConfig(sid, bot)
|
||||
|
||||
|
|
66
screen.go
66
screen.go
|
@ -1,48 +1,32 @@
|
|||
package tg
|
||||
|
||||
import (
|
||||
"path"
|
||||
type WidgetSpecial int
|
||||
const (
|
||||
widgetEmpty WidgetSpecial = iota
|
||||
widgetBack
|
||||
)
|
||||
|
||||
// The type implements changing screen to the underlying ScreenId
|
||||
type ScreenGo struct {
|
||||
Path Path
|
||||
Arg any
|
||||
|
||||
func (w WidgetSpecial) Render(_ Context) UI {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc ScreenGo) Act(c Context) {
|
||||
c.GoWithArg(sc.Path, sc.Arg)
|
||||
}
|
||||
var (
|
||||
Back = Widget(widgetBack)
|
||||
)
|
||||
|
||||
// The same as Act.
|
||||
func (sc ScreenGo) Serve(c Context) {
|
||||
sc.Act(c)
|
||||
}
|
||||
|
||||
// Unique identifier for the screen
|
||||
// and relative paths to the screen.
|
||||
type Path string
|
||||
/*// Unique identifier for the screen.
|
||||
type Path int
|
||||
const (
|
||||
PathEmpty Path = 0
|
||||
// Going to the path returns
|
||||
// a context to the previous screen.
|
||||
PathBack Path = -1
|
||||
)
|
||||
|
||||
// Returns true if the path is empty.
|
||||
func (p Path) IsEmpty() bool {
|
||||
return p == ""
|
||||
}
|
||||
|
||||
// Returns true if the path is absolute.
|
||||
func (p Path) IsAbs() bool {
|
||||
if len(p) == 0 {
|
||||
return false
|
||||
}
|
||||
return p[0] == '/'
|
||||
}
|
||||
|
||||
func (p Path) Dir() Path {
|
||||
return Path(path.Dir(string(p)))
|
||||
}
|
||||
|
||||
// Clean the path deleting exceed ., .. and / .
|
||||
func (p Path) Clean() Path {
|
||||
return Path(path.Clean(string(p)))
|
||||
return p == 0
|
||||
}
|
||||
|
||||
// Screen statement of the bot.
|
||||
|
@ -64,10 +48,10 @@ type Node struct {
|
|||
Path Path
|
||||
Screen *Screen
|
||||
Subs []*Node
|
||||
}
|
||||
}*/
|
||||
|
||||
// Return new root node with the specified widget in the screen.
|
||||
func NewRootNode(widget Widget, subs ...*Node) *RootNode {
|
||||
/*func NewRootNode(widget Widget, subs ...*Node) *RootNode {
|
||||
ret := &RootNode{}
|
||||
ret.Screen = NewScreen(widget)
|
||||
ret.Subs = subs
|
||||
|
@ -114,15 +98,13 @@ func (n *Node) ScreenMap(root Path) ScreenMap {
|
|||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
}*/
|
||||
|
||||
// Map structure for the screens.
|
||||
type ScreenMap map[Path] *Screen
|
||||
|
||||
// Returns the new screen with specified name and widget.
|
||||
func NewScreen(widget Widget) *Screen {
|
||||
/*func NewScreen(widget Widget) *Screen {
|
||||
return &Screen{
|
||||
Widget: widget,
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
|
4
send.go
4
send.go
|
@ -10,7 +10,7 @@ type MessageId int64
|
|||
// way to define what message will be
|
||||
// sent to the side of a user.
|
||||
type Sendable interface {
|
||||
SendConfig(SessionId, *Bot) (SendConfig)
|
||||
SendConfig(SessionID, *Bot) (SendConfig)
|
||||
SetMessage(*Message)
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ type SendConfig struct {
|
|||
type MessageMap map[string] *Message
|
||||
|
||||
// Convert to the bot.Api.Send format.
|
||||
func (config SendConfig) ToApi() tgbotapi.Chattable {
|
||||
func (config SendConfig) ToAPI() tgbotapi.Chattable {
|
||||
return config.Chattable
|
||||
}
|
||||
|
||||
|
|
20
session.go
20
session.go
|
@ -2,12 +2,12 @@ package tg
|
|||
|
||||
// The type represents map of sessions using
|
||||
// as key.
|
||||
type SessionMap map[SessionId]*Session
|
||||
type SessionMap map[SessionID]*Session
|
||||
|
||||
// Add new empty session by it's ID.
|
||||
func (sm SessionMap) Add(
|
||||
bot *Bot,
|
||||
sid SessionId,
|
||||
sid SessionID,
|
||||
scope SessionScope,
|
||||
) *Session {
|
||||
ret := NewSession(bot, sid, scope)
|
||||
|
@ -27,32 +27,32 @@ const (
|
|||
|
||||
// Represents unique value to identify chats.
|
||||
// In fact is simply ID of the chat.
|
||||
type SessionId int64
|
||||
type SessionID int64
|
||||
|
||||
// Convert the SessionId to Telegram API's type.
|
||||
func (si SessionId) ToApi() int64 {
|
||||
// Convert the SessionID to Telegram API's type.
|
||||
func (si SessionID) ToAPI() int64 {
|
||||
return int64(si)
|
||||
}
|
||||
|
||||
// The type represents current state of
|
||||
// user interaction per each of them.
|
||||
type Session struct {
|
||||
// Id of the chat of the user.
|
||||
Id SessionId
|
||||
// ID of the chat of the user.
|
||||
ID SessionID
|
||||
Scope SessionScope
|
||||
// Custom value for each user.
|
||||
Data any
|
||||
|
||||
bot *Bot
|
||||
pathHistory []Path
|
||||
pathHistory []Widget
|
||||
skippedUpdates *UpdateChan
|
||||
updates *UpdateChan
|
||||
}
|
||||
|
||||
// Return new empty session.
|
||||
func NewSession(bot *Bot, id SessionId, scope SessionScope) *Session {
|
||||
func NewSession(bot *Bot, id SessionID, scope SessionScope) *Session {
|
||||
ret := &Session{}
|
||||
ret.Id = id
|
||||
ret.ID = id
|
||||
ret.Scope = scope
|
||||
ret.bot = bot
|
||||
ret.updates = NewUpdateChan()
|
||||
|
|
11
update.go
11
update.go
|
@ -2,7 +2,6 @@ package tg
|
|||
|
||||
import tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
|
||||
type FileId string
|
||||
|
||||
type Update struct {
|
||||
tgbotapi.Update
|
||||
|
@ -64,8 +63,8 @@ func (u Update) HasDocument() bool {
|
|||
u.Message.Document != nil
|
||||
}
|
||||
|
||||
func (u Update) DocumentId() FileId {
|
||||
return FileId(u.Update.Message.Document.FileID)
|
||||
func (u Update) DocumentID() FileID {
|
||||
return FileID(u.Update.Message.Document.FileID)
|
||||
}
|
||||
|
||||
func (u *Update) DocumentName() string {
|
||||
|
@ -85,10 +84,10 @@ func (u Update) HasPhotos() bool {
|
|||
len(u.Message.Photo) != 0
|
||||
}
|
||||
|
||||
func (u Update) PhotoIds() []FileId {
|
||||
ret := make([]FileId, len(u.Message.Photo))
|
||||
func (u Update) PhotoIDs() []FileID {
|
||||
ret := make([]FileID, len(u.Message.Photo))
|
||||
for i, photo := range u.Message.Photo {
|
||||
ret[i] = FileId(photo.FileID)
|
||||
ret[i] = FileID(photo.FileID)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue