feat: another refactoring. Less code needed. No focus on marshalling now.

This commit is contained in:
Andrey Parhomenko 2024-07-21 18:02:47 +05:00
parent c576e891b8
commit 1471d7cdae
26 changed files with 205 additions and 260 deletions

13
beh.go
View file

@ -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
View file

@ -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.

View file

View file

@ -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,
})

View file

@ -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",

View file

@ -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

View file

@ -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,
)

View file

@ -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(

View file

@ -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,
})).SetRootWidget(
// Setting as the most top
// widget command handling
// so we can call them at any screen.
tg.NewCommandCompo().SetUsage(
UsageAction,
).SetPreStart(
PreStartAction,
).SetCommands(
BotCommands...,
),
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(
UsageAction,
).SetPreStart(
PreStartAction,
).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)
}
}

View file

@ -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",

View file

@ -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

View file

@ -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(),
),

View file

@ -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
}

View file

@ -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)
beh.Root.Serve(c)
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,52 +300,41 @@ 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)
if err != nil {
return err
}
} else {
return NoWidgetForScreenErr
// Running the new one.
c.session.skippedUpdates, err = c.runWidget(pth, arg)
if err != nil {
return err
}
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
View file

@ -0,0 +1,4 @@
#!/bin/sh
wgo sh -c './btest && ./exe/test'

View file

@ -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
View file

@ -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
}

View file

@ -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
}

View file

@ -10,7 +10,7 @@ type InvoiceCompo struct {
}
func (compo *InvoiceCompo) SendConfig(
sid SessionId, bot *Bot,
sid SessionID, bot *Bot,
) (*SendConfig) {
return nil
}

View file

@ -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,

View file

@ -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

View file

@ -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)

View file

@ -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,
}
}
}*/

View file

@ -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
}

View file

@ -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()

View file

@ -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
}