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

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